1. 程式人生 > >3D環境中碰撞檢測

3D環境中碰撞檢測

Axis-aligned bounding boxes(AABB包圍盒)原文地址
在遊戲中,為了簡化物體之間的碰撞檢測運算,通常會對物體建立一個規則的幾何外形將其包圍。其中,AABB(axis-aligned bounding box)包圍盒被稱為軸對齊包圍盒。

與2D碰撞檢測一樣,軸對齊包圍盒是判斷兩個物體是否重疊的最快演算法,物體被包裹在一個非旋轉的(因此軸對齊的)盒中,並檢查這些盒在三維座標空間中的位置,以確定它們是否重疊。
這裡寫圖片描述
由於效能原因,軸對齊是有一些約束的。兩個非旋轉的盒子之間是否重疊可以通過邏輯比較進行檢查,而旋轉的盒子則需要三角運算,這會導致效能下降。如果你有旋轉的物體,可以通過修改邊框的尺寸,這樣盒子仍可以包裹物體,或者選擇使用另一種邊界幾何型別,比如球體(球體旋轉,形狀不會變)。下圖是一個AABB物體旋轉,動態調節盒大小適應物體的例子。
這裡寫圖片描述

Note: 參考這裡,使用Three.js進行邊界體積碰撞檢測。

1,點與 AABB

如果檢測到一個點是否在AABB內部就非常簡單了 — 我們只需要檢查這個點的座標是否在AABB內; 分別考慮到每種座標軸. 如果假設 Px, Py 和 Pz 是點的座標, BminX–BmaxX, BminY–BmaxY, 和 BminZ–BmaxZ 是AABB的每一個座標軸的範圍, 我們可以使用以下公式計算兩者之間的碰撞是否發生:

f(P,B)=(Px>=BminXPx<=BmaxX)(Py>=BminYPy<=BmaxY)(Pz>=BminZ
Pz<=BmaxZ)

或者用JavaScript:

function isPointInsideAABB(point, box) {
  return (point.x >= box.minX && point.x <= box.maxX) &&
         (point.y >= box.minY && point.y <= box.maxY) &&
         (point.z >= box.minY && point.z <= box.maxZ
); }

2,AABB 與 AABB

檢查一個AABB是否和另一個AABB相交類似於檢測兩個點一樣. 我們只需要基於每一條座標軸並利用盒子的邊緣去檢測. 下圖顯示了我們基於 X 軸的檢測 — 當然, AminX–AmaxX 和 BminX–BmaxX 會不會重疊?
這裡寫圖片描述
在數學上的表示就像這樣:

f(A,B)=(AminX<=BmaxXAmaxX>=BminX)(AminY<=BmaxYAmaxY>=BminY)(AminZ<=BmaxZAmaxZ>=BminZ)

在JavaScript我們可以這樣:

function intersect(a, b) {
  return (a.minX <= b.maxX && a.maxX >= b.minX) &&
         (a.minY <= b.maxY && a.maxY >= b.minY) &&
         (a.minZ <= b.maxZ && a.maxZ >= b.minZ);
}

球體碰撞

球體碰撞邊緣檢測比AABB盒子稍微複雜一點,但他的檢測仍相當容易的。球體的主要優勢是他們不變的旋轉,如果包裝實體旋轉,邊界領域仍將是相同的。他們的主要缺點是,除非他們包裝的實體實際上是球形,包裝的實體通常不是一個完美的球形(比如用這樣的球形包裝一個人將導致一些錯誤,而AABB盒子將更合適)。

1,點與球

檢查是否一個球體包含一個點,我們需要計算點和球體的中心之間的距離。如果這個距離小於或等於球的半徑,這個點就在裡面。
這裡寫圖片描述

兩個點A和B之間的歐氏距離是
(Ax-Bx)2)+(Ay-By)2+(Az-Bz)

,我們的公式指出,球體碰撞檢測是:
f(P,S)=Sradius>=(Px-Sx)2+(Py-Sy)2+(Pz-Sz)2

或者用JavaScript:

function isPointInsideSphere(point, sphere) {
  // we are using multiplications because is faster than calling Math.pow
  var distance = Math.sqrt((point.x - sphere.x) * (point.x - sphere.x) +
                           (point.y - sphere.y) * (point.y - sphere.y) +
                           (point.z - sphere.z) * (point.z - sphere.z));
  return distance < sphere.radius;
}

上面的程式碼有一個平方根,是一個開銷昂貴的計算。一個簡單的優化,以避免它由半徑平方,所以優化方程不涉及distance < sphere.radius * sphere.radius.

2,球體與球體

測試一個球和一個AABB的碰撞是稍微複雜,但過程仍然簡單和快速。一個合乎邏輯的方法是,檢查AABB每個頂點,計算每一個點與球的距離。然而這是大材小用了,測試所有的頂點都是不必要的,因為我們可以僥倖計算AABB最近的點(不一定是一個頂點)和球體的中心之間的距離,看看它是小於或等於球體的半徑。我們可以通過逼近球體的中心和AABB的距離得到這個值。
這裡寫圖片描述

在 JavaScript, 我們可以像這樣子做:

function intersect(sphere, box) {
  // get box closest point to sphere center by clamping
  var x = Math.max(box.minX, Math.min(sphere.x, box.maxX));
  var y = Math.max(box.minY, Math.min(sphere.y, box.maxY));
  var z = Math.max(box.minZ, Math.min(sphere.z, box.maxZ));

  // this is the same as isPointInsideSphere
  var distance = Math.sqrt((x - sphere.x) * (x - sphere.x) +
                           (y - sphere.y) * (y - sphere.y) +
                           (z - sphere.z) * (z - sphere.z));

  return distance < sphere.radius;
}

使用一個物理引擎

原文 3D physics engines provide collision detection algorithms, most of them based on bounding volumes as well. The way a physics engine works is by creating a physical body, usually attached to a visual representation of it. This body has properties such as velocity, position, rotation, torque, etc., and also a physical shape. This shape is the one that is considered in the collision detection calculations.
We have prepared a live collision detection demo (with source code) that you can take a look at to see such techniques in action — this uses the open-source 3D physics engine cannon.js.

轉發者說: 上面英文並不難,本人才疏學淺有翻譯不對的地方請指正 或直接看原文

3D物理引擎提供了碰撞檢測演算法,大多數也是基於邊界檢測,物理引擎是建立一個虛擬形狀,這個模擬形狀是在模擬碰撞中要使用到的,請檢視演示demo 這裡是原始碼同時也可以看一下開始的物理引擎CAMON.JS

[參考資料]

Related articles on MDN:

External resources: