본문 바로가기
Three.js/Advanced techniques

[threejs-journey 3-2] Imported models

by SL123 2025. 8. 20.

개요

Three.js는  다양한 기본 도형(Geometries)을 제공하지만, 복잡하고 정교한 모델을 제작하려면 블렌더 (Blender), 3ds Max, Maya와 같은 전문 3D 소프트웨어를 사용하는 것이 훨씬 효율적입니다.

 

3D 모델 파일 포맷은 매우 다양하며, 각각 다른 목적을 가지고 있습니다. 포맷을 선택할 때는 아래와 같은 기준들을 고려하게 됩니다.

  • 데이터 종류: Geometry, Material, Animation 등 어떤 정보를 담을 수 있는가?
  • 파일 크기(무게): 파일의 용량이 얼마나 큰가?
  • 압축률: 데이터를 얼마나 효율적으로 압축하는가?
  • 호환성: 다양한 소프트웨어와 엔진에서 지원되는가?
  • 라이선스: 오픈 소스인가, 특정 기업에 종속적인가?
  • 데이터 형식: 텍스트 기반의 ASCII인가, 컴퓨터가 직접 읽는 바이너리(Binary)인가?

널리 사용되는 포맷으로는 OBJ, FBX, STL, PLY, COLLADA, 3DS 등이 있지만, 현재 실시간 웹 3D 환경에서는 glTF가 사실상의 표준으로 자리잡고 있습니다.

 

포맷 확인 사이트

https://en.wikipedia.org/wiki/List_of_file_formats#3D_graphics

 

List of file formats - Wikipedia

From Wikipedia, the free encyclopedia List of computer file types This is a list of computer file formats, categorized by domain. Some formats are listed under multiple categories. Each format is identified by a capitalized word that is the format's full o

en.wikipedia.org

 

 

 

glTF

glTFGL Transmission Format의 약자로, 3D 에셋을 효율적으로 전송하고 로드하기 위해 설계된 파일 포맷입니다.

  • 개발 주체: 크로노스 그룹(Khronos Group)에서 개발했습니다. 이 그룹은 OpenGL, WebGL, Vulkan 등 중요한 그래픽스 표준을 제정하는 비영리 컨소시엄으로, glTF가 높은 신뢰성과 호환성을 갖는 이유이기도 합니다.
  • 포함 데이터: glTF는 3D Scene을 구성하는 거의 모든 요소를 담을 수 있습니다.
    • 지오메트리 (정점, UV, 노말 등)
    • 머터리얼 (PBR 기반의 사실적인 재질)
    • 카메라 및 조명
    • Scene Graph(객체들의 계층 구조)
    • 애니메이션 (스켈레톤, 모핑)
  • 강력한 호환성: 주요 게임 엔진(Unity, Unreal Engine), 3D 라이브러리(Three.js Babylon.js), 모델링 툴에서 폭넓게 지원하여 '3D계의 JPEG'라고도 불립니다.

단, 모든 경우에 glTF가 정답은 아닙니다. 예를 들어, 수많은 입자로 이루어진 파티클 시스템처럼 폴리곤 메시(Mesh) 기반이 아닌 데이터는 glTF로 표현하기에 적합하지 않을 수 있습니다.

 

3D Asset 다운로드 Github

https://github.com/KhronosGroup/glTF-Sample-Assets

 

GitHub - KhronosGroup/glTF-Sample-Assets: An assortment of assets that demonstrate features and capabilities of the glTF format

An assortment of assets that demonstrate features and capabilities of the glTF format - KhronosGroup/glTF-Sample-Assets

github.com

 

glTF의 4가지 주요 포맷

glTF는 사용 목적에 따라 주로 4가지 형태로 제공됩니다.

