Почему эта проекция дает неправильный результат? - PullRequest
0 голосов
/ 09 декабря 2018

Я пытаюсь получить так называемые координаты мирового пространства для 2D-рендеринга, создавая матрицу ортогональной проекции.Но результат разочаровывает, я ожидаю синий треугольник, но в правом нижнем углу есть только несколько синих пикселей.

enter image description here

Вот мой код на языке Nim,Это очень упрощенная версия, которая воспроизводит проблему.

Важная часть находится в функции "projection2D" или, возможно, в вершинном шейдере.Я не думаю, что проблема в другом, но для безопасности я выложу полный пример

type 
    OGLfloat = float32
    OGLuint = uint32
    OGLint = int32

type Mat4x4* = array[16, OGLfloat] # 4 x 4 Matrix

# Here OpenGL constants should be here but not pasted to save space.
#....

const 
    POSITION_LENGTH = 3.OGLint
    COLOR_LENGTH = 4.OGLint

const
    WINDOW_W = 640
    WINDOW_H = 480

let
    colorDataOffset = POSITION_LENGTH * OGLint(sizeof(OGLfloat))

# OpenGL function import should be here.
#...

var 
    # Thanks to an orthogonal projection matrix I expect to use coordinates in pixels.

    vertices = @[OGLfloat(420), 0, 0, # Position   
                                0, 0, 1, 1, # Color
                          640, 480, 0,
                                0, 0, 1, 1,
                          0, 480, 0,
                                0, 0, 1, 1]

    indices = @[OGLuint(0), 1 , 2]

proc projection2D(left, right, bottom, top, far, near:float):Mat4x4 =
    result = [OGLfloat(1), 0, 0, 0, # Start from an identity matrix.
                        0, 1, 0, 0, 
                        0, 0, 1, 0, 
                        0, 0, 0, 1]

    # Orthographic projection, inspired from a Wikipedia article example.

    result[0] = OGLfloat(2.0 / (right - left))
    result[5] = OGLfloat(2.0 / (top - bottom))
    result[3] = OGLfloat(- ((right + left) / (right - left)))
    result[7] = OGLfloat(- ((top + bottom) / (top - bottom)))
    result[10] = OGLfloat(-2 / (far - near))
    result[11] = OGLfloat(-((far + near)/(far - near)))

# These parameters comes from "learnopengl.com". 
var projectionMatrix = projection2D(0.0, OGLfloat(WINDOW_W), OGLfloat(WINDOW_H), 0.0, - 1.0, 1.0)

var glfwErr = glfwInit()
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3)
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3)
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE)
var winHandle = glfwCreateWindow(WINDOW_W, WINDOW_H)
glfwMakeContextCurrent(winHandle)
var glewErr = glewInit()

var
    shadID:OGLuint
    vertSrc:cstring = """
        #version 330 core
        layout (location = 0) in vec3 aPos;
        layout (location = 1) in vec4 aColor;

        out vec4 vColor;

        uniform mat4 projection;

        void main()
        {
            gl_Position = projection * vec4(aPos, 1.0f);
            vColor = aColor;
        }
        """
    fragSrc:cstring = """
        #version 330 core
        out vec4 FragColor;    
        in vec4 vColor;

        void main()
        {
            FragColor = vColor;
        }

        """

proc send_src(vert:var cstring, frag:var cstring):OGLuint =
    var success:OGLint
    # vertex
    var vertexShader = glCreateShader(GL_VERTEX_SHADER)
    glShaderSource(vertexShader, 1, addr vert, nil)
    glCompileShader(vertexShader)
    # Check compilation errors.
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, addr success)
    if bool(success) == false:
        echo(" vertex shader compilation failed (send_src)")
    else:
        echo("vertexShader compiled (send_src)")

    # fragment
    var fragmentShader = glCreateShader(GL_FRAGMENT_SHADER)
    glShaderSource(fragmentShader, 1, addr frag, nil)
    glCompileShader(fragmentShader)
    # Check compilation errors.
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, addr success)
    if bool(success) == false:
        echo("fragment shader compilation failed (send_src)")
    else:
        echo("fragmentShader compiled (send_src)")

    # Shader program
    result = glCreateProgram()
    glAttachShader(result, vertexShader)
    glAttachShader(result, fragmentShader)
    glLinkProgram(result)
    # Check for linkage errors.
    glGetProgramiv(result, GL_LINK_STATUS, addr success)
    if success == 0:
        echo("program linking failed (send_src)") 
    else:
        echo("shader linked (send_src)")

    glDeleteShader(vertexShader)
    glDeleteShader(fragmentShader)

