BabylonJS 기초 5일차 : 기본 메시(Primitive) 만들기와 변형



BabylonJS 기초 5일차 : 기본 메시(Primitive) 만들기와 변형


한눈에 보는 요약

이 글의 목표는 BabylonJS에서 가장 많이 사용하는 박스, 구, 원통과 같은 기본 메시(Primitive)를 생성하고, 위치·회전·스케일을 조정하며, 부모(Parent)를 활용해 구조적으로 오브젝트를 제어하는 전체 흐름을 이해하는 것입니다.

Babylon.MeshBuilder를 사용하면 한 줄의 코드로 다양한 기본 메시를 만들 수 있고, position/rotation/scaling 속성을 이용해 3D 공간에서 원하는 곳에 배치하고 크기와 방향을 바꿀 수 있습니다.

실습의 최종 산출물은 “회전하는 큐브 + UI 슬라이더로 회전 속도 조절” 장면입니다. 아래 예제 코드를 그대로 복사해 실행해 보시면, 초보자도 손쉽게 BabylonJS 메시와 애니메이션의 기초를 익히실 수 있습니다.

목차

  • 1. BabylonJS에서 메시란 무엇인가
  • 2. MeshBuilder로 기본 메시 만들기
  • 3. position / rotation / scaling / parent 이해
  • 4. 회전 애니메이션과 속도 조절 UI 설계
  • 5. 전체 코드 예제: 회전하는 큐브 + 속도 슬라이더
  • 6. 기본 메시 정리 표
  • 7. 실습 단계 가이드

핵심 포인트

  • BabylonJS에서 메시(Mesh)는 화면에 보이는 3D 객체의 형상을 담당하며, 카메라·빛·재질과 함께 장면의 기본 구성 요소입니다.
  • Babylon.MeshBuilder는 박스, 구, 원통, 평면 등 다양한 기본 메시를 간단한 함수 호출로 생성할 수 있는 유틸리티입니다.
  • position, rotation, scaling은 각각 메시의 위치, 회전, 크기를 나타내며, Vector3 값으로 축별(X, Y, Z) 제어가 가능합니다.
  • parent를 설정하면 자식 메시가 부모 메시를 기준으로 상대적인 위치·회전·스케일을 가지므로, 복잡한 구조를 하나의 덩어리처럼 움직일 수 있습니다.
  • 엔진의 렌더 루프(runRenderLoop 또는 onBeforeRenderObservable)를 활용해 매 프레임마다 rotation 값을 변경하면 자연스러운 회전 애니메이션을 만들 수 있습니다.
  • HTML range 입력이나 간단한 UI를 연결하면 사용자가 실시간으로 회전 속도(또는 방향)를 조절하는 인터랙티브 데모를 만들 수 있습니다.


상세 설명

1. BabylonJS에서 메시(Mesh)란 무엇인가

BabylonJS에서 메시(Mesh)는 3D 공간에 배치되는 “형상(Geometry)”입니다. 화면에 보이는 박스, 구, 캐릭터, 나무, 건물 등은 모두 메시로 표현되며, 각 메시에는 삼각형(버텍스) 정보와 재질(Material), 위치·회전·스케일 등의 속성이 붙어 있습니다.

하나의 BabylonJS 장면(Scene)은 보통 다음 요소들로 구성됩니다.

  • Engine: 캔버스 위에 3D를 그려주는 BabylonJS 엔진
  • Scene: 카메라, 빛, 메시, 이펙트 등을 담고 있는 컨테이너
  • Camera: 어떤 방향에서 장면을 바라볼지 결정
  • Light: 메시가 실제 3D처럼 보이도록 조명 효과를 적용
  • Mesh: 실질적으로 화면에 보이는 3D 객체

오늘 실습에서는 이 중에서 “Mesh”와 그 변형(Transform)에 집중해, 기본 도형을 자유롭게 만들고 움직이는 기초를 다집니다.

2. MeshBuilder로 기본 메시 만들기

BabylonJS는 MeshBuilder라는 유틸리티 클래스를 통해 자주 사용하는 기본 메시를 빠르게 만들 수 있도록 돕습니다. 대표적인 메서드는 다음과 같습니다.

  • MeshBuilder.CreateBox: 박스(큐브/직육면체) 생성
  • MeshBuilder.CreateSphere: 구 생성
  • MeshBuilder.CreateCylinder: 원통 또는 원뿔 생성
  • MeshBuilder.CreateGround: 바닥 평면 생성

