天行健 君子当自强而不息

几何检测(5)

新建网页 1

 

两个AABB的相交性检测

检测两个静止AABB的相交性是很简单的,只需要在每一维上单独检查它们的重合程度即可。如果在所有维上都没有重合,那么这两个AABB就不会相交。intersectAABBs()就是用这项技术来实现的。

    //---------------------------------------------------------------------------
    // Check if two AABBs intersect, and return true if so.  Optionally return
    // the AABB of their intersection if an intersection is detected.
    //---------------------------------------------------------------------------
   
bool intersectAABBs(const AABB3& box1, const AABB3& box2, AABB3* boxIntersect) 
    {
        
// Check for no overlap
   
    if (box1.min.x > box2.max.x) return false;
        
if (box1.max.x < box2.min.x) return false;
        
if (box1.min.y > box2.max.y) return false;
        
if (box1.max.y < box2.min.y) return false;
        
if (box1.min.z > box2.max.z) return false;
        
if (box1.max.z < box2.min.z) return false;
   
        
// We have overlap.  Compute AABB of intersection, if they want it.
   
    if (boxIntersect != NULL) 
        {
            boxIntersect->min.x = max(box1.min.x, box2.min.x);
            boxIntersect->max.x = min(box1.max.x, box2.max.x);
            boxIntersect->min.y = max(box1.min.y, box2.min.y);
            boxIntersect->max.y = min(box1.max.y, box2.max.y);
            boxIntersect->min.z = max(box1.min.z, box2.min.z);
            boxIntersect->max.z = min(box1.max.z, box2.max.z);
        }
        
        
return true;
    }

AABB间的动态测试稍微复杂一些。考虑一个由极值点sminsmax定义的静止AABB和一个由mminmmax定义的运动AABB。运动AABB的运动由向量d给出,t从0变换到1。

目标是计算运动边界框碰撞到静止边界框的时刻t(假设两个边界框刚开始时不相交)。要计算出t,我们需要计算出两个边界框在所有维上同时重合的第一个点。因为它的应用范围在2D或3D中,我们先在2D中解决它(扩展到3D也是非常容易的)。先单独分析每个坐标,解决两个(在3D中就是三个)独立的一维问题,再把它们组合到一起就得到最终答案。

现在要解决的问题变成一维问题了。我们需要知道两个矩形边界框在特定维上重合的时间区间,假设把问题投影到x轴上,如图13.15所示:

黑色矩形代表沿数轴滑动的运动AABB。当图13.15中的t=0时,运动的AABB完全位于静止AABB的左边,当t=1时运动AABB完全位于静止AABB右边。tenter是两个AABB开始相交时的时刻,tleave是两个AABB脱离接触的时刻。对于正在讨论的维,设mmin(t)mmax(t)代表运动AABB在时刻t的最小值和最大值:

mmin(t) = mmin(0) + td

mmax(t) = mmax(0) + td

mmin(0)mmax(0)是运动AABB的起始位置,d是位移向量d在这个维上的分量。类似地用sminsmax来定义静止AABB(当然,它们和t是不相关的,因为这个AABB是静止的)。tenter就是当mmax(t)等于smin时的t值:

这里有三个要点:

(1)如果分母d为0,那么两个矩形边界框总是相交,或永不相交。

(2)如果运动AABB开始位于静止AABB的右边并向左移动,那么tenter将大于tleave。此时我们交换两个值以确保tenter < tleave

(3)tenter tleave的值可能会超出[0, 1]这个区间,为了应付t值超出区间的情况,可以认为运动AABB是沿着平行于d的无限轨道移动。当tenter> 1tleave < 0时,在所讨论的时间内它们是不相交的,

现在我们已经能够求出两个边界框重合的时间范围了,其边界为tentertleave。在这段时间内两个边界框会在某一维上相交,而所有维上的时间区间的交集就是两个边界框相交的时间段。图13.16展示了在2D中的两个时间区间(不要和图13.15混淆,图 13.16中的数轴是时间轴,而图13.15中的数轴是x轴)。

如果区间为空,那么两个边界框永远不会相交;如果区间范围在[0, 1]之外,那么在所讨论的时间段内它们不相交。实际上,这个时间区间给出的信息比我们想要的多,因为我们只需要知道它们开始相交的时间点,而不需要知道结束相交的点。然而,我们仍然要维持这个区间来检测时间区间是否为空。

intersectMovingAABB()有上述过程的完整实现:

    //---------------------------------------------------------------------------
    // Return parametric point in time when a moving AABB collides
    // with a stationary AABB.  Returns > 1 if no intersection.
    //---------------------------------------------------------------------------
   
