Bounding volume collision detection using THREE.js

Recommended: Use
NSDT scene editor
Quickly build 3D application scenes

Using Box3 and Sphere

三.js has objects representing mathematical volumes and shapes – for 3D AABBs and bounding spheres we can use Box3 and Sphereobject. Once instantiated, they have methods that can be used for intersection testing against other volumes.

Instance box

To create a Box3 instance we need to provide the lower and upper limits of the box. Typically, we want this AABB to be “linked” to an object (such as a character) in our 3D world. In Three.js, instances have the properties and bounds of an object. Remember that in order to define this property you need to call it manually beforehand. GeometryboundingBoxminmaxGeometry.computeBoundingBox

.JS copied to clipboard

const knot = new THREE.Mesh(
  new THREE.TorusKnotGeometry(0.5, 0.1),
  new MeshNormalMaterial({}),
);

knot.geometry.computeBoundingBox();
const knotBBox = new Box3(
  knot.geometry.boundingBox.min,
  knot.geometry.boundingBox.max,
);

Note: This property takes itself as a reference, not . Therefore, any transformations (such as scale, position, etc.) applied to will be ignored when calculating the calculation box. boundingBoxGeometryMeshMesh

A simpler alternative to the previous problem is to set these bounds later using , which will calculate the dimensions taking into account theTransformandAny subgrid. Box3.setFromObject

.JS copied to clipboard

const knot = new THREE.Mesh(
  new THREE.TorusKnotGeometry(0.5, 0.1),
  new MeshNormalMaterial({}),
);

const knotBBox = new Box3(new THREE.Vector3(), new THREE.Vector3());
knotBBox.setFromObject(knot);

Instanced sphere

Instantiating a Sphere object is similar. We need to provide the center and radius of the sphere, which can be added to the properties available in . boundingSphereGeometry

.JS copied to clipboard

const knot = new THREE.Mesh(
  new THREE.TorusKnotGeometry(0.5, 0.1),
  new MeshNormalMaterial({}),
);

const knotBSphere = new Sphere(
  knot.position,
  knot.geometry.boundingSphere.radius,
);

Unfortunately, there is no equivalent Sphere instance. Therefore, if we apply a transformation or change the position, we need to update the bounding sphere manually. For example: Box3.setFromObjectMesh

.JS copied to clipboard

knot.scale.set(2, 2, 2);
knotBSphere.radius = knot.geometry.radius * 2;

Cross test

Points and Box3 / Sphere

Both have a include dot method to perform this test. Box3Sphere

.JS copied to clipboard

const point = new THREE.Vector3(2, 4, 7);
knotBBox.containsPoint(point);

Box3 and Box3

The Box3.intersectsBox method can be used to perform this test.

.JS copied to clipboard

knotBbox.intersectsBox(otherBox);

Note: This is different from checking whether Box3 completely wraps another. Box3.containsBox

Sphere and Sphere

Similar to before, there is a Sphere.intersectsSphere method to perform this test.

.JS copied to clipboard

knotBSphere.intersectsSphere(otherSphere);

Sphere and Box3

Unfortunately, this test is not implemented in Three.js, but we can patch Sphere to implement the Sphere and AABB intersection algorithm.

.JS copied to clipboard

// expand THREE.js Sphere to support collision tests vs. Box3
// we are creating a vector outside the method scope to
// avoid spawning a new instance of Vector3 on every check

THREE.Sphere.__closest = new THREE.Vector3();
THREE.Sphere.prototype.intersectsBox = function (box) {
  // get box closest point to sphere center by clamping
  THREE.Sphere.__closest.set(this.center.x, this.center.y, this.center.z);
  THREE.Sphere.__closest.clamp(box.min, box.max);

  const distance = this.center.distanceToSquared(THREE.Sphere.__closest);
  return distance <this.radius * this.radius;
};

Demo

We have prepared some live demos to demonstrate these techniques and provide source code to examine.

  • Points and boxes and spheres
  • Box vs. Box and Sphere
  • Sphere vs. box and sphere

A knot object, a large sphere object and a small sphere object in 3-D space. Three vectors are drawn on the small sphere. The vectors point in the directions of the three axes that define the space. Text at the bottom reads: Drag the ball around.

Use BoxHelper

As an alternative to using raw and object, Three.js has a useful object that makes handling bounding boxeseasier:BoxHelper (formerly Deprecated). This helper obtains and computes the bounding box volume (including its subgrids) for it. This will generate a new box representing the bounding box, which shows the shape of the bounding box, and can be passed to the method seen previously to make the bounding box the same as .Box3SphereBoundingBoxHelperMeshMeshsetFromObjectMesh

BoxHelper is the recommended way to handle 3D collision of bounding volumes in Three.js. You’ll miss out on orb testing, but the trade-off is well worth it.

The advantages of using this helper are:

  • It has a method that if the linked grid rotates or changes its dimensions, it will resize its bounding box grid and update its position. update()
  • It takes subgrids into account when calculating the size of the bounding box, so the original grid and all itssubgrids are wrapped.
  • We can easily debug collisions byrendering the created es. By default, they are created using materials (tri.js materials for drawing wireframe style geometry). MeshBoxHelperLineBasicMaterial

The main disadvantage is that it only creates a box bounding volume, so if you need a sphere to test with AABB, you need to create your own object. Sphere

To use it, we need to create a new instance and provide the geometry and (optionally) the color that will be used for the wireframe material. We also need to add the newly created object to the scene in order to render it. We assume our scene variable is called. BoxHelperthree.jsscene

.JS copied to clipboard

const knot = new THREE.Mesh(
  new THREE.TorusKnotGeometry(0.5, 0.1),
  new THREE.MeshNormalMaterial({}),
);
const knotBoxHelper = new THREE.BoxHelper(knot, 0x00ff00);
scene.add(knotBoxHelper);

To also have our actual bounding box, we create a new object and give it the shape and position of . Box3Box3BoxHelper

.JS copied to clipboard

const box3 = new THREE.Box3();
box3.setFromObject(knotBoxHelper);

If we change position, rotation, scale, etc., we need to call this method so that the instance matches its link. We also need to call it again to make .Meshupdate()BoxHelperMeshsetFromObjectBox3Mesh

.JS copied to clipboard

knot.position.set(-3, 2, 1);
knot.rotation.x = -Math.PI / 4;
// update the bounding box so it stills wraps the knot
knotBoxHelper.update();
box3.setFromObject(knotBoxHelper);

The way to perform collision testing is the same as explained in the previous section – we use Box3 objects in the same way as above.

.JS copied to clipboard

// box vs. box
box3.intersectsBox(otherBox3);
// box vs. point
box3.containsPoint(point.position);

Demo

You can view both demos on our live demos page. The first shows the use of . The second performs box-to-box testing. BoxHelper

A knot object, a sphere object and a cube object in 3-D space. The knot and the sphere are encompassed by a virtual bounding box. The cube is intersecting the bounding box of the sphere. Text at the bottom reads: Drag the cube around. Press Esc to toggle B-Boxes.

Original link: Boundary volume collision detection using THREE.js (mvrlink.com)