Three.js Frustum Camera Visibility Guide
In web-based 3D graphics, rendering performance depends heavily on
only drawing what is visible to the user. This article explains the
function of the Frustum class in Three.js, detailing how it
represents the camera’s viewing volume and how it is used to determine
whether 3D objects are visible on screen. You will learn about automatic
frustum culling and how to manually perform visibility checks using the
Three.js API.
What is a Frustum?
In 3D computer graphics, a frustum (specifically a viewing frustum) is the region of space in the modeled world that may appear on the screen. It is shaped like a pyramid with its top cut off, representing the field of view of a camera.
The frustum is defined by six flat planes: * Near plane: The closest boundary to the camera. * Far plane: The furthest boundary from the camera. * Left, Right, Top, and Bottom planes: The lateral boundaries of the camera’s field of view.
Any 3D object located entirely outside of these six planes is invisible to the camera and does not need to be rendered.
Automatic Frustum Culling in Three.js
By default, Three.js handles visibility optimization automatically through a process called frustum culling.
Every standard 3D object inheriting from THREE.Object3D
has a property called frustumCulled, which is set to
true by default. Before rendering a frame, the Three.js
WebGLRenderer automatically checks each object’s bounding
sphere or bounding box against the camera’s frustum. If the object lies
completely outside the frustum, Three.js skips rendering it, saving
valuable GPU resources.
The Role of the THREE.Frustum Class
The THREE.Frustum class allows developers to manually
recreate, update, and query this viewing volume. This is highly useful
for CPU-side optimization, such as disabling heavy calculations, pausing
animations, or managing game logic for entities that are currently
off-screen.
To use the Frustum class, you must first instantiate it
and then update its planes to match the camera’s current position and
projection.
Updating the Frustum
Because the camera can move, rotate, or change its field of view, the frustum must be updated dynamically. This is done by extracting the frustum planes from the camera’s projection matrix:
const frustum = new THREE.Frustum();
const cameraViewProjectionMatrix = new THREE.Matrix4();
// Force update the camera matrices to ensure accuracy
camera.updateMatrixWorld();
camera.matrixWorldInverse.copy(camera.matrixWorld).invert();
cameraViewProjectionMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
// Update the frustum with the camera's projection matrix
frustum.setFromProjectionMatrix(cameraViewProjectionMatrix);Checking for Object Visibility
Once the frustum is updated, you can use several built-in methods to test the visibility of points, spheres, boxes, or entire objects:
frustum.intersectsObject(object): Checks if the bounding sphere of the specifiedObject3Dintersects the frustum.frustum.intersectsSphere(sphere): Checks if a specificTHREE.Sphereis inside or intersecting the frustum.frustum.intersectsBox(box): Checks if a specificTHREE.Box3is inside or intersecting the frustum.frustum.containsPoint(point): Checks if a specificTHREE.Vector3point lies inside the frustum.
Example: Manual Visibility Check
Here is how you can check if a mesh is visible to the camera during the animation loop:
function animate() {
requestAnimationFrame(animate);
// Update the frustum helper matrix
camera.updateMatrixWorld();
cameraViewProjectionMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
frustum.setFromProjectionMatrix(cameraViewProjectionMatrix);
// Check if a specific mesh is visible
if (frustum.intersectsObject(myMesh)) {
// Code to execute when the object is visible (e.g., play animation)
myMesh.material.color.setHex(0x00ff00);
} else {
// Code to execute when the object is hidden (e.g., pause updates)
myMesh.material.color.setHex(0xff0000);
}
renderer.render(scene, camera);
}By leveraging the THREE.Frustum class, developers can
write highly performant applications by ensuring that both rendering
pipelines and CPU-bound logical updates are only executed for objects
within the user’s active field of view.