高级碰撞检测及响应算法——碰撞检测
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),于是我們可以找出最先發生的那個碰撞
?? *?? 繼續檢測下一個三角片直到所有的三角片都被檢測,最后可知道角色有沒有碰撞,與哪些三角片以何種形式碰撞,以及最先的碰撞是哪個。
總結
以上是生活随笔為你收集整理的高级碰撞检测及响应算法——碰撞检测的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java文件切割
- 下一篇: 更新软件后,英国客户给我的寻星工具耐心提