Confused when using GLM for projection

Hi all

I’m currently learning Vulkan and implementing Model-View-Projection to map 3D points to the 2D screen using GLM, however I’m quite confused due to the way GLM works, and due to the conflicting information online.

For context I’ve never programmed in OpenGL before so I’m unfamiliar with it. I understand the mathematics of matrix multiplication and projection. I understand that Vulkan uses a different screen space coordinate system than OpenGL, and then GLM was designed for the latter.

I’m trying to implement a left-handed, y up, world coordinate system which I then need to project onto Vulkan’s 2D coordinate space, WITHOUT flipping the viewport. At the moment I’m drawing a unit cube with local coordinates (0,0,0) (1,0,0) … (1,1,1) etc. This way I can easily see where positive world space maps to in NDC space, and thus screen space.

This is the code I’m using for projection:

#define GLM_FORCE_LEFT_HANDED
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/glm.hpp
#include <glm/gtc/matrix_transform.hpp>

...

model = glm::mat4(1.0); 

view = glm::lookAt(
    glm::vec3(0.0f, 0.0f, -5.0f),
    glm::vec3(0.0f, 0.0f, 0.0f),
    glm::vec3(0.0f, 1.0f, 0.0f));

proj = glm::perspective(
    glm::radians(45.0f),
    width / height,
    0.1f,
    100.0f);

This maps the cube to the bottom right corner of the screen, facing away, so right-handed with +y down, +x right, and +z is into the screen. This is expected for Vulkan since that is NDC space, but what’s confusing me is why I need to specify GLM_FORCE_LEFT_HANDED, when the finished transformation is right handed. Removing it projects the cube using left-handed space.


  1. Vulkan uses a right-handed NDC coordinate space. So why does GLM in setup.hpp specify Vulkan uses left-handedness? Is GLM referring to something else here? Neither OpenGL or Vulkan enforce a word coordinate system. But GLM does, correct? so I’m assuming that GLM_FORCE_LEFT_HANDED is to swap explicitly for whatever world space you want.

  2. Does GLM implicitly swap left and right handedness when projecting to NDC space? This might be a stupid question but I’ve consistently read online that OpenGL is right handed which isn’t true in NDC space, so what is being referred to here? It would also explain why my cube was projected the opposite of what I expected.

  3. What is the significance of the camera facing the -Z axis? Many examples online assume this is the case without any explanation. I understand what that means mathematically but not why it’s necessary in the implementation, or if it’s needed at all. My example doesn’t do that, because I wanted +z to be into the screen, but I’d still like an explanation.

When it comes to NDC space (and therefore clip-space) and window space, their handedness is a bit ephemeral. The reason being that you can effectively flip the handedness of the spaces by just flipping glDepthRange, which maps “farther” values in NDC space to smaller numbers in window space. So when you say that OpenGL NDC space is left-handed, the validity of this statement depends on using the default glDepthRange (or one which makes these spaces left-handed).

Handedness is a property of a coordinate system. And since matrices represent transformations from one coordinate system to another, handedness is part of that. What it means to “look at a point from a point with an up direction” has an assumption as to where “left” is. The same goes for many other matrices, including the projection matrix.

OpenGL 1.0 had transformations built into its fixed-function pipeline. So you had functions like glRotate, glFrustum, as well as utility functions like gluLookAt and gluPerspective built on top of these. Since these are building matrices, they must be built relative to a handedness convention of the spaces they’re dealing with. OpenGL’s T&L system effectively defined two pre-clip-space spaces: model space (the space of the vertices that are about to be multiplied by the modelview matrix) and camera space (the space after being multiplied by the modelview matrix, but before the projection matrix. Lighting takes place here).

While OpenGL NDC space was “left-handed,” all of the OpenGL matrix functions created right-handed matrices. That is, model space and camera space were created as right-handed. This also means that the OpenGL functions that produce camera-to-clip-space transformations (glFrustum, glOrtho and the like) must perform a handedness swap to match OpenGL’s “left-handed” clip-space.

Of course, OpenGL ditched fixed function stuff years ago. However, GLM’s matrix functionality was built as a semi-drop-in replacement for that. As such, it adopted OpenGL’s fixed-function conventions: the matrices generated are right-handed, and projection matrix functions will perform a handedness swap to match with OpenGL clip-space.

And then along came Vulkan. Its default clip-space and NDC space use a right-handed coordinate system (and uses a different NDC and window-space depth coordinate system). As such, GLMs matrices do not work with it by default. Hence the need for the #define configurations.

As for why the handedness works out, here’s the reason.

GLM’s projection matrices do a handedness-swap. This was added because OpenGL’s old matrices were right-handed but clip-space was by default left-handed. However, just because you change GLM’s conventions does not stop GLM’s projection functions from doing a handedness swap.

So those functions will swap handedness. It will just be from left-handed to right-handed. Which is what Vulkan needs (along with setting the [0, 1] clip-space depth).

1 Like

Thanks for your answer. It was very helpful in understanding some of the historical aspects.

So If I understand you correctly when using GLM OpenGL world space by default is right handed, and left handed for Vulkan, all because of that implicit flip when projecting?
Is there a way to turn that off? I’d rather GLM just do the math without needing to ‘correct’ for historical decisions.
Although it’s not important now since I’ve decided to use my own perspective matrix, that transforms from my chosen coordinate system directly to Vulkan clip space.

Also, am I correct in saying that glm::lookAt transforms into the -Z axis? I’m still trying to find a good resource on why the camera faces the -Z axis, or is this again just a historical choice? Many blog post and tutorials just state it without explaining.
Although again I can just create my own, which would solve the issue?

Thanks again for your detailed answer.

What does it matter? It’s just math; it doesn’t make the matrix more computationally expensive or anything. It’s just a convention.

Because they had to pick an axis and that seemed reasonable.

But if you really want a reason, recall that graphics is an application of mathematics. When you draw a graph in math, the +X axis goes to the right and +Y goes up. In a right-handed 3D coordinate system, that means +Z goes up out of the page towards you. So when looking at a graph, you are looking down the -Z axis.

There is no “issue” to solve. It’s all just math; there is no “right” or “wrong”. I mean, if you don’t like these conventions, you’re free to change use whatever you like. But GLM’s purpose was not to adhere to individual tastes.

When you use someone else’s matrix library to build your transformation matrices, you’re adopting their conventions.

1 Like