Sunday, October 9, 2016

GLSL-build-in (n-1)/ OpenGL specs/ Variable/Spec: gl_FragCoord.z / gl_FragCoord.w /Coordinate Transformations ..

[References]

1. "gl_FragCoord, from stackoverflow.com and OpenGL 4.3 core profile specification"

This was asked (by the same person) and answered elsewhere. I'm paraphrasing and embellishing the answer here:
As stated in section 15.2.2 of the OpenGL 4.3 core profile specification (PDF)
  • gl_FragCoord.w is 1 / clip.w (Always 1/clip.w in fragment shader.)
  • where clip.w is the W component of the clip-space position (ie: what you wrote to gl_Position).

gl_FragCoord.z is generated by the following process, assuming the usual transforms:

  • Camera-space to clip-space transform, via projection matrix multiplication in the vertex shader. 
  • clip.z = (projectionMatrix * cameraPosition).z
  • Transform to normalized device coordinates. 
  • ndc.z = clip.z / clip.w
  • Transform to window coordinates, using the
  • win.z = ((dfar-dnear)/2) * ndc.z + (dfar+dnear)/2.

Now, using the default depth range of near=0, far=1, we can define win.z in terms of clip-space: (clip.z/clip.w)/2 + 0.5. If we then divide this by gl_FragCoord.w, that is the equivalent of multiplying by clip.w, thus giving us:
  • (gl_FragCoord.z/gl_FragCoord.w)
  • = clip.z/2 + clip.w/2
  • = (clip.z + clip.w) / 2

Using the standard projection matrix,
  • clip.z represents a scale and offset from camera-space Z component. 
  • The scale and offset are defined by the camera's near/far depth values. 
  • clip.w is, again in the standard projection matrix, just the negation of the camera-space Z. 


Therefore, we can redefine our equation in those terms:
  • (gl_FragCoord.z / gl_FragCoord.w)
  • = (A * cam.z + B -cam.z)/2
  • = (C * cam.z + D)
Where
  • A and B represent the offset and scale based on near/far, 
  • and C = (A - 1)/2 
  • and D = B / 2.


Therefore, 
gl_FragCoord.z / gl_FragCoord.w 
  • is not the camera-space (or world-space) distance to the camera. 
  • Nor is it the camera-space planar distance to the camera. 
  • But it is a linear transform of the camera-space depth. 
  • You could use it as a way to compare two depth values together, if they came from the same projection matrix and so forth.

To actually compute the camera-space Z, you need to either pass the camera near/far from your matrix (OpenGL already gives you the range near/far) and compute those A and B values from them, or you need to use the inverse of the projection matrix. 
  • Alternatively, you could just use the projection matrix directly yourself, since fragment shaders can use the same uniforms available to vertex shaders. 
  • You can pick the A and B terms directly from that matrix. A = projectionMatrix[2][2], and B = projectionMatrix[3][2].

...

[References]


You can refer to chapter 2.13 "Coordinate Transformations" of OpenGL specs, and to the fact, that gl_FragCoord.w in fragment shader is always 1/clp_w.

The path of eye_z in pipeline is as follows:

1. from eye-space to clip-space: clp_z = (gl_ProjectionMatrix * eye_pos).z;
1. from clip-space to normalized device coordinates: ndc_z = clp_z/clp_w;
2. from ndc to window space: wnd_z = ndc_z * (dfar-dnear)/2 + (dfar+dnear)/2, where dfar and dnear are parameters from glDepthRange and mostly equal to 0 and 1, so for simplicity let's assume gl_FragCoord.z = wnd_z = (clp_z/clp_w)/2 + 0.5. Thus, when you divide this by gl_FragCoord.w (which is equal to multiply by clp_w) you will get clp_z/2 + clp_w/2;

Now let's have a look at what is clp_z and clp_w. If you look at projection matrix, and especially at it's 3rd row (which is responsible for calculation of clp_z), it's clearly seen that clp_z is the product of scale and shift operations upon eye_z: from [eye_near..eye_far] range to [-1..1] range, while clp_w is equal to -eye_z (4th row); That way, you can write your equation (clp_z/2 + clp_w/2) as: (A*eye_z + B)/2 - eye_z/2 = C*eye_z + D - which is not equal to eye_z in common case, but is linear combination of it with constant coefficients.

So you can use it as a measure to compare depths, or as a parameter in any formula that have linear dependency from eye_z and etc., but not as direct measure of eye_z. For that purpose you need inversion of projection matrix, or simply store eye_z in vertex shader and send it to fragment shader as a varying;

....



...

No comments:

Post a Comment