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.

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:

Avoid depthWrite: false (Keep depthWrite: true) for: