Fix Three.js Z-Fighting Using polygonOffset
This article explains how to resolve z-fighting in Three.js using the
polygonOffset material property. You will learn what causes
this rendering artifact when dealing with coplanar geometry and receive
a step-by-step guide on how to configure your materials to ensure
overlapping surfaces render correctly without flickering.
Understanding Z-Fighting in Three.js
Z-fighting occurs when two or more polygons share the same, or nearly the same, 3D space (coplanar surfaces). Because the GPU’s depth buffer has limited precision, it cannot accurately determine which polygon is closer to the camera. This results in the surfaces rapidly flickering and overlapping as the camera moves.
The most effective way to solve this in Three.js without physically
moving your geometry is by using the polygonOffset property
on your materials.
How to Implement polygonOffset
The polygonOffset property tells the WebGL renderer to
artificially nudge the depth calculation of a material either forward or
backward in the depth buffer.
To implement this, you need to set three specific properties on your
THREE.Material:
polygonOffset: A boolean that enables or disables the offset. Set this totrue.polygonOffsetFactor: A factor that scales the depth offset based on the slope of the polygon relative to the camera.polygonOffsetUnits: A constant value added to the depth calculation to push the polygon forward or backward.
Code Example
Here is how to apply these properties to a material that you want to render on top of another coplanar surface:
import * as THREE from 'three';
// Create the background material (rendered normally)
const backgroundMaterial = new THREE.MeshBasicMaterial({
color: 0xcccccc
});
// Create the foreground material (offset to prevent z-fighting)
const foregroundMaterial = new THREE.MeshBasicMaterial({
color: 0xff0000,
polygonOffset: true,
polygonOffsetFactor: -1,
polygonOffsetUnits: -4
});
// Apply to meshes
const backgroundMesh = new THREE.Mesh(geometry, backgroundMaterial);
const foregroundMesh = new THREE.Mesh(geometry, foregroundMaterial);How to Choose the Right Values
The values you assign to polygonOffsetFactor and
polygonOffsetUnits determine how much the material is
shifted:
- Negative Values (e.g., -1, -4): Pull the geometry closer to the camera. Use negative values on the material you want to guarantee renders on top.
- Positive Values (e.g., 1, 4): Push the geometry away from the camera. Use positive values on the background material if you prefer to push it away instead of pulling the foreground forward.
Recommended Default Values
For most desktop and mobile displays, starting with a
polygonOffsetFactor of -1 and a
polygonOffsetUnits of -4 is highly effective.
If you still notice flickering at extreme angles, gradually increase the
magnitude of the units (e.g., -1, -8).
Avoid using excessively large numbers, as this can cause the offset material to render through other unrelated objects that should rightfully block it from view.