I try to obtain a so called world space coordinates for 2D rendering by creating a orthogonal projection matrix. But the result is disappointing I expect a blue triangle but there is only some blue pixels in the bottom right.
Here is my code in Nim language. It's a very simplified version who reproduces the issue.
The important part is in the function "projection2D", or maybe in the vertex shader. I don't think the problem is elsewhere but for safety I post the complete example
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()
Don't hesitate to share your thoughts !
You have to transpose the projection matrix, this means the 3rd parameter of glUniformMatrix4fv
has to be GL_TRUE
:
glUniformMatrix4fv(glGetUniformLocation(shadID, "projection"),
1, GL_TRUE, addr projectionMatrix[0])
Or you have to initialize the matrix according to the specification (see below):
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)))
See The OpenGL Shading Language 4.6, 5.4.2 Vector and Matrix Constructors, page 101:
To initialize a matrix by specifying vectors or scalars, the components are assigned to the matrix elements in column-major order.
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
Note, in compare to a mathematical matrix where the columns are written from top to bottom, which feels natural, at the initialization of an OpenGL matrix, the columns are written from the left to the right. This leads to the benefit, that the x, y, z components of an axis or of the translation are in direct succession in the memory.