Hello,
I would like to know in what space is defined the value gl_FragCoord.z. As far as I know gl_FragCoord.x and y are in screen coordinates, but the .z component seems to gives values always between more or less 0.9 and 1.0.
How can I compute the world or eye .z coordinate from gl_FragCoord.z?
and because z is non-linear to favour the nearer triangles, most of a scenes z will be in that very small range (0.9 to 1.0).
You can linearise it by encoding (z-near)/(far-near) in the z column of projection matrix, and pre-multiply z by w in the vertex shader : (z * w) / w = z
I suppose what you actually meant was Zclip = Wclip * (2*(Zview-near)/(far-near) - 1), as OpenGL clip space ranges from -Wclip to +Wclip, or -1 to 1 after perspective division. It’s only the viewport transformation that scales this to [n, f] as given by glDepthRange(n, f).
Your formula would very likely move the near clip plane behind the camera (if far > 2 * near) which can lead to weird artifacts.
But there is another catch: Z interpolation is always linear in screen space, i.e. the difference between Z in neighboring fragments is constant, unlike perspective correct varyings which are usually linear in view space but non-linear in screen space.
Because of this, if you use a perspective projection the per-vertex Z values in screen space must be non-linear. Otherwise the interpolated Z values are wrong, and you could have a small object appear in front of a large polygon even though it’s actually behind.
Sorry for the thread revive…
I have seen this code in a few places - but I cannot seem to figure out the math behind it. Is there an article or tutorial somewhere on this?
gl_FragCoord is window-relative (screen) coordinates. .xy is pixel units of the center of the fragment. .z is the depth value that’ll be written to the depth buffer (0…1). .w is 1/clip_space.w, where (for perspective projections only!!) clip_space.w is -eye_space.z, so: gl_FragCoord.w = -1/eye_space.z … for perspective projections only!
For orthographic projections, gl_FragCoord.w should just be 1 (i.e. 1/clip_space.w, i.e. 1/eye_space.w, where eye_space.w == 1).
For confirmation, see the last row of the perspective and orthographic projection transforms in Appendix F of the Red Book.
So for a perspective projection, you probably want -1/gl_FragCoord.w. Keep in mind this gives you eye-space Z, where values in front of the screen are negative. If you want positive, don’t negate.
Also, you said you wanted a distance from the point to the camera (eyepoint, presumably). This is not what eye_space Z is! eye_space Z is a minimum distance from the XY plane in eye-space to the point, not a radial distance from the eyepoint (0,0,0) to the point. This is the same concept as between EYE_PLANE fog coordinate mode and EYE_RADIAL fog coordinate modes, from the pre-shader days.
Your wording suggests the first (oZone3D). This states the expression is “The distance between the camera and the current pixel (the z axis value)”. Sorry, I’m not buying it.
As a beginner modern GL learner, I still have some questions.
eventually, I want this rendered depth map of an object to store in a OpenCV Mat data type (as a picture).
Is this means I must render this object to frame buffer then do some memory copy to another data type? I even don’t need it to display on the screen. How to achieve this fast and avoid to cost CPU time? it seems your redirect link showed something about glBlitzFrameBuffer(), but I don’t know what function should I use in this case.
That would be the easiest way to go. Render the scene, then use glReadPixels to fetch the color (or depth) buffer back to the CPU, then do what you want with it.
glBlitFramebuffer is a function that provides for transferring data from one framebuffer to another. You instead want to get your data back from the framebuffer (and/or a texture) to the CPU.
…this example seems involved in the texture calculations. I’m not sure whether to use texture there, can you just calculate the linear depth from the gl_FragCoord.z?
Even easier: for perspective projections specifically (see posts above), -1/gl_FragCoord.w will be eye-space Z. Just scale and shift that by near/far to get in your desired 0…1 value.
But to your point about texture, if you don’t care about what the color buffer for the scene is, and you just want the color buffer to be a linearized version of the depth buffer, then yes, you can just skip the intermediate texture (and multipass), and do all this in a single pass writing directly to the framebuffer using a simple fragment that basically just pipes your linearized 0…1 value to the color output (using the depth buffer behind-the-scenes for occlusion as normal). So for computing your color value in the frag shader, something like: