Questions about opengl coordinate system


I have been using opengl for a while and have been used to knowing that opengl uses a right hand coordinate system, I have recently started looking at vulkan too and found out there were some differences in the coordinate systems between the two rendering methods. Because of this I decided to do a deep dive in to what happens from the defining of a point to it being draw on the screen and while doing this I have seen some confusion in my previous understanding.

This brings me to my two questions

  1. when I have done my mvp transformation I have transformed my vertex in to clip space. From the information I can find the coordinate system of clip space (opengl) is y up positive, X right positive, z in to screen positive. This seems to be a left handed coordinate system not a right handed coordinate system.

Why is opengl defined as a right handed coordinate system when it looks to be left handed? The coordinate system of the underlying data is irrelevant to opengl because the first it really gets to see it is post projection calculations (which is not defined in opengl itself) and post projection is clip space.

  1. my understanding (right or wrong) is that clip space is where opengl does the culling (using the w coordinate). If my understanding is correct, why is there a further transformation needed in the z direction when converting to window coordinates? Opengl transforms (-1,1) to (0,1).

What operations are further being done in window space that haven’t already been done from a depth perspective and why couldn’t they be done in clip space?

Conventionally, object space and eye space are right-handed. Conventionally, NDC is left-handed, but the projection transformations generated by e.g. glOrtho, glFrustum or gluPerspective flip the Z axis.

Even whether NDC is left- or right-handed is subjective. E.g. if you use glDepthFunc(GL_GREATER), then fragments with greater depth replace those with lesser depth, so increasing depth is effectively toward the viewpoint.


It’s all subjective. Classifying a coordinate system as left-handed or right-handed requires imposing a physical interpretation on the axes. But OpenGL is just performing calculations; interpretation is up to the programmer.

In NDC (which is clip space after projective division), the clip volume is the signed unit cube, as the calculations are simpler that way. The depth buffer holds unsigned normalised values, so those need to be transformed (the actual transformation is controlled by glDepthRange). AFAIK, OpenGL doesn’t actually use [0,1] for X/Y coordinates anywhere; the NDC values [-1,1] are transformed directly to window coordinates [0,w]×[0,h] according to glViewport.

Your definition of the behavior of Z assumes a default glDepthRange. If you reverse the near and far values, then negative Z values are “in the screen” negative instead. And even then, you can reverse the effective meaning of depth by changing the glDepthFunc.

It’s all relative.

Who said that OpenGL is “defined as a right handed coordinate system” to begin with? Compatibility OpenGL effectively defined its model space and other spaces as right-handed, but clip-space has always been what it is.

The difference now is that in Core OpenGL, you have to be aware of it. You can’t hide it behind matrix calls and such; you have to know what’s going on.

What exactly do you mean by “culling”? Sure, if a primitive is not visible, it is “culled”, but that is only a consequence of the process of clipping (which is where clip space gets its name). A primitive which is partially within clip space is clipped to the clip space boundaries, so that all of its vertices are within clip space.

As a consequence of this, a primitive which is not visible at all is “clipped” to nothing: culled.

Because the depth buffer has historically been a normalized, unsigned integer, and therefore only stores values on the range [0, 1]. In fact, depth values are clamped to this range as well, just to make sure (even if you’re writing to a floating-point depth buffer).

Thanks for both of your answers, I was struggling (when doing the projection matrix maths) to understand where the handedness came in to many conversations I have read on the web and you have both answered it by effectively saying that the handedness is arbitrary and purely defined by how the system is configured with regards to depth. With a like for like depth configuration vulkan and direct x will have a different handedness due to the y NDC coordinates being different in terms of where the positive direction is.

Also thanks for clarifying what the final step is for when converting NDC to window coordinates with respect to depth