Three.js Shadow Bias for Directional Lights
In Three.js, achieving realistic shadows with directional lights
often requires fine-tuning the shadow.bias property. This
article explains what shadow.bias is, how it resolves
common rendering artifacts like “shadow acne” and “peter panning,” and
how to find the optimal values for your 3D scenes.
What is Shadow Bias?
Three.js uses shadow mapping to render shadows. The engine renders the scene’s depth from the perspective of the directional light, storing this data in a texture called a shadow map. When rendering the final scene, Three.js compares the depth of each pixel with the shadow map to determine if it is in shadow.
Due to the limited resolution of shadow maps and precision limits in floating-point calculations, surfaces can incorrectly shadow themselves. This results in an unwanted visual artifact known as shadow acne, which appears as ugly, dark patterns or stripes across surfaces that should be lit.
The shadow.bias property solves this by adding a tiny
offset to the depth comparison. It slightly pushes the depth calculation
away from the light source, preventing surfaces from self-shadowing.
Tuning Shadow Bias: Finding the Balance
Adjusting shadow.bias is a balancing act between two
common artifacts:
1. Shadow Acne (Bias is too low)
If your shadow.bias is set to 0 (the
default) or is too low, you will see shadow acne. To fix this, you
gradually increase the bias value.
// Example of applying shadow bias to a directional light
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.castShadow = true;
// Start with a very small positive value
directionalLight.shadow.bias = -0.0005; 2. Peter Panning (Bias is too high)
If you increase the bias too much, you will encounter an artifact called Peter Panning (named after the character whose shadow is detached from his body). The shadow will appear to float, detached from the base of the object casting it.
To resolve Peter Panning, decrease the shadow.bias until
the shadow reconnects with the object, while keeping it high enough to
prevent shadow acne.
Using Normal Bias
In addition to shadow.bias, Three.js offers
shadow.normalBias. While standard bias offsets the depth
along the light’s direction, normal bias offsets the depth along the
geometry’s surface normals.
Using shadow.normalBias is highly effective for
directional lights, especially on curved surfaces, as it reduces shadow
acne without causing severe Peter Panning.
// A common setup combining both properties
directionalLight.shadow.bias = -0.0005;
directionalLight.shadow.normalBias = 0.02;Best Practices for Tuning Directional Light Shadows
To minimize the need for extreme bias values, optimize your shadow camera’s frustum first:
- Tighten the Shadow Camera Frustum: Directional
lights use an orthographic camera for shadows
(
directionalLight.shadow.camera). Shrink thetop,bottom,left,right,near, andfarplanes of this camera so they tightly fit only the area where shadows are needed. A smaller frustum increases shadow map resolution and depth precision, drastically reducing shadow acne. - Adjust incrementally: Always tune
shadow.biasandshadow.normalBiasin very small increments (e.g., steps of0.0001or0.01respectively) until you find the sweet spot for your specific scene scale.