포맷 파일 구성 특징 장점 단점
glTF (기본) .gltf +
.bin
+
텍스처
(.png 등)
JSON(.gltf)에 씬 구조, 바이너리(.bin)에 지오메트리, 별도 파일에 텍스처를 저장 데이터 구조가 분리되어 있어 텍스처나 지오메트리 수정이 용이함 여러 파일을 관리해야 하는 번거로움
glTF Binary (.glb) .glb 하나 모든 데이터(JSON, 바이너리, 텍스처)가 하나의 바이너리 파일에 통합됨 파일 하나로 관리가 편하고 전송이 간편함 데이터를 수정하거나 일부만 교체하기 매우 어려움
glTF Draco glTF 기본 포맷과 동일 지오메트리 데이터(.bin)가 Draco 알고리즘으로 압축됨 원본보다 파일 크기가 획기적으로 작아짐 압축 해제(디코딩)를 위한 추가 라이브러리와 약간의 처리 시간이 필요
glTF Embedded .gltf 하나 모든 데이터(텍스처 포함)가 Base64 형태로 JSON 파일 안에 내장(embed)됨 파일 하나로 관리가 가능 텍스트 기반이라 파일 크기가 매우 커지고 로딩 성능이 저하됨
  • 수정할 필요가 없고 간편한 배포를 원한다면? -> glTF Binary(.glb)를 추천합니다.
  • 모델의 텍스처나 데이터를 코드에서 수정해야 한다면? -> glTF (기본) 포맷이 더 편리합니다.
  • 모델의 용량이 너무 커서 로딩 속도가 문제라면? -> glTF Draco 를 고려해야 합니다.(자세한 내용은 아래에서 설명)

 

 

Three.js에서 glTF 모델 로드하기

GLTFLoader를 사용해 Three.js Scene에 glTF 모델을 추가할 수 있습니다.

 

 

기본 로드 및 Scene Graph 이해

먼저 GLTFLoader를 import하고 모델을 로드합니다.

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';

const gltfLoader = new GLTFLoader();

gltfLoader.load(
    '/models/Duck/glTF/Duck.gltf', // 1. 모델 파일 경로
    (gltf) => { // 2. 로드 성공 시 콜백
        console.log('성공', gltf);
        scene.add(gltf.scene); // 로드된 씬 전체를 추가
    },
    (progress) => { // 3. 로드 진행률 콜백
        console.log('진행중', progress);
    },
    (error) => { // 4. 로드 실패 시 콜백
        console.log('실패', error);
    }
);

 

여기서 중요한 점은 로드된 gltf 객체가 메시(Mesh) 자체가 아니라는 것입니다. 3D 소프트웨어에서 저장한 환경 전체가 담긴 공구 세트와 같습니다. 그 구조는 보통 다음과 같습니다.

  • gltf.scene: THREE.Group 객체로, 모델의 최상위 노드입니다.
  • gltf.scene.children: 실제 메시, 카메라 ,조명 등 모든 객체를 담고 있는 배열입니다.
  • gltf.animations: 모델에 포함딘 애니메이션 클립 배열입니다
THREE.Group: scene
└───Array: children
    └───THREE.Object3D
        └───Array: children
            ├───THREE.PerspectiveCamera
            └───THREE.Mesh

 

복잡한 모델 처리하기

단순한 모델은 scene.add(gltf.scene)으로 충분하지만, 여러 개의 메시로 구성된 복잡한 모델은 children 배열을 직접 처리해야 할 때가 있습니다.

 

FlightHelemet 모델처럼 여러 children을 가진 경우를 예로 들어보겠습니다.

gltfLoader.load('/models/FlightHelmet/glTF/FlightHelmet.gltf', (gltf) => {
    // scene.add(gltf.scene.children[0]); // 이렇게 하면 파이프 하나만 추가됩니다.
});

 

 

잘못된 접근 방식: for...of 루프 안에서 원본 배열의 요소를 제거(scene에 add하면 기존 부모에서 제거됨)하면 반복이 제대로 동작하지 않습니다.

// 잘못된 코드: 루프가 중간에 멈춤
for(const child of gltf.scene.children) {
    scene.add(child);
}

 

