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>=BminX∧Px<=BmaxX)∧(Py>=BminY∧Py<=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<=BmaxX∧AmaxX>=BminX)∧(AminY<=BmaxY∧AmaxY>=BminY)∧(AminZ<=BmaxZ∧AmaxZ>=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: