refactor(topics): 멀티 에이전트용 지식 재편 — _Common(공통 기본기) + Domain_* 구조
에이전트 8종(대화형/프로그래머 C·S/디자이너/설계자/기획자/QA/PD/PM)에게 [공통 기본 능력 + 롤별 Specialty] 2층으로 지식을 주입하기 위한 재분류. 문서 내용·포맷은 무수정, 폴더 이동만 (6,372개 문서 수 보존 확인). - Topic_Programming → Domain_Programming (내부 구조 보존) - Topic_Graphic → Domain_Design - Topic_Business → Domain_Product - Topic_General → Domain_General - _Common 신설: Math(구 Topic_Math_Specialty), Reasoning(구 General/From_Thinking & Reasoning), Reasoning_Creativity(구 General/From_창의성), Communication(Poetic_Blog_Writing + From_writing) - 타 도메인의 From_* 폴더는 유지 (출처 표기일 뿐, 이미 도메인에 맞게 분류된 문서) - 빈 폴더 정리 (memory/procedures) - 에이전트→폴더 매핑은 workspace의 .astra/agent-knowledge-map.json (9개 에이전트) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
---
|
||||
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 |
|
||||
Reference in New Issue
Block a user