Table of Contents#
- What is a Bounding Box in Three.js?
- Why Groups Matter: Understanding
THREE.Group - Prerequisites
- Step-by-Step Guide: Calculate Bounding Box for a Group
- Handling Edge Cases
- Practical Examples
- References
1. What is a Bounding Box in Three.js?#
A bounding box is an axis-aligned rectangular prism (or "box") that encloses a 3D object, representing its minimum and maximum spatial extent along the X, Y, and Z axes. In Three.js, the THREE.Box3 class is used to define such boxes.
Key Properties of THREE.Box3:#
min: ATHREE.Vector3representing the smallest (minimum) X, Y, Z coordinates of the box.max: ATHREE.Vector3representing the largest (maximum) X, Y, Z coordinates of the box.
By default, a Box3 is initialized with min set to (Infinity, Infinity, Infinity) and max set to (-Infinity, -Infinity, -Infinity) (an "empty" box). To compute the bounding box for an object, we use methods like setFromObject(), which traverses the object and its children to calculate the combined min and max values.
2. Why Groups Matter: Understanding THREE.Group#
A THREE.Group is a lightweight container object in Three.js. It does not render anything itself but allows you to group multiple 3D objects (meshes, sprites, or even other groups) into a single logical unit. This is useful for:
- Organizing complex scenes (e.g., grouping all parts of a car into a "carGroup").
- Applying transformations (position, rotation, scale) to multiple objects simultaneously.
To get the bounding box of a group, we need to compute the combined spatial extent of all its children. This is where Box3 shines, as it can traverse the group’s hierarchy and merge the bounds of all nested objects.
3. Prerequisites#
Before diving in, ensure you have:
- Basic familiarity with Three.js (e.g., scene, camera, renderer setup).
- Three.js library included in your project (via CDN or npm).
- A code editor (e.g., VS Code) and browser for testing.
Quick Setup via CDN:
Add this to your HTML file to include Three.js:
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r132/three.min.js"></script> 4. Step-by-Step Guide: Calculate Bounding Box for a Group#
Let’s walk through creating a group, adding objects, and computing its bounding box.
4.1 Set Up a Basic Three.js Scene#
First, we’ll set up a minimal Three.js scene with a camera, renderer, and lighting. This ensures we can visualize our group and its bounding box.
// Scene setup
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Add lighting (to see objects)
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(5, 5, 5);
scene.add(directionalLight);
// Camera position
camera.position.z = 15; 4.2 Create a Group and Add Objects#
Next, create a group and add 3D objects (e.g., cubes, spheres) to it. We’ll position the objects at different coordinates to demonstrate how Box3 merges their bounds.
// Create a group
const myGroup = new THREE.Group();
scene.add(myGroup); // Add group to the scene
// Add objects to the group
const geometry1 = new THREE.BoxGeometry(2, 2, 2); // Cube 1: size 2x2x2
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const cube1 = new THREE.Mesh(geometry1, material);
cube1.position.set(-3, 0, 0); // Position left of center
myGroup.add(cube1);
const geometry2 = new THREE.SphereGeometry(1.5, 32, 32); // Sphere: radius 1.5
const sphere = new THREE.Mesh(geometry2, material);
sphere.position.set(3, 2, 0); // Position right and above center
myGroup.add(sphere);
const geometry3 = new THREE.BoxGeometry(1, 4, 1); // Cube 2: tall and thin
const cube2 = new THREE.Mesh(geometry3, material);
cube2.position.set(0, -2, 0); // Position below center
myGroup.add(cube2); Now, myGroup contains three objects with varying positions and sizes.
4.3 Compute the Bounding Box with Box3#
To calculate the group’s bounding box, use THREE.Box3 and its setFromObject() method. This method recursively traverses the group and all its children to compute the combined min and max bounds.
// Create a Box3 and compute bounds from the group
const groupBoundingBox = new THREE.Box3().setFromObject(myGroup);
// Optional: Visualize the bounding box with Box3Helper
const boxHelper = new THREE.Box3Helper(groupBoundingBox, 0xff0000); // Red color
scene.add(boxHelper); // Add helper to scene to see the box What setFromObject() Does:
- Traverses all children of
myGroup(including nested groups). - For each child, computes its individual bounding box.
- Merges these boxes into a single
min/maxpair representing the group’s total extent.
4.4 Extract Height, Width, and Depth#
Once we have groupBoundingBox, we can extract dimensions using min and max:
- Width:
max.x - min.x(extent along X-axis). - Height:
max.y - min.y(extent along Y-axis). - Depth:
max.z - min.z(extent along Z-axis).
Code:
// Extract dimensions
const width = groupBoundingBox.max.x - groupBoundingBox.min.x;
const height = groupBoundingBox.max.y - groupBoundingBox.min.y;
const depth = groupBoundingBox.max.z - groupBoundingBox.min.z;
console.log("Group Dimensions:");
console.log(`Width: ${width.toFixed(2)}`); // e.g., 8.00
console.log(`Height: ${height.toFixed(2)}`); // e.g., 8.00
console.log(`Depth: ${depth.toFixed(2)}`); // e.g., 4.00 Output Explanation:
- The cube on the left (
position.x = -3) and sphere on the right (position.x = 3) give a width of3 - (-3) + 2 (cube width) + 3 (sphere diameter)? Wait, no—setFromObject()accounts for object sizes and positions. For precise values, the code above will log the exact computed width/height/depth.
5. Handling Edge Cases#
5.1 Empty Groups#
If a group has no children, setFromObject() will leave min as (Infinity, Infinity, Infinity) and max as (-Infinity, -Infinity, -Infinity), resulting in negative dimensions. Always check if the box is valid first:
const groupBoundingBox = new THREE.Box3().setFromObject(myGroup);
if (groupBoundingBox.isEmpty()) {
console.log("Group is empty—no bounding box.");
} else {
// Extract dimensions
} 5.2 Nested Groups#
setFromObject() recursively traverses all children, including nested groups. For example:
const parentGroup = new THREE.Group();
const childGroup = new THREE.Group();
childGroup.add(new THREE.Mesh(new THREE.BoxGeometry(1,1,1)));
parentGroup.add(childGroup);
const box = new THREE.Box3().setFromObject(parentGroup); // Works! Includes childGroup's mesh. 5.3 Dynamic Updates (Moving Objects in the Group)#
If objects in the group move, rotate, or scale, the bounding box will not update automatically. You must recompute it using setFromObject() after the change:
// Example: Move cube1 and update the bounding box
cube1.position.x = 5; // Move cube1 to the right
groupBoundingBox.setFromObject(myGroup); // Recompute bounds
boxHelper.update(); // Update the visual helper 6. Practical Examples#
6.1 Position a 2D Label Above a Group#
Use the group’s bounding box to position a DOM label (e.g., a "Name Tag") above the group in 3D space.
// Get the top-center of the group's bounding box
const groupCenter = new THREE.Vector3();
groupBoundingBox.getCenter(groupCenter); // Center of the box
const labelPosition = new THREE.Vector3(
groupCenter.x,
groupBoundingBox.max.y + 1, // 1 unit above the top of the box
groupCenter.z
);
// Convert 3D position to 2D screen coordinates
const screenPosition = labelPosition.project(camera);
const x = (screenPosition.x * 0.5 + 0.5) * window.innerWidth;
const y = -(screenPosition.y * 0.5 - 0.5) * window.innerHeight;
// Create a DOM label
const label = document.createElement("div");
label.textContent = "My Group";
label.style.position = "absolute";
label.style.left = `${x}px`;
label.style.top = `${y}px`;
label.style.color = "white";
document.body.appendChild(label); 6.2 Collision Detection Between Groups#
Check if two groups intersect using Box3.intersectsBox():
// Create a second group
const otherGroup = new THREE.Group();
otherGroup.add(new THREE.Mesh(new THREE.BoxGeometry(3,3,3), material));
otherGroup.position.set(5, 0, 0);
scene.add(otherGroup);
// Compute its bounding box
const otherBoundingBox = new THREE.Box3().setFromObject(otherGroup);
// Check for collision
if (groupBoundingBox.intersectsBox(otherBoundingBox)) {
console.log("Groups are colliding!");
} else {
console.log("No collision.");
} 7. References#
- Three.js Official Documentation: Box3
- Three.js Official Documentation: Group
- Three.js Box3Helper
- Three.js Coordinate System
By following this guide, you can accurately compute the bounding box of groups in Three.js and use their dimensions for positioning, collision detection, and more. Experiment with different group configurations to master edge cases like dynamic updates and nested hierarchies!