Search code examples
c++game-physicsphysicsray

Ray-triangle intersection. It work with two directions


I don't know why, but ray-triangle intersection algorithm (Möller-Trumbore and Watertight) works with 2 directions.

from origin to end - how it must work

from end to origin - the problem

white cross - an unnecessary intersection

screenshot

I tried to use 3 component vectors, so I have removed all W values from code.

Ray

    class Ray
    {
    public:
        Ray(){}
        ~Ray(){}
    
        Vector4 m_origin;
        Vector4 m_end;
        Vector4 m_direction;
    
        void update()
        {
            m_end._f32[3] = 0.f;
            m_origin._f32[3] = 0.f;
    
            m_direction._f32[ 0 ] = m_end._f32[0] - m_origin._f32[0];
            m_direction._f32[ 1 ] = m_end._f32[1] - m_origin._f32[1];
            m_direction._f32[ 2 ] = m_end._f32[2] - m_origin._f32[2];
            m_direction._f32[ 3 ] = 0.f;
            m_direction.normalize(); // without W
        }
    };

Triangle and algorithm

    struct Triangle
    {
        Vector4 v1; // vertex 1 position
        Vector4 v2; // vertex 2 position
        Vector4 v3; // vertex 3 position
        Vector4 e1; // edge, see update()
        Vector4 e2; // edge, see update()
    
        void update()
        {
            e1 = Vector4( v2._f32[0] - v1._f32[0],
                v2._f32[1] - v1._f32[1],
                v2._f32[2] - v1._f32[2],
                0.f);
                
            e2 = Vector4( v3._f32[0] - v1._f32[0],
                v3._f32[1] - v1._f32[1],
                v3._f32[2] - v1._f32[2],
                0.f);
        }
    
        // Möller-Trumbore algorithm
        bool rayTest_MT( const Ray& ray, bool withBackFace, f32& T, f32& U, f32& V, f32& W )
        {
            Vector4  pvec = ray.m_direction.cross_return(e2);
            f32 det  = e1.dot(pvec);
            
            if( withBackFace )
            {
                if( std::abs(det) < 0.0000001f && det > -0.0000001f )
                    return false;
            }
            else
            {
                if( det < 0.0000001f && det > -0.0000001f )
                    return false;
            }
    
            Vector4 tvec(
                ray.m_origin._f32[0] - v1._f32[0],
                ray.m_origin._f32[1] - v1._f32[1],
                ray.m_origin._f32[2] - v1._f32[2],
                0.f);
    
            //tvec.setW(1.f);//...
    
            f32 inv_det = 1.f / det;
            U = tvec.dot(pvec) * inv_det;
    
            if( U < 0.f || U > 1.f )
                return false;
    
            Vector4  qvec = tvec.cross_return(e1);
            V    = ray.m_direction.dot(qvec) * inv_det;
    
            if( V < 0.f || U + V > 1.f )
                return false;
    
            T = e2.dot(qvec) * inv_det;
            W = 1.f - U - V;
            return true;
        }
    };

Simple vector

Vector4
{
public:
    f32 _f32[4];

    Vector4()
    {
        _f32[ 0 ] = 0.f;
        _f32[ 1 ] = 0.f;
        _f32[ 2 ] = 0.f;
        _f32[ 3 ] = 0.f;
    }
.........

Maybe I NEED to use W component...somewhere🤔 NO. see my comment.

Möller-Trumbore algorithm from scratchapixel.com

Watertight Ray/Triangle Intersection


Solution

  • Unit distance on ray of intercept

    The last value calculated T is the unit distance on the ray from the ray origin. Where T == 0.0f is at the origin, T == 1.0f is at the ray end, T > 1.0f are past the ray end and T < 0.0f are before the ray origin.

    Thus you need to check T to make sure that the intercept is on the correct part of the ray. Examples

    • Ahead of the not including the origin. return T > EPSILON;

    • Ahead of the ray including the origin. return T > -EPSILON;

    • On the ray including origin and end. return T > -EPSILON && T < 1.0f + EPSILON;

    • On the ray excluding origin and end. return T > EPSILON && T < 1.0f - EPSILON;

    and so on...

    EPSILON

    We use EPSILON to deal with floating point errors, for this algorithm you may want to use a larger value than EPSILON especially if the angle between the ray and the polygon normal is close to 90deg