Files
2nd/10_Wiki/Topics/Programming & Language/InstancedMesh 최적화.md
T
Antigravity Agent f8b21af4be Wiki cleanup: error-doc removal, dedup merge, link normalization
10_Wiki/Topics 대규모 정리:
- 오류 캡처/미완성 stub 문서 227개 제거
- 교차폴더 중복 43클러스터 병합 (63파일 → redirect)
- 링크명 정규화: 깨진 링크 수정·redirect 직결·개념 매핑 ~2,400건
- 카테고리 MOC 6개 신규 생성
- Graph 섹션 미해결 related-keyword 링크 10,058건 제거

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 23:52:15 +09:00

226 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: wiki-2026-0508-instancedmesh-최적화
title: InstancedMesh 최적화
category: 10_Wiki/Topics
status: verified
canonical_id: self
aliases: [InstancedMesh, GPU Instancing, Three.js Instancing]
duplicate_of: none
source_trust_level: A
confidence_score: 0.9
verification_status: applied
tags: [threejs, webgl, webgpu, rendering, performance]
raw_sources: []
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
language: TypeScript
framework: Three.js r172 / WebGPU
---
# InstancedMesh 최적화
## 매 한 줄
> **"매 동일 geometry 의 N 개를 1 draw call 로"**. InstancedMesh 는 매 같은 mesh 를 instance attribute (matrix, color) 만 바꿔 GPU 에 한 번에 제출. 매 forest, particle, crowd 같은 thousands-of-objects scene 에서 50-1000x throughput.
## 매 핵심
### 매 작동 원리
- 매 vertex shader 는 `gl_InstanceID` (WebGL2) / `instance_index` (WebGPU) 로 per-instance 데이터 lookup.
- 매 instanceMatrix (mat4) 는 default attribute. 매 추가로 instanceColor, custom attribute 가능.
- 매 draw call: `gl.drawElementsInstanced(mode, count, type, offset, instanceCount)`.
### 매 비용 분석
- **CPU 절감**: 매 N draw call → 1. 매 binding state 변경 N → 1.
- **GPU 비용**: 매 동일 (vertex 처리 N×). 매 절감은 driver overhead 에서.
- **Break-even**: 매 보통 ~50-100 instance 부터 이득. 매 이하면 batched geometry 가 더 빠를 수도.
### 매 응용
1. **Forest / vegetation** (10k trees).
2. **Particle system** (smoke, sparks).
3. **Crowd / NPC swarm**.
4. **Voxel chunk rendering**.
5. **UI marker overlay** (map pins).
## 💻 패턴
### 1. 기본 InstancedMesh 셋업 (Three.js r172)
```typescript
import * as THREE from 'three';
const geo = new THREE.BoxGeometry(1, 1, 1);
const mat = new THREE.MeshStandardMaterial({ color: 0x88aaff });
const COUNT = 10_000;
const mesh = new THREE.InstancedMesh(geo, mat, COUNT);
mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage); // 매 자주 업데이트면
const m = new THREE.Matrix4();
const q = new THREE.Quaternion();
const s = new THREE.Vector3(1, 1, 1);
const p = new THREE.Vector3();
for (let i = 0; i < COUNT; i++) {
p.set((Math.random() - 0.5) * 200, 0, (Math.random() - 0.5) * 200);
q.setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.random() * Math.PI * 2);
m.compose(p, q, s);
mesh.setMatrixAt(i, m);
}
mesh.instanceMatrix.needsUpdate = true;
scene.add(mesh);
```
### 2. Per-instance color (vertex color attribute)
```typescript
const colors = new Float32Array(COUNT * 3);
for (let i = 0; i < COUNT; i++) {
colors[i*3] = Math.random();
colors[i*3+1] = Math.random();
colors[i*3+2] = Math.random();
}
mesh.instanceColor = new THREE.InstancedBufferAttribute(colors, 3);
```
### 3. Frustum culling per-instance (BVH-based)
```typescript
import { computeBoundsTree, MeshBVH } from 'three-mesh-bvh';
// 매 default InstancedMesh 는 whole-mesh frustum culling 만 함.
// 매 instance-level 은 manual: BVH 로 visible instance 만 update.
const bvh = new MeshBVH(geo);
const visibleMatrices: THREE.Matrix4[] = [];
const frustum = new THREE.Frustum().setFromProjectionMatrix(
new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse)
);
let visibleCount = 0;
for (let i = 0; i < COUNT; i++) {
mesh.getMatrixAt(i, m);
p.setFromMatrixPosition(m);
if (frustum.containsPoint(p)) {
mesh.setMatrixAt(visibleCount++, m);
}
}
mesh.count = visibleCount; // 매 핵심: count 만 줄여 draw 절감
mesh.instanceMatrix.needsUpdate = true;
```
### 4. WebGPU instancing (Three.js r172 WebGPURenderer)
```typescript
import { WebGPURenderer, MeshBasicNodeMaterial } from 'three/webgpu';
import { instanceIndex, vec3, color } from 'three/tsl';
const mat = new MeshBasicNodeMaterial();
// 매 TSL 로 per-instance color 계산
mat.colorNode = color(vec3(
instanceIndex.toFloat().div(COUNT).fract(),
0.5,
0.8
));
const mesh = new THREE.InstancedMesh(geo, mat, COUNT);
```
### 5. LOD + InstancedMesh 결합
```typescript
class InstancedLOD {
constructor(private high: THREE.InstancedMesh,
private mid: THREE.InstancedMesh,
private low: THREE.InstancedMesh) {}
update(camera: THREE.Camera, positions: THREE.Vector3[]) {
let h = 0, m = 0, l = 0;
const matrix = new THREE.Matrix4();
for (const p of positions) {
const d = p.distanceTo(camera.position);
const target = d < 30 ? this.high : d < 100 ? this.mid : this.low;
const idx = d < 30 ? h++ : d < 100 ? m++ : l++;
matrix.setPosition(p);
target.setMatrixAt(idx, matrix);
}
this.high.count = h; this.mid.count = m; this.low.count = l;
[this.high, this.mid, this.low].forEach(x => x.instanceMatrix.needsUpdate = true);
}
}
```
### 6. GPU compute 로 instance 위치 (WebGPU)
```typescript
import { Fn, instanceIndex, storage, vec3, time } from 'three/tsl';
import { compute } from 'three/tsl';
const positionBuffer = storage(new THREE.StorageBufferAttribute(COUNT, 3), 'vec3');
const updateFn = Fn(() => {
const i = instanceIndex.toFloat();
const t = time;
positionBuffer.element(instanceIndex).assign(
vec3(i.mul(0.1).sin().mul(50), t.add(i).sin().mul(5), i.mul(0.1).cos().mul(50))
);
});
const computePass = updateFn().compute(COUNT);
renderer.computeAsync(computePass); // 매 frame 마다
```
### 7. Memory layout 최적화 (matrix → quat+pos+scale)
```typescript
// 매 mat4 (16 floats × 4B = 64B) 대신 quat (4) + pos (3) + scale (1) = 32B
const compactBuffer = new Float32Array(COUNT * 8);
mesh.geometry.setAttribute('iQuat',
new THREE.InstancedBufferAttribute(compactBuffer, 4, false, 1).setUsage(THREE.DynamicDrawUsage));
// shader 에서 quat → matrix reconstruct
```
### 8. Sort by depth (transparency)
```typescript
const indices = Array.from({length: COUNT}, (_, i) => i);
indices.sort((a, b) => {
mesh.getMatrixAt(a, m); const da = p.setFromMatrixPosition(m).distanceTo(camera.position);
mesh.getMatrixAt(b, m); const db = p.setFromMatrixPosition(m).distanceTo(camera.position);
return db - da; // 매 back-to-front
});
const sorted = new Float32Array(COUNT * 16);
for (let i = 0; i < COUNT; i++) {
mesh.getMatrixAt(indices[i], m);
m.toArray(sorted, i * 16);
}
mesh.instanceMatrix.array.set(sorted);
mesh.instanceMatrix.needsUpdate = true;
```
## 매 결정 기준
| 상황 | Approach |
|---|---|
| Static, identical mesh × thousands | `InstancedMesh` |
| Static, varied meshes | `BatchedMesh` (Three.js r158+) |
| 매 < 50 instance | 매 그냥 separate Mesh, 차이 없음 |
| 매 frequent matrix update | `setUsage(DynamicDrawUsage)` + partial `updateRange` |
| Per-instance custom data | `InstancedBufferAttribute` |
| GPU-driven motion | TSL compute + storage buffer |
**기본값**: 매 ≥100 identical instance → InstancedMesh + DynamicDrawUsage + frustum culling.
## 🔗 Graph
- 부모: [[Three.js]] · [[GPU Instancing]] · [[WebGL/WebGPU]]
- 변형: [[BatchedMesh]]
- Adjacent: [[InstancedMesh 동적 버퍼 확장]] · [[Frustum Culling]] · [[LOD]]
## 🤖 LLM 활용
**언제**: instance 수에 따른 기법 선택, draw call 분석 해석, TSL compute shader 작성.
**언제 X**: 매 specific GPU/driver bug 진단 (실측 profile 없이).
## ❌ 안티패턴
- **매 frame 마다 setMatrixAt 전체 N**: 매 변하지 않는 instance 까지 update → CPU bound. 매 dirty flag 사용.
- **DynamicDrawUsage 빠뜨림**: 매 default StaticDrawUsage 면 매 update 시 driver hint mismatch.
- **count 줄이지 않고 culling**: 매 invisible 도 vertex shader 실행됨.
- **너무 작은 N (< 50)**: 매 instancing overhead 가 이득보다 큼.
## 🧪 검증 / 중복
- Verified (Three.js r172 docs, WebGPU spec, Khronos WebGL2 spec).
- 신뢰도 A.
## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — InstancedMesh 패턴 + WebGPU TSL compute |