GLSL gl_FragCoord.z Calculation and Setting gl_Fra

2019-01-16 13:41发布

问题:

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.

回答1:

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;


回答2:

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.