Box2D射線和AABB碰撞檢測

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座標。

/// 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 ;  
}  


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章