9148c358d0
Topic_Agent/Topic_Blog/Topics/Topics_Biz/Topics_Meeting/Topics_Rag의 마크다운 지식 문서를 Topic_General/Topic_Programming/Topic_Graphic/Topic_Business 4개 카테고리로 재분류. - 중복 제거: frontmatter의 status:duplicate/merged + duplicate_of/redirect_to 필드로 자기 자신을 중복으로 선언한 리다이렉트 stub 1032개 제거, 완전 동일 내용 파일 472개 제거, 동일 파일명·다른 내용 충돌 시 더 큰(완전한) 버전만 유지(162개 제거) — 총 1639개 중복 제거. - 분류: 폴더 단위로 명확한 항목(AI_and_ML/Coding/Architecture 등 → Programming, Comfyui/Visual_Effects → Graphic, Topics_Biz/Topics_Meeting/사업 등 → Business, Poetic_Blog_Writing/창의성/Game_Design 등 → General)은 폴더 우선순위로, 나머지 혼재 폴더(Topic_Agent/Topic_Blog/Topics 루트/Thinking & Reasoning/Other/UI_UX_Assets)는 title/tags 키워드 스코어링으로 파일 단위 분류(불명확한 경우 General로 폴백). 원본 폴더명은 "From_*" 서브폴더로 보존해 추적 가능성 유지. - 최종 배치: Programming 2784 / General 1608 / Graphic 285 / Business 249 = 4926개 문서. - 에이전트 운영 상태(.astra/.agent/.obsidian/sessions/memory/_company/docs/lessons/_shared/src)는 지식 콘텐츠가 아니므로 재분류 대상에서 제외하고 원위치 유지. - Topics/Topic_email(상위 보호 폴더 Topic_email과 파일명 100% 중복) 삭제 — 보호 폴더 자체는 미변경. - 완전히 비게 된 Topic_Agent/Topic_Blog/Topics_Biz/Topics_Rag 폴더 제거.
6.7 KiB
6.7 KiB
id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
| id | title | category | status | canonical_id | aliases | duplicate_of | source_trust_level | confidence_score | verification_status | tags | raw_sources | last_reinforced | github_commit | tech_stack | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| wiki-2026-0508-instancedmesh-사용-시-드로우-콜-최적화의-한계 | InstancedMesh 사용 시 드로우 콜 최적화의 한계점 사례 연구 | 10_Wiki/Topics | verified | self |
|
none | A | 0.9 | applied |
|
2026-05-10 | pending |
|
InstancedMesh 사용 시 드로우 콜 최적화의 한계점 사례 연구
매 한 줄
"매 InstancedMesh 매 1 draw call로 N copies — but 매 frustum culling, material variation, animation, picking 의 cost가 instance 수에 따라 explode.". 매 naive 사용 시 draw call 매 줄어들어도 GPU vertex/fragment 매 burden, CPU matrix update 매 bottleneck. 매 production 매 LOD + spatial partition + GPU culling 매 결합.
매 핵심
매 한계 list
- No per-instance frustum culling: 매 single bounding sphere → 매 모든 instance가 frustum 안에 있다고 GPU가 가정.
- No per-instance material: 매 same material → color/texture variation 매 instanceColor / instance attribute 의 manual.
- Animation cost: 매 instance마다 matrix update → CPU bound at 10k+.
- Picking 어려움: raycaster 매 instance index 매 별도 처리.
- Memory: 16 floats × N instances = 매 1M instances → 64MB matrix buffer.
- Shadow map: 매 light 마다 또 한 번 instanced draw — culling 없으면 shadow waste.
매 case: 100k cubes
- Naive InstancedMesh: 매 1 draw call, GPU 60fps but matrix update 60ms/frame on CPU.
- Static (
setMatrixAtonce): 매 GPU bound, fillrate 매 issue → LOD 필요. - Dynamic: 매 매 frame matrix update → useDynamicDrawUsage + partial updates.
매 응용 (해결 전략)
- GPU instancing + GPU culling: compute shader 매 frustum check, 매 indirect draw.
- Spatial partitioning: octree / BVH로 매 chunk 단위 InstancedMesh.
- LOD groups: distance 별 다른 InstancedMesh (high/med/low/billboard).
- BatchedMesh (Three.js r170+): 매 different geometries를 single draw call.
- Hierarchical LOD + impostor: 매 far away는 single quad billboard.
💻 패턴
Frustum culling 수동 (Three.js)
const frustum = new THREE.Frustum();
const m = new THREE.Matrix4();
m.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
frustum.setFromProjectionMatrix(m);
const dummy = new THREE.Object3D();
const sphere = new THREE.Sphere(new THREE.Vector3(), boundingRadius);
let visibleCount = 0;
for (let i = 0; i < totalInstances; i++) {
sphere.center.copy(positions[i]);
if (frustum.intersectsSphere(sphere)) {
dummy.position.copy(positions[i]);
dummy.updateMatrix();
instancedMesh.setMatrixAt(visibleCount++, dummy.matrix);
}
}
instancedMesh.count = visibleCount;
instancedMesh.instanceMatrix.needsUpdate = true;
Per-instance color
const mesh = new THREE.InstancedMesh(geo, mat, N);
const color = new THREE.Color();
for (let i = 0; i < N; i++) {
color.setHSL(i / N, 0.7, 0.5);
mesh.setColorAt(i, color);
}
mesh.instanceColor.needsUpdate = true;
// shader auto: gl_InstanceID → vInstanceColor
Dynamic draw usage (partial updates)
mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
// only update changed instances
mesh.setMatrixAt(idx, newMatrix);
mesh.instanceMatrix.updateRange = { offset: idx * 16, count: 16 };
mesh.instanceMatrix.needsUpdate = true;
Chunked InstancedMesh (spatial bucket)
class ChunkedInstances {
constructor(geo, mat, chunkSize = 64) {
this.chunks = new Map(); // "x,y,z" → InstancedMesh
this.chunkSize = chunkSize;
}
add(pos) {
const key = this.chunkKey(pos);
if (!this.chunks.has(key)) {
this.chunks.set(key, new THREE.InstancedMesh(geo, mat, 1024));
}
// ...
}
cullChunks(frustum) {
for (const [key, mesh] of this.chunks) {
mesh.visible = frustum.intersectsBox(this.chunkBox(key));
}
}
}
BatchedMesh (Three.js r170+)
const batched = new THREE.BatchedMesh(maxGeoms, maxVerts, maxIdx);
const geoIdA = batched.addGeometry(geoA);
const geoIdB = batched.addGeometry(geoB);
const instA = batched.addInstance(geoIdA);
batched.setMatrixAt(instA, matrixA);
// 매 different geometries 매 single draw call
GPU compute culling (WebGPU)
// compute shader: input matrices + frustum planes → atomic counter + visible matrix buffer
const cullPipeline = device.createComputePipeline({...});
pass.setPipeline(cullPipeline);
pass.dispatchWorkgroups(Math.ceil(N / 64));
// then drawIndexedIndirect from visible buffer
매 결정 기준
| 상황 | Approach |
|---|---|
| < 1k static instances | Plain InstancedMesh |
| 1k–100k mostly static | InstancedMesh + manual frustum culling |
| 100k+ static | Chunked InstancedMesh (spatial) + LOD |
| Dynamic per-frame (particles) | Points / GPU particle system |
| Different geometries | BatchedMesh (r170+) |
| Massive (1M+) | WebGPU compute culling + indirect draw |
| Picking 필요 | InstancedMesh + raycaster.firstHitOnly = true |
기본값: < 10k는 plain InstancedMesh, 그 이상 매 chunked + LOD.
🔗 Graph
- 부모: GPU Instancing · Three.js
- 변형: BatchedMesh
- Adjacent: Frustum Culling · LOD · Indirect Draw · WebGPU
🤖 LLM 활용
언제: 매 같은 geometry+material의 N copies, 매 N > 50, 매 draw call 매 hot path bottleneck. 언제 X: 매 highly varying geometry (use BatchedMesh), 매 < 50 copies (overhead > benefit), 매 fully dynamic mesh (skinning per instance은 expensive).
❌ 안티패턴
- No bounding 갱신: 매 instance 매 spread out 매 single boundingSphere가 too large → frustum culling 작동 안 함.
- 매 frame full matrix rebuild: 매 instance 매 100% update assumption 매 wrong → updateRange 활용.
- Different materials → multiple InstancedMesh: 매 점 defeats the purpose. Use texture atlas + instance attribute.
- Skip LOD: 매 far instance 매 close instance와 same vertex count → fillrate explosion.
- InstancedMesh on top of skinned mesh: 매 shader 매 manual instancing 필요 — Three.js native skinning 매 instance와 conflict.
🧪 검증 / 중복
- Verified (Three.js docs r170+, mrdoob InstancedMesh PR, WebGPU spec).
- 신뢰도 A.
🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — InstancedMesh 한계 / 해결 패턴 / BatchedMesh + WebGPU compute culling |