glViewport(0, 0, WINDOW_W, WINDOW_H)
shadID = send_src(vertSrc, fragSrc)

var VAO, VBO, EBO:OGLuint
glGenVertexArrays(1, addr VAO)
glGenBuffers(1, addr VBO)
glGenBuffers(1, addr EBO)
glBindVertexArray(VAO)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, vertices.len * sizeof(OGLfloat), 
             addr vertices[0], GL_STATIC_DRAW)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.len * sizeof(OGLuint), 
             addr indices[0], GL_STATIC_DRAW)
# Position layout
glVertexAttribPointer(0, POSITION_LENGTH, GL_FLOAT, GL_FALSE, (POSITION_LENGTH + COLOR_LENGTH) * OGLint(sizeof(OGLfloat)), 
                      nil)
glEnableVertexAttribArray(0)
# Color layout
glVertexAttribPointer(1, COLOR_LENGTH, GL_FLOAT, GL_FALSE, (POSITION_LENGTH + COLOR_LENGTH) * OGLint(sizeof(OGLfloat)), 
                      cast[pointer](colorDataOffset))
glEnableVertexAttribArray(1)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)
glUseProgram(shadID)

while bool(glfwWindowShouldClose(winHandle)) == false:
    glClearColor(0.2, 0.3, 0.3, 1.0)
    glClear(GL_COLOR_BUFFER_BIT)
    glBindVertexArray(VAO)
    glUniformMatrix4fv(glGetUniformLocation(shadID, "projection"), 1, GL_FALSE, addr projectionMatrix[0])
    glDrawElements(GL_TRIANGLES, OGLint(indices.len), GL_UNSIGNED_INT, nil)
    glfwSwapBuffers(winHandle)
    glfwPollEvents()

glDeleteVertexArrays(1, addr VAO)
glDeleteBuffers(1, addr VBO)
glDeleteBuffers(1, addr EBO)
glfwDestroyWindow(winHandle)
glfwTerminate()

Не стесняйтесь поделиться своими мыслями!

1 Ответ

0 голосов
/ 09 декабря 2018

Вы должны транспонировать матрицу проекции, это означает, что 3-й параметр glUniformMatrix4fv должен быть GL_TRUE:

glUniformMatrix4fv(glGetUniformLocation(shadID, "projection"), 
                   1, GL_TRUE, addr projectionMatrix[0])

или васдолжны инициализировать матрицу в соответствии со спецификацией (см. ниже):

 proc projection2D(left, right, bottom, top, far, near:float):Mat4x4 =
    result = [OGLfloat(1), 0, 0, 0, # Start from an identity matrix.
                        0, 1, 0, 0, 
                        0, 0, 1, 0, 
                        0, 0, 0, 1]

    # Orthographic projection, inspired from a Wikipedia article example.

    result[0] = OGLfloat(2.0 / (right - left))
    result[5] = OGLfloat(2.0 / (top - bottom))
    result[12] = OGLfloat(- ((right + left) / (right - left)))
    result[13] = OGLfloat(- ((top + bottom) / (top - bottom)))
    result[10] = OGLfloat(-2 / (far - near))
    result[14] = OGLfloat(-((far + near)/(far - near)))

См. Язык затенения OpenGL 4.6, 5.4.2 Векторные и матричные конструкторы, стр. 101 :

Чтобы инициализировать матрицу, указав векторы или скаляры, компоненты присваиваются элементам матрицы в главном порядке столбцов .

mat4(float, float, float, float,  // first column
     float, float, float, float,  // second column
     float, float, float, float,  // third column
     float, float, float, float); // fourth column

Обратите внимание, что по сравнению с математической матрицей, где столбцы пишутся сверху вниз, что выглядит естественным, при инициализации матрицы OpenGL столбцы пишутся слева направо.Это приводит к тому преимуществу, что компоненты x, y, z оси или перевода находятся в прямой последовательности в памяти.

См. Также Тип данных (GLSL) - Матричные конструкторы

...