使用 THREE.js 进行边界体积碰撞检测
本文介绍如何使用 Three.js 库在边界框和球体之间实现冲突检测。假设在阅读本文之前,您已经先阅读了我们的 3D 碰撞检测介绍性文章,并了解了 Three.js 的基本知识。
推荐:使用NSDT场景编辑器快速搭建3D应用场景
使用 Box3 和 Sphere
三.js具有表示数学体积和形状的对象 - 对于3D AABB和边界球体,我们可以使用Box3
和Sphere
对象。实例化后,它们具有可用于针对其他卷进行交集测试的方法。
实例化盒子
要创建 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);
Box3
与Box3
Box3.intersectsBox
方法可用于执行此测试。
.JS复制到剪贴板
knotBbox.intersectsBox(otherBox);
注意:这与检查 Box3 是否完全包装另一个的方法不同。Box3.containsBox
Sphere
与Sphere
与以前类似的方式,有一个 Sphere.intersectsSphere
方法来执行此测试。
.JS复制到剪贴板
knotBSphere.intersectsSphere(otherSphere);
Sphere
与Box3
不幸的是,这个测试没有在 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;
};
演示
我们准备了一些现场演示来演示这些技术,并提供了要检查的源代码。
使用 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
由3D建模学习工作室 整理翻译,转载请注明出处!