올바른 해결 방법

  1. while 루프 사용: children 배열의 길이가 0이 될 때까지 첫 번째 요소를 계속 추가합니다. 직관적이지만 무한 루프의 위험이 있습니다.
  2. 배열 복사 (추천): children 배열을 얕은 복사(shallow copy)한 후, 복사된 배열로 루프를 실행합니다. 원본 배열에 영향을 주지 않아 안전하고 명학합니다.
  3. Scene 전체 추가 (가장 간단): 대부분의 경우, 불필요한 객체(예: 모델링 프로그램의 기본 카메라)가 포함될 수 있지만 가장 간단한 방법은 gltf.scene 그룹 자체를 추가하는 것입니다.
while(gltf.scene.children.length) {
    scene.add(gltf.scene.children[0]);
}
const children = [...gltf.scene.children];
for(const child of children) {
    scene.add(child);
}
scene.add(gltf.scene);

 

 

 

Draco 압축 사용하기

Draco는 Google에서 개발한 3D Geometry 압축 알고리즘으로, 모델 파일의 용량을 크게 줄여줍니다. (예: 145KB -> 45KB)

 

 

Drace 홈페이지 및 깃허브 링크 

https://google.github.io/draco/

 

Draco 3D Graphics Compression

Draco is an open-source library for compressing and decompressing 3D geometric meshes and point clouds. It is intended to improve the storage and transmission of 3D graphics. View on GitHub »

google.github.io

https://github.com/google/draco

 

GitHub - google/draco: Draco is a library for compressing and decompressing 3D geometric meshes and point clouds. It is intended

Draco is a library for compressing and decompressing 3D geometric meshes and point clouds. It is intended to improve the storage and transmission of 3D graphics. - google/draco

github.com

 

 

Draco로 압축된 모델을 로드하려면 DRACOLoader를 설정해야 합니다. 그렇지 않으면 No DRACOLoader instance provided 오류가 발생합니다.

 

설정 단계

  1. Draco 라이브러리 파일 복사: node_modules/three/examples/jsm/libs/draco/ 폴더 안의 파일들을 static이나 public 같은 정적 파일 폴더로 복사합니다. 
  2. DRACOLoader 설정 및 GLTFLoader에 등록합니다.
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';

// 1. DRACOLoader 인스턴스 생성
const dracoLoader = new DRACOLoader();

// 2. 디코더 파일이 있는 경로 설정
dracoLoader.setDecoderPath('/draco/'); // 1단계에서 복사한 폴더 경로

// 3. GLTFLoader에 DRACOLoader 등록
const gltfLoader = new GLTFLoader();
gltfLoader.setDRACOLoader(dracoLoader);

// 4. Draco 압축 모델 로드
gltfLoader.load(
    '/models/Duck/glTF-Draco/Duck.gltf',
    (gltf) => {
        scene.add(gltf.scene);
    }
);

 

Draco의 장단점

  • 장점: 모델 파일의 크기가 매우 작아져 로딩 시간이 단축됩니다.
  • 단점:
    • 압축 해제 라이브러리(DRACOLoader와 디코더 파일)를 추가로 로드해야 합니다.
    • 사용자의 컴퓨터에서 지오메트리 압축을 푸는 데 CPU 자원과 시간이 소모되어, 로드 직후 잠깐의 멈춤(freeze) 현상이 발생할 수 있습니다.

결론적으로 수 MB에 달하는 무거운 모델이 많을 경우 Draco 압축은 매우 유용하지만, 몇백 KB 수준의 가벼운 모델 하나만 사용한다면 굳이 사용할 필요는 없습니다.

 


애니메이션 재생하기

 

glTF 파일에 포함된 애니메이션을 재생하려면 AnimationMixer를 사용해야 합니다.

  1. mixer 변수를 전역으로 선언하여 tick 함수에서 접근할 수 있도록 합니다.
  2. 모델 로드 성공 콜백에서 AnimationMixer를 생성하고, 재생할 애니메이션 클립(gltf.animations)으로 action을 만듭니다.
  3. tick 함수(애니메이션 루프)에서 매 프레임마다 mixer.update()를 호출하여 애니메이션을 갱신합니다.
