Babylon JS 2일차 : Babylon.js 기초 뼈대 - 엔진/씬/렌더루프 감 잡기

Babylon JS 2일차 : Babylon.js 기초 뼈대 - 엔진/씬/렌더루프 감 잡기

한눈에 보는 요약

Babylon.js 앱은 크게 네 단계, 즉 캔버스 준비 → 엔진 생성 → 씬 생성 → 렌더 루프 및 resize 처리만 이해하면 기본 뼈대는 거의 완성됩니다. 이 구조는 이후에 카메라, 조명, 메쉬를 얼마나 복잡하게 올리더라도 변하지 않는 “골격” 역할을 합니다.

엔진(Engine)은 WebGL을 직접 다루는 대신, Babylon.js가 화면을 그릴 수 있도록 도와주는 그래픽 엔진이고, 씬(Scene)은 그 위에 올리는 3D 세계(무대)입니다. runRenderLoop는 매 프레임마다 씬을 다시 그리는 게임 루프이고, resize 처리는 브라우저 창 크기가 바뀌었을 때 화질과 비율이 깨지지 않도록 맞춰주는 역할을 합니다.

이 글에서는 이 네 가지 요소를 중심으로 최소 동작 템플릿(“내 기본 템플릿”)을 만들고, 앞으로의 모든 Babylon.js 예제에 재사용할 수 있는 기초 구조를 함께 완성해 보겠습니다.

목차


핵심 포인트

  • Babylon.js 앱은 기본적으로 Engine → Scene → renderLoop → resize 네 가지 축으로 구성됩니다.
  • Engine은 WebGL 컨텍스트를 관리하며, 모든 렌더링의 출발점입니다.
  • Scene은 카메라, 조명, 메쉬(오브젝트)를 담는 컨테이너로, “무대 하나”라고 이해하면 쉽습니다.
  • engine.runRenderLoop()는 매 프레임마다 scene.render()를 호출해 애니메이션과 인터랙션을 부드럽게 유지합니다.
  • window.resize 이벤트에서 engine.resize()를 호출해야 브라우저 창 크기 변경 시 캔버스 해상도와 비율이 깨지지 않습니다.
  • 이 네 가지를 묶어 “내 기본 템플릿”으로 만들어 두면, 이후 예제에서는 씬 내용에만 집중하면서 빠르게 개발할 수 있습니다.

상세 설명

1. Babylon.js 앱의 전체 흐름

Babylon.js로 화면을 그릴 때의 전체 흐름은 크게 보면 다음과 같습니다.

  • HTML에서 렌더링 대상이 될 <canvas> 요소를 준비합니다.
  • 자바스크립트에서 이 캔버스를 이용해 BABYLON.Engine 인스턴스를 생성합니다.
  • 엔진을 기반으로 BABYLON.Scene을 만들고, 여기에 카메라·조명·메쉬 등을 추가합니다.
  • engine.runRenderLoop() 안에서 매 프레임마다 scene.render()를 호출합니다.
  • window.addEventListener("resize", ...)로 창 크기 변경을 감지해 engine.resize()를 호출합니다.

즉, HTML에서는 “그릴 공간”만 마련해 두고, 실제로 무엇을 어떻게 그릴지는 모두 Babylon.js 엔진과 씬, 그리고 렌더 루프에서 담당한다고 이해하시면 됩니다.

2. Engine과 Scene, 그리고 렌더 루프

Engine은 WebGL 컨텍스트를 잡고, GPU와의 연결을 관리하는 가장 하위 레벨의 Babylon.js 객체입니다. 보통 한 페이지에 하나의 Engine을 두고, 그 엔진 위에 여러 개의 Scene을 만들 수도 있습니다. 하지만 처음에는 “엔진 하나 + 씬 하나” 구조로 시작하는 것이 가장 이해하기 쉽습니다.

Scene은 카메라, 빛, 메쉬, 물리 엔진 등 3D 세계에 필요한 요소들을 묶어서 관리하는 단위입니다. 예를 들어, 게임에서 “맵 하나” 또는 “레벨 하나” 정도로 생각해 볼 수 있습니다. 씬이 없으면 카메라와 메쉬를 넣을 곳이 없으므로, 어떤 객체를 추가하기 전에 항상 먼저 Scene을 생성해야 합니다.

