"매 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 비용 절감.
매 응용
Onshape / Fusion 360 web: 매 browser 안 assembly editing.
Plant 3D walkthrough: 매 oil refinery / factory digital twin.
AR overlay: 매 Vision Pro / Quest 3 의 maintenance instruction.
VR design review: 매 stakeholder 의 immersive walkthrough.
💻 패턴
Screen-space LOD selection
functionpickLOD(part: CadPart,camera: Camera):number{constscreenCoverage=projectedRadius(part.bounds,camera)/camera.viewport.height;if(screenCoverage>0.3)return0;// full mesh
if(screenCoverage>0.1)return1;// 1/4 triangles
if(screenCoverage>0.03)return2;// 1/16 triangles
if(screenCoverage>0.005)return3;// billboard
return-1;// cull entirely
}
GPU instancing for fasteners
constboltMesh=loadMesh('m6_socket_head.glb');consttransforms=newFloat32Array(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
// One draw call dispatches all visible meshlets
constdrawArgs=newUint32Array([indexCount,instanceCount,firstIndex,baseVertex,firstInstance]);device.queue.writeBuffer(indirectBuffer,0,drawArgs);pass.drawIndexedIndirect(indirectBuffer,0);
Progressive streaming
asyncfunctionstreamAssembly(modelId: string){constmanifest=awaitfetch(`/cad/${modelId}/manifest.json`).then(r=>r.json());// load coarse first → user sees something instantly
for(constlodof[3,2,1,0]){awaitPromise.all(manifest.parts.map(p=>cache.has(`${p.id}_lod${lod}`)?null:loadPart(p,lod)));requestRedraw();}}
Hi-Z occlusion
// Down-sampled depth pyramid → occluder test before draw
consthiZ=buildHiZPyramid(depthTexture);for(constpartofvisibleAfterFrustum){if(occludedByHiZ(part.bounds,hiZ,camera))continue;drawList.push(part);}