Getting true z value from depth buffer

Sampling from the depth buffer in the shader returns values ​​from 0 to 1, as expected. Given the planes of the near and far shots of the camera, how to calculate the true value of z at this point, that is, the distance from the camera?

+42
graphics shader opengl
Jul 11 '11 at 15:07
source share
2 answers

From http://web.archive.org/web/20130416194336/http://olivers.posterous.com/linear-depth-in-glsl-for-real

// == Post-process frag shader =========================================== uniform sampler2D depthBuffTex; uniform float zNear; uniform float zFar; varying vec2 vTexCoord; void main(void) { float z_b = texture2D(depthBuffTex, vTexCoord).x; float z_n = 2.0 * z_b - 1.0; float z_e = 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear)); } 

[edit] So, here is the explanation (with two errors, see Christian comment below):

The prospective OpenGL matrix is ​​as follows: from songho.ca

When you multiply this matrix by a uniform point [x, y, z, 1], it gives you: [anyway, it doesn't matter, Az + B, -z] (with A and B 2 large components in the matrix).

OpenGl next performs a split perspective: it divides this vector into its w-component. This operation is performed not in shaders (except for special cases, such as shadow copying), but in hardware; you cannot control it. w = -z, so the value of Z becomes equal to -A / z -B.

Now we are in the coordinates of the normalized devices. The Z value is between 0 and 1. For some stupid reason, OpenGL requires it to be moved to the range [-1,1] (just like x and y). Used scaling and offset.

This final value is then stored in the buffer.

The above code does the exact opposite:

  • z_b is the original value stored in the buffer
  • z_n linearly converts z_b from [-1,1] to [0,1]
  • z_e is the same formula as z_n = -A / z_e -B, but is solved for z_e instead. This is equivalent to z_e = -A / (z_n + B). A and B must be computed on the CPU and sent as a uniform, btw.

Opposite function:

 varying float depth; // Linear depth, in world units void main(void) { float A = gl_ProjectionMatrix[2].z; float B = gl_ProjectionMatrix[3].z; gl_FragDepth = 0.5*(-A*depth + B) / depth + 0.5; } 
+48
Jul 11 2018-11-11T00:
source share

I know this is an old, old question, but I have met here several times, so I decided to share my code that does the inverse and inverse transforms.

This is based on @ Calvin1602's answer. They work in GLSL or in plain old C code.

 uniform float zNear = 0.1; uniform float zFar = 500.0; // depthSample from depthTexture.r, for instance float linearDepth(float depthSample) { depthSample = 2.0 * depthSample - 1.0; float zLinear = 2.0 * zNear * zFar / (zFar + zNear - depthSample * (zFar - zNear)); return zLinear; } // result suitable for assigning to gl_FragDepth float depthSample(float linearDepth) { float nonLinearDepth = (zFar + zNear - 2.0 * zNear * zFar / linearDepth) / (zFar - zNear); nonLinearDepth = (nonLinearDepth + 1.0) / 2.0; return nonLinearDepth; } 
+9
Nov 01 '15 at 18:30
source share



All Articles