사용 형태는 대부분 비슷합니다.

  • 첫 번째 인자: 메시에 붙일 고유 이름(문자열)
  • 두 번째 인자: 옵션 객체(크기, 높이, 분할 수 등)
  • 세 번째 인자: 어떤 Scene에 추가할지

예를 들어, 아래와 같이 기본 메시를 만들 수 있습니다.

const box = BABYLON.MeshBuilder.CreateBox("box", {
  size: 1.5     // 정육면체 한 변의 길이
}, scene);

const sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {
diameter: 1,  // 지름
segments: 16  // 표면 분할 수(값이 클수록 더 둥글게 보임)
}, scene);

const cylinder = BABYLON.MeshBuilder.CreateCylinder("cylinder", {
height: 2,        // 높이
diameter: 1,      // 윗면·아랫면 지름(동일)
tessellation: 24  // 주변을 몇 개의 면으로 나눌지
}, scene); 

위 코드에서는 박스, 구, 원통을 각각 생성하고, scene에 자동으로 추가합니다. 이렇게 생성된 메시들은 기본적으로 원점(0,0,0)에 위치하며, 이후 position/rotation/scaling 값을 통해 원하는 위치와 크기로 조정합니다.

3. position / rotation / scaling / parent 이해

메시를 3D 공간에서 자유롭게 배치하고 변형하려면, 최소한 아래 네 가지 속성의 의미를 이해해야 합니다.

3-1. position: 위치

mesh.position은 해당 메시의 중심이 3D 공간에서 어디에 있는지를 나타내는 속성입니다. BabylonJS에서는 BABYLON.Vector3 타입으로 표현되며, X(좌우), Y(위아래), Z(앞뒤) 축 값을 가집니다.

  • mesh.position = new BABYLON.Vector3(0, 1, 0); : 원점에서 위로 1만큼 올려 배치
  • mesh.position.x = 2; : 현재 위치에서 X축 방향으로 2만큼 이동

기본 Primitive 메시(박스, 구 등)는 생성 시 중심이 메시의 가운데에 있기 때문에, 박스를 바닥 위에 올리고 싶다면 Y축 위치를 박스 높이의 절반만큼 올려야 합니다(예: size 1.5라면 position.y = 0.75 또는 1.5로 올린 뒤 아래에 ground를 두는 방식 등).

3-2. rotation: 회전

mesh.rotation은 메시가 각 축을 기준으로 얼마나 회전했는지를 나타냅니다. 역시 Vector3 타입이며, 단위는 “라디안(rad)”입니다.

  • mesh.rotation.y = Math.PI / 4; : Y축 기준으로 45도 회전
  • mesh.rotation.x = Math.PI / 2; : X축 기준으로 90도 회전

애니메이션을 만들 때는 렌더 루프 안에서 mesh.rotation.y += 0.01;처럼 값을 조금씩 증가시키면 계속 회전하는 효과를 얻을 수 있습니다. 오늘 실습에서도 큐브의 Y축 회전을 이런 방식으로 구현합니다.

3-3. scaling: 크기(스케일)

mesh.scaling은 메시의 크기를 축별로 얼마나 늘리거나 줄일지 나타냅니다. 기본값은 (1, 1, 1)이며, 각 축에 대해 독립적으로 조절할 수 있습니다.

  • mesh.scaling = new BABYLON.Vector3(2, 1, 1); : X축 방향으로 두 배 넓게
  • mesh.scaling.y = 3; : Y축 방향(높이)만 세 배로

실제 모델링 툴처럼 정밀한 단위 관리까지는 하지 않더라도, “1이 기본 크기”라는 감각을 갖고 상대적인 비율로 스케일을 조절하면 빠르게 원하는 비율을 맞출 수 있습니다.

3-4. parent: 부모 자식 관계

