Пересечение луч-треугольник. Работает с двумя направлениями

Не знаю почему, но алгоритм пересечения луч-треугольник (Möller-Trumbore и Watertight) работает с двумя направлениями.

от начала до конца - как это должно работать

из конца в начало - проблема

белый крест - ненужный перекресток

скриншот

Я пытался использовать 3 вектора компонентов, поэтому удалил все значения W из кода.

Рэй

    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
        }
    };

Треугольник и алгоритм

    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;
        }
    };

Простой вектор

Vector4
{
public:
    f32 _f32[4];

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

Возможно, мне НУЖНО использовать компонент W... где-то???? НЕТ. см. мой комментарий.

Алгоритм Мёллера-Трумбора с сайтаcratapixel.com

Пересечение водонепроницаемого луча и треугольника


person Тёма Басов    schedule 23.07.2020    source источник
comment
Хорошо, я могу получить длину (переменная T). И if T < 0.f тогда это неправильное направление. Но я не думаю, что это правильное решение. Или можно использовать if(T < 0.f) return false ?   -  person Тёма Басов    schedule 23.07.2020
comment
Пожалуйста, дайте ссылку на статью с алгоритмом, который вы пытаетесь реализовать. Кроме того, что такое Vector4? Что такое v# и e# в Triangle? Старайтесь давать осмысленные имена переменным, когда это возможно, и добавляйте документацию, когда это невозможно.   -  person Benny K    schedule 23.07.2020
comment
Я не вижу связи между тем, что вы реализовали, и этим: jcgt.org/ опубликовано/0002/01/05/paper.pdf. В любом случае невозможно проверить вашу математику, не имея всего кода, поэтому я предлагаю вам попробовать использовать его на простом тестовом примере, где вы можете вычислить все числа вручную и посмотреть, где он не работает.   -  person Benny K    schedule 23.07.2020
comment
Вы не проверяете, что T положителен (впереди источника), последний возврат должен быть if (T > 0.00001f) { return true; } else { return false;}   -  person Blindman67    schedule 24.07.2020
comment
@ Blindman67 да, ты прав. Из всех реализаций Möller-Trumbore, которые я видел, только одна имеет это if (T > 0.00001f). Я даже добавляю его в свой код, но позже удаляю. Вы можете ответить на этот вопрос.   -  person Тёма Басов    schedule 24.07.2020


Ответы (1)


Единичное расстояние на луче пересечения

Последнее рассчитанное значение T является единичным расстоянием по лучу от начала луча. Где T == 0.0f находится в начале луча, T == 1.0f — в конце луча, T > 1.0f — за концом луча, а T < 0.0f — перед началом луча.

Таким образом, вам нужно проверить T, чтобы убедиться, что пересечение находится в правильной части луча. Примеры

  • Впереди не считая происхождения. return T > EPSILON;

  • Впереди луча, включая начало координат. return T > -EPSILON;

  • О луче, включая начало и конец. return T > -EPSILON && T < 1.0f + EPSILON;

  • На луче, исключая начало и конец. return T > EPSILON && T < 1.0f - EPSILON;

и так далее...

ЭПСИЛОН

Мы используем EPSILON для обработки ошибок с плавающей запятой, для этого алгоритма вы можете использовать большее значение, чем EPSILON, особенно если угол между лучом и нормалью полигона близок к 90 градусам.

person Blindman67    schedule 24.07.2020