What is Three.js frustumCulled and when to disable it
In Three.js, performance optimization is key to maintaining smooth
frame rates, and the frustumCulled property plays a vital
role in this process. This article explains what
frustumCulled is, how it works to improve rendering
performance, and the specific scenarios where you must manually disable
it to prevent objects from rendering incorrectly or disappearing from
your 3D scene.
Understanding frustumCulled in Three.js
By default, Three.js enables frustum culling for all 3D objects
(Object3D.frustumCulled = true).
The “frustum” is the three-dimensional region of space visible to the camera. It is shaped like a pyramid with the top cut off. Frustum culling is an optimization technique where Three.js checks if an object’s bounding box or bounding sphere is inside this visible region before rendering it.
- If the object is inside the frustum: It is sent to the GPU to be rendered.
- If the object is outside the frustum: Three.js skips rendering it entirely, saving valuable CPU and GPU resources.
To perform this check quickly, Three.js uses the object’s static geometry to calculate a bounding sphere. However, because this calculation happens on the CPU before the rendering step, it can sometimes become inaccurate.
When to manually disable frustumCulled
There are specific situations where the CPU-calculated bounding volume no longer matches the actual position or shape of the object. When this happens, Three.js may mistakenly believe an object is off-screen and stop rendering it, causing the object to flicker or suddenly disappear while still in the camera’s line of sight.
You can resolve this by setting the property to
false:
mesh.frustumCulled = false;Here are the primary scenarios where you should manually disable
frustumCulled:
1. Vertex Shader Displacements (GPU Animations)
If you are using a custom ShaderMaterial to animate or
displace vertices on the GPU (for example, creating waving water,
wind-blown grass, or exploding particles), the CPU is unaware of these
changes. The CPU still uses the original, flat geometry to calculate the
bounding sphere. If the GPU pushes the vertices into the camera’s view
while the original bounding sphere remains off-camera, Three.js will
cull the object. Disabling frustum culling ensures the shader-displaced
object remains visible.
2. Complex Skinned Meshes and Bone Animations
Skinned meshes (like 3D characters) are animated using a skeleton.
Sometimes, character animations can cause hands, limbs, or held objects
to stretch far beyond the character’s original bounding box. If the root
of the character goes off-screen, Three.js might cull the entire mesh,
causing the extended limbs or accessories to instantly disappear.
Setting mesh.frustumCulled = false on the skinned mesh or
its sub-meshes prevents this popping effect.
3. Dynamic Geometry Modifications
If you are constantly modifying the vertices of a geometry
programmatically on the fly, the bounding sphere will become outdated.
While you can manually recalculate the bounding sphere using
geometry.computeBoundingSphere() after every change, doing
so can be CPU-intensive. For frequently updated, highly dynamic
geometries, disabling frustum culling is often a more performant and
simpler solution.
4. Instanced Meshes with Wide Spreads
When using InstancedMesh to render thousands of
identical objects (like trees or rocks) across a large map, Three.js
calculates a single bounding box that encompasses all instances. If the
camera looks away from the origin of the instanced mesh but is still
looking at some of the instances spread across the map, the entire group
might get culled. Disabling frustum culling on the
InstancedMesh guarantees that all instances remain visible
regardless of the camera’s position.