Путаница в смещениях плоскости zFar и zNear с использованием glm::перспектива

#matrix #opengl #graphics #glm-math

Вопрос:

Я использую glm, чтобы помочь создать программный растризатор для самообразования. В своем классе камеры я использую glm::lookat() для создания матрицы обзора и glm::perspective() для создания матрицы перспективы.

Кажется, я получаю то, что ожидаю для своих левой, правой верхней и нижней плоскостей обрезки. Тем не менее, я, похоже, либо делаю что-то неправильно для своих ближних/дальних планов, либо в моем понимании есть ошибка. Я достиг точки, в которой мой «гугл-фу» подвел меня.

Действуя в предположении, что я правильно извлекаю плоскости клипа из моей матрицы перспективы glm::и использую общее уравнение плоскости:

aX bY cZ d = 0

Я получаю странные значения d или «смещения» для моих плоскостей zNear и zFar. Насколько я понимаю, значение d-это значение, которое я бы смещал/переводил в точку P0 плоскости вдоль вектора нормали.

Они равны 0.200200200 и -0.200200200 соответственно. Однако мои нормали правильно ориентированы на 1,0 f и -1.f вдоль оси z, как и ожидалось для плоскости, перпендикулярной моему базисному вектору z.

Поэтому при тестировании такой точки, как мировое пространство (0, 0, -5), на этих плоскостях она преобразуется моей матрицей представлений в:

(0, 0, 5.81181192)

поэтому, проверяя его на этих плоскостях в цепочке клипов, указанная примерная вершина будет отбракована.

Вот начало класса камеры, устанавливающего соответствующие матрицы:

 static constexpr glm::vec3 UPvec(0.f, 1.f, 0.f);
static constexpr auto zFar = 100.f;
static constexpr auto zNear = 0.1f;


Camera::Camera(glm::vec3 eye, glm::vec3 center, float fovY, float w, float h) :

viewMatrix{ glm::lookAt(eye, center, UPvec) },
perspectiveMatrix{ glm::perspective(glm::radians<float>(fovY), w/h, zNear, zFar) },

frustumLeftPlane {setPlane(0, 1)},
frustumRighPlane {setPlane(0, 0)},
frustumBottomPlane {setPlane(1, 1)},
frustumTopPlane {setPlane(1, 0)},
frstumNearPlane  {setPlane(2, 0)},
frustumFarPlane {setPlane(2, 1)},
 

Объекты-усечения основаны на следующей структуре:

 struct Plane
{
    glm::vec4 normal;
    float offset;
};
 

Я извлек 6 плоскостей отсечения из перспективной матрицы, как показано ниже:

 Plane Camera::setPlane(const intamp; row, const boolamp; sign)
{
    float temp[4]{};
    Plane plane{};
    if (sign == 0)
    {
        for (int i = 0; i < 4;   i)
        {
            temp[i] = perspectiveMatrix[i][3]   perspectiveMatrix[i][row];
        }
    }
    else
    {
        for (int i = 0; i < 4;   i)
        {
            temp[i] = perspectiveMatrix[i][3] - perspectiveMatrix[i][row];
        }
    }

    plane.normal.x = temp[0];
    plane.normal.y = temp[1];
    plane.normal.z = temp[2];
    plane.normal.w = 0.f;
    plane.offset = temp[3];
    plane.normal = glm::normalize(plane.normal);

    return plane;
}
 

Любая помощь была бы признательна, так как сейчас я в растерянности.

Большое спасибо.

Комментарии:

1. Что именно вы пытаетесь реализовать? Отсечение (как это делается OpenGL) происходит после шага проецирования и не требует, чтобы вы получали плоскости отсечения из матрицы проецирования. Это не связано: это плохая практика-использовать bool, а затем проходить/тест на числовые значения. Если у вас есть bool, вместо этого используйте true, false.

2. Я не использую OpenGL напрямую. Я создаю программный рендерер, поэтому мне приходится самому реализовывать всю логику отсечения. Я просто не понимаю, почему мои самолеты zNear и zFar находятся так близко друг к другу.

Ответ №1:

Параметр d плоского уравнения описывает, насколько плоскость смещена от начала координат вдоль плоской нормали. Это также учитывает длину нормы.

Нельзя просто нормализовать нормаль, не регулируя также параметр d, так как нормализация изменяет длину нормы. Если вы хотите нормализовать плоское уравнение, вам также необходимо применить шаг деления к координате d:

 float normalLength = sqrt(temp[0] * temp[0]   temp[1] * temp[1]   temp[2] * temp[2]);

plane.normal.x = temp[0] / normalLength;
plane.normal.y = temp[1] / normalLength;
plane.normal.z = temp[2] / normalLength;
plane.normal.w = 0.f;
plane.offset = temp[3] / normalLength;
 

Примечание 1: Обычно можно хранить смещение плоского уравнения в w-координате vec4 вместо отдельной переменной. Причина в том, что типичная операция, которую вы выполняете с ним, — это проверка расстояния от точки до плоскости dist = n * x - d (для данной точки x, нормаль n, смещение d, * — это точечное произведение), которое затем может быть записано как dist = [n, d] * [x, -1] .

Примечание 2: Большинство программных, а также аппаратных растризаторов выполняют обрезку после шага проецирования, так как это дешевле и проще в реализации.

Комментарии:

1. Спасибо за советы и отзывы. Действительно ценю это. Допустил ли я ошибку, предположив, что шаг проекции-это шаг перед делением на W, который переводит единицу в NDC?

2. это правильно. отсечение обычно происходит после проекции и перед разделением перспективы. Вам в основном нужно сравнить x,y,z с w. Если в пространстве клипа -w <= x <= w для всех трех осей, то точка находится внутри видимой области.

3. Большое спасибо!.