Почему координата z переворачивается после умножения на матрицу в GLSL - OpenGL - PullRequest
2 голосов
/ 12 февраля 2020

Я создаю небольшой игровой движок, в котором я хочу рисовать вещи, используя OpenGL. Я абстрагировал все объекты OpenGL в классы (Buffers, VertexArrays, Shaders, Programs ...). Все работало нормально, пока я не добрался до 3D-рендеринга. Я реализовал свои собственные матрицы и векторы (я не использовал как glm), и когда я умножаю свою позицию вершины в шейдере на любую матрицу, координата z переворачивается (z = -z). Я даже пытался с матрицей идентичности. Вот вершинный шейдер:

#version 330 core

layout(location = 0) in vec4 i_pos;
layout(location = 1) in vec4 i_color;

out vec4 p_color;

uniform mat4 u_MVP;
uniform vec4 u_pos;

void main()
{    
    gl_Position = u_MVP * (i_pos + u_pos);
    p_color = i_color;
}

Я использовал униформу u_Pos только для отладки. И здесь я установил форму:

void Frame() override
    {       
        deltaTime = timer.Reset();

        if (Input::GetKey(Key::W).value == KeyDown) pos.z += deltaTime;     
        if (Input::GetKey(Key::S).value == KeyDown) pos.z -= deltaTime;             

        //mat4f(1.0f) creates a identity matrix
        shaderSelection.SetUniform("u_MVP", mat4f(1.0f));       
        shaderSelection.SetUniform("u_pos", vec4f(pos));

        ren.DrawTriangles(vertexArray, indexBuffer, shaderSelection);
    }

Хотя я уверен, что нет ничего с матричной структурой, вот она:

template<typename T = float, int sizeX = 4, int sizeY = 4>
struct BLAZE_API mat
{
private:
    T v[sizeY][sizeX];
public:
    mat()
    {
        for (unsigned i = 0; i < sizeX * sizeY; i++)
            ((T*)v)[i] = 0;
    }
    mat(T* ptr, bool transpose = false)
    {
        if (transpose)
            for (unsigned i = 0; i < sizeX * sizeY; i++)
                ((T*)v)[i] = ptr[i];
        else
            for (unsigned i = 0; i < sizeX * sizeY; i++)
                ((T*)v)[i] = ptr[i % sizeY * sizeX + i / sizeY];        
    }
    mat(T n)
    {
        for (int x = 0; x < sizeX; x++)
            for (int y = 0; y < sizeY; y++)
                if (x == y)
                    operator[](x)[y] = n;
                else
                    operator[](x)[y] = 0;
    }   
    mat(const mat<T, sizeX, sizeY>& mat)
    {
        for (int x = 0; x < sizeX; x++)
            for (int y = 0; y < sizeY; y++)
                v[x][y] = mat[x][y];
    }

    inline T* operator[] (unsigned i) const { return (T*)(v[i]); }  
    inline void operator= (const mat<T, sizeX, sizeY>& mat)
    {
        for (int x = 0; x < sizeX; x++)
            for (int y = 0; y < sizeY; y++)
                v[x][y] = mat[x][y];
    }
};

И SetUniform делает это:

glUniformMatrix4fv( ... , 1, GL_FALSE, m[0]);

Я сделал матричную структуру такой, чтобы мне не приходилось использовать GL_TRUE для transpose параметра в glUniformMatrix4fv. Я уверен, что не моя матричная реализация инвертирует координату z. Это похоже на то, что камера смотрит в направлении -Z, но когда я перемещаю объект в направлении + X, он перемещается также на + X на экране (также относится к направлению Y), чего не должно быть, если камера обращена -Z. Это должно произойти, если я могу это изменить?

1 Ответ

3 голосов
/ 12 февраля 2020

Если вы не преобразуете координаты вершины (или не преобразуете ее с помощью Identity matrix ), то вы непосредственно устанавливаете координаты в нормализованном пространстве устройства. ND C - это уникальный куб, слева, снизу, около (-1, -1, -1) и справа, сверху, далеко от (1, 1, 1). Это означает, что ось X направлена ​​вправо, ось Y направлена ​​вверх, а ось Z указывает на вид.

Обычно система координат OpenGL представляет собой правую систему. В пространстве обзора ось X направлена ​​вправо, а ось Y направлена ​​вверх.
Поскольку ось Z является перекрестным произведением оси X и оси Y, она указывает вне области просмотра и кажется перевернутым.

Чтобы компенсировать разницу в направлении оси Z в пространстве обзора по сравнению с нормированным пространством устройства, ось Z должна быть инвертирована.
Типичная матрица проекции OpenGL (например, glm::ortho, glm::perspective или glm::frustum) превращает правостороннюю систему в левостороннюю систему и отражает ось Z.

Это означает, что если вы используете (типичную) матрицу проекции (и никаких других преобразований), то координаты вершин равны координатам пространства вида. Ось X направлена ​​вправо, ось Y направлена ​​вверх, а ось Z направлена ​​наружу.

Проще говоря, в нормированном пространстве устройства камера указывает на + Z. В поле зрения (до преобразования с помощью типичной проекционной матрицы) камера указывает на -Z.


Обратите внимание, если вы установите Frustum для просмотра , затем 0 < near и near < far. Оба условия должны быть выполнены. Геометрия должна быть между ближней и дальней плоскостями, иначе она обрезается. Обычно матрица вида используется для просмотра сцены с определенной точки зрения. Ближняя и дальняя плоскость области просмотра выбираются таким образом, чтобы геометрия находилась между ними.
Поскольку глубина не является линейной (см. Как визуализировать глубину линейно в современном OpenGL с фрагментом gl_FragCoord.z шейдер? ), ближняя плоскость должна располагаться как можно ближе к геометрии.

...