How Frustum Culling Works in Three.js

In Three.js, automatic frustum culling is a crucial performance optimization technique that prevents the GPU from wasting resources on objects outside the camera’s field of view. By calculating whether a 3D object’s bounding volume intersects with the camera’s viewing frustum, Three.js automatically skips the rendering process for off-screen objects. This article explains the mechanics of this process, how bounding volumes are used, and how developers can configure or troubleshoot frustum culling in their applications.

What is the Viewing Frustum?

The viewing frustum is the 3D region of space modeled by the camera that is visible on the screen. It is shaped like a truncated pyramid, bounded by six planes: near, far, top, bottom, left, and right. Anything lying entirely outside this pyramid is invisible to the user.

The Mechanism of Automatic Frustum Culling

By default, Three.js enables automatic frustum culling for all renderable objects, such as meshes and points. Before sending data to the GPU for rendering during each frame, Three.js performs a CPU-side check:

  1. Bounding Volume Generation: For every mesh, Three.js computes a simplified bounding volume, typically a bounding sphere or a bounding box (Box3). This volume represents the outermost boundaries of the complex 3D geometry.
  2. Frustum Intersection Test: The engine extracts the six mathematical planes defining the camera’s current viewing frustum. It then tests whether the object’s bounding volume intersects with these planes.
  3. Conditional Drawing: If the bounding volume is entirely outside the frustum, Three.js flags the object to be skipped during the current render pass. If any part of the bounding volume intersects or lies completely inside the frustum, the object is sent to the GPU to be drawn.

How It Improves Rendering Performance

Rendering 3D scenes can be highly resource-intensive. Frustum culling improves performance in several key areas:

Managing Frustum Culling in Your Code

While automatic frustum culling works seamlessly for most static objects, developers occasionally need to manage it manually. This is controlled via the frustumCulled property on Object3D:

mesh.frustumCulled = true; // Enabled by default

If you set this property to false, Three.js will bypass the intersection test and always send the object to the GPU, regardless of camera position.

Troubleshooting Common Issues

Sometimes, an object might suddenly disappear or “pop” out of view when its center moves off-screen, even if parts of it should still be visible. This usually happens when the bounding volume does not accurately reflect the geometry’s actual shape, a common issue when using custom vertex shaders or skinned mesh animations that displace vertices dynamically.

To resolve this, you can manually recompute the bounding volumes after modifying the geometry:

mesh.geometry.computeBoundingSphere();
mesh.geometry.computeBoundingBox();

Alternatively, if the geometry is highly dynamic and constantly changes shape, disabling frustum culling (mesh.frustumCulled = false) for that specific object ensures it remains visible, though at a slight performance cost.