Настройка луча (начало, направление) и пересечения треугольника (без glm)

Edit3: Мои проблемы были в совершенно разных функциях, чем я ожидал. плохо пусть код останется, может это кому-то поможет :) (и не забывайте отлаживать!).

Я пытаюсь найти вектор, где линия пересекается с треугольником.

Текущее состояние: случайные пересечения, даже если мышь не находится на полу, и в зависимости от вида камеры (матрица просмотра)

Шаги

  1. Отменить проекцию координации мыши
  2. Проверить пересечение линии / треугольника

Отменить проекцию координат мыши

Я проверил источник glm :: unproject и gluUnproject и создал эту функцию.

   pixel::CVector3 pixel::CVector::unproject(
    CVector2 inPosition,
    pixel::CShape window,
    pixel::matrix4 projectionMatrix,
    pixel::matrix4 modelViewMatrix,
    float depth
    )
{
    // transformation of normalized coordinates
    CVector4 inVector;
    inVector.x = (2.0f * inPosition.x) / window.width - 1.0f;
    inVector.y = (2.0f * inPosition.y) / window.height - 1.0f;
    inVector.z = 2.0f * depth - 1.0f;
    inVector.w = 1.0f;

    // multiply inverted matrix with vector
    CVector4 rayWorld = pixel::CVector::multMat4Vec4(pixel::CMatrix::invertMatrix(projectionMatrix * modelViewMatrix), inVector);

    CVector3 result;
    result.x = rayWorld.x / rayWorld.w;
    result.y = rayWorld.y / rayWorld.w;
    result.z = rayWorld.z / rayWorld.w;



    return result;
}

Проверка перекрестка

pixel::CVector3 pixel::Ray::intersection(
    Ray ray,
    pixel::CVector3 v0,
    pixel::CVector3 v1,
    pixel::CVector3 v2
    )
{
    // compute normal
    CVector3 a, b, n;
    a = v1 - v0;
    b = v2 - v0;

    n = ray.direction.cross(b);

    // find determinant
    float det = a.dot(n);

    if (det < 0.000001f)
    {
        std::cout << "Ray intersecting with backface triangles \n";
        return pixel::CVector::vector3(0.0f, 0.0f, 0.0f);
    }
    det = 1.0f / det;

    // calculate distance from vertex0 to ray origin
    CVector3 s = ray.origin - v0;
    float u = det * s.dot(n);

    if (u < -0.000001f || u > 1.f + 0.000001f)
    {
        std::cout << "U: Intersection outside of the triangle!\n";
        return pixel::CVector::vector3(0.0f, 0.0f, 0.0f);
    }

    CVector3 r = s.cross(a);
    float v = det * ray.direction.dot(r);
    if (v < -0.000001f || u + v > 1.f + 0.000001f)
    {
        std::cout << "V/U: Intersection outside of triangle!\n";
        return pixel::CVector::vector3(0.0f, 0.0f, 0.0f);
    }

    // distance from ray to triangle
    det = det *  b.dot(r);

    std::cout << "T: " << det << "\n";

    CVector3 endPosition;
    endPosition.x = ray.origin.x + (ray.direction.x * det);
    endPosition.y = ray.origin.y + (ray.direction.y * det);
    endPosition.z = ray.origin.z + (ray.direction.z * det);

    return endPosition;
}

