Triangle Ray Intersection Algorithm [works perfectly]

Hi,

Edit: This actually works just fine. Rays don’t end at their magnitude, so of course this would return true.


Enclosed is a photo of a ray and a triangle. The ray does not visibly intersect the triangle. But according to every triangle intersection algorithm I can find, it does.

This one’s stumped me for an entire weekend and now I’m pissed. Normally when someone gets an algorithm that won’t work right, simply changing the algorithm or copying and pasting in some else’s algorithm is all you have to do. Then, look for differences, and figure out where yours went wrong.

Not this time.

This is a simple triangle intersection algorithm I’ve changed over and over. I’ve googled the hell out of this, and modified several others’ algorithms (see below) with my data. I have looked over other forum threads, and others’ source code, which is where I got the original versions of some of this code.

Basically, they all seems to work right for many triangles and rays, but occasionally they all get stumped on one. This particular one (and some variations on it) are always “wrong”, and it’s driving me nuts. I put wrong in quotes because clearly it’s doing what I’m telling it to do, it’s just that the results aren’t what I want.

For the sake of convenience, I have added all necessary code here so you can copy and paste it into a project and see it fail for yourself. In every case below, it’s checking to see if the ray intersects the triangle, and in every case, it returns ‘true’. It should be false.

Please help, I’m at the end of my rope, and I’m pretty sure this rope doesn’t intersect a triangle either.

-Chilton


    bool BetweenTriangleAndRay()
    {

        //ref Vector3 p1, ref Vector3 p2, ref Vector3 p3, ref Vector3 rayForward, ref Vector3 rayOrigin
    Vector3 rayOrigin = new Vector3(1,0,0);
    Vector3 rayForward = new Vector3(0,2,0);
    //Ray ray = new Ray(P0,P1);
    Vector3 p1 = new Vector3(3,     2.3f,     -4);
    Vector3 p2 = new Vector3(-7,     2.3f,     2);
    Vector3 p3 = new Vector3(3,     2.3f,      2);

    Debug.DrawLine(rayOrigin,rayForward, Color.red, 10);

    Debug.DrawLine(p1, p2, Color.blue, 10);
    Debug.DrawLine(p2, p3, Color.blue, 10);
    Debug.DrawLine(p3, p1, Color.blue, 10);


        //Find vectors for two edges sharing vertex/point p1
        var e1 = p2 - p1;
        var e2 = p3 - p1;

        // calculating determinant
        var p = Vector3.Cross( rayForward,  e2);

        //Calculate determinat
        var det = Vector3.Dot( e1,  p);

        //if determinant is near zero, ray lies in plane of triangle otherwise not
        if (det > -0.000001f && det < 0.000001f) { return false; }
        var invDet = 1.0f / det;

        //calculate distance from p1 to ray origin
        var t = rayOrigin - p1;

        //Calculate u parameter
        var u = Vector3.Dot( t,  p) * invDet;

        //Check for ray hit
        if (u < 0 || u > 1) { return false; }

        //Prepare to test v parameter
        var q = Vector3.Cross( t,  e1);

        //Calculate v parameter
        var v = Vector3.Dot( rayForward,  q) * invDet;

        //Check for ray hit
        if (v < 0 || u + v > 1) return false;

        if ((Vector3.Dot(e2, q) * invDet) > 0.000001)
        {
            //ray does intersect
            return true;
        }

        // No hit at all
        return false;
    }

Another attempt, found this algorithm in another place, adapted it for the same test…

     public static bool Intersect()
     {

    Vector3 P0 = new Vector3(1,0,0);
    Vector3 P1 = new Vector3(0,2,0);
    Ray ray = new Ray(P0,P1);
    Vector3 p1 = new Vector3(3,     2.3f,     -4);
    Vector3 p2 = new Vector3(-7,     2.3f,     2);
    Vector3 p3 = new Vector3(3,     2.3f,      2);


         // Vectors from p1 to p2/p3 (edges)
         Vector3 e1, e2;
         Vector3 p, q, t;
         float det, invDet, u, v;
         //Find vectors for two edges sharing vertex/point p1
         e1 = p2 - p1;
         e2 = p3 - p1;
         // calculating determinant
         p = Vector3.Cross(ray.direction, e2);
         //Calculate determinat
         det = Vector3.Dot(e1, p);
         //if determinant is near zero, ray lies in plane of triangle otherwise not
         if (det > -Mathf.Epsilon && det < Mathf.Epsilon) { return false; }
         invDet = 1.0f / det;
         //calculate distance from p1 to ray origin
         t = ray.origin - p1;
         //Calculate u parameter
         u = Vector3.Dot(t, p) * invDet;
         //Check for ray hit
         if (u < 0 || u > 1) { return false; }
         //Prepare to test v parameter
         q = Vector3.Cross(t, e1);
         //Calculate v parameter
         v = Vector3.Dot(ray.direction, q) * invDet;
         //Check for ray hit
         if (v < 0 || u + v > 1) { return false; }
         if ((Vector3.Dot(e2, q) * invDet) > Mathf.Epsilon)
         {
             //ray does intersect
             return true;
         }
         // No hit at all
         return false;
     }

