Three.js From Zero · Article s6-06
S6-06 LOD Tools
LOD Tools & Mesh Simplification
Level of detail: show the heavy mesh up close, the cheap mesh far away. Tools that generate the cheap meshes automatically. Quadric Error Metric (QEM) simplification, screen-size thresholds, continuous LOD.
1. Why LOD
A character at 5m: 50k triangles visible. At 50m: maybe 100 pixels. Rendering 50k triangles for 100 pixels is waste. 5k-triangle version looks identical.
2. Discrete LOD (stepwise)
Author 3-5 versions of each mesh:
- LOD0: 50k tris, screen distance 0-10m.
- LOD1: 15k tris, 10-30m.
- LOD2: 5k tris, 30-80m.
- LOD3: 1k tris, 80m+.
- LOD4: billboard impostor, 200m+.
3. Three.js has LOD built in
const lod = new THREE.LOD();
lod.addLevel(highMesh, 0);
lod.addLevel(medMesh, 10);
lod.addLevel(lowMesh, 50);
lod.addLevel(imposter, 200);
scene.add(lod);
Each frame, LOD.update(camera) swaps to the correct child based on distance.
4. Where the cheap meshes come from
Option A: artist-authored
Artist manually models 3 versions. Best quality, costly.
Option B: automatic simplification
Algorithm decimates triangles while preserving shape.
Quadric Error Metric (QEM) (Garland 1997): collapse an edge to a single vertex. Compute error as sum of squared distances to neighboring planes. Pick the edge with lowest error. Repeat until target triangle count hit.
Option C: meshoptimizer's simplifier
// In JS via meshoptimizer npm module
import { simplify } from 'meshoptimizer';
const reducedIndices = simplify(indices, positions, targetIndexCount, errorThreshold);
C++-fast. Used by gltf-transform.
5. Author pipeline
# Generate LODs in one step
gltf-transform simplify model.glb model.lod.glb \
--ratio 0.5 # target 50% of current tris
--error 0.01 # max error (model units)
Output still a single mesh. For multiple LOD levels baked into one glTF, author multiple meshes in Blender or use MSFT_lod extension.
6. Demo — LOD swap you can watch
A torus knot with 3 LOD levels. Slide distance to watch it snap between high/med/low versions.
7. Screen-space size vs distance
Distance alone is wrong — narrow FOV makes distant objects big on screen.
Better: project the bounding sphere. If projected radius < 50 px, use LOD2. < 10 px, billboard.
8. Continuous LOD (CLOD)
Instead of discrete steps, progressive mesh. Hoppe 1996: ordered sequence of edge collapses. At runtime, unpack as many as you need. Smooth transitions.
Rarely shipped today — discrete + good simplifier + Nanite-style meshlet DAG won.
9. Impostors (the ultimate LOD)
At far distance, render a 2D billboard instead of 3D geometry.
- Static impostor: pre-render the object from 24 angles, display the closest one. Trees, foliage.
- Octahedral impostor: sample 8×8 grid of view directions, interpolate. Better angle coverage.
- Runtime render-to-texture: unique per instance.
10. Gotchas
- Pop-in: obvious LOD switches. Fade between for half a second.
- Normal map mismatch: LOD0 vs LOD2 normal maps disagree on high-frequency detail. Bake normals from LOD0 onto LOD2's UVs.
- Silhouette: simplifier can collapse a delicate feature. Enforce min-vertex distance.
- Skinned meshes: joint indices must survive decimation. Some tools fail here.
11. Takeaways
- LOD = swap detail levels by distance or screen size.
- THREE.LOD does discrete swaps. Works today.
- QEM simplification generates cheap LODs automatically.
- gltf-transform's
simplifywraps meshoptimizer. - Use impostors for the extreme-distance tier.