Box2D 射线和AABB的碰撞检测

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ring0hx/article/details/7677418
最近需要用到射线和运动AABB的实时碰撞检测算法,作为基础看了下box2d的碰撞检测。

box2d使用了一种叫做slab的碰撞检测算法。所谓slab是指两个平行平面之间的空间,由此我们可以把3D空间中的AABB盒子看做是由AABB的3组平行面形成的3个方向的slab的交集。根据这个定义,我们可以得到以下两个结论:
1.如果一个点在AABB中,那么这个点必定同时在这3个slab中。
2.如果一条射线和AABB相交,那么这条射线和3个slab的相交部分必定有重合部分。
这很容易理解,如果射线和3个slab的相交线段没有重合,那么这些线段就不可能同时存在于3个slab中,也就不可能在AABB盒子中。下图展示了2D空间中射线R1和R2与AABB相交的情形。R1在x-slab和y-slab中的线段没有重合部分,因此R1和AABB不相交。R2在x-slab和y-slab中的线段有重合部分,因此R2和AABB相交。



根据上述原理,检查2D中射线和AABB的碰撞,只需要检查射线和x-slab,y-slab的交线是否有重合。
首先我们需要得到射线和slab边界平面的交点。射线可以用参数方程表示为R(t) = P0 + t d , (其中P0为射线起点,d为射线的方向向量),平面由隐式定义方程X·n=D, (其中X为平面上的点,n为平面法向量,D为原点到平面的距离)给出。将平面方程中的X用P0 + t d替换解得交点的参数t=(D−P0·n)/(d·n).由于AABB的slab平面都分别和两个坐标轴平行,公式可以进一步简化:设P0=(px,py,pz), d=(dx,dy,dz), t和x-slab面的交点的参数计算公式可化简为t=(D-px)/dx,而此处的D就是AABB的边界面x坐标。

理解了原理再来看box2d的代码就很容易了

/// Ray-cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1).
struct b2RayCastInput
{
    b2Vec2 p1, p2;
    float32 maxFraction;
};

/// Ray-cast output data. The ray hits at p1 + fraction * (p2 - p1), where p1 and p2
/// come from b2RayCastInput.
struct b2RayCastOutput
{
    b2Vec2 normal;
    float32 fraction;
};

bool b2AABB::RayCast(b2RayCastOutput* output, const b2RayCastInput& input) const
{
	 float32 tmin = -b2_maxFloat;
	 float32 tmax = b2_maxFloat;

	 b2Vec2 p = input.p1;
	 b2Vec2 d = input.p2 - input.p1;
	 b2Vec2 absD = b2Abs(d);

	 b2Vec2 normal;

	 for (int32 i = 0; i < 2; ++i)
	 {
		 if (absD(i) < b2_epsilon)
		 {
			 // Parallel.
			 if (p(i) < lowerBound(i) || upperBound(i) < p(i))
			 {
				return false ;
			 }
		 }
		 else
		 {
			 float32 inv_d = 1.0f / d(i);
			 float32 t1 = (lowerBound(i) - p(i)) * inv_d;
			 float32 t2 = (upperBound(i) - p(i)) * inv_d;

			 // Sign of the normal vector.
			 float32 s = -1.0f;

			 if (t1 > t2)
			 {
				 b2Swap(t1, t2);
				 s = 1.0f;
			 }

			 // Push the min up
			 if (t1 > tmin)
			 {
				 normal.SetZero();
				 normal(i) = s;
				 tmin = t1;
			 }

			 // Pull the max down
			 tmax = b2Min(tmax, t2);

			 if (tmin > tmax)
			 {
				 return false ;
			 }
		 }
	 }

	 // Does the ray start inside the box?
	 // Does the ray intersect beyond the max fraction?
	 if (tmin < 0.0f || input.maxFraction < tmin)
	 {
		 return false ;
	 }

	 // Intersection.
	 output->fraction = tmin;
	 output->normal = normal;
	 return true ;
}


b2RayCastInput是射线输入,P1是射线起点,P2是射线上的另一个点,maxFraction表示需要检测的射线范围的参数值。如果需要检测的是一个线段,则把P1,P2设成线段的两个端点,maxFraction设为1即可。

b2RayCastOutput是碰撞检测的输出,fraction是碰撞点位置的参数值。

算法很简单,就是利用上面的方程分别求出射线和AABB四个面的交点参数t1和t2,然后检查射线和x-slab,y-slab的交线是否有重合部分。

猜你喜欢

转载自blog.csdn.net/ring0hx/article/details/7677418