… and one more attempt, another algorithm adapted, slightly different input, same result…

int intersect3D_RayTriangle()
{

    Vector3 P0 = new Vector3(0,0,0);
    Vector3 P1 = new Vector3(0,1,0);
    Vector3 V0 = new Vector3(3,     2.3f,     -4);
    Vector3 V1 = new Vector3(-7,     2.3f,     2);
    Vector3 V2 = new Vector3(3,     2.3f,      2);

    Debug.DrawLine(P0,P1, Color.red, 10);

    Debug.DrawLine(V0, V1, Color.blue, 10);
    Debug.DrawLine(V1, V2, Color.blue, 10);
    Debug.DrawLine(V2, V0, Color.blue, 10);

  
    Vector3 I;
    Vector3 u, v, n;              // triangle vectors
    Vector3 dir, w0, w;           // ray vectors
    float     r, a, b;              // params to calc ray-plane intersect

    // get triangle edge vectors and plane normal
    u = V1 - V0;
    v = V2 - V0;
    n = Vector3.Cross(u, v);              // cross product
    if (n == Vector3.zero)             // triangle is degenerate
        return -1;                  // do not deal with this case

    dir = P1 - P0;              // ray direction vector
    w0 = P0 - V0;
    a = -Vector3.Dot(n,w0);
    b = Vector3.Dot(n,dir);
    if (Mathf.Abs(b) < .00001f) {     // ray is  parallel to triangle plane
        if (a == 0)                 // ray lies in triangle plane
            return 2;
        else return 0;              // ray disjoint from plane
    }

    // get intersect point of ray with triangle plane
    r = a / b;
    if (r < 0.0)                    // ray goes away from triangle
        return 0;                   // => no intersect
    // for a segment, also test if (r > 1.0) => no intersect

    I = P0 + r * dir;            // intersect point of ray and plane

    // is I inside T?
    float    uu, uv, vv, wu, wv, D;
    uu = Vector3.Dot(u,u);
    uv = Vector3.Dot(u,v);
    vv = Vector3.Dot(v,v);
    w = I - V0;
    wu = Vector3.Dot(w,u);
    wv = Vector3.Dot(w,v);
    D = uv * uv - uu * vv;

    // get and test parametric coords
    float s, t;
    s = (uv * wv - vv * wu) / D;
    if (s < 0.0 || s > 1.0)         // I is outside T
        return 0;
    t = (uv * wu - uu * wv) / D;
    if (t < 0.0 || (s + t) > 1.0)  // I is outside T
        return 0;

    return 1;                       // I is in T
}

3139034--238096--Screen Shot 2017-07-10 at 8.35.35 AM.png

Okay, I started off looking for a line segment and somehow ended with a ray cast. I see now that this would of course always work, as a ray doesn’t end at the magnitude of the ray.

So, this works just fine. The problem was it wasn’t the right tool for the job.

nice, thanks for collecting those , here are a few methods to get the vector / point given the triangle normals / points and the barycentric coordinates ( u and v values ) :

public static Vector3 BarycentricPoint(Vector3 p1, Vector3 p2, Vector3 p3, Vector2 uv)
{
    // https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/barycentric-coordinates

    return p2 * uv.x + p3 * uv.y + p1 * (1 - uv.x - uv.y);
}
public static Vector3 BarycentricLerp( Vector3 a, Vector3 b, Vector3 c, Vector2 uv )
{
    var l1 = Vector3.Lerp(a, b, uv.x);
    var l2 = Vector3.Lerp(a, c, uv.y);
    var v = ( l1 + l2 ) / 2f;
    return ( v - a ) * 2f + a;
}
public static Vector3 BarycentricSlerp( Vector3 a, Vector3 b, Vector3 c , Vector2 uv )
{
    var l1 = Vector3.Slerp( a, b, uv.x );
    var l2 = Vector3.Slerp( a, c, uv.y );
    return Vector3.Slerp( l1, l2, uv.y / (uv.x + uv.y) );
}