OpenGL / Direct3D projection matrices

Hi there

Just trying to wrap my head around the differences in the transformations in OpenGL and D3D.

As far as I know OpenGL usually transforms z into the range [-1;1] and then later on maps that to a depth buffer value of [0;1].

Direct3D however, always works in the [0;1] range for z values.

Is this so far correct?

Now what if I use a projection matrix in OpenGL, that also transforms z to [0;1] range? I always thought this notion of -1 being at the near plane and +1 being at the far plane was a built in convention, that you cannot get around. However the code that I work with, always sets the projection matrices up this way, and apparently that works too.

Is there any other setting, that could affect this? I would have expected, that objects that are in front of the near plane (outside the view-frustum, the ones that fall into the [-1;0] range) would also get rasterized as “inside” the view frustum on OpenGL based systems, but this is not the case.

Anything that I might have missed?

Thanks,
Jan.

Check Mark Kilgard’s presentation “OpenGL 3.2 and More”:

http://www.slideshare.net/Mark_Kilgard/opengl-32-and-more

see slides 13 to 31 “Direct3Disms”

notably:
slide 17 “OpenGL & Direct3D Conventions”
slide 26 “Direct3D vs. OpenGL Coordinate System Conventions”

That is a very interesting presentation, thank you!

However, it does not address my problem at hand. We ARE using a projection matrix that transforms everything to [0;1] range instead of [-1;1] range (manually set up, not through glFrustum or anything) and yet it works. That surprises me. I thought we might address this in the shaders, but so far did not find anything where this might get “fixed”.

So my question is: Is there a way to tell OpenGL that you want to use a clip-space of [0;1] ? If so, what state might influence that? I’m currently out of ideas, how this engine might achieve what it does. As far as I can tell, it shouldn’t work (but it does).

Thanks,
Jan.

Is there a way to tell OpenGL that you want to use a clip-space of [0;1] ?

No. Clip space is [-1, 1], always.

What you’re doing is using half of clip space. Indeed, I imagine that you’ll find that drawing objects in front of the near clip plane will work fine, whether depth clamping is enabled or disabled.

Why do you need to have clip space be [0, 1]? One matrix is just as good as another; that’s a problem a #define can solve easily. The only reason I can think of to really are about the nature of clip space is if you’re working directly with clip space in some way?

I am currently in the process of cleaning up our engine’s math code and I stumbled across this odd behavior. I want to make it right, but I am confused as to why it actually currently works. The graphical output is the same in OpenGL and Direct3D, which is not what I would expect from looking how the matrices are set up. I cannot really change anything, before I have understood what is currently going on, because then I will most likely break several things. Most likely there is some other state setup somewhere deep inside the engine that magically corrects this, but I cannot think of any thing.

Jan.

Isn’t the only difference that you are throwing away about half your depth buffer resolution?
Or is additionally the meaning of what is near and far in your [0,1] clip space reversed? In that case maybe look for glDepthFunc() changing the direction the depth buffer works?

Isn’t the only difference that you are throwing away about half your depth buffer resolution?

He’s throwing away one bit, depending on how his perspective matrix works out.

That was my thinking too. However, isn’t the projection matrix actually what ‘defines’ the near and far plane? I mean, OpenGL should clip everything away that’s not in the unit-cube. So I would have to set up the projection matrix such that it transforms everything on the near plane to z == -1. Our current matrix transforms points on the near plane to z == 0. However there will still be other points that will be transformed into the [-1;0] range.

As far as I have calculated this through, it seems that about 1/3 of the space in between the camera position and the near plane should end up in the [-1;0] range. That would explain why it “works”, but it is most certainly not intended.

Jan.

However, isn’t the projection matrix actually what ‘defines’ the near and far plane?

The clipping of the near and far planes are defined by clip space. Hence the name. Since OpenGL defines clip-space as ±W in all three axes, your near/far values only fill part of clip space.

I thought with DX11 they moved DX matrix math to the same as OpenGL? or did I miss read that somewhere?

Now what if I use a projection matrix in OpenGL, that also transforms z to [0;1] range? I always thought this notion of -1 being at the near plane and +1 being at the far plane was a built in convention, that you cannot get around. However the code that I work with, always sets the projection matrices up this way, and apparently that works too.

It works but you are losing half the depth precision.

The DX matrix is also a valid projection matrix, so everything will look correct. The only difference is the location of the near plane. As DX assumes the near plane is at z=0 in clip space, but OpenGL clips at z=-1, the effective near plane will be closer to the camera.

The DX projection maps an eye z value to (z-zn)/z * (zf-zn)/zf, where zn and zf are the near and far plane z coords. Thus the value mapped to -1 is z = zn*(zf-zn)/(2zf-zn). If zf is much larger than zn, this value is close to 0.5*zn, so you can assume that the OpenGL near plane has only half the distance than you’d expect in DirectX.