Create Invisible Occlusion Volumes in Three.js

This article explains how to create an invisible bounding volume in Three.js that hides (occludes) other 3D objects behind it. By utilizing a material with its colorWrite property disabled, you can render an invisible mesh that still writes to the depth buffer, effectively masking out background geometry. This technique is widely used for creating augmented reality portals, visual transitions, and hiding the interior parts of complex 3D models.

Understanding ColorWrite and Depth Buffering

In 3D rendering, the GPU uses two primary buffers to determine what appears on screen: the color buffer (which stores the pixels you see) and the depth buffer (which stores the distance of objects from the camera).

Normally, when a mesh is rendered, it writes to both buffers. By setting the colorWrite property of a material to false, you tell Three.js to skip writing any visual pixels to the screen. However, because depthWrite remains enabled (true by default), the mesh’s shape is still recorded in the depth buffer. Any object positioned behind this invisible mesh will fail the depth test and will not be rendered, creating a perfect occlusion effect.

Step-by-Step Implementation

To implement an invisible occluder, you need to create a mesh with a custom material and ensure it renders before the objects you want to hide.

1. Define the Occluding Material

Create a basic material and set colorWrite to false.

const occluderMaterial = new THREE.MeshBasicMaterial({
    colorWrite: false, // Prevents rendering to the color buffer (makes it invisible)
    depthWrite: true   // Allows writing to the depth buffer (enables occlusion)
});

2. Create and Position the Occluding Geometry

Define the geometry that represents your boundary. This can be a box, plane, cylinder, or any custom shape.

// Create a box geometry to act as the invisible boundary
const occluderGeometry = new THREE.BoxGeometry(2, 2, 2);
const occluderMesh = new THREE.Mesh(occluderGeometry, occluderMaterial);

// Position the occluder in your scene
occluderMesh.position.set(0, 0, 0);
scene.add(occluderMesh);

3. Handle Render Order

For depth occlusion to work reliably, the invisible occluder should be rendered before the objects it is meant to hide. While Three.js automatically sorts opaque objects from front to back to optimize rendering, you can explicitly guarantee the rendering sequence using the renderOrder property.

Assign a lower renderOrder to the occluder, or a higher renderOrder to the objects behind it.

// Render the occluder first
occluderMesh.renderOrder = 1;

// Create the object that will be hidden
const targetGeometry = new THREE.SphereGeometry(0.5, 32, 32);
const targetMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const targetMesh = new THREE.Mesh(targetGeometry, targetMaterial);

// Position the target behind the occluder relative to the camera
targetMesh.position.set(0, 0, -2); 

// Render the target after the occluder
targetMesh.renderOrder = 2; 

scene.add(targetMesh);

With this setup, the GPU first processes the occluderMesh, blocking out that region of the screen in the depth buffer without drawing any pixels. When it subsequently attempts to render targetMesh, any parts of the sphere that lie behind the invisible box are discarded, achieving the desired occlusion.