高级碰撞检测及响应算法——碰撞检测

1.概述

移动的物体可以由椭球体近似表达,这种椭球体更容易逼近类人和动物的形状,比如说人的头,就是一个X-Y-Z轴半径相等的椭球体,髋骨,盆骨等都可以较好地用椭球体体现出来。多个椭球体组成的集合的形状也使它们易于在障碍物上平滑地移动,这一点在3D游戏中显得特别重要,因为玩家绝不希望在激烈的战斗中自己被卡在某个死角里不能动弹。

我们希望能在场景中来回移动我们的物体(或者角色)。它可以由一个椭球体表现,其中椭球体的中心位置代表了角色的位置,半径向量则定义了椭球体沿三个轴向的尺寸。见图3.1。

图3.1:椭球体的半径向量

通过对角色施加某方向的力,他就能在场景世界中移动。这个过程由速度向量(velocity vector)表示。我们希望椭球体能够在场景世界中移动,那么它的新的位置等于它当前位置加上速度向量。见图3.2。

图3.2:通过一个速度移动椭球体

但是我们还不清楚我们是否能成功完成这个移动,因为可能在过程中会出现一些事情,例如组成场景世界的一个或者多个三角片挡住了椭球体的去路。我们不可能事先准确地知道椭球体会撞上哪个三角片,所以我们应该检查所有的三角片(这里,可以将一个大的网格体化分成一个八叉树,这个八叉树被用来帮助我们检查那些靠近角色的三角片)。同时,我们还不能在检测到一个可能碰撞的三角片后就立即停止检测,因为我们要检测出所有潜在的障碍,近而找出最近的那一个碰撞。如果我们检测到了一个与三角片A发生的碰撞后就停止,而没有继续检测其它的三角片,例如三角片B,那么将发生如图3.3所示的情况,即三角片B比三角片A更先发生碰撞。

图3.3:必须检测所有的三角片

碰撞检测过程应该为后继的响应阶段提供至少两个必要的信息:

   *   球体在场景中的碰撞位置。

   *   球体发生碰撞之前,沿速度方向到碰撞点的距离。

所以,对于单个三角片的碰撞检测,我们首先要清楚是否会发生碰撞(这将产生一个bool值),如果发生了碰撞,算法应该能够为碰撞响应提供上述两个必要的信息。

2.单个三角片的检测

在e空间中,有一个三角片,它由p1,p2和p3三个顶点所定义,它们以顺时钟排序。另外有一个球体,它位于基点,basePoint,并正沿着它的速度方向运动,速度大小为velocity。如果我们用 t 作为自变量,用方程表达球体的运动过程,那么我们有球体的位置方程:

在三维空间中,一个球体可以由它的位置和半径唯一表示(在e空间中半径是1)。对于运动的球体,半径是常数但是位置是不断变化的。上面的公式给出了球体在任何时刻 t 的位置,这就是所谓的扫掠球(swept sphere)。图3.4表示了这种关系,这一点很重要。由于velocity的大小不清楚,但它与时间的乘积直接影响着扫掠球的位置,而扫掠球每次所作的检测只在一个velocity的长度范围内。那么问题可以转化为:能否在一个[ 0,velocity ]范围内找到碰撞的情况,或者说能否在[0,1]的范围内,找到能满足碰撞特征的t值。在此过程中,解二次方程所得到的t 值的情况,跟velocity的大小有关,图3.9说明了这种关系。

图3.9:速度大小与碰撞的可能情况

同时,对于当前扫掠球的位置而言,可能与它所面对的三角片发生5种关系。具体来讲:

   ***   当VN=0时,此时球体平行于平面运动,球体中心到平面的距离要么为1, 要么小于等于1。对于前一种情况,检测提前退出;对于后一种情况,显然在t[0,1]范围内的所有球体位置中,球体与平面均保持相交。

   ***   当VN不等于0时,此时球体总会与平面发生碰撞(前提是速度方向有正对平面正面的分量),其形式无非有4种。第一种就是与三角片平面相交,但是不与三角片相交。

   ***   球体与三角片内部某点相交。

   ***   球体与三角片某顶点相交。

   ***   球体与三角片某边缘相交。