mesh.parent는 메시를 다른 메시에 붙여서, 부모 메시를 중심으로 상대적인 위치·회전·스케일을 가질 수 있도록 해 줍니다.

  • 박스의 부모로 “pivot(축)” 메시를 만들어 두면, pivot만 회전시켜도 박스가 함께 회전합니다.
  • 캐릭터 몸통을 부모로 하고 팔·다리를 자식으로 두면, 몸통을 이동할 때 팔·다리가 함께 움직입니다.

오늘 예제에서는 보이지 않는 작은 박스(pivot)를 만들고, 큐브를 해당 박스의 자식으로 만든 뒤, 부모만 회전시켜 “회전하는 큐브”를 구현합니다. 이렇게 하면 이후에 큐브의 위치나 스케일을 바꾸더라도 회전 축을 일관되게 유지하기 쉽습니다.

4. 회전 애니메이션과 속도 조절 UI 설계

BabylonJS에서 애니메이션을 구현하는 가장 간단한 방법은 엔진의 렌더 루프에서 매 프레임마다 속성을 조금씩 바꾸는 것입니다.

  • engine.runRenderLoop(() => { scene.render(); }); : BabylonJS 기본 렌더 루프
  • scene.onBeforeRenderObservable.add(() => { ... }); : 매 프레임 실행되는 콜백 추가

오늘 예제에서는 scene.onBeforeRenderObservable을 사용해 매 프레임마다 회전 속도를 적용합니다. 프레임마다 달라질 수 있는 시간 차이를 고려해 engine.getDeltaTime()을 사용하면, 기기 성능이 달라도 비슷한 속도로 회전하도록 만들 수 있습니다.

또한 HTML의 <input type="range">를 이용해 화면 상단에 슬라이더를 두고, 사용자가 슬라이더를 움직이면 자바스크립트 변수 rotationSpeed에 값이 반영되도록 연결합니다. 그 값을 매 프레임 회전 양에 곱해 주면 “UI로 속도 조절” 기능이 완성됩니다.

5. 실습 시 자주 하는 실수와 팁

  • 캔버스 크기 문제: HTML/CSS에서 #renderCanvas에 width/height를 지정하지 않으면 장면이 너무 작게 보일 수 있으므로, 보통 전체 화면에 꽉 차게 스타일을 지정합니다.
  • 카메라 미설정: 카메라를 만들었지만 camera.attachControl(canvas, true);를 호출하지 않으면 마우스로 회전/줌이 되지 않습니다.
  • 빛 강도: 기본 조명은 너무 어둡게 보일 수 있으므로 light.intensity = 0.8 ~ 1.0 정도로 조절하면서 확인하는 것이 좋습니다.
  • 렌더 루프 누락: engine.runRenderLoop를 호출하지 않으면 화면이 한 번만 렌더링되고 애니메이션이 보이지 않습니다.

코드 예시: 회전하는 큐브 + 속도 슬라이더

아래 예제는 하나의 HTML 파일로 완결되는 코드입니다. 그대로 복사해 index.html로 저장한 뒤 브라우저에서 열면, 박스/구/원통이 배치된 장면과 함께 “회전하는 큐브 + 속도 조절 슬라이더”를 확인할 수 있습니다.

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <title>BabylonJS 기본 메시 실습 - 회전하는 큐브</title>
  <style>
    html, body {
      width: 100%;
      height: 100%;
      margin: 0;
      padding: 0;
      overflow: hidden;
    }
    #renderCanvas {
      width: 100%;
      height: 100%;
      touch-action: none;
      display: block;
    }
    .control-panel {
      position: fixed;
      top: 16px;
      left: 16px;
      background: rgba(255, 255, 255, 0.9);
      padding: 8px 12px;
      border-radius: 8px;
      font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
      font-size: 14px;
      z-index: 10;
    }
    .control-panel label {
      font-weight: 600;
      margin-right: 4px;
    }
    .control-panel input[type="range"] {
      vertical-align: middle;
    }
  </style>
