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


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值