图3.4:球体的位置方程

算法的检测程序就从这里开始。其功能是用检测函数将一个扫掠球信息体(空间速度与位置、碰撞是否发生、什么地方发生和发生距离等)与单个三角片信息体(主要是三个顶点坐标信息)进行碰撞检测。并不断循环调用该函数,同时更换函数中的三角片信息体,使当前扫掠球与所有的三角片都被检测到,最后整理并填入扫掠球信息体中的碰撞信息,并在响应过程中更新速度与位置信息。

下面,第一个任务是检测扫掠球是否与三角片所在的平面相交。如果不相交,扫掠球当然不会跟三角片相交。所以我们构造一个平面,叫三角片平面trianglePlane,它是由三角片的三个顶点所确定的平面。如果我们有单位化的平面法向量N和平面常数Cp,那么我们可以计算点p到平面的有符号距离(signed distance):

图3.5可以看出该方法的原理。

图3.5:点p到平面的有符号距离的计算

图中OP为p点的方向向量,图中OI线段是N•p的绝对值,Cp是ON段所表示的距离,两者之和为p点到三角片平面的符号距离。之所以叫符号距离,是因为当点p位于三角片的正面区域(即法向量所指的一侧)时,该距离为正值;如果点p位于三角片的反面区域,该距离为负值。

如果扫掠球与三角片平面相交,如果速度大小刚好合适,那么总存在一个时刻 t0,扫掠球刚好位于平面的“前边”(front side),此时扫掠球(中心点)到三角片平面的有符号距离刚好为1;如果速度足够大,则它会穿越三角片平面,在某时刻 t1,它刚好位于平面"后边"(back side),则在时间t∈[t0,t1]上,扫掠球与平面处于相交状态。那么我们可以这样计算时刻 t0:

时刻 t1 是扫掠球到三角片平面的有符号距离刚好为-1时的时间点。同样,我们可以得到 t1:

如果 t0和 t1都不在[0,1]的范围内,即不在一次扫掠检测范围内,那么我们可以知道在速度方向上,扫掠球没有(或者说还没有)跟平面相交,于是不可能与三角片相交。否则,我们知道碰撞应该发生在时刻tcollisoin∈[t0,t1]。注意有一个特殊情况,如果N*velocity=0——那么我们不能使用上面的公式。但是这种情况在什么时候发生呢?它发生在速度向量垂直于平面法向量,或者说球体平行于平面运动的时候。这种情况下,有两种可能,一种是从basePoint点到三角片平面的绝对距离小于1,即嵌入三角片平面中。如果是这样,我们直接将变量设为:t0=0、t1=1,那么扫扫掠球将在一次检测范围内总是与平面相交。另一种是绝对距离大于1,那么我们知道碰撞不可能发生,可以提前结束检测。

既然我们知道了两个时刻 t0和 t1的值(可能两个值相同),那么出现了三种潜在的碰撞情况:

   *   在三角片内碰撞。

   *   与三角片其中一个顶点碰撞。

   *   与三角片其中一条边缘碰撞。

3.三角片内碰撞

这种碰撞情况最常见(当然取决于三角片的面积),如果扫掠球确实在三角片内部相交,那么可以说与顶点或者边缘的碰撞几率将变得很小,所以如果我们能快速地检测出发生在三角片内的碰撞,那么我们将节约大量的用于检测发生在顶点或者边缘的碰撞情况的时间。所以,我们先看这种情况。注意,这种情况只会发生在球体并没有嵌入平面的时候,即N*velocity≠0时。一个嵌入到平面的球体只可能与顶点或者边缘碰撞,就像游泳的人只能撞到岛屿的边缘或者顶角一样。

这里,主要计算出平面上的球体的接触点,我们将它叫作planeIntersectionPoint。我们已经知道接触发生在时刻 t0,该时刻是球体位于平面前边的临界时刻,但是这个接触点究竟在平面上哪个位置?这个平面接触点用如下的公式计算:

