1. 程式人生 > 其它 >OBB方向包圍盒碰撞檢測演算法(原理與程式碼)

OBB方向包圍盒碰撞檢測演算法(原理與程式碼)

技術標籤: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;
}