Использование

    if (event.button.button == SDL_BUTTON_RIGHT)
            {

                camera->setCameraActive();
                float mx = event.motion.x;
                float my = window->info.height - event.motion.y;
                // ray casting
                pixel::Ray ray;

                std::cout << "\n\n";

                    // near
                pixel::CVector3 rayNear = pixel::CVector::unproject(
                    pixel::CVector::vector2(mx, my),
                    pixel::CVector::shape2(window->info.internalWidth, window->info.internalHeight),
                    camera->camInfo.currentProjection,
                    camera->camInfo.currentView,
                    1.0f
                    );
                // far
                pixel::CVector3 rayFar = pixel::CVector::unproject(
                    pixel::CVector::vector2(mx, my),
                    pixel::CVector::shape2(window->info.internalWidth, window->info.internalHeight),
                    camera->camInfo.currentProjection,
                    camera->camInfo.currentView,
                    0.0f
                    );


                // normalized direction results in the same behavior
                ray.origin = cameraPosition;

                ray.direction = pixel::CVector::normalize(rayFar- rayNear);

                std::cout << "Raycast \n";
                std::cout << "Mouse Position: " << mx << " - " << my << "\n";
                std::cout << "Camera Position: " << ray.origin.x << " - " << ray.origin.y << " - " << ray.origin.z << "\n";
                std::cout << "Ray direction: " << ray.direction.x << " - " << ray.direction.y << " - " << ray.direction.z << "\n";


                pixel::CVector3 vertOne = pixel::CVector::vector3(0.0f, 0.0f, -300.0f);
                pixel::CVector3 vertTwo = pixel::CVector::vector3(0.0f, 0.0f, 0.0f);
                pixel::CVector3 vertThree = pixel::CVector::vector3(300.0f, 0.0f, 0.0f);
                pixel::CVector3 vertFour = pixel::CVector::vector3(300.0f, 0.0f, -300.0f);


                pixel::CVector3 rayHit = pixel::Ray::intersection(ray, vertOne, vertTwo, vertThree);
                pixel::CVector3 rayHit2 = pixel::Ray::intersection(ray, vertThree, vertFour, vertOne);
                std::cout << "Ray hit: " << rayHit.x << " - " << rayHit.y << " - " << rayHit.z << "\n";
                std::cout << "Ray hit: " << rayHit2.x << " - " << rayHit2.y << " - " << rayHit2.z << "\n";
                std::cout << "--------------------\n";
                towerHouse->modelMatrix = pixel::CMatrix::translateMatrix(rayHit);

Вывод

Поскольку ive никогда не использовал glm :: unproject или gluUnproject, я не знаю, как должен выглядеть нормальный вывод, но получаю такие результаты:

Направление луча: 0,109035 -0,0380502 0,0114562

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

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

Все эти ошибки выглядят так, будто основной источник проблемы - это непредсказуемость.

Есть какие-нибудь подсказки в правильном направлении?


person Raphael Mayer    schedule 30.12.2013    source источник
comment
Я заметил одну вещь: ваша функция unproject не возвращает направление, а возвращает точку. Когда вы возвращаете некоторую точку на ближней плоскости в мировое пространство с помощью inv (proj * biew), вы получаете координату этой точки в мировом пространстве. Вам нужно вычесть положение камеры в мировом пространстве, чтобы получить направление луча.   -  person derhass    schedule 30.12.2013
comment
@derhass спасибо, звучит разумно. и я действительно получаю лучшие результаты. способ более точный. я должен переименовать функцию unproject в screenPointToWorldPos. Теперь он перемещает объект в направлении курсора, но далеко или маленькими шагами (например, мышь находится на x = 30, объект находится на x = 5, если мышь находится на x = 50, объект находится около x = 7)   -  person Raphael Mayer    schedule 30.12.2013
comment
Почему вы получаете координаты мирового пространства из вашей UnProject функции? Если бы вы следовали glm и GLU, вы, вероятно, заметили бы, что они используют матрицу ModelView и дают вам координаты объект-пространство. Вы действительно используете в этом примере только матрицу представления? И ваш расчет координат NDC далек от того, Y инвертирован, а Z неправильно настроен для диапазона глубин. Z должно быть 2 * глубина - 1.0, потому что глубина в NDC колеблется от [-1,1] вместо [0,1] (диапазон глубины по умолчанию) в пространстве окна.   -  person Andon M. Coleman    schedule 30.12.2013
comment
Я написал общий обзор математики, связанной с снятием защиты с оконного (экранного) пространства на объектное пространство в другой ответ. Вы можете найти это полезным, это, безусловно, поможет объяснить, почему вы не можете просто ввести значение глубины, как вы делаете прямо сейчас.   -  person Andon M. Coleman    schedule 30.12.2013
comment
Спасибо за информацию @ AndonM.Coleman. У меня был реализован диапазон глубины, то же самое, что не инвертировать ось y, то, что я разместил здесь, является результатом нескольких дней поиска информации и их смешивания. Я пробовал это с view * modelMatrix, но это тоже привело к сбоям. это матрица вида модели или только матрица модели? glm имеет только параметр modelMatrix.   -  person Raphael Mayer    schedule 30.12.2013
comment
Если вы хотите преобразовать в объектное пространство, это ModelView. Это комбинация этих двух матриц. Поскольку GL использует матрицы, состоящие из главных столбцов, эта матрица является результатом: View * Model (чтение умножения матриц справа налево).   -  person Andon M. Coleman    schedule 30.12.2013
comment
@ AndonM.Coleman Я пробовал это несколько раз: добраться до ближайшей точки с глубиной -1.0f и дальней точки с глубиной 1.0f, а затем нормализовать далеко - близко, чтобы получить направление, это приводит к направлению глубины 0, 0, 0,25xxx. в качестве viewMatrix я использовал camera- ›camInfo.currentView * floor-› modelMatrix. По-прежнему безуспешно.   -  person Raphael Mayer    schedule 30.12.2013
comment
Если вы это сделаете, ваш луч пойдет не в том направлении. Честно говоря, я понятия не имею, почему floor вводится в это обсуждение, это будет матрица преобразования для одного объекта в вашей сцене. Если у вас нет преобразования объекта в мир, который применяется к каждому объекту (это было бы объединено в матрицу ModelView), тогда вы можете просто использовать матрицу просмотра, как и изначально. Учитывая луч (в настоящее время обратный), который вы отбрасываете, вам необходимо добавить его начало координат в точку вашего взгляда (в мировом пространстве) и проверить пересечение с каждым объектом (после применения их матриц объекта-мира).   -  person Andon M. Coleman    schedule 30.12.2013
comment
Объектное или видовое пространство было бы гораздо более простым для тестирования координатным пространством.   -  person Andon M. Coleman    schedule 30.12.2013
comment
@ AndonM.Coleman, это не просто тест, который мне нужен. Мне нужно положение перекрестка в мировом пространстве, так как я пытаюсь переместить объект в это положение щелчка на полу. Я обновил ответ отредактированной функцией непроекта.   -  person Raphael Mayer    schedule 30.12.2013


Ответы (2)


Это далеко не ответ на этот вопрос, но это слишком сложно объяснить в комментариях или чате.

Прежде всего:

            // near
            pixel::CVector3 rayNear = pixel::CVector::raycast(
                pixel::CVector::vector2(mx, my),
                pixel::CVector::shape2(window->info.internalWidth, window->info.internalHeight),
                camera->camInfo.currentProjection,
                camera->camInfo.currentView,
                1.0f // WRONG
                );
            // far
            pixel::CVector3 rayFar = pixel::CVector::raycast(
                pixel::CVector::vector2(mx, my),
                pixel::CVector::shape2(window->info.internalWidth, window->info.internalHeight),
                camera->camInfo.currentProjection,
                camera->camInfo.currentView,
                0.0f // WRONG
                );

Ближний - это 0,0 в пространстве окна, а дальний - 1,0 (зависит от диапазона глубины, но если вы изменили диапазон глубины, вы уже должны это знать).

В вашей функции приведения лучей у вас есть:

CVector3 result;
result.x = rayWorld.x / rayWorld.w;
result.y = rayWorld.y / rayWorld.w;
result.z = rayWorld.z / rayWorld.w;

Есть вероятность, что w == 0.0, и в настоящее время результат еще не луч ... это позиция в объектном пространстве (а не в мире). Обычно вы всегда будете работать с хорошо управляемыми матрицами, но если вы когда-нибудь посмотрите на формальную реализацию UnProject (...), вы заметите, что они обрабатывают случай, когда w == 0.0 со специальным возвращаемым значением или путем установки флага состояния.

            pixel::CVector3 vertOne = pixel::CVector::vector3(0.0f, 0.0f, -300.0f);
            pixel::CVector3 vertTwo = pixel::CVector::vector3(0.0f, 0.0f, 0.0f);
            pixel::CVector3 vertThree = pixel::CVector::vector3(300.0f, 0.0f, 0.0f);
            pixel::CVector3 vertFour = pixel::CVector::vector3(300.0f, 0.0f, -300.0f);

В каком координатном пространстве находятся эти вершины? Предположительно объектное пространство, что означает, что если вы отбрасываете луч из точки глаза вашей камеры (определенной в мировом пространстве), который проходит через точку на вашей дальней плоскости, и пытаетесь чаще проверять пересечение с треугольником в объектном пространстве. чем не скучаешь. Это связано с тем, что начало координат, масштаб и поворот для каждого из этих пространств могут отличаться. Вам нужно преобразовать эти точки в мировое пространство (в вашем исходном коде был floor->modelMatrix, который хорошо подходит для этой цели), прежде чем вы попробуете этот тест.

person Andon M. Coleman    schedule 30.12.2013
comment
спасибо за подробный ответ. Вершины находятся в пространстве obj, но я не переворачиваю / вращаю / масштабирую их, поэтому они также находятся в мировом пространстве. Но меня беспокоит то, что я по-прежнему получаю те же значения для ближнего и дальнего. те же x, y, z. независимо от того, какое значение глубины я передаю функции. - person Raphael Mayer; 31.12.2013
comment
Часто, когда вы получаете одинаковые значения X и Y из непроектной функции для разных значений WinZ, это происходит потому, что вы используете ортогональную матрицу проекции (в отличие от перспективы, которая масштабирует значения X и Y с расстоянием). Но получение того же значения Z необычно ... Я бы подумал о проверке значений перед нормализацией вектора. Вероятно, вы не получаете одно и то же значение Z каждый раз, просто, поскольку нет изменений в X или Y, вектор нормализуется до (0,0,1). - person Andon M. Coleman; 31.12.2013
comment
Я устанавливаю проекцию прямо перед тем, как использовать ее. Я показываю zFar и zNear отдельно от направления. Я даже удалил разделитель w. они всегда такие же. изменение глубины на 0,1 и 1000.0f в unproject () дает мне разные значения z (конечно, не в диапазоне -1 / 1) и те же значения x, y. - person Raphael Mayer; 31.12.2013
comment
Хорошо, я протестировал непроекцию с помощью glm, и, конечно же, она работает нормально. Я также воссоздал свою непроекцию с помощью glm, и она возвращает то же самое, что и функция glm. Ошибка должна быть где-то в моих математических функциях (mult4vec4, инверсия матриц и т. Д.) - person Raphael Mayer; 31.12.2013
comment
хотя мои проблемы были связаны с моими собственными функциями, я бы не смог заставить его работать без ваших хороших объяснений. Благодарность! - person Raphael Mayer; 01.01.2014

Я отследил проблему и исправил ошибки. У меня были неправильные операторы умножения матрицы * матрицы и матрицы * вектора.

person Raphael Mayer    schedule 01.01.2014