How depthWrite Affects Transparency in Three.js
In Three.js, managing how transparent objects render in relation to
the depth buffer (z-buffer) is crucial for avoiding visual artifacts
like clipping and invisible geometry. This article explains how the
depthWrite property controls whether transparent objects
write their depth data to the z-buffer, how this impacts rendering
order, and how to configure it to resolve common transparency sorting
issues in 3D scenes.
Understanding the Z-Buffer and depthWrite
The z-buffer (or depth buffer) is a grayscale image corresponding to the screen viewport that keeps track of the depth (distance from the camera) of every pixel rendered in a 3D scene. When a new pixel is about to be drawn, the renderer compares its depth to the value already stored in the z-buffer (the depth test). If the new pixel is closer, it is drawn, and the z-buffer is updated with the new depth value.
The depthWrite property is a boolean on Three.js
materials (Material.depthWrite) that dictates whether a
material’s depth values are saved to this z-buffer.
depthWrite: true(Default): The object writes its depth to the z-buffer. Subsequent objects behind it will fail the depth test and will not be rendered.depthWrite: false: The object is rendered on the screen, but its depth is not written to the z-buffer. Objects behind it can still be drawn afterward.
The Problem with Transparency and depthWrite: true
By default, Three.js renders opaque objects first (from front to back) to optimize rendering performance, followed by transparent objects (from back to front) to ensure proper color blending.
However, if a transparent material has depthWrite set to
true, it writes its depth to the z-buffer. If the
renderer’s sorting algorithm incorrectly estimates the distance of
overlapping transparent objects, a transparent object in the foreground
might be rendered before a transparent (or even opaque) object behind
it.
Because depthWrite is active, the foreground transparent
object writes its “close” depth to the z-buffer. When the renderer tries
to draw the object behind it, the z-buffer block prevents the background
object from rendering. This results in “transparency clipping,” where
objects behind a transparent surface mysteriously disappear.
How depthWrite: false Solves Transparency Artifacts
Setting depthWrite: false on transparent materials is
the standard solution for these rendering artifacts.
When you set depthWrite: false on a transparent
material: 1. The transparent object is still drawn on the screen and
blends its colors with whatever is already behind it. 2. It does
not write its depth to the z-buffer. 3. Any geometry rendered
after this transparent object will still pass the depth test
and be drawn correctly, allowing you to see through the transparent
object to the objects behind it.
const transparentMaterial = new THREE.MeshStandardMaterial({
color: 0xffffff,
transparent: true,
opacity: 0.5,
depthWrite: false // Prevents this material from blocking objects behind it
});When to Use and When to Avoid depthWrite: false
While disabling depth writing solves most transparency sorting issues, it is not a one-size-fits-all solution.
Use depthWrite: false
for:
- Particles and Volumetrics: Smoke, fire, dust, and
clouds should almost always have
depthWriteset tofalseto allow smooth blending without clipping. - Glass and Liquids: Simple glass planes, windows, and water surfaces.
- Overlapping Transparent UI Elements: Billboards or 2D sprites floating in 3D space.
Avoid
depthWrite: false (Keep depthWrite: true)
for:
- Opaque Objects: Standard solid models must write to the depth buffer to occlude objects behind them.
- Complex Semi-Transparent Solids: If a single
transparent 3D model has complex internal geometry (like a
semi-transparent car body or human figure), setting
depthWrite: falsecan cause self-sorting issues where the back of the object renders on top of the front. In these cases, you may need to keepdepthWrite: trueand rely on splitting the mesh or adjusting therenderOrderproperty.