给定边界框和一条线(两个点),确定该线是否与框相交

时间:2010-07-13 08:29:03

标签: c# math vector c#-2.0

给定一个边界框,其中包含bounds.min.(x/y/z)bounds.max.(x/y/z)等定义以及3D空间中的两个点(表示为Vector3个对象),如何确定该行是否由两个点与边界框相交?

5 个答案:

答案 0 :(得分:10)

C ++中有一个可在线使用的实现:Line Box Intersectionhttp://www.3dkingdoms.com/weekly/weekly.php?a=3

另一个链接,包含许多相交测试的引用(和代码):http://www.realtimerendering.com/intersections.html

如果您想了解有关交叉口测试的更多信息,请阅读本书:Real-Time Collision Detection (Amazon)

编辑:此paper中的算法(“高效且稳健的Ray-Box交叉算法”,Amy Williams和Steve Barrus以及R. Keith Morley和Peter Shirley;图形,gpu和游戏工具杂志,Vol.10(1),49-54,2005)看起来特别简洁,并附带(C ++)源代码。

答案 1 :(得分:6)

如果您想自己进行数学运算,可以采用以下方法:将线条与边界框创建的6个平面中的每一个相交。

该行的向量表示为X = B + t * D,其中B是基点的元组(x,y,z)(例如,您的第一个点),D是该行的方向,再次表示为元组(dx,dy,dz)。你通过从另一个点中减去一个点得到方向,所以如果你有点P1(x1,y1,z1)和P2(x2,y2,z2),那么D = P2 - P1和B = P1,意思是D =(x2-x1,y2-y1,z2-z1)。我们将调用此向量的元素dx,dy和dz。

平面的参数表示是x + y + z = c。因此,将您的边界框转换为此表示,然后使用您的线的参数表示,例如三个方程x = x1 + t dx,y = y1 + t dy,z = y1 + t * dz,用于替换平面方程中的x,y和z。解决问题。由于您的6个平面中的每个平面都将与由2个轴创建的平面平行,因此您的问题变得更加容易;例如,对于与x和y轴创建的平面平行的平面,平面方程只是变为z = c,而c是其中一个边界框点的z坐标,依此类推。

现在用t来计算直线与平面的交点。 (如果t是< 0或> 1,那么你的线与P1-P2的OUTSIDE相交,如果t> = 0且t< = 1,那么你的线在P1和P2之间的某个平面上相交)

现在你还没有完成。平面方程给你一个平面,而不是一个矩形,所以与平面的交点实际上可能在你的矩形外面,但是因为你现在有了交点的坐标(x = x1 + t * dx等),你可以很容易地看到这个点是否在你的边界框的矩形内。现在缩小您的问题以检查2D空间中的点是否在边界框矩形内,这很容易检查。

当然,如果你实际使用这个解决方案,你应该做的第一件事是检查线是否也沿着一个轴对齐,因为在这种情况下,你的交叉点代码变得微不足道,它也将处理问题线不与某些平面相交,例如大量或微小数量的t,甚至可能是上溢或下溢。

我打赌有更快的方法可以做到这一点,但它会奏效。

答案 2 :(得分:5)

以下是似乎正在运行的代码,从Greg S的答案转换为C#:

bool CheckLineBox(Vector3 B1, Vector3 B2, Vector3 L1, Vector3 L2, ref Vector3 Hit)
{
    if (L2.x < B1.x && L1.x < B1.x) return false;
    if (L2.x > B2.x && L1.x > B2.x) return false;
    if (L2.y < B1.y && L1.y < B1.y) return false;
    if (L2.y > B2.y && L1.y > B2.y) return false;
    if (L2.z < B1.z && L1.z < B1.z) return false;
    if (L2.z > B2.z && L1.z > B2.z) return false;
    if (L1.x > B1.x && L1.x < B2.x &&
        L1.y > B1.y && L1.y < B2.y &&
        L1.z > B1.z && L1.z < B2.z)
    {
        Hit = L1;
        return true;
    }
    if ((GetIntersection(L1.x - B1.x, L2.x - B1.x, L1, L2, ref Hit) && InBox(Hit, B1, B2, 1))
      || (GetIntersection(L1.y - B1.y, L2.y - B1.y, L1, L2, ref Hit) && InBox(Hit, B1, B2, 2))
      || (GetIntersection(L1.z - B1.z, L2.z - B1.z, L1, L2, ref Hit) && InBox(Hit, B1, B2, 3))
      || (GetIntersection(L1.x - B2.x, L2.x - B2.x, L1, L2, ref Hit) && InBox(Hit, B1, B2, 1))
      || (GetIntersection(L1.y - B2.y, L2.y - B2.y, L1, L2, ref Hit) && InBox(Hit, B1, B2, 2))
      || (GetIntersection(L1.z - B2.z, L2.z - B2.z, L1, L2, ref Hit) && InBox(Hit, B1, B2, 3)))
        return true;

    return false;
}

