--- id: wiki-2026-0508-cad-렌더링-최적화 title: CAD 렌더링 최적화 category: 10_Wiki/Topics status: verified canonical_id: self aliases: [CAD Rendering Optimization, CAD Performance, Engineering Visualization] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [cad, rendering, gpu, lod, webgpu] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: TypeScript framework: WebGPU/Three.js --- # CAD 렌더링 최적화 ## 매 한 줄 > **"매 millions-of-triangles model 의 60fps 표시 = LOD + culling + GPU instancing 의 합."**. 매 CAD assembly 는 mechanical part 가 hundreds-of-thousands 단위로 쌓여 brute-force rendering 시 GPU 가 즉사. 매 2026 모던 stack 은 WebGPU + meshlet (Nanite-style) + indirect draw 를 사용해 browser 에서도 native-like performance 달성. ## 매 핵심 ### 매 bottleneck axis - **Geometry**: 매 triangle count — 매 fillet/thread 같은 detail 이 수십 million 까지 폭증. - **Draw call**: 매 part 별 separate draw → CPU/GPU sync overhead 가 frame budget 잠식. - **Overdraw**: 매 transparent assembly 의 layered fragment shading. - **Memory**: 매 32-bit index + per-vertex normal/UV/color → VRAM 빠르게 saturate. ### 매 technique stack - **Tessellation control**: 매 NURBS → mesh 변환 시 view-dependent chord tolerance. - **LOD**: 매 distance / screen-coverage 기반 mesh swap. - **Frustum / occlusion culling**: 매 BVH + Hi-Z buffer. - **Instancing**: 매 동일 part (bolt/screw) 의 single draw call. - **Meshlet (Nanite-like)**: 매 cluster 단위 GPU culling + virtual geometry. - **Deferred shading**: 매 overdraw 비용 절감. ### 매 응용 1. **Onshape / Fusion 360 web**: 매 browser 안 assembly editing. 2. **Plant 3D walkthrough**: 매 oil refinery / factory digital twin. 3. **AR overlay**: 매 Vision Pro / Quest 3 의 maintenance instruction. 4. **VR design review**: 매 stakeholder 의 immersive walkthrough. ## 💻 패턴 ### Screen-space LOD selection ```typescript function pickLOD(part: CadPart, camera: Camera): number { const screenCoverage = projectedRadius(part.bounds, camera) / camera.viewport.height; if (screenCoverage > 0.3) return 0; // full mesh if (screenCoverage > 0.1) return 1; // 1/4 triangles if (screenCoverage > 0.03) return 2; // 1/16 triangles if (screenCoverage > 0.005) return 3; // billboard return -1; // cull entirely } ``` ### GPU instancing for fasteners ```typescript const boltMesh = loadMesh('m6_socket_head.glb'); const transforms = new Float32Array(boltCount * 16); // packed mat4 fillTransforms(transforms, boltInstances); device.queue.writeBuffer(instanceBuffer, 0, transforms); pass.setPipeline(instancedPipeline); pass.setVertexBuffer(0, boltMesh.vertices); pass.setVertexBuffer(1, instanceBuffer); pass.drawIndexed(boltMesh.indexCount, boltCount); // single call for 50k bolts ``` ### BVH-based frustum culling ```typescript class BVHNode { bounds: AABB; children?: [BVHNode, BVHNode]; parts?: CadPart[]; } function cullVisible(node: BVHNode, frustum: Frustum, out: CadPart[]) { const test = frustum.testAABB(node.bounds); if (test === 'outside') return; if (test === 'inside' || !node.children) { out.push(...(node.parts ?? collectAll(node))); return; } cullVisible(node.children[0], frustum, out); cullVisible(node.children[1], frustum, out); } ``` ### Meshlet cluster (Nanite-style) ```wgsl // WebGPU compute shader — cluster culling @group(0) @binding(0) var meshlets: array; @group(0) @binding(1) var visibleList: array; @group(0) @binding(2) var camera: Camera; @compute @workgroup_size(64) fn cullMeshlets(@builtin(global_invocation_id) gid: vec3u) { let idx = gid.x; if (idx >= arrayLength(&meshlets)) { return; } let m = meshlets[idx]; if (frustumTest(m.boundingSphere, camera) && coneTest(m.normalCone, camera.position)) { let slot = atomicAdd(&visibleList[0], 1u); visibleList[slot + 1u] = idx; } } ``` ### Indirect draw aggregation ```typescript // One draw call dispatches all visible meshlets const drawArgs = new Uint32Array([ indexCount, instanceCount, firstIndex, baseVertex, firstInstance ]); device.queue.writeBuffer(indirectBuffer, 0, drawArgs); pass.drawIndexedIndirect(indirectBuffer, 0); ``` ### Progressive streaming ```typescript async function streamAssembly(modelId: string) { const manifest = await fetch(`/cad/${modelId}/manifest.json`).then(r => r.json()); // load coarse first → user sees something instantly for (const lod of [3, 2, 1, 0]) { await Promise.all(manifest.parts.map(p => cache.has(`${p.id}_lod${lod}`) ? null : loadPart(p, lod) )); requestRedraw(); } } ``` ### Hi-Z occlusion ```typescript // Down-sampled depth pyramid → occluder test before draw const hiZ = buildHiZPyramid(depthTexture); for (const part of visibleAfterFrustum) { if (occludedByHiZ(part.bounds, hiZ, camera)) continue; drawList.push(part); } ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | < 100k triangles, single part | brute force, no LOD | | 1M-10M triangles, assembly | BVH + frustum culling + LOD | | 10M-100M triangles | + GPU instancing + meshlets | | > 100M (plant/ship) | virtual geometry + streaming + occlusion | | Mobile / VR | aggressive LOD + foveated rendering | **기본값**: BVH culling + 4-tier LOD + instanced fasteners (covers 90% mid-size assemblies). ## 🔗 Graph - 부모: [[Computer_Graphics]] · [[GPU_Architecture]] - 변형: [[Nanite_Virtual_Geometry]] · [[Mesh_Shaders]] - 응용: [[Digital_Twin]] · [[AR_VR_Rendering]] - Adjacent: [[WebGPU]] · [[Three.js]] · [[Level_of_Detail]] ## 🤖 LLM 활용 **언제**: CAD/BIM viewer 설계, performance bottleneck 분석, LOD threshold tuning. **언제 X**: photorealistic offline rendering (path tracing 영역). ## ❌ 안티패턴 - **Per-part separate draw call**: 매 50k draws/frame 은 어떤 GPU 도 죽음. - **CPU-side culling only**: 매 GPU-driven culling 없이는 modern bandwidth 활용 불가. - **Uniform LOD across assembly**: 매 close-up bolt 는 detail 필요, far wall 은 billboard 충분. - **No tessellation budget**: 매 NURBS → mesh 변환 시 chord tolerance 가 화면 무관하면 메모리 폭발. ## 🧪 검증 / 중복 - Verified (Onshape engineering blog 2025, Unreal Nanite SIGGRAPH 2021, WebGPU spec 2024). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — CAD rendering pipeline, LOD, meshlet, WebGPU patterns |