NSDT工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜索引擎 - AI模型在线查看 - Three.js虚拟轴心开发包 - 3D模型在线减面 - STL模型在线切割 - 3D道路快速建模
本文介绍了用于在 3D 环境中实现碰撞检测的不同包围体技术。 后续文章将介绍特定 3D 库中的实现。
1、轴对齐边界框
与 2D 碰撞检测一样,轴对齐边界框 (AABB) 是确定两个游戏实体是否重叠的最快算法。 这包括将游戏实体包装在非旋转(因此轴对齐)框中,并检查这些框在 3D 坐标空间中的位置以查看它们是否重叠。
由于性能原因,存在轴对齐约束。 两个非旋转框之间的重叠区域可以仅通过逻辑比较来检查,而旋转框需要额外的三角运算,计算速度较慢。 如果您有要旋转的实体,则可以修改边界框的尺寸,使其仍然包裹对象,或者选择使用其他边界几何类型,例如球体(对旋转不变)。下面的动画 GIF 显示了 AABB 的图形示例,该示例调整其大小以适应旋转实体。 盒子不断改变尺寸以紧密贴合内部的实体。
1.1 点 vs. AABB
检查一个点是否在 AABB 内非常简单——我们只需要检查该点的坐标是否落在 AABB 内即可; 分别考虑每个轴。 如果我们假设Px、Py和Pz是该点的坐标, BminX–BmaxX
、 BminY–BmaxY
和 BminZ–BmaxZ
是AABB各轴的范围,我们可以使用以下公式计算两者之间是否发生碰撞: 以下公式:
JavaScript代码如下:
function isPointInsideAABB(point, box) {
return (
point.x >= box.minX &&
point.x <= box.maxX &&
point.y >= box.minY &&
point.y <= box.maxY &&
point.z >= box.minZ &&
point.z <= box.maxZ
);
}
1.2 AABB vs. AABB
检查 AABB 是否与另一个 AABB 相交与点测试类似。 我们只需要使用盒子的边界对每个轴进行一次测试。 下图显示了我们在 X 轴上执行的测试 - 基本上,范围 AminX–AmaxX
和 BminX–BmaxX
是否重叠?
从数学上来说,这看起来像这样:
JavaScript代码如下:
function intersect(a, b) {
return (
a.minX <= b.maxX &&
a.maxX >= b.minX &&
a.minY <= b.maxY &&
a.maxY >= b.minY &&
a.minZ <= b.maxZ &&
a.maxZ >= b.minZ
);
}
2、边界球
使用边界球来检测碰撞比 AABB 稍微复杂一些,但测试起来仍然相当快。 球体的主要优点是它们对旋转具有不变性,因此如果包裹的实体旋转,包围球体仍然是相同的。 它们的主要缺点是,除非它们包裹的实体实际上是球形的,否则包裹通常不太合适(即用边界球包裹人会导致很多误报,而 AABB 会是更好的匹配)。
2.1 点 vs. 边界球
要检查球体是否包含点,我们需要计算该点与球体中心之间的距离。 如果该距离小于或等于球体的半径,则该点位于球体内部。
考虑到两点 A 和 B 之间的欧氏距离为:
我们的点与球体碰撞检测公式如下:
JavaScript代码如下:
function isPointInsideSphere(point, sphere) {
// we are using multiplications because is faster than calling Math.pow
const distance = Math.sqrt(
(point.x - sphere.x) * (point.x - sphere.x) +
(point.y - sphere.y) * (point.y - sphere.y) +
(point.z - sphere.z) * (point.z - sphere.z),
);
return distance < sphere.radius;
}
2.2 球体 vs. 球体
球体与球体测试与点与球体测试类似。 这里我们需要测试的是球体中心之间的距离是否小于或等于它们的半径之和。
从数学上来说,这看起来像:
JavaScript代码如下:
function intersect(sphere, other) {
// we are using multiplications because it's faster than calling Math.pow
const distance = Math.sqrt(
(sphere.x - other.x) * (sphere.x - other.x) +
(sphere.y - other.y) * (sphere.y - other.y) +
(sphere.z - other.z) * (sphere.z - other.z),
);
return distance < sphere.radius + other.radius;
}
2.3 球体 vs. AABB
测试球体和 AABB 是否发生碰撞稍微复杂一些,但仍然简单且快速。 一种合乎逻辑的方法是检查 AABB 的每个顶点,对每个顶点进行点与球测试。 然而,这有点过分了——测试所有顶点是不必要的,因为我们只需计算 AABB 的最近点(不一定是顶点)与球体中心之间的距离,看看它是否小于或等于球体的中心距离就可以了。 半径。 我们可以通过将球体的中心限制在 AABB 的极限内来获得该值。
JavaScript代码如下:
function intersect(sphere, box) {
// get box closest point to sphere center by clamping
const x = Math.max(box.minX, Math.min(sphere.x, box.maxX));
const y = Math.max(box.minY, Math.min(sphere.y, box.maxY));
const z = Math.max(box.minZ, Math.min(sphere.z, box.maxZ));
// this is the same as isPointInsideSphere
const distance = Math.sqrt(
(x - sphere.x) * (x - sphere.x) +
(y - sphere.y) * (y - sphere.y) +
(z - sphere.z) * (z - sphere.z),
);
return distance < sphere.radius;
}
3、使用物理引擎
3D 物理引擎提供碰撞检测算法,其中大多数也基于包围体。 物理引擎的工作方式是创建一个物理实体,通常附加到它的视觉表示上。 这个物体具有速度、位置、旋转、扭矩等属性,还有物理形状。 该形状是碰撞检测计算中考虑的形状。
我们准备了一个实时碰撞检测演示(带有源代码),你可以查看该演示以了解此类技术的实际应用 - 它使用开源 3D 物理引擎 cannon.js。
BimAnt翻译整理,转载请标明出处