float intersectMovingAABB(const AABB3& stationaryBox, const AABB3& movingBox, const Vector3& d) 
    {
        
// We'll return this huge number if no intersection
   
    const float kNoIntersection = 1e30f;
   
        
// Initialize interval to contain all the time under consideration
   
    float    tEnter = 0.0f;
        
float    tLeave = 1.0f;
        
        
// Compute interval of overlap on each dimension, and intersect this interval with the interval 
        // accumulated so far.  As soon as an empty interval is detected, return a negative result
        // (no intersection.)  In each case, we have to be careful for an infinite of empty interval on 
        // each dimension.
   
        // Check x-axis
   

        
if (d.x == 0.0f) 
        {
            
// Empty or infinite inverval on x
   
        if ((stationaryBox.min.x >= movingBox.max.x) || (stationaryBox.max.x <= movingBox.min.x)) 
            {
                
// Empty time interval, so no intersection.
   
            return kNoIntersection;
            }
   
            
// Inifinite time interval - no update necessary
   
    } 
        
else 
        {        
            
float oneOverD = 1.0f / d.x;    // Divide once
   
            // Compute time value when they begin and end overlapping
   
        float xEnter = (stationaryBox.min.x - movingBox.max.x) * oneOverD;
            
float xLeave = (stationaryBox.max.x - movingBox.min.x) * oneOverD;
   
            
// Check for interval out of order
   
        if (xEnter > xLeave) 
                swap(xEnter, xLeave);        
   
            
// Update interval
   
        if (xEnter > tEnter) tEnter = xEnter;
            
if (xLeave < tLeave) tLeave = xLeave;
   
            
// Check if this resulted in empty interval
   
        if (tEnter > tLeave)
                
return kNoIntersection;        
        }
        
        
// Check y-axis
   

        
if (d.y == 0.0f) 
        {
            
// Empty or infinite inverval on y
   
        if ((stationaryBox.min.y >= movingBox.max.y) || (stationaryBox.max.y <= movingBox.min.y)) 
            {
                
// Empty time interval, so no intersection
   
            return kNoIntersection;
            }
   
            
// Inifinite time interval - no update necessary
   
    } 
        
else 
        {
            
// Divide once
   
        float    oneOverD = 1.0f / d.y;
   
            
// Compute time value when they begin and end overlapping
   
        float    yEnter = (stationaryBox.min.y - movingBox.max.y) * oneOverD;
            
float    yLeave = (stationaryBox.max.y - movingBox.min.y) * oneOverD;
   
            
// Check for interval out of order
   
        if (yEnter > yLeave)
                swap(yEnter, yLeave);        
   
            
// Update interval
   
        if (yEnter > tEnter) tEnter = yEnter;
            
if (yLeave < tLeave) tLeave = yLeave;
   
            
// Check if this resulted in empty interval
   
        if (tEnter > tLeave) 
                
return kNoIntersection;        
        }
        
        
// Check z-axis
   

        
if (d.z == 0.0f) 
        {
            
// Empty or infinite inverval on z
   
        if ((stationaryBox.min.z >= movingBox.max.z) || (stationaryBox.max.z <= movingBox.min.z)) 
            {
                
// Empty time interval, so no intersection
   
            return kNoIntersection;
            }
   
            
// Inifinite time interval - no update necessary
   
    } 
        
else 
        {
            
// Divide once
   
        float    oneOverD = 1.0f / d.z;
   
            
// Compute time value when they begin and end overlapping
   
        float    zEnter = (stationaryBox.min.z - movingBox.max.z) * oneOverD;
            
float    zLeave = (stationaryBox.max.z - movingBox.min.z) * oneOverD;
   
            
// Check for interval out of order
   
        if (zEnter > zLeave) 
                swap(zEnter, zLeave);        
   
            
// Update interval
   
        if (zEnter > tEnter) tEnter = zEnter;
            
if (zLeave < tLeave) tLeave = zLeave;
   
            
// Check if this resulted in empty interval
   
        if (tEnter > tLeave) 
                
return kNoIntersection;        
        }
   
        
// OK, we have an intersection.  
        // Return the parametric point in time where the intersection occurs.
   
    return tEnter;
    }

不幸的是,在实际情况中,物体的边界框很少是轴对齐于同一个坐标空间的。然而,因为这个检测相对较快,所以可以把它当作一个预备测试,可以先排除一些物体,然后再做一个特殊(通常计算量更大的)检测。

posted on 2008-02-27 16:06 lovedday 阅读(640) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论