37 lines
6.3 KiB
Markdown
37 lines
6.3 KiB
Markdown
# [[Three.js WebGL Rendering Optimization|Three.js WebGL Rendering Optimization]]
|
|
|
|
## 📌 Brief Summary
|
|
Three.js WebGL 렌더링 최적화는 3D 웹 애플리케이션의 프레임 속도를 향상시키고 메모리 사용량을 줄이기 위한 기술적 접근 방식을 의미합니다 [1, 2]. 이 최적화의 핵심은 CPU와 GPU 간의 병목 현상을 유발하는 드로우 콜(Draw Call) 횟수를 최소화하고, 메모리 대역폭을 효율적으로 관리하는 것입니다 [3, 4]. 이를 위해 `InstancedMesh`, `BatchedMesh`, 기하학 병합(Geometry Merging), 텍스처 압축, 그리고 시야 절두체 컬링(Frustum Culling)과 같은 다양한 기법이 활용되며, 2026년 도입된 WebGPU 기술은 컴퓨트 셰이더를 통해 렌더링 한계를 더욱 확장시켰습니다 [3, 5-8].
|
|
|
|
## 📖 Core Content
|
|
|
|
**드로우 콜 최적화 (Draw Call Optimization)**
|
|
* CPU가 GPU에 렌더링 명령을 내리는 드로우 콜은 상태 변경 및 준비 과정에서 막대한 오버헤드를 발생시킵니다 [4, 9, 10]. 매끄러운 60fps 성능을 유지하기 위해 프레임당 드로우 콜을 100회 미만으로 유지하는 것이 권장됩니다 [11-13].
|
|
* 동일한 기하학적 구조와 재질을 가진 객체가 수백, 수천 개 반복될 때는 단일 드로우 콜로 렌더링을 처리할 수 있는 `InstancedMesh`를 사용하는 것이 가장 효율적입니다 [11, 14, 15].
|
|
* 반면 재질은 공유하지만 각기 다른 기하학적 구조를 가진 객체들을 렌더링할 때는 `BatchedMesh`를 사용하여 하나의 드로우 콜로 묶을 수 있습니다 [5, 16, 17].
|
|
* 정적인 환경 객체(건물, 지형 등)는 `BufferGeometryUtils`를 사용하여 로드 시점에 하나의 기하학으로 병합(Merge)하는 것이 유리합니다 [6, 16, 18]. 단, 병합된 객체는 개별적으로 애니메이션을 주거나 독립적인 컬링(Culling)을 적용할 수 없는 단점이 있습니다 [6, 18].
|
|
|
|
**메모리 및 에셋 관리 (Memory and Asset Management)**
|
|
* Three.js는 GPU 리소스를 자동으로 가비지 컬렉션(Garbage Collect)하지 않으므로, 사용이 끝난 기하학, 재질, 텍스처, 렌더 타겟은 반드시 `.dispose()`를 호출하여 GPU 메모리에서 명시적으로 삭제해야 메모리 누수를 막을 수 있습니다 [19-21].
|
|
* 3D 모델의 기하학적 데이터를 Draco로 압축하면 파일 크기를 90~95%까지 줄일 수 있으며, KTX2 (Basis Universal) 텍스처 압축을 사용하면 일반 PNG 대비 GPU 메모리(VRAM) 사용량을 대폭 절감할 수 있습니다 [12, 22-24].
|
|
* 수많은 작은 텍스처들은 텍스처 아틀라스(Texture Atlas)나 배열 텍스처(Data Array Texture)로 결합하여 텍스처 바인딩 횟수를 줄여야 합니다 [25-27].
|
|
|
|
**InstancedMesh의 구조적 한계와 성능 병목**
|
|
* **비효율적인 절두체 컬링(Frustum Culling):** `InstancedMesh`는 내부의 개별 인스턴스가 아닌, 전체 인스턴스를 포함하는 거대한 바운딩 볼륨을 기준으로 컬링을 수행합니다 [28]. 즉, 단 하나의 인스턴스만 시야에 있어도 화면 밖의 모든 인스턴스에 대해 강제적인 정점 연산이 발생하여 GPU 자원이 낭비될 수 있습니다 [28, 29].
|
|
* **오버드로우(Overdraw) 발생:** `InstancedMesh` 내의 인스턴스들은 렌더링 순서가 자동 정렬되지 않으므로 앞뒤 객체 간 오버드로우(동일 픽셀 영역에 중복 계산)가 발생합니다 [30, 31]. 이는 불투명 객체의 프래그먼트 병목을 유발하고 투명한 객체에서는 심각한 시각적 오류를 발생시킵니다 [31, 32].
|
|
* **레이캐스팅(Raycasting) 제약:** 움직이는 인스턴스들에 대해 레이캐스팅이 정상 작동하려면 인스턴스 행렬이 변경된 후 반드시 `computeBoundingSphere()`와 `computeBoundingBox()`를 호출하여 바운딩 볼륨을 다시 계산해야 합니다 [33, 34].
|
|
|
|
**셰이더, LOD 및 WebGPU 렌더링 파이프라인**
|
|
* 카메라 거리에 따라 고해상도 메쉬를 저해상도 메쉬나 단순한 임포스터(빌보드)로 교체하는 LOD (Level of Detail) 시스템을 구현하면 프레임 속도를 크게 방어할 수 있습니다 [13, 35-37].
|
|
* 저사양 통합 GPU 환경에서는 오버드로우로 인한 픽셀 처리 부하를 줄이기 위해 먼저 깊이(Depth)만 렌더링한 후(색상 쓰기 비활성화), 실제 렌더링에서 깊이 테스트를 EQUAL로 설정하여 가려진 픽셀 연산을 생략하는 'Depth Pre-Pass' 기법이 효과적입니다 [38, 39]. 또한 물리 기반 렌더링인 `MeshStandardMaterial` 대신 계산 비용이 저렴한 `MeshPhongMaterial`을 활용하면 성능 향상에 도움이 됩니다 [40, 41].
|
|
* 2026년 기준, WebGL에서 WebGPU(`WebGPURenderer`)로 전환하면 컴퓨트 셰이더(Compute Shaders)를 활용하여 CPU가 처리하던 대규모 파티클 시스템 업데이트, 물리 시뮬레이션 연산 등을 GPU 병렬 처리로 넘겨 비약적인 성능 향상(최대 100배)을 이룰 수 있습니다 [7, 42-45].
|
|
|
|
## 🔗 Knowledge Connections
|
|
- **Related Topics:** [[Draw Call|Draw Call]], [[InstancedMesh|InstancedMesh]], [[BatchedMesh|BatchedMesh]], [[Level of Detail (LOD)|Level of Detail (LOD)]], [[Frustum Culling|Frustum Culling]], [[WebGPU|WebGPU]]
|
|
- **Projects/Contexts:** Segments.ai WebGPU Migration, [[InstancedMesh2 library|InstancedMesh2 Library]], Three.js WebGPURenderer (r171+)
|
|
- **Contradictions/Notes:**
|
|
- `BatchedMesh`는 다수의 고유 기하학 객체를 단일 드로우 콜로 묶어주는 훌륭한 최적화 도구이지만 [5, 16], 20만 개가 넘는 수준의 과도하게 많은 지오메트리에 적용할 경우 버퍼의 draw "starts" 및 "counts" 데이터를 매 프레임 업데이트해야 하는 오버헤드로 인해 오히려 CPU 사용률이 폭증하고 기존의 Merged Mesh 방식보다 성능이 크게 저하되는 현상이 발생할 수 있습니다 [46-49].
|
|
- `InstancedMesh`는 드로우 콜을 혁신적으로 줄여주지만, 인스턴스들이 정렬되지 않아 막대한 오버드로우를 유발하므로, 객체가 겹치는 씬의 경우 여러 개의 개별 Mesh를 공유 속성으로 렌더링하는 것보다 오히려 프레임 속도가 더 떨어지는 역설적인 상황이 발생할 수 있습니다 [30, 31, 50].
|
|
|
|
---
|
|
*Last updated: 2026-04-19* |