Efficient AABB/triangle intersection in C#

eej*_*i42 6 c# 3d graphics geometry

Can anyone recommend an efficient port to CSharp of any of the public AABB/triangle intersection algorithms.

I've been looking at Moller's approach, described abstractly here, and if I were to port it, I would probably start from this C++ version. This C++ library by Mike Vandelay seems like it could also be a great starting point.

...or... any other "wheel" that can take a triangle of Vector3's and tell me if it intersects with an AABB), relatively efficiently.

There seem to be a variety of algorithms, but most seem to be written in c++, or just described abstractly in white papers and I need a c# specific implementation for our application. Efficiency is not key, but c# is. (though efficiency is obviously nice too of course ;p )

任何C#选项,在我通过"数学"端口之前;)将不胜感激!谢谢.

Mar*_*rot 22

对于任何两个凸网格,要查找它们是否相交,您需要检查是否存在分离平面.如果是这样,它们就不会相交.可以从任何形状的任何面或边缘交叉产品中挑选平面.

该平面定义为法线和偏离Origo.因此,您只需要检查AABB的三个面和三角形的一个面.

bool IsIntersecting(IAABox box, ITriangle triangle)
{
    double triangleMin, triangleMax;
    double boxMin, boxMax;

    // Test the box normals (x-, y- and z-axes)
    var boxNormals = new IVector[] {
        new Vector(1,0,0),
        new Vector(0,1,0),
        new Vector(0,0,1)
    };
    for (int i = 0; i < 3; i++)
    {
        IVector n = boxNormals[i];
        Project(triangle.Vertices, boxNormals[i], out triangleMin, out triangleMax);
        if (triangleMax < box.Start.Coords[i] || triangleMin > box.End.Coords[i])
            return false; // No intersection possible.
    }

    // Test the triangle normal
    double triangleOffset = triangle.Normal.Dot(triangle.A);
    Project(box.Vertices, triangle.Normal, out boxMin, out boxMax);
    if (boxMax < triangleOffset || boxMin > triangleOffset)
        return false; // No intersection possible.

    // Test the nine edge cross-products
    IVector[] triangleEdges = new IVector[] {
        triangle.A.Minus(triangle.B),
        triangle.B.Minus(triangle.C),
        triangle.C.Minus(triangle.A)
    };
    for (int i = 0; i < 3; i++)
    for (int j = 0; j < 3; j++)
    {
        // The box normals are the same as it's edge tangents
        IVector axis = triangleEdges[i].Cross(boxNormals[j]);
        Project(box.Vertices, axis, out boxMin, out boxMax);
        Project(triangle.Vertices, axis, out triangleMin, out triangleMax);
        if (boxMax <= triangleMin || boxMin >= triangleMax)
            return false; // No intersection possible
    }

    // No separating axis found.
    return true;
}

void Project(IEnumerable<IVector> points, IVector axis,
        out double min, out double max)
{
    double min = double.PositiveInfinity;
    double max = double.NegativeInfinity;
    foreach (var p in points)
    {
        double val = axis.Dot(p);
        if (val < min) min = val;
        if (val > max) max = val;
    }
}

interface IVector
{
    double X { get; }
    double Y { get; }
    double Z { get; }
    double[] Coords { get; }
    double Dot(IVector other);
    IVector Minus(IVector other);
    IVector Cross(IVector other);
}

interface IShape
{
    IEnumerable<IVector> Vertices { get; }
}

interface IAABox : IShape
{
    IVector Start { get; }
    IVector End { get; }
}

interface ITriangle : IShape {
    IVector Normal { get; }
    IVector A { get; }
    IVector B { get; }
    IVector C { get; }
}
Run Code Online (Sandbox Code Playgroud)

一个很好的例子是盒子(±10,±10,±10)和三角形(12,9,9),(9,12,9),(19,19,20).没有任何面可以用作分离平面,但它们不相交.分离轴<1,1.0>,其从<1,0,0>和<-3,3,0>之间的叉积获得.

图形

  • 我很接近.在2D情况下本来就足够了,但在3D中你还需要检查边缘交叉产品. (2认同)

Reg*_*lez 6

我注意到这个实现中的一个小错误会导致漏报.如果您的三角形有一条平行于一个轴的边(例如(1,0,0)),那么在计算时您将得到一个空向量

triangleEdges[i].Cross(boxNormals[j])
Run Code Online (Sandbox Code Playgroud)

这将导致下面的测试中的平等,并给你一个假阴性.

在行处用<和>替换<=和> =

 if (boxMax <= triangleMin || boxMin >= triangleMax)
Run Code Online (Sandbox Code Playgroud)

(严格比较删除这些情况).

除此之外,效果很好!

谢谢

  • 这行他的意思是:如果 (boxMax &lt;= triangleMin || boxMin &gt;= triangleMax) 编辑它并且它完美无缺,Markus Jarderot 没有在他的回答中修复它 (2认同)