AABB的球面相交试验

Sphere intersection test of AABB

本文关键字:面相 AABB      更新时间:2023-10-16

我有一个球体,我想知道我的轴对齐边界框(AABB)是否完全、部分或根本不在球体内。我发现了很多算法,但它们只给出部分或外部结果。有指针吗?

您可能已经在"Graphics Gems":中找到了Jim Arvo最流行的算法来确定AABB是否与实心球体相交

        dmin = 0;
        for( i = 0; i < 3; i++ ) {
            if( C[i] < Bmin[i] ) dmin += SQR( C[i] - Bmin[i] ); else
            if( C[i] > Bmax[i] ) dmin += SQR( C[i] - Bmax[i] );     
            }
        if( dmin <= r2 ) return TRUE;

其中,Bmin存储每个轴的AABB的最小值,Bmax存储每个轴AABB的最大值,C是球中心的坐标,r2是半径的平方。例如,这个解决方案也出现在这个stackoverflow问题中:立方体-球体相交测试?

正如您已经发现的,如果AABB完全在球体内,但您希望将这种情况作为特殊情况进行检测,则此算法还会返回TRUE。这样做的一种方法是颠倒上面的算法。该算法的工作原理基本上是找到AABB中最接近球中心的点,并将该点和球中心之间的平方坐标增量相加。如果这个和小于球体半径的平方,那么(在勾股定理之后)AABB的点位于球体内部。因此,AABB部分或全部包含在球体内。

现在假设您已经运行了该检查,您想了解AABB是仅部分包含还是完全包含。为此,让我们运行类似的检查,但不是用最靠近圆心的AABB点,而是用离圆心最远的点。如果该点与圆心的距离小于球体半径,则AABB完全包含在球体内。

有趣的是,吉姆·阿沃(Jim Arvo)引用的算法中已经包含了一个可以做到这一点的算法。原始代码包含对"空心"或"实心"球体和AABB的检查。不幸的是,位于的原始代码http://www.ics.uci.edu/~arvo/code/BoxSphereIntersect.c已不可用,但互联网档案中仍有:http://web.archive.org/web/20100323053111/http://www.ics.uci.edu/~arvo/code/BoxSphereIntersect.c你基本上对空心球的情况感兴趣。我不知道你是否希望你的AABB框是空心的(不同的是,当球体在框内时,你的检查是否返回true),所以我将在这里粘贴这两种情况:

switch( mode )
    {
    case 0: /* Hollow Box and Hollow Sphere */
        dmin = 0;
        dmax = 0;
        face = FALSE;
        for( i = 0; i < n; i++ ) {
            a = SQR( C[i] - Bmin[i] );
            b = SQR( C[i] - Bmax[i] );
            dmax += MAX( a, b );
            if( C[i] < Bmin[i] ) {
                face = TRUE;
                dmin += a;
                }
            else if( C[i] > Bmax[i] ) {
                face = TRUE;
                dmin += b;
                }
            else if( MIN( a, b ) <= r2 ) face = TRUE;
            }
        if( face && ( dmin <= r2 ) && ( r2 <= dmax ) ) return TRUE;
        break;
    case 2: /* Solid Box and Hollow Sphere */
        dmax = 0;
        dmin = 0;
        for( i = 0; i < n; i++ ) {
            a = SQR( C[i] - Bmin[i] );
            b = SQR( C[i] - Bmax[i] );
            dmax += MAX( a, b );
            if( C[i] < Bmin[i] ) dmin += a; else
            if( C[i] > Bmax[i] ) dmin += b;
            }
        if( dmin <= r2 && r2 <= dmax ) return TRUE;
        break;

为了解决您最初的问题,您现在需要更改返回条件。如果dmin小于r2,但dmax大于r2,则AABB位于球面上(部分交点)。如果dmindmax小于r2,则AABB完全位于球体内。

这里详细介绍了相交测试,结果为至少部分相交的true和无相交的false

现在,您需要检查AABB是否完全在球体内。通过检查是否所有点都在球体内,可以很容易地做到这一点。该测试可以简化为检查AABB的两个相对顶点是否在球体内。将距离的平方与球体半径的平方进行比较,可以使测试非常快速。

你可以很容易地将两个测试链接在一起:

  • 检查AABB是否完全包含。如果没有,请检查它是否被部分包含
  • 反之亦然:检查AABB是否至少部分包含。如果是,请检查它是否已完全包含

根据每种情况发生的频率,如果你觉得需要速度,其中一种可能会更好地评测你的代码。

尝试此算法:如果球体位于(或部分位于)AABB的所有平面的inside side上,则球体与AABB碰撞。平面的CCD_ 15表示指向AABB中心的半空间。

因此,您应该检查6个AABB平面(xmin/xmax、ymin/ymax、zmin/zmax)中每一个的球体与轴对齐平面的交点。如果按球体半径拉伸平面并检查sphere center pointextruded plane,则此比较非常简单。

第页。S.我没有在练习中试过。该算法基于类似的方法来确定三角形内的点(https://stackoverflow.com/a/2049593/326017)