runRenderLoop는 Babylon.js가 제공하는 렌더링 루프입니다. 내부적으로는 브라우저의 requestAnimationFrame을 사용하며, 프레임이 갱신될 때마다 등록된 콜백을 호출해 씬을 다시 그립니다. 이 구조 덕분에 애니메이션, 물리 연산, 사용자 입력 반응 등이 모두 자연스럽게 매 프레임마다 업데이트될 수 있습니다.

3. resize 처리의 중요성

Canvas 기반 WebGL 앱은 초기 크기만 맞춰놓고 resize를 처리하지 않으면, 브라우저 창을 늘리거나 줄일 때 다음과 같은 문제가 생깁니다.

  • 화면이 늘어나거나 줄어들며 비율이 깨져 보입니다.
  • 실제 캔버스 해상도는 그대로인데 CSS만 늘어나, 화면이 흐릿하게 보일 수 있습니다.
  • 모바일·태블릿 환경에서 회전(가로/세로)이 일어날 때 화면이 비정상적으로 잘릴 수 있습니다.

Babylon.js에서 이 문제를 해결하는 방법은 간단합니다. windowresize 이벤트에 리스너를 등록하고, 그 안에서 engine.resize()를 한번만 호출해 주면 됩니다. 그러면 엔진이 내부적으로 캔버스 크기와 카메라의 투영 설정을 적절히 다시 계산해 줍니다.


최소 동작 템플릿 코드 예시

이제 실제로 동작하는 “최소한의 Babylon.js 템플릿” 예시를 살펴보겠습니다. 아래 코드를 그대로 HTML 파일로 저장해 실행해 보시면, 회전 가능한 기본 구(공)이 하나 등장하는 3D 씬을 확인할 수 있습니다.

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="utf-8">
  <title>Babylon.js 기본 템플릿</title>
  <style>
    html, body {
      width: 100%;
      height: 100%;
      margin: 0;
      overflow: hidden;
    }
    #renderCanvas {
      width: 100%;
      height: 100%;
      touch-action: none;
    }
  </style>
  <!-- Babylon.js CDN -->
  <script src="https://cdn.babylonjs.com/babylon.js"></script>
</head>
<body>
  <canvas id="renderCanvas"></canvas>

  <script>
    // 1) 캔버스와 엔진 생성
    const canvas = document.getElementById("renderCanvas");
    const engine = new BABYLON.Engine(canvas, true);

    // 2) 씬 생성 함수 정의
    function createScene() {
      const scene = new BABYLON.Scene(engine);

      // 카메라
      const camera = new BABYLON.ArcRotateCamera(
        "camera",
        Math.PI / 2,
        Math.PI / 3,
        4,
        BABYLON.Vector3.Zero(),
        scene
      );
      camera.attachControl(canvas, true);

      // 조명
      const light = new BABYLON.HemisphericLight(
        "light",
        new BABYLON.Vector3(0, 1, 0),
        scene
      );

      // 메쉬(구)
      const sphere = BABYLON.MeshBuilder.CreateSphere(
        "sphere",
        { diameter: 1 },
        scene
      );

      return scene;
    }

    // 3) 씬 생성
    const scene = createScene();

    // 4) 렌더 루프
    engine.runRenderLoop(function () {
      scene.render();
    });

    // 5) 창 크기 변경 대응
    window.addEventListener("resize", function () {
      engine.resize();
    });
  </script>
</body>
</html>

위 예시에서 1~3번까지는 “초기화 단계”이고, 4번의 runRenderLoop와 5번의 resize 이벤트 처리가 실제로 화면을 계속 업데이트하고, 창 크기 변화에도 안정적으로 대응하는 “운영 단계”입니다. 앞으로 이 파일을 복사해 이름만 바꾸고, createScene() 내부의 카메라·조명·메쉬만 바꿔주면 손쉽게 새로운 예제를 만들 수 있습니다.


비교: Engine / Scene / runRenderLoop / resize

아래 표는 이 글의 핵심 개념인 Engine, Scene, runRenderLoop, resize의 역할과 사용 위치를 한 번에 비교한 것입니다.