</head>
<body>
  <div class="control-panel">
    <label for="speedRange">큐브 회전 속도</label>
    <input type="range" id="speedRange" min="-5" max="5" step="0.1" value="2">
    <span id="speedValue">2.0</span>
  </div>

  <canvas id="renderCanvas"></canvas>

  <!-- BabylonJS CDN -->
  <script src="https://cdn.babylonjs.com/babylon.js"></script>

  <script>
    const canvas = document.getElementById("renderCanvas");
    const engine = new BABYLON.Engine(canvas, true);

    function createScene() {
      const scene = new BABYLON.Scene(engine);

      // 카메라 설정 (마우스로 회전/줌 가능한 ArcRotateCamera)
      const camera = new BABYLON.ArcRotateCamera(
        "camera",
        Math.PI / 4,   // 알파(좌우 회전)
        Math.PI / 4,   // 베타(위아래 각도)
        10,            // 반지름(거리)
        new BABYLON.Vector3(0, 1, 0), // 바라보는 대상
        scene
      );
      camera.attachControl(canvas, true);

      // 조명 설정 (위쪽에서 비추는 HemisphericLight)
      const light = new BABYLON.HemisphericLight(
        "light",
        new BABYLON.Vector3(0, 1, 0),
        scene
      );
      light.intensity = 0.9;

      // 바닥(ground) 메시 생성
      const ground = BABYLON.MeshBuilder.CreateGround("ground", {
        width: 10,
        height: 10
      }, scene);

      // 회전의 기준이 될 pivot(보이지 않는 작은 박스)
      const pivot = BABYLON.MeshBuilder.CreateBox("pivot", {
        size: 0.1
      }, scene);
      pivot.isVisible = false; // 화면에는 보이지 않도록

      // 회전할 큐브 박스
      const box = BABYLON.MeshBuilder.CreateBox("box", {
        size: 1.5
      }, scene);
      box.position = new BABYLON.Vector3(0, 1.5, 0); // 바닥 위로 올리기
      box.parent = pivot; // pivot을 부모로 설정

      // 왼쪽에 구(sphere)
      const sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {
        diameter: 1,
        segments: 16
      }, scene);
      sphere.position = new BABYLON.Vector3(-3, 1, 0);

      // 오른쪽에 원통(cylinder)
      const cylinder = BABYLON.MeshBuilder.CreateCylinder("cylinder", {
        height: 2,
        diameter: 1,
        tessellation: 24
      }, scene);
      cylinder.position = new BABYLON.Vector3(3, 1, 0);

      // 재질(Material) 설정
      const boxMat = new BABYLON.StandardMaterial("boxMat", scene);
      boxMat.diffuseColor = new BABYLON.Color3(0.2, 0.6, 1.0);
      box.material = boxMat;

      const sphereMat = new BABYLON.StandardMaterial("sphereMat", scene);
      sphereMat.diffuseColor = new BABYLON.Color3(1.0, 0.6, 0.2);
      sphere.material = sphereMat;

      const cylinderMat = new BABYLON.StandardMaterial("cylinderMat", scene);
      cylinderMat.diffuseColor = new BABYLON.Color3(0.4, 0.9, 0.4);
      cylinder.material = cylinderMat;

      const groundMat = new BABYLON.StandardMaterial("groundMat", scene);
      groundMat.diffuseColor = new BABYLON.Color3(0.9, 0.9, 0.9);
      ground.material = groundMat;

      // 회전 속도 제어를 위한 변수와 UI 연동
      let rotationSpeed = 2.0; // 기본 속도 (라디안/초 단위)
      const speedRange = document.getElementById("speedRange");
      const speedValue = document.getElementById("speedValue");

      function updateSpeedLabel(value) {
        speedValue.textContent = value.toFixed(1);
      }
      updateSpeedLabel(rotationSpeed);

      speedRange.addEventListener("input", function (event) {
        rotationSpeed = parseFloat(event.target.value);
        updateSpeedLabel(rotationSpeed);
      });

      // 매 프레임마다 회전 적용
      scene.onBeforeRenderObservable.add(() => {
        const deltaTime = engine.getDeltaTime() / 1000; // ms -> 초
        pivot.rotation.y += rotationSpeed * deltaTime;
      });

      // 창 크기 변경 시 엔진 리사이즈
      window.addEventListener("resize", function () {
        engine.resize();
      });

      return scene;
    }

    const scene = createScene();

    engine.runRenderLoop(function () {
      scene.render();
    });
  </script>
</body>
</html>

