I had some trouble reconstructing position from depth sampled from the depth buffer. I use the equivalent of gluPerspective in GLM. The code in GLM is:

GLM_FUNC_QUALIFIER detail::tmat4x4 perspective
    valType const & fovy, 
    valType const & aspect, 
    valType const & zNear, 
    valType const & zFar
    valType range = tan(radians(fovy / valType(2))) * zNear;    
    valType left = -range * aspect;
    valType right = range * aspect;
    valType bottom = -range;
    valType top = range;

    detail::tmat4x4 Result(valType(0));
    Result[0][0] = (valType(2) * zNear) / (right - left);
    Result[1][2] = (valType(2) * zNear) / (top - bottom);
    Result[2][3] = - (zFar + zNear) / (zFar - zNear);
    Result[2][4] = - valType(1);
    Result[3][5] = - (valType(2) * zFar * zNear) / (zFar - zNear);
    return Result;

There doesn't seem to be any errors in the code. So I tried to invert the projection, the formula for the z and w coordinates after projection are:

enter image description here

and dividing z' with w' gives the post-projective depth (which lies in the depth buffer), so I need to solve for z, which finally gives:

enter image description here

Now, the problem is I don't get the correct position (I have compared the one reconstructed with a rendered position). I then tried using the respective formula I get by doing the same for this Matrix. The corresponding formula is:

enter image description here

For some reason, using the above formula gives me the correct position. I really don't understand why this is the case. Have I done something wrong? Could someone enlighten me please?


1 Answer 1


I believe the difference between the two stems from considering the post-projective (NDC) z range to be [-1, 1] (in your first formula) and [0, 1] (in your second). OpenGL uses the former while D3D uses the latter. It's strictly a matter of convention - in OpenGL, of course, the depth buffer range eventually ends up being [0, 1] as well, but the NDC space is throught of as [-1, 1] along all three axes and the conversion to [0, 1] is part of the viewport transformation. In D3D, the NDC space is thought of as [-1, 1] in XY and [0, 1] in Z, with the viewport transformation not doing anything to Z.

When you sample the depth buffer, you get back a result in [0, 1], so to unproject it back to world space you have to use a D3D-style matrix. This is equivalent to aborbing the Z part of OpenGL's viewport transformation into the projection matrix.

  • \$\begingroup\$ So in principle, if I understood correctly, after I apply the first perspective projection (of the ones I posted) to the vertices, the z goes to the [-1, 1] range and then OpenGL clamps it to [0, 1], right? That would make sense then. Wouldn't it be more sensible to use the second matrix then instead? \$\endgroup\$ Jun 20, 2012 at 17:59
  • \$\begingroup\$ Yes, it would be more sensible to use the second matrix, since the depth buffer value comes to you in [0, 1] format. (BTW: OpenGL doesn't clamp the value to [0, 1], it remaps it linearly from [-1, 1] to [0, 1].) \$\endgroup\$ Jun 21, 2012 at 19:33

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .