1. 程式人生 > >矩形旋轉碰撞,OBB方向包圍盒演算法實現

矩形旋轉碰撞,OBB方向包圍盒演算法實現

如何進行2D旋轉矩形的碰撞檢測,可以使用一種叫OBB的檢測演算法(Oriented bounding box)方向包圍盒。這個演算法是基於SAT(Separating Axis Theorem)分離軸定律的。而OBB不僅僅是計算矩形的碰撞檢測,而是一種演算法模型。簡單解釋一下概念,包圍盒和分離軸定律。

包圍盒:是根據物體的集合形狀,來決定盒子的大小和方向,這樣可以選擇最緊湊的盒子來代表物體。見下圖


黑色的就是包圍盒,可以是凸多邊形,最貼近檢測物體即可。

分離軸定律:兩個凸多邊形物體,如果我們能找到一個軸,使得兩個在物體在該軸上的投影互不重疊,則這兩個物體之間沒有碰撞發生,該軸為Separating Axis

那麼用一般去檢測那些軸呢,垂直於多邊形每條邊的軸。如下圖:


所以,分離軸定律變成,兩個多邊形在所有軸上的投影都發生重疊,則判定為碰撞;否則,沒有發生碰撞。

下面,我只考慮矩形的情況,如何檢測分離軸。

很明顯,矩形4條邊,有4條檢測軸,那麼2個矩形就有8個。但是矩形有2個軸是重複的,所以只需要檢測2條軸就可以了,既是矩形的兩條互相垂直的邊所在的軸。


如上圖,判斷碰撞,我們需要判斷2個矩形在4個軸上的投影是否重疊。這裡有2種可能的方式。第一種,把每個矩形的4個頂點投影到一個軸上,這樣算出4個頂點最長的連線距離,以後同樣對待第二個矩形,最後判斷2個矩形投影距離是否重疊。

第二種方式,把2個矩形的半徑距離投影到軸上,以後把2個矩形的中心點連線投影到軸上,以後判斷2個矩形的中心連線投影,和2個矩形的半徑投影之和的大小。本文使用這種方式。

這裡用到一些向量的數學知識。如下圖:


P點為矩形在X軸上的投影點,矩形在垂直軸上的投影點為原點。這裡也能看出來,點P所在的矩形軸, 在X軸上的投影長度為OP,如果矩形逆時針繞遠點O旋轉,OP在X軸上的投影長度變小,直到為0,OP垂直於X軸。也就是,OP在X軸上的投影長度的最大與最小值。這也解釋了,為什麼我們選擇檢測軸為垂直於多邊形邊的軸,因為在這些軸上我們能取到極值,中間的那些軸就沒必要檢測了。

如何表示軸,我們需要用向量,正確的使用單位向量,能夠簡化模型,降低思考的難度。如下圖:


假設P點的座標為(px, py), 那麼向量P就是(px, py),點P在X軸上的投影點Q座標是(qx, qy),那麼向量Q就是(qx, qy)。我們假設X軸上的單位向量是(1, 0)。那麼向量P和X軸上單位向量點乘有:

向量P * X軸單位向量 = |P| * |X軸單位向量| * cosPQ  = px * 1 + py * 0 = px

又因為單位向量的長度等於1所以,px就是向量Q的長度。這是非常有意義的,我們就得到一個規律,就是把一個向量點乘一個單位向量,我們得到的是這個向量在這個單位向量上的投影長度。用程式碼表示為:

  1. /** 
  2.  * dot-multiply 
  3.  */
  4. privatefloat dot(float[] axisA, float[] axisB) {  
  5.     return Math.abs(axisA[0] * axisB[0] + axisA[1] * axisB[1]);  
  6. }  

這裡float[] 存放的是一個點的x ,y 座標。axisB 為單位向量,這個結果就是axisA向量在,單位向量axisB上投影的長度。

下面我們看一下,單位向量如何表示:


單位向量是用單位圓來描述的。假設這個圓的半徑為1,那麼圓上的任何一個座標到原點構成的向量都可以看作一個單位向量,並且長度為1。這般,明顯的點P就是一個單位向量。點P在單位圓上移動,那麼這個單位向量就在旋轉,向量P就和角A建立了關係。很明顯的得出,cosA 就是向量P的X座標,sinA 就是向量P的Y座標。

