# [[Three.js WebGL 렌더링 최적화]]

## 📌 Brief Summary
Three.js WebGL 렌더링 최적화는 주로 CPU와 GPU 간의 통신 병목 현상을 유발하는 드로우 콜(Draw Call)을 줄이고, 메모리 및 렌더링 연산 효율을 극대화하는 일련의 과정입니다 [1-6]. 이를 위해 인스턴싱(Instancing), 배칭(Batching), 기하학적 병합, 텍스처/모델 압축 및 LOD(Level of Detail)와 같은 다양한 기법이 복합적으로 적용됩니다 [7-12]. 최적화를 통해 렌더링 프레임 속도를 방어할 수 있으나, 각 기법은 절두체 컬링(Frustum Culling) 정밀도 저하나 오버드로우(Overdraw) 유발과 같은 구조적 한계와 트레이드오프를 가지므로 씬(Scene)의 특성에 맞는 전략적 접근이 필수적입니다 [13-17].

## 📖 Core Content
**드로우 콜(Draw Call) 최소화 전략**
*   **드로우 콜의 병목:** 드로우 콜은 CPU가 GPU에 렌더링 상태를 설정하고 기하학적 데이터를 그리도록 지시하는 명령으로, 원활한 60fps 성능을 위해서는 프레임당 100개 미만으로 유지하는 것이 강력히 권장됩니다 [3, 9, 18, 19]. 폴리곤 수보다 드로우 콜의 수가 CPU를 마비시키는 주된 원인이 됩니다 [5].
*   **InstancedMesh:** 동일한 지오메트리와 재질을 공유하는 다수의 객체를 단 한 번의 드로우 콜로 렌더링합니다 [9, 20]. 렌더링 속도와 메모리 효율을 크게 높이지만, 엔진 레벨에서 전체 인스턴스에 대한 단일 바운딩 볼륨을 사용하여 개별 객체의 시야 절두체 컬링(Frustum Culling)이 불가합니다 [13, 21]. 또한 렌더링 순서 자동 정렬이 되지 않아 픽셀 중복 연산(Overdraw)이 발생할 수 있으며 스킨드 애니메이션 적용이 매우 제한적입니다 [15, 16, 22].
*   **BatchedMesh:** 동일한 재질을 사용하지만 지오메트리가 서로 다른 객체들을 하나의 드로우 콜로 묶어 처리할 수 있게 합니다 [10, 23-27]. 인스턴스별 컬링 및 가시성 제어가 가능하여 다양성을 확보할 수 있으나, 내부적인 정렬(Sorting)과 컬링 과정에서 막대한 CPU 부하를 유발하여 오히려 프레임이 하락하는 사례도 존재합니다 [27-30].
*   **Geometry 병합(Merging):** 움직이지 않는 정적인 환경 요소는 `BufferGeometryUtils`를 사용하여 하나의 지오메트리로 병합하면 드로우 콜을 효과적으로 1회로 줄일 수 있습니다 [10, 31, 32].

**에셋 데이터 및 메모리 관리**
*   **데이터 압축:** Draco 압축을 적용하여 지오메트리 파일 크기를 최대 90-95% 줄일 수 있습니다 [7, 12, 18]. 텍스처는 KTX2 및 Basis Universal 형식으로 압축하여 VRAM 메모리 점유율을 PNG 대비 약 75~90% 낮출 수 있습니다 [7, 18, 33, 34].
*   **LOD (Level of Detail):** 카메라와의 거리에 따라 폴리곤 수가 적은 메쉬로 교체하거나 원경의 객체를 임포스터(Billboard Impostor)로 대체하여 연산량을 크게 줄일 수 있습니다 [8, 11, 19, 35, 36].
*   **명시적 리소스 해제:** Three.js는 GPU 리소스를 자동으로 가비지 컬렉팅하지 않으므로, 사용이 끝난 지오메트리, 재질, 텍스처, 렌더 타겟에 대해 반드시 `.dispose()`를 호출하여 GPU 메모리 누수를 방지해야 합니다 [37-40].

**셰이더, 라이팅 및 포스트 프로세싱 최적화**
*   텍스처 아틀라스(Texture Atlas)나 배열 텍스처(Data Array Textures)를 사용하여 여러 재질을 하나로 통합하면, 재질 스위칭으로 인한 상태 변경 오버헤드를 대폭 줄일 수 있습니다 [26, 41-44].
*   모바일 기기에서는 셰이더 정밀도를 `mediump`로 설정하여 성능을 높이고, 분기문(Branching/Conditionals) 대신 `mix()` 및 `step()`을 활용하며, 동적 루프를 피해야 합니다 [45, 46]. 
*   복잡한 씬에서는 Depth Pre-Pass 기법을 도입하여 보이지 않는 프래그먼트에 대한 무거운 셰이더 연산(오버드로우)을 사전에 차단할 수 있습니다 [47, 48].
*   정적 씬에서는 라이트맵과 그림자를 런타임 이전에 베이크(Bake)하여 실시간 연산을 피하는 것이 좋습니다 [49].

## 🔗 Knowledge Connections
- **Related Topics:** [[드로우 콜 (Draw Call)]], [[InstancedMesh]], [[BatchedMesh]], [[LOD (Level of Detail)]], [[Frustum Culling]], [[Draco 압축]], [[Texture Atlas]], [[WebGPU]]
- **Projects/Contexts:** [[Utsubo Three.js Optimization Tips (2026)]], [[InstancedMesh2 라이브러리]], [[Threedium Image-To-3D WebGL 파이프라인]], [[Three.js Roadmap: Draw Calls]]
- **Contradictions/Notes:**
  - `InstancedMesh`는 드로우 콜을 혁신적으로 줄여주지만, 자동 정렬 기능이 없고 전체를 렌더링하는 특성 때문에 씬 내 객체들이 중첩될 경우 막대한 오버드로우 연산이 발생하여, 1회 드로우 콜임에도 다수의 개별 메쉬를 렌더링할 때보다 오히려 프레임률(FPS)이 낮아지는 기현상이 발생할 수 있습니다 [13-16, 50].
  - `BatchedMesh`는 여러 지오메트리를 하나의 드로우 콜로 모아주어 효율적인 것으로 소개되나, 객체의 가시성을 확인하고 렌더링 순서를 정렬하는 연산 비용이 커 특정 다량의 객체 렌더링 씬에서는 병합 메쉬(Merged Mesh)를 사용할 때보다 CPU 사용량을 치솟게 만들고 30~50% 더 낮은 FPS를 기록하는 성능 병목 현상이 보고되고 있습니다 [28-30, 51, 52].

---
*Last updated: 2026-04-19*