이 예제 코드는 BabylonJS의 기본 구조(엔진, 씬, 카메라, 라이트)를 설정한 후, MeshBuilder를 이용해 기본 메시를 배치하고, 부모-자식 구조를 통해 회전 축을 관리하며, HTML 슬라이더를 통해 회전 속도를 실시간으로 제어하는 전체 흐름을 보여 줍니다. 초보자라면 먼저 이 코드를 그대로 실행해 본 후, 크기·색상·위치·속도 값을 바꿔 보면서 감을 익히는 것을 추천합니다.

기본 메시 정리 표

BabylonJS에서 자주 사용하는 기본 Primitive 메시와 주요 옵션을 한눈에 정리한 표입니다. 실습 중에 참조용으로 활용하시면 좋습니다.

메시 종류생성 함수주요 옵션 파라미터대표 사용 예주의사항 / 팁
박스(Box)MeshBuilder.CreateBoxsize, width, height, depth큐브, 직육면체, 간단한 건물/박스 등기본 중심이 가운데에 있으므로, 바닥 위에 올릴 때는 Y 위치를 높이의 절반 이상으로 올립니다.
구(Sphere)MeshBuilder.CreateSpherediameter, segments공, 행성, 조명용 더미 오브젝트 등segments를 너무 크게 잡으면 폴리곤 수가 증가해 성능에 영향을 줄 수 있습니다.
원통(Cylinder)MeshBuilder.CreateCylinderheight, diameter, tessellation기둥, 컵 모양, 탑, 다리 구조 등height와 diameter 비율을 조절해 원기둥, 얇은 막대 등 다양한 형태를 만들 수 있습니다.
바닥(Ground)MeshBuilder.CreateGroundwidth, height, subdivisions캐릭터가 서는 바닥, 광장, 바다 등너무 큰 바닥을 만들기보다, 필요한 영역만큼 만들고 텍스처를 활용하는 것이 효율적입니다.

실행 단계

  1. 프로젝트 폴더와 HTML 파일 만들기
    작업용 폴더를 하나 만든 뒤, 그 안에 index.html 파일을 생성합니다. 텍스트 에디터(Visual Studio Code 등)를 열고 본문에 소개된 전체 코드를 붙여 넣습니다.
  2. 브라우저에서 파일 열기
    파일 탐색기에서 index.html을 더블 클릭하거나, 브라우저 주소창에 파일 경로를 드래그해 올려 페이지를 열어 봅니다. 화면 전체에 BabylonJS 캔버스와 좌측 상단의 회전 속도 슬라이더가 나타나야 합니다.
  3. 회전 속도 조절해 보기
    슬라이더를 좌우로 움직이면 수치가 바뀌면서 큐브의 회전 속도 및 방향이 변합니다. 0으로 맞추면 멈추고, 음수 값으로 이동하면 반대 방향으로 회전합니다.
  4. 메시 배치와 속성 변경 연습
    코드에서 box.positionsphere.positioncylinder.position 값을 변경해 메시 위치를 바꿔 보고, scaling과 재질 색상(diffuseColor)도 다양하게 수정해 봅니다. 작은 변경을 반복하면서 좌표와 스케일 감각을 익히는 것이 중요합니다.
  5. 추가 Primitive 메시나 부모 관계 실험
    새로운 박스를 하나 더 만들고, 기존 pivot의 자식으로 붙여 서로 다른 위치에 두었다가, pivot 회전이 두 박스에 동시에 적용되는지 확인해 봅니다. 또 다른 pivot을 만들어 여러 개의 회전 그룹을 구성해 보는 것도 좋은 연습입니다.

추가로 생각해볼 점

  • 현재 예제에서는 단순 회전만 구현했지만, 같은 방식으로 위치나 스케일을 시간에 따라 변경하면 이동, 점프, 도약 등 다양한 애니메이션을 만들 수 있습니다.
  • HTML 슬라이더 대신 BabylonJS GUI(Overlay UI)를 사용하면, 3D 장면 위에 직접 UI를 띄워 일관된 디자인을 유지할 수 있습니다. 이후 단계에서 GUI 라이브러리도 함께 학습해 보시기를 권장합니다.
  • 성능을 고려해야 하는 프로젝트에서는 Primitive 개수와 분할 수(segments, tessellation)를 조절하고, 필요할 때만 고해상도 메시를 사용하는 설계를 미리 고민하는 것이 좋습니다.


Reactions

댓글 쓰기

0 댓글