KDOP+BVH 碰撞检测算法

在计算机图形学、物理引擎以及求解器中的接触/绑定搜索(Contact / Tie Search)中,包围盒(Bounding Box)是加速几何查询和剔除无关计算的核心技术。常见的包围盒有 AABB(轴对齐包围盒)、OBB(定向包围盒)和K-DOP(Discrete Oriented Polytope,离散定向多胞体) 。其中,AABB(Axis-Aligned Bounding Box)其实是 K-DOP 的一种特例,即6-DOP。

K-DOP 的定义

k-DOP 可以理解为:用一组固定方向上的平行平面把几何体包起来。

如果有 k 个边界平面,就有 k/2 个方向。随着 k 值的增加,K-DOP 会包含更多方向的平面,从而能更紧密地包裹物体,但也会增加碰撞检测的计算开销。

下表列出了几个常见的K-DOP。

包围盒类型 k 值 示意图 特点与适用场景
AABB 6 image-20260916105648716 构建与碰撞检测速度最快,但对倾斜物体的包裹性较差。
14-DOP 14 image-20260916105730577 切去立方体的 8 个角,提供更好的紧密度。
18-DOP 18 image-20260916105805062 切去立方体的 12 条棱,常用于复杂几何体的高效碰撞剔除。
26-DOP 26 image-20260916105825777 包裹极其紧密,接近凸包,常用于高精度物理碰撞检测。

在代码实现上,每个方向 d_i 可以用两个点表示:

min_i = min(d_i · p)
max_i = max(d_i · p)

所以一个 k-DOP 实际存的是:

[min_0, max_0]
[min_1, max_1]
...
[min_{k/2-1}, max_{k/2-1}]

其中

  • \(p\) 是输入的模型顶点坐标(变量,来自网格数据)。
  • \(d_i\) 是预先设定好的投影方向(常量,决定了包围盒的形状类型)。
  • min_imax_i 保存了整个模型在 \(d_i\) 方向上的“最远边界”,由这 \(k/2\) 对区间共同夹出的空间,就是该物体的 K-DOP 包围盒

两个 k-DOP 的碰撞判断

碰撞检测很简单。两个物体在方向 (d_i) 上的投影区间分别为:

\[[A_i^{\min}, A_i^{\max}],\qquad [B_i^{\min}, B_i^{\max}] \]

只要任意一个方向上的区间不相交:

\[A_i^{\max}<B_i^{\min} \quad\text{或}\quad B_i^{\max}<A_i^{\min} \]

image-20260916132343239

两个 k-DOP 就一定不相交。只有所有方向上的区间都重叠,才认为包围体重叠,进入后续精确接触检测。

image-20260916132413503

BVH + k-DOP 搜索

单独 k-DOP 只能判断两个对象是否可能相交。为了避免所有面元两两比较,需要把 k-DOP 放进 BVH,然后进行接触搜索。BVH(Bounding Volume Hierarchy) 是一种在计算机图形学中广泛使用的空间加速数据结构。它实际上是一种树结构,叶节点对应一个真实的面单元,根节点包围整个表面,内部节点的包围体包含其所有子节点。

cf73afc6cc2406a765d67cc8cf3478e6

BVH 划分示意图[1]

BVH 节点可以定义成

struct BvhNode {
    BoundingVolume bounding_volume;

    int left_child;
    int right_child;

    int entry_index;  // 仅叶节点有效
};

判断是否为叶节点的方法是:

bool IsLeaf() const
{
    return m_entry_index != kBadIndex;
}

因此有两种节点状态:

叶节点:
    entry_index = 表面单元索引
    left/right  = 无效
    bounding_volume = 该单元的 k-DOP

内部节点:
    entry_index = 无效
    left/right  = 两个子节点索引
    bounding_volume = 两个子树包围体的并集

树的构建过程是:

  1. 给每个表面单元建立叶节点包围体
  2. 根据单元中心坐标选择划分轴
  3. 用 SAH 代价 ( Surface Area Heuristic ) 选择最佳分割位置
  4. 递归构建左右子树
  5. 将子树包围体合并为父节点包围体

两个 BVH 做碰撞搜索时,从两个根节点开始:

(rootA, rootB)
        |
        ├─ 包围体不重叠:整对子树全部丢弃
        |
        └─包围体重叠:
              |
              ├─ 都是叶节点:输出候选单元对
              |      
              └─ 至少一个是内部节点:展开子节点继续比较

两个表面单元的包围体重叠不能说明表面真的接触,后面还需要做精筛。

参考文献

[1] https://blog.csdn.net/zhanxi1992/article/details/127462494

posted @ 2026-09-16 14:07  雅可比晒太阳  阅读(8)  评论(0)    收藏  举报