Prevent Three.js Shadow Acne with Shadow Bias

Shadow acne is a common visual artifact in 3D graphics that appears as strange, striped patterns of dark lines across illuminated surfaces. This article explains what causes shadow acne in Three.js and provides a clear, actionable guide on how to eliminate it by adjusting the shadow bias properties on your light sources.

What is Shadow Acne?

Shadow acne occurs due to the limited resolution and precision of shadow maps. When Three.js renders shadows, it creates a depth map from the perspective of the light source. During the main render pass, the engine compares the depth of each pixel to the shadow map to determine if it is in shadow.

Because of pixel grid alignment and floating-point precision limitations, multiple fragments on a flat or curved surface can incorrectly calculate that they are behind themselves. This self-shadowing creates alternating bands of light and dark pixels, known as shadow acne.

How to Prevent Shadow Acne in Three.js

The primary way to fix shadow acne is by offsetting the depth comparison using shadow bias. Three.js provides two properties on light shadow objects to resolve this issue: bias and normalBias.

To apply these, you modify the shadow properties of your light source (such as DirectionalLight or SpotLight):

// Create a directional light that casts shadows
const light = new THREE.DirectionalLight(0xffffff, 1);
light.castShadow = true;

// Adjust bias to eliminate shadow acne
light.shadow.bias = -0.0005;
light.shadow.normalBias = 0.02;

scene.add(light);

Understanding Bias vs. Normal Bias

To get the best visual results, it is important to understand how these two properties interact:

  1. shadow.bias: This property shifts the depth query slightly along the direction of the light. While a small negative value (e.g., -0.0001 to -0.001) can quickly fix shadow acne on flat surfaces, setting it too high causes a phenomenon called “Peter Panning,” where shadows appear detached and hover away from the base of the object.
  2. shadow.normalBias: This property shifts the shadow calculation along the geometry’s surface normals. It is highly effective for curved surfaces and organic shapes. Because it offsets the query outwards from the surface rather than away from the light, it drastically reduces shadow acne without causing the shadow detachment associated with regular bias.
  1. Start with normalBias: Set your light.shadow.normalBias to a low value like 0.02 or 0.05. For most models, this will clean up the acne on curved surfaces without detaching the shadows.
  2. Fine-tune with bias: If artifacts still exist on flat surfaces, introduce a very small negative light.shadow.bias (such as -0.0001).
  3. Optimize Shadow Map Resolution: Sometimes, increasing the shadow map size (e.g., light.shadow.mapSize.width = 2048) reduces the precision errors that cause acne in the first place, allowing you to use smaller bias values.