Three.js 웹게임용 GLB 최적화 실전 가이드: Cyan 모델 사례

GLB 최적화에서 파일 크기만 보면 중요한 비용을 놓치기 쉽습니다. 다운로드 크기는 초기 로딩에 영향을 주지만, 2048px 텍스처와 많은 Draw Call은 기기의 GPU 메모리와 렌더링 비용을 계속 사용합니다.

이 글은 GemSlasher의 Cyan 플레이어 모델을 복제본으로 분석한 실제 사례입니다. 원본을 보존한 상태에서 안전 정리, Meshopt, 텍스처 최대 1024px 축소를 차례로 적용하고, 구조와 외형을 다시 확인했습니다.

사례와 목표

원본 cyan_web_animated.glb는 12.53 MiB이며, Mesh 1개, Material 1개, Triangle 39,499개, 예상 Draw Call 1회, Animation 5개, Skin 1개로 구성됩니다. 텍스처는 모두 2048×2048이고 3장입니다.

원본 GLBNumber

12.53 MiB

Meshopt 적용본Number

9.27 MiB · 원본 대비 26.0% 감소

Meshopt + 1024px 텍스처Number

3.57 MiB · 원본 대비 71.5% 감소

예상 텍스처 GPU 메모리Number

약 64 MiB → 약 16 MiB

GPU 메모리 값은 RGBA와 밉맵을 기준으로 한 예산 추정입니다. 실제 사용량과 프레임 시간은 브라우저, 기기, 장면 구성에 따라 달라집니다.

1. 먼저 모델 비용을 읽기

GLB Optimizer의 분석 탭에 모델을 넣으면 Geometry, Material, Animation, Texture를 함께 확인할 수 있습니다. Cyan은 메시와 Draw Call이 단순한 편이므로, 이 사례에서는 폴리곤보다 텍스처가 더 큰 최적화 대상입니다.

Cyan GLB의 메시, 애니메이션, 텍스처 정보를 분석하는 GLB Optimizer 화면
원본 구조를 기록한 뒤에만 최적화본과 비교합니다.

텍스처 원본 데이터는 emission JPEG 65.8 KiB, normal PNG 7.04 MiB, base color JPEG 793.7 KiB였습니다. 특히 Normal Map은 PNG라도 단순히 WebP로 바꾸면 안 됩니다. 색상 텍스처와 달리 데이터 채널의 값이 재질 계산에 쓰이기 때문입니다.

2. 안전 정리와 Meshopt를 분리해 보기

먼저 Resample, Dedup, Prune만 적용해 사용하지 않는 데이터와 중복 키를 정리했습니다. 이 단계의 결과는 12.00 MiB로, 4.2% 감소했습니다. Animation, Skin, Material 수는 원본과 같은 5 / 1 / 1을 유지해야 합니다.

그 다음 Meshopt medium 압축을 적용했습니다. 결과는 9.27 MiB로 줄었지만, 텍스처 해상도는 그대로라 예상 GPU 텍스처 메모리는 약 64 MiB로 변하지 않습니다. Meshopt는 주로 전송할 Geometry 데이터와 디코딩 효율을 다루며, 텍스처 예산을 자동으로 줄이지 않습니다.

3. 텍스처를 1024px로 낮추기

웹게임의 캐릭터가 화면을 항상 크게 차지하지 않는다면 2048px 텍스처 3장은 과할 수 있습니다. 이 사례에서는 세 텍스처의 긴 변을 1024px로 제한하고 파일 형식은 유지했습니다. JPEG 색상 텍스처는 JPEG로, PNG Normal Map은 PNG로 남겨 색상·데이터 용도를 섞지 않았습니다.

GLB Optimizer에서 텍스처 최대 크기를 1024px로 설정하는 화면
색상과 데이터 텍스처의 용도를 유지한 채 해상도만 낮춥니다.
emissionNumber

2048×2048 · 65.8 KiB → 1024×1024 · 3.8 KiB

normalNumber

2048×2048 · 7.04 MiB → 1024×1024 · 1.98 MiB

base colorNumber

2048×2048 · 793.7 KiB → 1024×1024 · 202.7 KiB

세 텍스처의 원본 바이트 합계는 약 7.88 MiB였고, 변환 후에는 약 2.18 MiB였습니다. Meshopt와 함께 다시 내보낸 GLB는 3.57 MiB입니다.

4. 원본과 최적화본을 눈으로 비교하기

다운로드 전에 같은 카메라와 조명에서 원본과 최적화본을 나란히 보세요. 이 사례에서는 실루엣, 의상 디테일, 발광 포인트, Skin 변형이 유지되었습니다. 다만 이 비교가 모든 거리와 조명에서의 품질을 보장하지는 않으므로, 실제 게임 카메라에서도 확인해야 합니다.

원본 Cyan 모델과 Meshopt 및 1024px 텍스처 최적화본을 같은 조건에서 비교한 화면
왼쪽은 원본, 오른쪽은 Meshopt와 1024px 텍스처를 적용한 결과입니다.

다음 항목을 확인하세요.

  • 가까운 카메라에서 얼굴, 의상 문양, Normal Map의 하이라이트가 지나치게 뭉개지지 않는가?
  • Animation 5개가 모두 재생되고 관절이 찢어지거나 뒤틀리지 않는가?
  • Material과 텍스처 연결이 원본과 같은가?
  • 최적화 중 TEXCOORD_0 out of [0,1] 같은 UV 경고가 있었다면 타일링 영역과 UV Seam을 별도로 확인했는가?

5. Three.js에서 Meshopt 결과 로드하기

Meshopt 압축 GLB는 로더에 Decoder를 연결해야 합니다. 배포 번들에서 meshoptimizer가 포함되는지와 모델 경로가 올바른지를 실제 서비스 환경에서 확인하세요.

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { MeshoptDecoder } from 'three/examples/jsm/libs/meshopt_decoder.module.js';

const loader = new GLTFLoader();
loader.setMeshoptDecoder(MeshoptDecoder);

const gltf = await loader.loadAsync('/assets/models/cyan-optimized.glb');
scene.add(gltf.scene);

압축 전후에 같은 씬을 열어 네트워크 전송 크기와 로딩 오류를 확인하세요. 파일이 작아도 Decoder 등록이 빠지면 모델은 로드되지 않습니다.

가져가기 전 체크리스트

  • 원본 GLB를 별도로 보관했는가?
  • 안전 정리, Meshopt, 텍스처 축소를 한 번에 적용하지 않고 단계별 결과를 비교했는가?
  • 색상 텍스처와 Normal·ORM 같은 데이터 텍스처에 같은 포맷 규칙을 강제하지 않았는가?
  • 실제 게임 카메라 거리와 저사양 기기에서 품질을 확인했는가?
  • 로더와 Meshopt Decoder를 포함한 프로덕션 빌드에서 다시 로드했는가?

GLB/GLTF 분석 및 최적화 도구에서 같은 흐름으로 자신의 모델을 분석하고 비교할 수 있습니다. 모든 처리는 브라우저에서 실행되며 선택한 모델은 UneGames 서버에 업로드되지 않습니다.