--- id: wiki-2026-0508-instancedmesh title: InstancedMesh category: 10_Wiki/Topics status: verified canonical_id: self aliases: [InstancedMesh, GPU Instancing, three.js InstancedMesh] duplicate_of: none source_trust_level: A confidence_score: 0.95 verification_status: applied tags: [threejs, webgl, gpu-instancing, rendering, performance] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: JavaScript/TypeScript framework: three.js --- # InstancedMesh ## 매 한 줄 > **"매 thousands of meshes 매 single draw call"**. `THREE.InstancedMesh` 매 same-geometry+material 매 N copies 매 GPU instancing 매 single draw — 매 forest, crowd, particle, voxel 의 standard pattern. 2026 매 r170+ 매 BatchedMesh (multi-geometry instancing) 매 추가됨. ## 매 핵심 ### 매 InstancedMesh vs alternatives - **Mesh × N**: 매 N draw calls. 매 simple, 매 slow at >100. - **InstancedMesh**: 매 1 draw call, 매 same geom/material, 매 per-instance matrix + color. - **BatchedMesh** (r150+): 매 1 draw call, 매 different geom 가능, 매 same material. - **Custom shader InstancedBufferGeometry**: 매 full control, 매 boilerplate. ### 매 Per-instance attributes - **`setMatrixAt(i, matrix)`** — 매 position/rotation/scale. - **`setColorAt(i, color)`** — 매 per-instance tint (requires instanceColor). - **Custom `InstancedBufferAttribute`** — 매 임의 per-instance data (HP, age, type). ### 매 응용 1. **Forest/foliage**: 10k 매 trees 매 60fps. 2. **Crowd simulation**: 매 NPCs 매 unique pose 매 transform. 3. **Voxel terrain**: 매 cube instance 매 chunk. 4. **Particle system**: 매 GPU-driven particle. 5. **Bullet patterns**: 매 bullet hell 매 thousands of bullets. ## 💻 패턴 ### Basic InstancedMesh ```javascript import * as THREE from 'three'; const count = 10000; const geom = new THREE.BoxGeometry(1, 1, 1); const mat = new THREE.MeshStandardMaterial({ color: 0x44aa88 }); const mesh = new THREE.InstancedMesh(geom, mat, count); const m = new THREE.Matrix4(); for (let i = 0; i < count; i++) { m.setPosition( (Math.random() - 0.5) * 100, (Math.random() - 0.5) * 100, (Math.random() - 0.5) * 100 ); mesh.setMatrixAt(i, m); } mesh.instanceMatrix.needsUpdate = true; scene.add(mesh); ``` ### Per-instance color ```javascript mesh.instanceColor = new THREE.InstancedBufferAttribute( new Float32Array(count * 3), 3 ); const c = new THREE.Color(); for (let i = 0; i < count; i++) { c.setHSL(i / count, 0.7, 0.5); mesh.setColorAt(i, c); } mesh.instanceColor.needsUpdate = true; ``` ### Dynamic update (animated swarm) ```javascript const dummy = new THREE.Object3D(); function tick(time) { for (let i = 0; i < count; i++) { dummy.position.set( Math.sin(time + i) * 10, Math.cos(time * 0.5 + i * 0.3) * 5, Math.sin(time * 0.3 + i) * 10 ); dummy.rotation.y = time + i; dummy.updateMatrix(); mesh.setMatrixAt(i, dummy.matrix); } mesh.instanceMatrix.needsUpdate = true; } ``` ### Custom per-instance attribute (shader) ```javascript const hpAttr = new THREE.InstancedBufferAttribute(new Float32Array(count), 1); geom.setAttribute('aHp', hpAttr); mat.onBeforeCompile = (shader) => { shader.vertexShader = shader.vertexShader .replace('#include ', ` #include attribute float aHp; varying float vHp; `) .replace('#include ', ` #include vHp = aHp; `); shader.fragmentShader = shader.fragmentShader .replace('#include ', ` #include varying float vHp; `) .replace('#include ', ` gl_FragColor.rgb = mix(vec3(1,0,0), gl_FragColor.rgb, vHp); #include `); }; ``` ### Frustum culling per instance (CPU side) ```javascript const frustum = new THREE.Frustum().setFromProjectionMatrix( new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse) ); let visible = 0; for (let i = 0; i < total; i++) { if (frustum.containsPoint(positions[i])) { visibleMesh.setMatrixAt(visible++, matrices[i]); } } visibleMesh.count = visible; visibleMesh.instanceMatrix.needsUpdate = true; ``` ### BatchedMesh (r150+, multi-geometry) ```javascript const batch = new THREE.BatchedMesh(maxInstances, maxVerts, maxIndices, mat); const treeId = batch.addGeometry(treeGeom); const rockId = batch.addGeometry(rockGeom); for (let i = 0; i < 5000; i++) batch.addInstance(i % 2 === 0 ? treeId : rockId); ``` ### Raycasting against InstancedMesh ```javascript const raycaster = new THREE.Raycaster(); raycaster.setFromCamera(mouse, camera); const hits = raycaster.intersectObject(mesh); if (hits.length) { const i = hits[0].instanceId; console.log('Hit instance', i); } ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | 매 same geom/material × N (>100) | InstancedMesh | | 매 different geom × N | BatchedMesh (r150+) | | 매 fully GPU-driven (compute-style) | InstancedBufferGeometry + WebGPU | | 매 N < 50 | regular Mesh OK | | 매 LOD per instance | InstancedMesh × LOD level + custom culling | **기본값**: 매 N ≥ 100 → InstancedMesh. ## 🔗 Graph - 부모: [[three.js]] · [[GPU-Instancing]] - 변형: [[BatchedMesh]] - 응용: [[Crowd-Simulation]] - Adjacent: [[WebGPU]] · [[Frustum Culling]] · [[LOD]] ## 🤖 LLM 활용 **언제**: 매 three.js 매 thousands of repeated objects; 매 WebGL 매 draw call optimization. **언제 X**: 매 단일 unique mesh; 매 Unity (different API); 매 native C++ engine (use API-specific instancing). ## ❌ 안티패턴 - **Per-frame full rebuild**: 매 모든 matrix 매 update — 매 변하지 않는 instance 매 skip. - **Forgetting `needsUpdate`**: 매 setMatrixAt 후 매 GPU 매 stale. - **InstancedMesh × different materials**: 매 불가능 — BatchedMesh or 분리. - **Raycast against 100k instances every frame**: 매 BVH (three-mesh-bvh) 사용. ## 🧪 검증 / 중복 - Verified (three.js docs r170, official examples webgl_instancing_*, BatchedMesh PR #25556). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — full content (instancing patterns + BatchedMesh) |