使用 THREE.js 进行边界体积碰撞检测

本文介绍如何使用 Three.js 库在边界框和球体之间实现冲突检测。假设在阅读本文之前,您已经先阅读了我们的 3D 碰撞检测介绍性文章,并了解了 Three.js 的基本知识。

使用 THREE.js 进行边界体积碰撞检测
推荐:使用NSDT场景编辑器快速搭建3D应用场景

使用 Box3 和 Sphere

三.js具有表示数学体积和形状的对象 - 对于3D AABB和边界球体,我们可以使用Box3Sphere对象。实例化后,它们具有可用于针对其他卷进行交集测试的方法。

实例化盒子

要创建 Box3 实例,我们需要提供盒子的下限和上限。通常,我们希望这个AABB被“链接”到我们3D世界中的对象(如角色)。在 Three.js 中,实例具有对象的属性和边界。请记住,为了定义此属性,您需要事先手动调用。GeometryboundingBoxminmaxGeometry.computeBoundingBox

.JS复制到剪贴板

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,
);

注意:该属性将自身作为引用,而不是 .因此,在计算计算框时,应用于 的任何转换(例如比例、位置等)都将被忽略。boundingBoxGeometryMeshMesh

解决上一个问题的更简单的替代方法是稍后使用 设置这些边界,这将计算尺寸,同时考虑 3D 实体的变换任何子网格Box3.setFromObject

.JS复制到剪贴板

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);

实例化球体

实例化 Sphere 对象与此类似。我们需要提供球体的中心和半径,可以将其添加到 中可用的属性中。boundingSphereGeometry

.JS复制到剪贴板

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

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

遗憾的是,没有等效的 Sphere 实例。因此,如果我们应用变换或更改位置 ,我们需要手动更新边界球体。例如:Box3.setFromObjectMesh

.JS复制到剪贴板

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

交叉测试

点与Box3 / Sphere

两者都有一个包含点方法来执行此测试。Box3Sphere

.JS复制到剪贴板

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

Box3Box3

Box3.intersectsBox 方法可用于执行此测试。

.JS复制到剪贴板

knotBbox.intersectsBox(otherBox);

注意:这与检查 Box3 是否完全包装另一个的方法不同。Box3.containsBox

SphereSphere

与以前类似的方式,有一个 Sphere.intersectsSphere 方法来执行此测试。

.JS复制到剪贴板

knotBSphere.intersectsSphere(otherSphere);

SphereBox3

不幸的是,这个测试没有在 Three.js 中实现,但我们可以修补 Sphere 以实现 Sphere 与 AABB 交集算法。

.JS复制到剪贴板

// 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;
};

演示

我们准备了一些现场演示来演示这些技术,并提供了要检查的源代码。

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.

使用 BoxHelper

作为使用 raw 和 object 的替代方法,Three.js 有一个有用的对象,可以更轻松地处理边界框:BoxHelper(以前已弃用)。此帮助程序获取并为其计算边界框体积(包括其子网格)。这将生成一个表示边界框的新框,该框显示边界框的形状,并且可以传递给前面看到的方法,以便使边界框与 .Box3SphereBoundingBoxHelperMeshMeshsetFromObjectMesh

BoxHelper是处理 Three.js 中边界体积的 3D 碰撞的推荐方法。你会错过球体测试,但权衡是非常值得的。

使用此帮助程序的优点是:

  • 它有一个方法,如果链接的网格旋转或更改其尺寸,它将调整其边界框网格的大小,并更新其位置update()
  • 它在计算边界框的大小时会考虑子网格,因此原始网格及其所有子网格都已包装。
  • 我们可以通过渲染创建的 es 来轻松调试碰撞。默认情况下,它们是使用材质(用于绘制线框样式几何图形的三.js材质)创建的。MeshBoxHelperLineBasicMaterial

主要缺点是它只创建框边界体积,所以如果你需要球体与 AABB 测试,你需要创建自己的对象。Sphere

要使用它,我们需要创建一个新实例并提供几何体和(可选)将用于线框材料的颜色。我们还需要将新创建的对象添加到场景中才能渲染它。我们假设我们的场景变量被调用。BoxHelperthree.jsscene

.JS复制到剪贴板

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);

为了也拥有我们的实际边界框,我们创建了一个新对象并使其具有 的形状和位置。Box3Box3BoxHelper

.JS复制到剪贴板

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

如果我们更改位置、旋转、缩放等,我们需要调用该方法,以便实例与其链接匹配。我们还需要再次调用才能使 .Meshupdate()BoxHelperMeshsetFromObjectBox3Mesh

.JS复制到剪贴板

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);

执行碰撞测试的方式与上一节中解释的方式相同 — 我们使用 Box3 对象的方式与上述相同。

.JS复制到剪贴板

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

演示

您可以在我们的现场演示页面上查看两个演示。第一个展示了使用 .第二个执行盒与盒测试。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.

3D建模学习工作室 整理翻译,转载请注明出处!

NSDT场景编辑器 | NSDT 数字孪生 | GLTF在线编辑器 | 3D模型在线转换 | UnrealSynth虚幻合成数据生成器 | 3D模型自动纹理化工具
2023 power by nsdt©鄂ICP备2023000829号