OBB碰撞檢測算法

1 OBB包圍盒

OBB(Oriented Bounding Box)包圍盒也被稱作有向包圍盒或定向包圍盒,它會隨着物體的移動、縮放、旋轉。簡單來說,它就是一個能夠旋轉的AABB包圍盒。在Cocos2d-x中使用過物理引擎的開發者一定見過當我們在物理世界中創建一個物體並開啓調試模式時,這個物體會被紅色的矩形包圍,當物體做平移或旋轉時,這個紅色矩形也會做同樣的操作,這個紅色矩形正是該物體的OBB包圍盒。如圖1-1所示:


圖1-1 相對於AABB包圍盒來講,OBB在碰撞精度上要高於AABB,但是精確度的提高同時帶來的就是效率的降低,OBB的算法無疑是要比AABB複雜的,同樣內存消耗也會更大,這個問題我們可以從OBB的表達式來得到結論。

OBB包圍盒的表達式:


表達一個AABB包圍盒或OBB包圍盒都有若干種方式,但是無疑需要選中裏面最優的一種。對於AABB來說,通常只要使用兩個頂點的座標信息標識一個AABB包圍盒就可以了,其它的頂點信息都可以通過計算得出。但是對於OBB包圍盒的表達方式只有兩點信息顯然是不夠的。
想要唯一標識一個OBB包圍盒我們大概會想到,使用8個頂點的集合、6個面的集合、1個頂點和3各彼此正交的邊向量,又或者是1箇中心點、1個旋轉矩陣和3個1/2邊長(注:一個旋轉矩陣包含了三個旋轉軸,若是二維的OBB包圍盒則是一箇中心點,兩個旋轉軸,兩個1/2邊長)。
上述最後一種方法就是最常用的方法,下面來看一段Cocos2d-x 3.3beta0中CCOBB.h中的代碼:

01.Vec3 _center;   // 中心點

02./*

03.以下三個變量爲正交單位向量,

04.定義了當前OBB包圍盒的x,y,z軸

05.用於計算矢量投影

06.*/

07.Vec3 _xAxis;    // 包圍盒x軸方向單位矢量

08.Vec3 _yAxis;    // 包圍盒y軸方向單位矢量

09.Vec3 _zAxis;    // 包圍盒z軸方向單位矢量

10.Vec3 _extents;  // 3個1/2邊長,半長、半寬、半高

 

Cocos2d-x 3.0beta0在CCOBB.h中定義了五個成員變量,每一個數據類型都是一個三維向量,包含了3個浮點數。也就是說,表達一個OBB包圍盒需要15個float類型的變量,佔用60個字節,然而表示一個AABB包圍盒僅需要兩個頂點,24個字節,從這一點上來說,OBB的內存消耗算很高了。

一種減少開銷的方案是:只存儲旋轉矩陣的兩個軸,只是在測試時利用叉積計算第三個軸,這樣可以減少CPU操作開銷並節省3個浮點數分量,降低20%內存消耗。

 

OBB包圍盒的創建:

如何用一個高效的算法構建一個緊密包圍的OBB包圍盒或是AABB包圍盒是一個很複雜的問題,因爲不同的形狀物體的包圍是不同的,這裏我們不去探討構建OBB包圍盒的原理,只瞭解一下Cocos2d-x 3.3beta0 中爲我們提供的構建方法。 在Cocos2d-x 中使用了兩種方法去計算OBB,第一種方法是一種簡化的OBB構建算法,由一個AABB包圍盒來確定最終OBB包圍盒,另外一種方法是通過協方差矩陣來確定一個方向包圍盒(實際上無論是AABB包圍盒還是OBB包圍盒,真正的難點便在於包圍盒的構建上)。
在Cocos2d-x中第一種方法用起來更爲簡單一些,例如:

1.AABB aabb = _sprite->getAABB();//獲取一個Sprite3D對象的aabb包圍盒

2.OBB  _obbt = OBB(aabb);//創建obb包圍盒

 

2 OBB包圍盒的碰撞檢測方法

OBB包圍盒的碰撞檢測方法常採用的是分離軸定理,首先簡單說明一下分離軸定理(separating axis theorem),通過分離軸定理我們可以得到如下結論: 如果能找到一個軸,兩個凸形狀在該軸上的投影不重疊,則這兩個形狀不相交。如果這個軸不存在,並且那些形狀是凸形的,則可以確定兩個形狀相交(凹形不適用,比如月牙形狀,即使找不到分離軸,兩個月牙形也可能不相交)。 這個定理也可以這樣理解,如果能找到一條直線,令包圍盒A完全在直線的一邊,包圍盒B完全在另一邊,則兩包圍盒不重疊。而這條直線便成爲分離線(在三維世界中被稱爲分離面),並且一定垂直於分離軸。
這裏我們還是先來用二維世界中的OBB來進行演示,明白其中原理之後三維物體的OBB便十分容易理解了,如圖2-1所示:


圖2-1
在圖2-1中,針對某一分離軸L,如果包圍盒A與包圍盒B在軸L上的投影的半徑和小於包圍盒中心點間距在L的投影距離,那麼包圍盒A與包圍盒B處於分離狀態。使用運算式來表達就是:|T * L|>rA + rB。在Cocos2d-x 3.3beta0中驗證相交時採用的並非這個方法,Cocos2d-x選用的方法是,取物體A及物體B的最大點和最小點的投影座標,然後比較A的最大點與B的最小點,B的最小點與A的最大點。注意,儘管這是一個二維圖像,但是實際上三維的圖面體在分離軸上的投影跟二維的原理是一樣的,都是一個線段。 現在知道了分離軸定理的原理,下面則是如何選取分離軸。由於潛在的分離軸可能存在無數種,所以我們並非去要逐個驗證,而是選取幾個可測試的軸即可。例如Cocos2d-x中所選用的OBB包圍盒的凸面體均爲長方體,我們就以長方體的碰撞爲例,兩個長方體的碰撞可以將其歸結爲以下幾種組合:面-面碰撞、面-邊碰撞、邊-邊碰撞(頂點視爲邊的一部分)。所以實際上在取分離軸的時候,只需要分別取第一個包圍盒的3個座標軸,第二個包圍盒的3個座標軸,以及垂直於某一軸的9個軸(其他的分離軸都是跟這15個分離軸中的某一個軸平行的軸,投影所得線段值都一樣,無需再驗證)。
這裏再解釋以下什麼是垂直於某一軸的9個軸,具體的做法就是,首先取包圍盒A的x軸方向的某一邊矢量,再取包圍盒B的x軸方向的某一邊矢量,對兩個矢量做叉積,求出一個垂直於A的矢量與B的矢量的方向矢量,這個結果就是需要使用的分離軸。按照這種做法,取A的x軸方向的邊矢量分別與B的三個軸方向的邊矢量做叉積,再取A的y軸方向與B的三個軸方向的邊矢量做叉積,再取A的z軸方向的邊矢量與B的三個軸方向的邊矢量做叉積,結果一共3*3個分離軸。如圖2-2所示:


圖2-2
圖中的頂點順序是按照Cocos2d-x中定義的OBB包圍盒的頂點順序定義的。圖中的x代表的就是包圍盒的x軸方向邊的矢量(注意這裏寫的是矢量,不是x,y,z座標軸),當然這個方向的

矢量有無數個,我們只需取一個代表性的用來計算叉積即可。

最後在Cocos2d-x 3.3beta0版本中提供了OBB碰撞檢測函數 bool OBB::intersects(const OBB& box) const。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章