写在前面:
最近在做一个碰撞检测的工作,阅读了一下Memory-optimized bounding-volume hierarchies这篇文章,以下是这篇文章的原理简介。
包围层次盒简介:
一个完整的BV树由2*N-1个节点组成,其中N是输入模型中的几何体(通常是三角形)的数量。在完全树中,每个叶字节点包含一个几何体,这意味着其中有N个叶节点和N-1个内部节点。
其中内部节点用来存储空间划分的信息,叶子节点存储结合体的位置信息。
碰撞检测的代价:
方法一:
时间成本:
总
代
价
=
内
部
检
测
次
数
∗
单
次
内
部
节
点
检
测
代
价
+
叶
子
节
点
检
测
次
数
∗
单
次
叶
子
节
点
检
测
代
价
总代价=内部检测次数*单次内部节点检测代价+叶子节点检测次数*单次叶子节点检测代价
总代价=内部检测次数∗单次内部节点检测代价+叶子节点检测次数∗单次叶子节点检测代价
论文优化:
论文认为如果三角形发生碰撞,我们无论如何都要进行三角形重叠测试。在这种情况下,在进行三角-三角形测试之前进行OBB-OBB测试是浪费时间的。如果他们没有,我们也会检测到它,速度大致和以前一样快。
aabb检测速度和精确求相交检测时间差不多,他们认为可以跳过叶子节点的aabb检测直接进行精确的求交检测。用这种方式,就可以省略掉所有的叶子节点,从而大大的节省内存。
叶节点中只剩下一个基元指针。我们也可以删除它们,方法是将它们存储在父节点中,替换先前指向我们刚刚丢弃的叶节点的指针。通过将精确几何体的指针信息直接存放在上一层的内部节点之中从而实现了内存上的优化。
完整的树包含2*N-1个叶节点中的N个,通过这种方式直接扔掉了一半。而且这种测试还更加精确,很有可能查询会更早停止。与此同时,树的构造也更加精简。
方法二:
对于aabb tree的包围盒,用 int16代替float的32位数据,在损失不大的精度的同时,降低了至少一半的内存。
当然,使用稍微大一点的容量会导致在冲突查询期间进行更多的测试,这显然是要付出代价的,这是速度和内存之间通常的折衷。
最后,作者混合使用了这种方法和前一种方法(删除叶节点),这种混合成本低得多。混合使用这两种策略可以节省大量内存,从而大大减少缓存未命中。从内存中获取数据是处理流水线中最昂贵的步骤之一。作者认为在这个问题上,混合BV-tree比标准BV-tree要高效得多。
作者对包围盒进行量化,最终得到一个小到20字节的BV节点(量化的AABB为12字节,两个指针为8字节)。这不仅比单个标准AABB都小,而且还做到了标准树的一半节点。通过将指针替换为增量编码的16位索引,只需做一点额外的工作,就可以将其进一步减少到每个三角形16字节。
结论:
比较次数: 原始-原始对比确实会让测试更早的停下来。
速度: 要提供准确的比较和公平的数字要困难得多。通常,简单地更改两个模型的相对方向会使一个库比另一个库更快或更慢。在许多情况下,我们发现我们的实现优于RAPID(最多快5倍),主要是在对象高度重叠的情况下。这是非常令人鼓舞的,因为在模型之间有大量重叠的情况下,使用AABB树进行交集测试通常比使用OBB树多花50%的时间[4]。在其他一些情况下,Rapid更快,主要是在近距离的场景中,OBB确实比AABB更合适(事实上,Rapid有时在这些情况下只使用一种OBB-OBB测试,而我们需要更多的OBB-OBB测试)。有趣的是,在这些情况下,我们的版本通常并不比快速版本慢很多。
实验证实了丢弃叶节点和引入三角盒重叠测试通常会产生更快的查询。
结论:
论文提出了两种方法来大大降低包围盒层次结构的内存需求:使用原始BV测试丢弃叶节点,以及使用量化包围盒。混合使用这两种方法会将内存需求除以4,通常还会产生更快的冲突查询。
原文链接: http://www.codercorner.com/Opcode.htm