任程的博客

计算机图形、动画、虚拟现实 / 3D Computer Graphics, Computer Animation, Virtual Reality / uStep禹步信息科技
  首页  :: 联系 :: 订阅 订阅  :: 管理

分析碰撞检测库Opcode

Posted on 2007-12-10 23:38  任程  阅读(6448)  评论(8编辑  收藏  举报

一.概述

Opcode是一个开源的碰撞检测库,其最大的特点是占用内存少(与其他的碰撞检测库相比而言),对一个完全二叉树中的每个三角形仅用20字节,所以它的文档中说是“Memory-optimized bounding-volume hierarchies”。

在碰撞检测中,利用检测树(Bounding-volume hierachies)结构进行碰撞排除是最普遍的方法(如RAPIDSOLIDQuickCD,PQP都是如此),Opcode也不例外,Opcode中默认的包围盒是AABB。就是说若用Opcode进行一个Mesh的碰撞检测,则它会为此Mesh建立AABB树。

Opcode可以对以下情况作出检测:
- Mesh-mesh
- Sphere-mesh
- Ray-mesh
- AABB-mesh
- OBB-mesh
- Planes-mesh

二.检测过程

调用Opcode的代码做检测时主要包含三部分:

1.Mesh生成碰撞树

这涉及到Opcode中的Model类和OPCODECREATE类,Opcode建立碰撞树分两步:首先建立一般的树(这个树最后被遗弃),然后利用一般的树建立一个优化的树。

Opcode可以建立的四种树(关于这几种树在后面的内存优化部分会讲到):

- Normal trees (2*N-1 nodes, full size)

- No-leaf trees (N-1 nodes, full size)

- Quantized trees (2*N-1 nodes, half size)

- Quantized no-leaf trees (N-1 nodes, half size)

从代码中可以看出在建立一般树的时候,Opcode允许指定对节点进行分组时的依据,如利用GeometryAABB盒所在的最长轴(SPLIT_LARGEST_AXIS)进行分组。

2.为上面所列的查询建立相应的ColliderQurey方式

对一类检测建立一个Collider,如Mesh-mesh查询用AABBTreeCollider,Sphere-mesh查询用SphereCollider等。

Qurey方式包括First ContactAll Contacts两种,另外还可以指定是否采用temporal coherence(时间连续性)。

3.建立cache

关于具体的调用函数在其文档中已经给出,不再罗列。

三.内存优化

如上所述,Opcode进行内存优化是其显著的特点,所以在这里专门提出。其优化方法如下:

1.合理组织内存,每个节点所占用的空间较少。如RAPID为树中的每个节点保存三个指针(本身、左子、右子),而通过特意组织内存后,完全可以只要两个指针(本身、左子),而另右子指针总在左子+1的位置上。

2.去掉叶子节点。在一个完全树中共有2*N-1个节点,其中N个为叶子节点,每个叶子节点包含一个Primitive(三角形)和其相应的BV(包围盒),通常情况下检测到叶子节点时先测试其BV碰撞情况再测试PrimitiveOpcode去掉了N个叶子节点,这样大大节省了内存空间,它将原叶子节点的Primitive指针信息放到其父节点里,也剔除了原叶子节点的BV,检测时直接检测PrimitiveBV的相交情况。虽然PrimitiveBV的相交测试较BV-BV复杂,但其少了BV-BV这一步所以性能上并没有什么损失。

这样优化后的内存占用量为原来的50%
 

这样去掉叶子节点后进行检测的伪代码如下:
 

A and B are two bounding volumes

A0 and A1 are both children of A

B0 and B1 are both children of B

test(A, B)

{

       if(A overlaps B)

       {

              // A0B0

              if(A0 is leaf)

                     if(B0 is leaf) test(leaf, leaf) => Primitive-Primitive test

                     else test(leaf, box) => Primitive-BV test

              else

                     if(B0 is leaf) test(box, leaf) => Primitive-BV test

                     else test(box, box) => BV-BV test

              // Repeat for A0B1

              …

              // Repeat for A1B0

              …

              // Repeat for A1B1

              …

       }

}

3.Quantize。每个float数被Quatize16位的整型数,在runtime阶段再进行Dequantize,进行Quantize的方法在Deering, Michael, Geometry Compression, Computer Graphics (SIGGRAPH95 Proceedings), pp. 13-20, August 1995.中。

进行QuatizeDequantize会导致精度的下降,以至检测不够精确,因此OpcodeDequatize时采取措施保证Dequatized的包围盒大于等于原本的包围盒,以防止检测的丢失。这样无疑会带来速度的下降,好在在采用2中优化方法后,包围盒的数量减少了一半,所以这个影响并不像对一个标准树的影响那样大。另外可以利用SAT-liteClass III axes的方法做粗糙检测(coarser BV-BV tests),使其快速收敛到primitive-BVprimitive-primitive检测,在Opcode的代码中提供了采取此方法的选项。

这样的优化又在2的基础上将其内存占用量减为50%

四.总结

总体上来说,Opcode在降低内存占用量的同时,良好的保持了快速性。

值得注意的是,没有一种碰撞检测库可以在任何情况下都达到最快速度,通常都是针对某一种或某些情况速度快一些,即使同样的场景,若物体动作情况不同,碰撞面多少的不同都可能会导致各个库的速度变化较大。

Opcode的文档中是这样叙述的:

As far as speed is concerned, it is a lot more difficult to provide accurate comparisons and fair figures. Often, simply changing the relative orientation of two models makes one library faster or slower than another. In many cases we have found our implementation to outperform RAPID (up to 5 times faster), mainly when objects were deeply overlapping. This is very encouraging since intersection testing using AABB trees usually takes 50% longer than using OBB trees in cases where there is a lot of overlap among the models. In some other cases RAPID was faster, mainly in close-proximity scenarii where OBBs are really more appropriate than AABBs (and indeed RAPID sometimes uses a single OBB-OBB test in those situations, whereas we need a lot more). Nonetheless it’s interesting to note our version was often not significantly slower than RAPID in those cases.

另外值得一提的是,北卡(UNC)GAMMA Group做了大量的碰撞检测工作,开发了多个碰撞检测库,包括用GPUOcclusion Query来做精细检测。

 Ps:这篇笔记是一年前写出的,今天偶然翻出,稍微整理放到这里,欢迎点评。