Search code examples
c++openglglm-math

Use top-left origin in OpenGL


I am trying to set my co-ordinate system such that the y-axis points down the screen.

// Determine view-projection matrix
glm::mat4 projection = glm::ortho(
    -3.0f,    // left
    3.0f,     // right
    3.0f,     // bottom
    -3.0f);   // top

// Right handed rule:
// x points right
// y points down
// z points into the screen
glm::mat4 view = glm::lookAt(
    glm::vec3(0, 0, -1),    // camera position
    glm::vec3(0, 0, 0),     // look at
    glm::vec3(0, -1, 0)     // up vector
);

glm::mat4 viewProjMatrix = projection * view;

However, when I try to render 2 objects:

A at (0, 0)
B at (1, 1)

A appears in the centre of the screen, and B appears in the top-right. I would expect it to appear in the bottom-right.

What am I missing here?


Solution

  • tl;dr

    My camera was positioned correctly (in a manner of speaking), but I was flipping the y-axis in the call to glOrtho.

    Let's visualize this

    Imagine a right-handed co-ordinate system such as OpenGL uses by convention:

    • The x-axis points right
    • The y-axis points up
    • The z-axis points out of the screen

    If my objects were positioned in this world and we viewed them from the front, we would see this:

        B
    A
    

    This is the same as what I was seeing, but we weren't viewing from the front. As @Scheff pointed out in the comments, my call to glm::lookat was flipping my camera upside-down and positioning it "behind" the screen.

    Now, if you imagine that you are doing a handstand behind your monitor, A and B would now look like this:

    ∀
        𐐒
    

    Wait, but isn't this what I wanted - B in the bottom-right?

    Right, but in addition to my camera being positioned strangely, my y-axis was being flipped by the call to glm::ortho. Normally the value for bottom should be less than the value for top (as in this example).

    Thus producing, once again:

        B
    A
    

    So, one solution would have been to swap the top and bottom parameters in glOrtho:

    glm::mat4 view = glm::lookAt(
        glm::vec3(0, 0, -1),    // camera position
        glm::vec3(0, 0, 0),     // look at
        glm::vec3(0, -1, 0)     // up vector
    );
    glm::mat4 projection = glm::ortho(
        -3.0f,      // left
        3.0f,       // right
        -3.0f,      // bottom (less than top -> y-axis is not inverted!)
        3.0f);      // top
    

    How to flip the y-axis "correctly"

    The solution above works, but there is a much simpler solution - or at least much simpler to visualize! And that is just to position the camera in front of the scene and flip the y-axis using glOrtho (by keeping bottom greater than top):

    glm::mat4 view = glm::lookAt(
        glm::vec3(0, 0, 1),     // camera position
        glm::vec3(0, 0, 0),     // look at
        glm::vec3(0, 1, 0)      // up vector
    );
    glm::mat4 projection = glm::ortho(
        -3.0f,      // left
        3.0f,       // right
        3.0f,       // bottom (greater than top -> y-axis is inverted!)
        -3.0f);     // top
    

    Potential problems

    Texture orientation

    Flipping the y-axis may result in upside-down textures, which can be fixed by swapping your top and bottom texture co-ordinates.

    Back-face culling

    Viewing the scene from a different angle may result in faces being culled, if face culling is enabled. This can be fixed by changing the winding order of your vertices, or by changing which faces get culled.