Three.js From Zero · Article s6-06

S6-06 LOD Tools

Season 6 · Article 06

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 simplify wraps meshoptimizer.
  • Use impostors for the extreme-distance tier.