這樣我們就可以得出,單位向量P為(cosA,sinA)。這個模型的意義就是把單位向量P可以看成矩形的條邊。如下圖:


那麼矩形的另一個邊對應的單位向量S如何表示呢,向量S和向量P是垂直的,我們可以得出, S(-sinA, cosA), 向量S 點乘 向量P  = 0

至此,我們就可以通過一個旋轉角度,得到一個矩形的2個檢測軸的單位向量。程式碼如下:

  1. // unit vector of x axis
  2. privatefloat[] axisX;  
  3. // unit vector of y axis
  4. privatefloat[] axisY;  
  5. // 0 -360
  6. privatefloat rotation;  
  7. /** 
  8.  * Set axis x and y by rotation 
  9.  *  
  10.  * @param rotation float 0 - 360  
  11.  */
  12. public OBB setRotation(float rotation) {  
  13.     this.rotation = rotation;  
  14.     this.axisX[0] = MathUtils.cos(rotation);  
  15.     this.axisX[1] = MathUtils.sin(rotation);  
  16.     this.axisY[0] = -MathUtils.sin(rotation);  
  17.     this.axisY[1] = MathUtils.cos(rotation);  
  18.     returnthis;  
  19. }  

下一步如何計算矩形的半徑投影呢,什麼又是半徑投影呢,看下圖:


橙色線段,是矩形的2條檢測軸,3張圖是矩形旋轉的3個特殊位置截圖。藍色線段就是矩形半徑投影。其實就是,矩形在X軸上最遠處的交點,數學上意義就是2條檢測軸的投影之和。

2條檢測軸的向量和就是中心點到矩形一個頂點的向量,所以投影半徑也是中心點到矩形頂點的向量投影長度。注意向量的方向會影響投影長度。按照中間那幅圖,2條檢測軸向量和的投影是,2條檢測軸投影的差值。如果把其中一個軸,旋轉180度,那麼2個檢測軸和的投影就是,2條軸投影的和值。

至此,如果我們把矩形在任意角度的2條軸向量投影到單位向量上,根據前面的單位向量規律。我們就得到了軸向量在單位向量上投影的長度,而單位向量的長度為1,那麼我們得到的就是軸向量與單位向量的比例。在用這個比例乘以軸向量的長度,就得到了軸的投影長度,就能求出軸半徑的長度了。如圖


2個矩形檢測過程中,每次以一個矩形的檢測軸為座標系,投影另一個矩形的檢測軸。圖中,藍色線段為左邊矩形的半徑投影,黃色線段為右邊矩形檢測軸。我們需要把右邊2條檢測軸投影到藍色線段所在X軸的單位向量,得到投影比例,以後在乘以2條檢測軸的長度,就可以得到右邊矩形的半徑投影

紅色線段為2個矩形的中心點連心,計算其在X軸的投影長度。比較中心點連線的投影長度與2矩形的半徑投影長度之和,如果連線投影大,那麼在這條軸上沒有碰撞,否則碰撞。半徑投影程式碼如下:

  1. privatefloat halfWidth;  
  2. privatefloat halfHeight;  
  3. /** 
  4.  * Get axisX and axisY projection radius distance on axis 
  5.  */
  6. publicfloat getProjectionRadius(float[] axis) {  
  7.     // axis, axisX and axisY are unit vector
  8.     // projected axisX to axis
  9.     float projectionAxisX = this.dot(axis, this.axisX);  
  10.     // projected axisY to axis
  11.     float projectionAxisY = this.dot(axis, this.axisY);  
  12.     returnthis.halfWidth * projectionAxisX + this.halfHeight * projectionAxisY;  
  13. }  

判斷2矩形最終是否碰撞,需要依次檢測4個分離軸,如果在一個軸上沒有碰撞,則2個矩形就沒有碰撞。程式碼如下:
  1. /** 
  2.  * OBB is collision with other OBB 
  3.  */
  4. public boolean isCollision(OBB obb) {  
  5.     // two OBB center distance vector
  6.     float[] centerDistanceVertor = {  
  7.             this.centerPoint[0] - obb.centerPoint[0],  
  8.             this.centerPoint[1] - obb.centerPoint[1]  
  9.     };  
  10.     float[][] axes = {  
  11.             this.axisX,  
  12.             this.axisY,  
  13.             obb.axisX,  
  14.             obb.axisY,  
  15.     };  
  16.     for(int i = 0; i < axes.length; i++) {  
  17.         // compare OBB1 radius projection add OBB2 radius projection to centerDistance projection
  18.         if(this.getProjectionRadius(axes[i]) + obb.getProjectionRadius(axes[i])   
  19.                 <= this.dot(centerDistanceVertor, axes[i])) {  
  20.             returnfalse;  
  21.         }  
  22.     }  
  23.     returntrue;  
  24. }  