요소 역할 언제 생성/사용? 대표 코드 주의할 점
Engine WebGL 컨텍스트 관리, 렌더링의 출발점 페이지 초기 로드 시 1회 생성 const engine = new BABYLON.Engine(canvas, true); 보통 한 페이지에 하나만 생성하는 것이 좋습니다.
Scene 카메라, 조명, 메쉬를 담는 3D 세계 엔진 생성 후, 필요한 씬마다 생성 const scene = new BABYLON.Scene(engine); 씬을 만들지 않으면 아무것도 렌더링되지 않습니다.
runRenderLoop 매 프레임마다 씬을 렌더링하는 루프 씬 생성 후, 한 번만 등록 engine.runRenderLoop(() => scene.render()); 루프 안에서 무거운 연산을 과도하게 넣지 않도록 주의합니다.
resize 창 크기 변화에 맞게 캔버스 크기/해상도 조정 초기화 시 이벤트 리스너 등록 window.addEventListener("resize", () => engine.resize()); 등록을 잊으면 반응형 레이아웃에서 화면이 깨져 보일 수 있습니다.

실행 단계: 내 Babylon.js 기본 템플릿 만들기

  1. HTML 파일 생성 및 기본 구조 작성
    개발 폴더에 babylon-day2.html 같은 이름으로 새 파일을 만들고, <!DOCTYPE html>, <html>, <head>, <body> 기본 구조를 작성합니다. 이 단계에서 뼈대를 정확히 잡아두면 이후에 Babylon.js 외 다른 라이브러리를 섞어 쓸 때도 구조를 유지하기가 쉽습니다.
  2. canvas 요소와 스타일 지정
    <body> 안에 <canvas id="renderCanvas"></canvas>를 추가하고, CSS에서 html, body, #renderCanvas의 너비와 높이를 100%로 맞춰 전체 화면이 캔버스로 꽉 차도록 설정합니다. 모바일 환경까지 고려한다면 overflow: hidden; 처리도 함께 해 두는 것이 좋습니다.
  3. Babylon.js CDN 스크립트 연결
    <head> 영역에 <script src="https://cdn.babylonjs.com/babylon.js">를 추가합니다. 학습 단계에서는 CDN을 사용하는 것이 가장 빠르고 간편하며, 이후 프로젝트 규모가 커지면 NPM 번들링으로 전환해도 무방합니다.
  4. 엔진 및 씬 생성 코드 작성
    <script> 블록 안에서 const canvas = ..., const engine = ...로 엔진을 생성한 뒤, 별도의 createScene() 함수를 만들어 씬을 생성하고 카메라와 조명, 간단한 메쉬(예: 구, 박스)를 추가합니다. 이 함수는 앞으로도 계속 재사용할 예정이므로, 구조를 깔끔하게 유지하는 것이 중요합니다.
  5. runRenderLoop와 resize 이벤트 등록
    씬을 생성한 뒤 engine.runRenderLoop(() => scene.render());를 호출해 렌더 루프를 설정합니다. 바로 이어서 window.addEventListener("resize", () => engine.resize());를 등록해 창 크기 변화에도 대응하도록 합니다. 이 두 줄이 실제로 “애니메이션이 돈다 / 화면이 반응형으로 유지된다”를 보장하는 핵심입니다.
  6. “내 기본 템플릿”으로 저장하고 복사해 사용하기
    모든 설정이 잘 동작하는 것을 확인했다면, 이 파일을 복사해 다른 이름으로 저장하면서 createScene() 내부만 바꿔보십시오. 예를 들어 Day 3에서는 다른 모양의 메쉬를 추가하고, Day 4에서는 머티리얼과 텍스처를 변경하는 식으로 확장할 수 있습니다.

추가로 생각해볼 점

  • 지금은 하나의 씬만 사용했지만, 복잡한 프로젝트에서는 로딩 씬, 게임 씬, UI 씬 등 여러 씬을 만들고 상황에 따라 전환하는 패턴을 사용할 수 있습니다.
  • 엔진과 씬 생성 코드를 별도 자바스크립트 파일로 분리하고 모듈화해 두면, 팀 프로젝트나 장기 프로젝트에서 유지보수가 훨씬 수월해집니다.
  • Babylon.js는 TypeScript 지원이 매우 좋기 때문에, 일정 수준 이상 복잡해지면 타입 정의를 활용해 안전하게 개발하는 것도 고려할 만합니다.
Reactions

댓글 쓰기

0 댓글