Search code examples
openglmatrixopengl-3glm-mathorthographic

shapes skewed when rotated, using openGL, glm math, orthographic projection


For practice I am setting up a 2d/orthographic rendering pipeline in openGL to be used for a simple game, but I am having issues related to the coordinate system.

In short, rotations distort 2d shapes, and I cannot seem to figure why. I am also not entirely sure that my coordinate system is sound.

First I looked for previous answers, but the following (the most relevant 2D opengl rotation causes sprite distortion) indicates that the problem was an incorrect ordering of transformations, but for now I am using just a view matrix and projection matrix, multiplied in the correct order in the vertex shader:

gl_Position = projection * view * model vec4(1.0); //(The model is just the identity matrix.)

To summarize my setup so far: - I am successfully uploading a quad that should stretch across the whole screen:

GLfloat vertices[] = {
   -wf,  hf,  0.0f, 0.0, 0.0, 1.0, 1.0,  // top left
   -wf, -hf,  0.0f, 0.0, 0.0, 1.0, 1.0,  // bottom left
    wf, -hf,  0.0f, 0.0, 0.0, 1.0, 1.0,  // bottom right
    wf,  hf,  0.0f, 0.0, 0.0, 1.0, 1.0,  // top right
};
GLuint indices[] = {
    0, 1, 2,  // first Triangle
    2, 3, 0,   // second Triangle
};

wf and hf are 1, and I am trying to use a -1 to 1 coordinate system so I don't need to scale by the resolution in shaders (though I am not sure that this is correct to do.)

My viewport and orthographic matrix:

glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
...
glm::mat4 mat_ident(1.0f);
glm::mat4 mat_projection = glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f);

... though this clearly does not factor in the screen width and height. I have seen others use width and height instead of 1s, but this seems to break the system or display nothing.

I rotate with a static method that modifies a struct containing a glm::quaternion (time / 1000) to get seconds:

    main_cam.rotate((GLfloat)curr_time / TIME_UNIT_TO_SECONDS, 0.0f, 0.0f, 1.0f);
// which does: glm::angleAxis(angle, glm::vec3(x, y, z) * orientation)

Lastly, I pass the matrix as a uniform:

glUniformMatrix4fv(MAT_LOC, 1, GL_FALSE, glm::value_ptr(mat_projection * FreeCamera_calc_view_matrix(&main_cam) * mat_ident));

...and multiply in the vertex shader

   gl_Position = u_matrix * vec4(a_position, 1.0);
   v_position = a_position.xyz;

The full-screen quad rotates on its center (0, 0 as I wanted), but its length and width distort, which means that I didn't set something correctly.

My best guess is that I haven't created the right ortho matrix, but admittedly I have had trouble finding anything else on stack overflow or elsewhere that might help debug. Most answers suggest that the matrix multiplication order is wrong, but that is not the case here.

A secondary question is--should I not set my coordinates to 1/-1 in the context of a 2d game? I did so in order to make writing shaders easier. I am also concerned about character/object movement once I add model matrices.

What might be causing the issue? If I need to multiply the arguments to gl::ortho by width and height, then how do I transform coordinates so v_position (my "in"/"varying" interpolated version of the position attribute) works in -1 to 1 as it should in a shader? What are the implications of choosing a particular coordinates system when it comes to ease of placing entities? The game will use sprites and textures, so I was considering a pixel coordinate system, but that quickly became very challenging to reason about on the shader side. I would much rather have THIS working.

Thank you for your help.

EDIT: Is it possible that my varying/interpolated v_position should be set to the calculated gl_Position value instead of the attribute position?


Solution

  • Try accounting for the aspect ratio of the window you are displaying on in the first two parameters of glm::ortho to reflect the aspect ratio of your display.

    GLfloat aspectRatio = SCREEN_WIDTH / SCREEN_HEIGHT;
    glm::mat4 mat_projection = glm::ortho(-aspectRatio, aspectRatio, -1.0f, 1.0f, -1.0f, 1.0f);