最後,給出OBB完整的程式碼封裝

Java程式碼:

  1. /** 
  2.  * @author scott.cgi 
  3.  * @since  2012-11-19 
  4.  *   
  5.  * Oriented bounding box  
  6.  */
  7. publicclass OBB {  
  8.     privatefloat[] centerPoint;  
  9.     privatefloat halfWidth;  
  10. 相關推薦

    矩形旋轉碰撞,OBB方向包圍演算法實現

    如何進行2D旋轉矩形的碰撞檢測,可以使用一種叫OBB的檢測演算法(Oriented bounding box)方向包圍盒。這個演算法是基於SAT(Separating Axis Theorem)分離軸定律的。而OBB不僅僅是計算矩形的碰撞檢測,而是一種演算

    碼農乾貨系列【1】--方向包圍(OBB)碰撞檢測

    乾貨 最近一直在刪文章,不是要關博洗手什麼的,而是被刪的文章沒有達到“乾貨”的標準。乾貨的反義詞是水貨,比如我們經常吃的注水豬肉,它就是水貨,非乾貨。什麼是“乾貨”。?經過一番搜尋,標準的描述是:實用性比較強的,不含任何吹噓水分,也沒有虛假的成分,所以業內人士通常把這一類分享活動稱之為“乾貨”。 文章是否

    理解AABB包圍演算法

    void Box::updateAABB()     {         Vec half_diag( //            Math::abs(m_world.x_axis.x) * m_half_size.x + Math::abs(m_world.y_axis.x) * m_half_size.y

    AABB包圍演算法

    因為用到了Ogre的Vector3,所以首先需要配置Ogre的環境,在這裡不再贅述。 下面這是AABB標頭檔案: aabb.h #ifndef AABB3_H #define AABB3_H #include "OgreOde_Core.h" #include "Ogre

    原生js實現OBB包圍碰撞演算法

    網上看了很多OBB演算法例項結果都不是js實現的 要不然實現的就是一大堆庫看著是真的頭痛。 基本原理請看這篇部落格: https://blog.csdn.net/qing101hua/article/details/52997160 好了下面直接上程式碼 可以直接複製下來儲存到本地執行:

    【H5/JS】遊戲常用演算法-碰撞檢測-包圍檢測演算法(2)-矩形

    矩形包圍盒,顧名思義,就是使用一個矩形來包圍住影象,矩形的大小以剛好包圍住影象為最佳,這種包圍盒最適用的場景是剛好物體的形狀接近於矩形。在具體的應用中,描述矩形包圍盒的的常用方式有以下兩種,一:採用最小最大頂點法描述AABB包圍盒上圖中使用了最小最大頂點法來描述包圍盒資訊,由

    【H5/JS】遊戲常用演算法-碰撞檢測-包圍檢測演算法(1)-圓形

    檢測物體碰撞實際上是需要檢測物體是否相交,而實際應用中物體的形狀大小各異,如果直接對物體的邊緣進行碰撞檢測,實際計算過程的代價非常高昂。如果物體的數量太多,比如像網路遊戲中,通常少則幾千使用者,多則上萬、幾十萬使用者同時線上,而這些碰撞都要通過伺服器檢測,這樣計算的消耗,即使

    圓與旋轉矩形碰撞檢測(下篇)

    圓與旋轉矩形的碰撞檢測 本文我將解釋如何實現圓和旋轉矩形的碰撞檢測。碰撞檢測用於確定物件A是否碰撞了物件B。圓包含圓心位置x,y和一個半徑。矩形包含左上角的x,y位置、寬度、長度和一個可以旋轉的角度。我們假設矩形沿著它的中心點旋轉。 我將使用一個小程式、圖片和程

    給定一個模型檔案,計算出這個模型的OBB包圍的八個頂點 的 專案

    visual studio第一次開啟專案:  選擇帶有紅框的++ logo 已有開啟專案後,再開啟 開啟後,main函式 ,osg名稱空間 報錯, 由於只有一個頭檔案,所以osg明明空間的宣告應該在 這個標頭檔案裡, 接下來開啟標頭檔案, 如下圖 :  在 專案

    AABB包圍碰撞的理解

    return ((_min.x >= aabb._min.x && _min.x <= aabb._max.x) || (aabb._min.x >= _min.x && aabb._min.x <= _max.x)) &&

    [演算法]3D三角面片與包圍相交交點求法

    最近在研究複製包圍盒內相交網格的問題,有一個要解決的難題就是獲得一個任意三角面片與指定包圍盒的交點。現將問題簡化為2D空間, 假設現有三角形面片ABC(以下都簡稱ABC):, 包圍盒DEFG, 那麼三角面與包圍盒的位置關係有幾種: ABC一個點在包圍盒內部: ABC兩個點

    PCL ——最小包圍(畫出了最小包圍並求出頂點座標)

    PCL ——最小包圍盒 2018年09月21日 15:31:01 不懂音樂的欣賞者 閱讀數:35 標籤: PCL包圍盒外接矩形最小矩形收起 個人分類: PCL 1.包圍盒簡介   包圍盒也叫外接最小矩形,是一種求解離散點集最優包圍空間

    three.js 包圍簡單應用

    一、包圍盒 二、包圍盒簡單應用: (一)移動模型到合適的位置 計算多個模型組合的group的包圍盒 var bbox = new THREE.Box3().setFromObject(group) 將匯入的外部模型移動到世界座標中心。(比較粗糙的,實際上是包圍盒的中心移動到世界座

    怎麼使用PDF編輯軟體旋轉PDF頁面方向

    工作中經常會使用到PDF檔案,PDF檔案的修改編輯是需要用到PDF編輯軟體的,那麼,怎麼使用PDF編輯軟體旋轉PDF頁面方向呢,是不是有很多的小夥伴也想知道應該怎麼做呢,不會的小夥伴可以看看下面的文章瞭解一下哦。 1.開啟並執行迅捷PDF編輯器http://bianji.xjpdf.com/,在

    包圍計算】計算某個物件所有子物件的包圍

    版權所有。轉載請註明出處: IT_yanghui      在遊戲開發中,很多時候不需要知道物件下的子物件是什麼,只想給該物件外部加一個物理碰撞或者Collider,或者在開發中需要動態獲取該物件新增Collider,然而給每一個子物件分別加,明顯不現

    unity 獲取包圍

    1.獲取中心點 public Vector3 GetCenter(GameObject target) { Renderer[] mrs = target.gameObject.GetComponentsInChildren<Rend

    如何用純 CSS 創作炫酷的同心矩形旋轉動畫

    效果預覽 線上演示 按下右側的“點選預覽”按鈕可以在當前頁面預覽,點選連結可以全屏預覽。 https://codepen.io/comehope/pen/bMvbRp 可互動視訊教程 此視訊是可以互動的,你可以隨時暫停視訊,編輯視訊中的程式碼。 請用 chrom

    前端每日實戰:2.純 CSS 創作一個矩形旋轉 loader 特效

    原文地址:2.純 CSS 創作一個矩形旋轉 loader 特效 擴充套件後地址:https://scrimba.com/c/cNJVWUR  擴充套件地址:https://codepen.io/pen/   HTML程式碼: <div class="loader"&g

    pointfusion:深感測器融合估計的三維包圍

    https://www.arxiv-vanity.com/papers/1711.10871/ 摘要 我們提出點融合pointfusion,一個通用的3D物件檢測方法,利用影象和3D點雲資訊。與現有的方法,無論是使用多級管道或保持感測器和資料集特定的假設,點融合pointfusion概念上簡單,

    threeJS-Helper03-BoxHelperAndBox3Helper(包圍助手)

    需要電子檔書籍或者原始碼可以Q群:828202939   希望可以和大家一起學習、一起進步!! 如有錯別字或有理解不到位的地方,可以留言或者加微信15250969798,博主會及時修改!!!!! 博主的案例並不難,只是為了更好的給想入門threeJS的同學一