OBB方向包圍盒碰撞檢測演算法(原理與程式碼)
阿新 • • 發佈:2020-12-15
技術標籤:3D圖形 OpenGL
原理:OBB間的相交測試基於分離軸理論(separating axis theory)。若兩個OBB在一條軸線上(不一定是座標軸)上的投影不重疊,則這條軸稱為分離軸。若一對OBB間存在一條分離軸,則可以判定這兩個OBB不相交。對任何兩個不相交的凸三維多面體,其分離軸要麼垂直於任何一個多面體的某一個面,要麼同時垂直於每個多面體的某一條邊。因此,對一對OBB,只需測試15條可能是分離軸的軸(每個OBB的3個面方向再加上每個OBB的3個邊方面的兩兩組合),只要找到一條這樣的分離軸,就可以判定這兩個OBB是不相交的,如果這15條軸都不能將這兩個OBB分離,則它們是相交的。
程式碼:
FBox,FOrientedBox都是UE4資料結構 /**@前路漫漫 oriented box intersection */ bool IntersectOOBB(const FOrientedBox& OB1, const FOrientedBox& OB2) { const int32 ObVertNum = 8; FVector OB1Vert[ObVertNum] = { FVector::ZeroVector }; OB1.CalcVertices(OB1Vert); FVector OB2Vert[ObVertNum] = { FVector::ZeroVector }; OB2.CalcVertices(OB2Vert); //calculate 1d bound box auto affectMinMaxValue = [](float& min, float& max, float value) { min = std::min(min, value); max = std::max(max, value); }; //determin whether two obb(box1, box2) exit a separate axis on a given axis(axe) auto isSATFoundedOnAxe = [&affectMinMaxValue, &OB1Vert, &OB2Vert, &ObVertNum](const FOrientedBox& box1, const FOrientedBox& box2, const FVector& axe)->bool { float minBox1 = std::numeric_limits<float>::max(), maxBox1 = std::numeric_limits<float>::min(), minBox2 = std::numeric_limits<float>::max(), maxBox2 = std::numeric_limits<float>::min(); for (int32 i = 0; i < ObVertNum; i++) { affectMinMaxValue(minBox1, maxBox1, axe | OB1Vert[i]); } for (int32 i = 0; i < ObVertNum; i++) { affectMinMaxValue(minBox2, maxBox2, axe | OB2Vert[i]); } //try seg1 on seg2 or seg2 on seg1 return ((minBox2 <= maxBox1 && minBox2 >= minBox1) || (maxBox2 <= maxBox1 && maxBox2 >= minBox1)) || ((minBox1 <= maxBox2 && minBox1 >= minBox2) || (maxBox1 <= maxBox2 && maxBox1 >= minBox2)); }; /*Exclude the majority case of no collision*/ FBox Box1AABB(OB1Vert, ObVertNum); FBox Box2AABB(OB2Vert, ObVertNum); if (!Box1AABB.Intersect(Box2AABB)) return false; /*Use SAT to know if collision existe*/ if (!isSATFoundedOnAxe(OB1, OB2, OB1.AxisX)) return false; if (!isSATFoundedOnAxe(OB1, OB2, OB1.AxisY)) return false; if (!isSATFoundedOnAxe(OB1, OB2, OB1.AxisZ)) return false; if (!isSATFoundedOnAxe(OB1, OB2, OB2.AxisX)) return false; if (!isSATFoundedOnAxe(OB1, OB2, OB2.AxisY)) return false; if (!isSATFoundedOnAxe(OB1, OB2, OB2.AxisZ)) return false; if (!isSATFoundedOnAxe(OB1, OB2, OB1.AxisX ^ OB2.AxisX)) return false; if (!isSATFoundedOnAxe(OB1, OB2, OB1.AxisX ^ OB2.AxisY)) return false; if (!isSATFoundedOnAxe(OB1, OB2, OB1.AxisX ^ OB2.AxisZ)) return false; if (!isSATFoundedOnAxe(OB1, OB2, OB1.AxisY ^ OB2.AxisX)) return false; if (!isSATFoundedOnAxe(OB1, OB2, OB1.AxisY ^ OB2.AxisY)) return false; if (!isSATFoundedOnAxe(OB1, OB2, OB1.AxisY ^ OB2.AxisZ)) return false; if (!isSATFoundedOnAxe(OB1, OB2, OB1.AxisZ ^ OB2.AxisX)) return false; if (!isSATFoundedOnAxe(OB1, OB2, OB1.AxisZ ^ OB2.AxisY)) return false; if (!isSATFoundedOnAxe(OB1, OB2, OB1.AxisZ ^ OB2.AxisZ)) return false; return true; }