bool GetIntersection(float fDst1, float fDst2, Vector3 P1, Vector3 P2, ref Vector3 Hit)
{
    if ((fDst1 * fDst2) >= 0.0f) return false;
    if (fDst1 == fDst2) return false;
    Hit = P1 + (P2 - P1) * (-fDst1 / (fDst2 - fDst1));
    return true;
}

bool InBox(Vector3 Hit, Vector3 B1, Vector3 B2, int Axis)
{
    if (Axis == 1 && Hit.z > B1.z && Hit.z < B2.z && Hit.y > B1.y && Hit.y < B2.y) return true;
    if (Axis == 2 && Hit.z > B1.z && Hit.z < B2.z && Hit.x > B1.x && Hit.x < B2.x) return true;
    if (Axis == 3 && Hit.x > B1.x && Hit.x < B2.x && Hit.y > B1.y && Hit.y < B2.y) return true;
    return false;
}

答案 3 :(得分:2)

JavaScript版本,基于SpikeX答案和glMatrix:

// all args are Vec3, Hit will be filled by this algo
function checkLineBox( B1, B2, L1, L2, Hit)
{
    if (L2[0] < B1[0] && L1[0] < B1[0]) return false;
    if (L2[0] > B2[0] && L1[0] > B2[0]) return false;
    if (L2[1] < B1[1] && L1[1] < B1[1]) return false;
    if (L2[1] > B2[1] && L1[1] > B2[1]) return false;
    if (L2[2] < B1[2] && L1[2] < B1[2]) return false;
    if (L2[2] > B2[2] && L1[2] > B2[2]) return false;
    if (L1[0] > B1[0] && L1[0] < B2[0] &&
        L1[1] > B1[1] && L1[1] < B2[1] &&
        L1[2] > B1[2] && L1[2] < B2[2])
    {
        vec3.set( L1, Hit);
        return true;
    }

    if ((getIntersection(L1[0] - B1[0], L2[0] - B1[0], L1, L2, Hit) && inBox(Hit, B1, B2, 1))
      || (getIntersection(L1[1] - B1[1], L2[1] - B1[1], L1, L2, Hit) && inBox(Hit, B1, B2, 2))
      || (getIntersection(L1[2] - B1[2], L2[2] - B1[2], L1, L2, Hit) && inBox(Hit, B1, B2, 3))
      || (getIntersection(L1[0] - B2[0], L2[0] - B2[0], L1, L2, Hit) && inBox(Hit, B1, B2, 1))
      || (getIntersection(L1[1] - B2[1], L2[1] - B2[1], L1, L2, Hit) && inBox(Hit, B1, B2, 2))
      || (getIntersection(L1[2] - B2[2], L2[2] - B2[2], L1, L2, Hit) && inBox(Hit, B1, B2, 3)))
        return true;

    return false;
}

var temp = vec3.create();
function getIntersection( fDst1, fDst2, P1, P2, Hit)
{
    if ((fDst1 * fDst2) >= 0) return false;
    if (fDst1 == fDst2) return false;

    vec3.subtract(P2, P1, temp);
    vec3.scale( temp, (-fDst1 / (fDst2 - fDst1)));
    vec3.add( temp, P1, Hit);

    return true;
}

function inBox(Hit, B1, B2, Axis)
{
    if (Axis == 1 && Hit[2] > B1[2] && Hit[2] < B2[2] && Hit[1] > B1[1] && Hit[1] < B2[1]) return true;
    if (Axis == 2 && Hit[2] > B1[2] && Hit[2] < B2[2] && Hit[0] > B1[0] && Hit[0] < B2[0]) return true;
    if (Axis == 3 && Hit[0] > B1[0] && Hit[0] < B2[0] && Hit[1] > B1[1] && Hit[1] < B2[1]) return true;
    return false;
}

答案 4 :(得分:1)

您可以将边界框表示为12个三角形(6个面中每个都有2个)。然后你可以检查你的每一行的交叉点。我有一个线 - 三角交叉函数,但它是为我自己的软件渲染引擎编写的,而不是为D3D编写的。如果你需要代码,我可以尝试转换它。