23
votes

So, I've got an imposter (the real geometry is a cube, possibly clipped, and the imposter geometry is a Menger sponge) and I need to calculate its depth.

I can calculate the amount to offset in world space fairly easily. Unfortunately, I've spent hours failing to perturb the depth with it.

The only correct results I can get are when I go:

gl_FragDepth = gl_FragCoord.z

Basically, I need to know how gl_FragCoord.z is calculated so that I can:

  • Take the inverse transformation from gl_FragCoord.z to eye space
  • Add the depth perturbation
  • Transform this perturbed depth back into the same space as the original gl_FragCoord.z.

I apologize if this seems like a duplicate question; there's a number of other posts here that address similar things. However, after implementing all of them, none work correctly. Rather than trying to pick one to get help with, at this point, I'm asking for complete code that does it. It should just be a few lines.

2
Have you written a vertex shader as well? or only a fragment shader?Michael Slade
I won't give you code on general principle, but I can give you this link to the OpenGL Wiki. As well as this link to a tutorial of mine on impostors and depth that shows how to do this as well. Transforming the depth back is trivial.Nicol Bolas
Michael: yes, but it's just a pass through shader. As it happens, the calculations are done in world space, so I can calculate eye space in the fragment program. Nicol, I had already seen that page. I implement it as: vec4 clip_pos = gl_ProjectionMatrix * vec4(eye_pos,1.0); float ndc_depth = clip_pos.z / clip_pos.w; gl_FragDepth = (((clip_far-clip_near) * ndc_depth) + clip_near + clip_far) / 2.0; Unfortunately, the depth appears to fall outside the depth range, even though there's no offsetting. Thanks,imallett
@IanMallett: What are clip_near and clip_far? Because those sound suspiciously like the near and far clip distances used to compute the perspective projection matrix.Nicol Bolas
They are. Shouldn't they be? I had tried using gl_DepthRange and in the example, but the values, preliminarily, didn't seem to be correct on my card.imallett

2 Answers

36
votes

For future reference, the key code is:

float far=gl_DepthRange.far; float near=gl_DepthRange.near;

vec4 eye_space_pos = gl_ModelViewMatrix * /*something*/
vec4 clip_space_pos = gl_ProjectionMatrix * eye_space_pos;

float ndc_depth = clip_space_pos.z / clip_space_pos.w;

float depth = (((far-near) * ndc_depth) + near + far) / 2.0;
gl_FragDepth = depth;
7
votes

For another future reference, this is the same formula as given by imallett, which was working for me in an OpenGL 4.0 application:

vec4 v_clip_coord = modelview_projection * vec4(v_position, 1.0);
float f_ndc_depth = v_clip_coord.z / v_clip_coord.w;
gl_FragDepth = (1.0 - 0.0) * 0.5 * f_ndc_depth + (1.0 + 0.0) * 0.5;

Here, modelview_projection is 4x4 modelview-projection matrix and v_position is object-space position of the pixel being rendered (in my case calculated by a raymarcher).

The equation comes from the window coordinates section of this manual. Note that in my code, near is 0.0 and far is 1.0, which are the default values of gl_DepthRange. Note that gl_DepthRange is not the same thing as the near/far distance in the formula for perspective projection matrix! The only trick is using the 0.0 and 1.0 (or gl_DepthRange in case you actually need to change it), I've been struggling for an hour with the other depth range - but that is already "baked" in my (perspective) projection matrix.

Note that this way, the equation really contains just a single multiply by a constant ((far - near) / 2) and a single addition of another constant ((far + near) / 2). Compare that to multiply, add and divide (possibly converted to a multiply by an optimizing compiler) that is required in the code of imallett.