任何实时三维交互式程序,如果没有碰撞检测,都是没有价值,甚至无法使用的。游戏中最常用的碰撞检测技术莫过于包围盒(bounding volume)碰撞检测。对于以60pfs运行的游戏来说,处理每一帧数据的时间只有0.0167s左右,对于不同的游戏,碰撞检测大概需要占10~30%的时间,也就是说,所有碰撞检测必须在0.002~0.005s内完成,非常巨大的挑战。因此,任何包围盒都应该满足以下特性:
1. 快速的碰撞检测
2. 能紧密覆盖所包围的对象
3. 包围盒应该非常容易计算
4. 能方便的旋转和变换坐标
5. 低内存占用
最常见的包围盒有:Sphere,AABB,OBB等,外加一个比较特殊的frustum。Sphere能很好的满足1,3,4,5条,但通常包含了太多无用的空间,容易导致错误的碰撞结果。AABB应该是sphere与obb之间的解决方案,同时兼顾了效率和空间覆盖范围。OBB是三者中精度最高的,但检测代价也是最高的。
最终使用哪一种包围盒,是一个非常痛苦的过程,我们需要在效率和精度之间做出权衡取舍。前几天刚好完成了基本的碰撞检测函数,以下是我的一些测试数据,在一定程度上可以作为参考。纯C#代码实现,没有任何GPU加速,单线程在Q6600上运行。
Sphere-Sphere:100万次测试,大约有16000次碰撞,耗时0.016s。
AABB-AABB:100万次测试,1000次碰撞,耗时0.014s。
OBB-OBB: 使用传统的separate axis算法,100万次测试,30万次碰撞,耗时0.160s左右。对于没有碰撞的情况,几乎在前6条轴的检测中,就能结束检测,也就是说大约50万次(50%)测试都在检测第七条轴之前结束。
Vertical-agliened OBB - Vertical-agliened OBB:普通OBB的特殊版本,只能绕Y轴旋转。100w次测试,同样30万次碰撞,耗时0.08s,几乎比普通OBB快了一倍。
最后Frustum-AABB:使用<<Optimized View Frustum Culling Algorithms for Bounding Boxes>>中的算法,100w次测试,6万次碰撞,耗时0.096s。目前我计算n-vertex和p-vertex的方法是瓶颈,大约0.016s的时间花在计算这两个点上。 相比XNA中的BoundingFrustum.Intersects,同样的测试需要0.5s左右。
(以上均为对随机数据的测试,因此不同包围盒之间的实际碰撞次数并没有可比性,也不代表不同类型间的精度)
显然,AABB是性价比最高的,OBB虽然有较高精度,但相对其计算代价来说,并不划算,可以考虑用多个AABB来近似OBB,或者使用代价相对较低的Vertical-agliened OBB。Sphere看起来简单,但计算涉及到开方(虽然Math.Sqrt会直接编译为fsqrt指令),因此仍然没有AABB快(只需要6条逻辑比较指令)。