图3.6解释了这个公式的具体意义。球体上首先撞击平面的点由basePoint-planeNormal给出,接触点发生在时刻t0。

图3.6:三角片平面上的交点

现在我们要做的就是检查planeIntersectionPoint是否在三角片内部。有很多种函数方法完成这个功能。如果点在三角片内,那么我们得到了碰撞检测的结论:

   *   三角片平面上的交点就是三角片内的碰撞点,intersectionPoint=planeIntersectionPoint

   *   速度方向上,当前位置与发生碰撞的位置之间的距离,intersectionDistance=t0*|velocity|

4.扫掠测试(the sweep test)

如果球体没有碰撞三角片内部,那么我们不得不进行扫掠测试,从而判断是否与三角片的顶点或者边缘碰撞。这种情况下,主要的问题是检查是否存在一个时刻t,t∈[t0,t1]。在这个时刻点,扫掠球与顶点或者边缘发生了碰撞。

我们先看两种最简单的情况——扫掠球与一个顶点p碰撞。扫掠球与顶点的碰撞发生在什么时候呢?如果扫掠球中心与顶点之间的距离为1,那么认为两者发生了碰撞。为了便于计算,当球中心与顶点中心的距离的平方是1时发生碰撞。我们可以先定义一个等式:

上面的式子可以化为一个二次方程的形式:

这里,

二次方程通常有两个解,这在此例中是有意义的。扫掠球与顶点的距离为1的时间点可能有两个——三角片的正面与反面都存在这个时间点。我们关心的是与顶点的第一个碰撞(或者说最先与之发生的碰撞),所以我们取最小值(因为解就是碰撞的时间值)。如果有了合法的最小解x1,那么我们所需的交点数据可以得到:

intersectPoint = p

intersectionDistance=x1*|velocity|

即使与顶点发生了碰撞,我们仍然要检测三角片的边缘,看是否可能在更短的时间点时,与某一个片边缘发生了碰撞。这种检测比检测顶点碰撞更复杂,但是在本质上是一样的。考虑由p1到p2的边缘,令

首先我们检测是否存在某时间点,扫掠球与一条沿着边缘方向的直线(infinite line)发生碰撞。这种情况发生在扫描球中心与无穷线的距离为1时。图3.7描述了这种情况。

 图3.7:扫掠球与边缘所在直线碰撞这个关系也可以得到一个二次方程,其中的方程变量系数为:

 如果该方程有最小值x1,那么我们知道扫掠球会与直线相交于同一点,但是我们还不知道这个点是否在三角边的边缘线段内。所以,如果我们用函数 f 来描述直线L,使得L(f0),那么L(0)=p1,L(1)=p2,图3.8说明了f0的意义。

图3.8:判断交点是否在边缘线段内

于是我们可以在直线上能过计算f0 判断交点是否在边缘线段内:

如果f0∈[0,1],那么交点在边缘线段中,并且在时刻x1球碰撞了三角片的边缘。有关交点的数据如下:

intersectionPoint=p1+f0*edge

intersetionDistance=x1*|velocity|

5.总结

以上就是碰撞检测部分的全部内容。总结一下整个流程:

   *   首先计算三角片平面

   *   然后找出扫掠球与三角片平面相交的时间点 t0和 t1

   *   接下来检测发生在三角片内部的碰撞。由于安排在顶点或者边缘碰撞的前面,所以我们可以节省下扫掠测试的时间

   *   然后,我们将球体扫掠三角片的顶点和边缘

   *   完成以上步骤,我们就知道是否发生碰撞(bool),在哪里发生碰撞(intersectionPoint),距离如何(intersectionDistance),于是我们可以找出最先发生的那个碰撞

   *   继续检测下一个三角片直到所有的三角片都被检测,最后可知道角色有没有碰撞,与哪些三角片以何种形式碰撞,以及最先的碰撞是哪个。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_38220914

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值