computeBoundingBox vs computeBoundingSphere in Three.js
In Three.js, calculating boundary volumes is essential for frustum
culling, raycasting, and collision detection. This article analyzes the
performance differences between computeBoundingBox and
computeBoundingSphere, explaining how they work under the
hood, their computational overhead during creation, and their efficiency
during runtime rendering tests.
Mathematical and Computational Complexity
To understand the performance difference, we must look at how Three.js calculates these two spatial boundaries over a 3D mesh’s geometry.
1. computeBoundingBox()
This method calculates an Axis-Aligned Bounding Box (AABB) by finding the minimum and maximum X, Y, and Z coordinates among all vertices in the geometry.
- Algorithm: A single-pass loop through the vertex buffer array.
- Complexity: \(O(N)\), where \(N\) is the number of vertices.
- Operations: For each vertex, it performs simple comparison operations (min/max checks) for the X, Y, and Z coordinates. Because comparison operations are incredibly cheap on modern CPUs, this calculation is highly efficient.
2. computeBoundingSphere()
This method calculates a bounding sphere that encapsulates all vertices of the geometry. It determines a center point and a radius.
- Algorithm: Three.js calculates the bounding box first (or uses the existing one) to find the center of the geometry. Then, it loops through all vertices to find the maximum Euclidean distance from this center point to any vertex, which becomes the radius.
- Complexity: \(O(N)\) but requires more mathematical operations per vertex than the bounding box.
- Operations: For each vertex, it calculates the
squared distance to the center (subtractions and multiplications). Once
the maximum squared distance is found, a single square root operation
(
Math.sqrt) is performed at the end to get the actual radius.
Creation Phase Winner:
computeBoundingBox is computationally cheaper to generate
than computeBoundingSphere because it relies solely on
simple comparison operations rather than arithmetic distance
calculations.
Runtime Performance (Usage Phase)
While generating a bounding box is faster than generating a bounding sphere, the primary performance difference lies in how Three.js uses these boundaries during the render loop.
By default, Three.js uses these boundaries for frustum culling—the process of ignoring objects that are outside the camera’s field of view to save GPU processing power.
Frustum Culling with Spheres
Testing whether a sphere is inside the camera frustum is incredibly fast. The engine only needs to calculate the distance from the sphere’s center to the six frustum planes and compare it to the sphere’s radius. This involves simple dot products and addition.
Frustum Culling with Boxes
Testing an Axis-Aligned Bounding Box against the camera frustum is mathematically more complex. It requires testing the eight corners of the box against the frustum planes, which involves significantly more conditional checks and floating-point operations.
Rendering Phase Winner: Bounding spheres are significantly faster for real-time frustum culling and collision detection checks.
Best Practices for Optimization
To ensure your Three.js application runs at a fluid 60 FPS or higher, follow these optimization guidelines:
- Avoid Calling Them in the Animation Loop: Both
computeBoundingBox()andcomputeBoundingSphere()should be called once after loading or modifying geometry. Never call them inside arequestAnimationFrameloop, as iterating through thousands of vertices every frame will cause severe CPU bottlenecks. - Leverage Default Culling: Three.js automatically
computes bounding spheres behind the scenes the first time a mesh is
rendered to perform frustum culling. You rarely need to manually call
computeBoundingSphere()unless you are doing custom raycasting or physics calculations before the first render. - Use Spheres for Broad-Phase, Boxes for Narrow-Phase: If you are building a custom collision system, use bounding spheres first to quickly rule out objects that are far apart (broad-phase). Only use the more computationally expensive bounding boxes when spheres intersect and you need more precise spatial alignment (narrow-phase).