// ... 로더 설정 ...

let mixer = null;

gltfLoader.load(
    '/models/Fox/glTF/Fox.gltf',
    (gltf) => {
        // 1. AnimationMixer 생성
        mixer = new THREE.AnimationMixer(gltf.scene);

        // 2. 첫 번째 애니메이션으로 Action 생성 및 실행
        const action = mixer.clipAction(gltf.animations[0]);
        action.play();

        gltf.scene.scale.set(0.025, 0.025, 0.025);
        scene.add(gltf.scene);
    }
);

const clock = new THREE.Clock();

const tick = () => {
    const deltaTime = clock.getDelta();

    // 3. 매 프레임마다 mixer 업데이트
    if(mixer) {
        mixer.update(deltaTime);
    }

    // ... 렌더링 ...
    window.requestAnimationFrame(tick);
};

tick();

 

gltf.anmations 배열을 콘솔에 출력해보면 모델에 어떤 애니메이션들이 있는지 확인할 수 있으며, 인덱스를 바꿔 다른 애니메이션을 재생할 수 있습니다.

 

 

 

Three.js Editor

 

https://threejs.org/editor/

 

three.js editor

 

threejs.org

 

Three.js 공식 온라인 데이터는 3D 모델을 장면에 올리기 전에 미리 테스트 하고 구조를 확인하기 좋은 도구입니다. 단, .glb 파일이나 .gltf Embedded처럼 단일 파일로 구성된 모델만 드래그 앤 드롭으로 쉽게 확인할 수 있습니다.

 

 

 

결론

Three.js를 활용하여 풍부한 3D 경험을 제공하기 위해서는 복잡한 모델을 효율적으로 다루는 것이 필수적입니다. 이 과정에서 glTF는 단순한 파일 포맷을 넘어, 웹 3D 에셋의 생성, 전송, 렌더링 전반을 아우르는 핵심 표준으로 기능합니다.

 

핵심은 '상황에 맞는 최적의 선택'을 하는 것입니다.

  1. 단순성과 편의성이 중요하다면 .glb 포맷을 기본으로 사용합니다. 파일 하나로 모든 것을 관리할 수 있어 배포와 관리가 가장 용이합니다.
  2. 파일 용량이 웹사이브 성능에 병목을 일으킨다면 Draco 압축을 적극적으로 도입해야 합니다. 비록 약간의 초기 디코딩 비용이 발생하지만, 사용자의 다운로드 시간을 획기적으로 줄여 이탈률을 낮추는 데 결정적인 역할을 합니다.
  3. 모델을 로드할 때는 gltf.scene이 단일 메시가 아닌 하나의 완전한 '장면(Scene)' 또는 '그룹(Group)' 임을 항상 인지해야 합니다. 모델이 구조를 파악하고, 불필요한 요소를 제외하거나 여러 자식(children)을 올바르게 Scene에 추가하는 방벙을 숙지하는 것이 중요합니다.
  4. 애니메이션은 AnimationMixer라는 별도의 재생 장치가 필요하며, 렌더링 루프에서 지속적으로 시간을 업데이트해주어야 생명력을 얻는다는 점을 기억해야 합니다.

결론적으로, glTF의 구조와 다양한 옵션을 이해하고 Three.js의 로더 시스템을 올바르게 활용함으로써, 개발자는 가벼운 파일 용량, 빠른 로딩 속도, 풍부한 인터렉션을 모두 갖춘 고품질의 3D 웹 애플리케이션을 구축할 수 있습니다.

 

'Three.js > Advanced techniques' 카테고리의 다른 글

[threejs-journey 3-3] Raycaster and Mouse Events  (0) 2025.08.23
[threejs-journey 3-1] Physics  (0) 2025.08.19