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.