[Depth buffer] From linear -> non-linear depth values

Started by
6 comments, last by JohnnyCode 9 years, 9 months ago

I'm currently trying to figure with what equation the non-linear depth value of the fragment's gl_FragCoord.z value is calculated. I know how to transform the fragment's depth values back to their linear equivalent, but I couldn't find any good resource on the reverse operation.

Currently my shader uses the following function to transform the gl_FragCoord's z-component to its linear variant, but what equation is used for transforming from the linear to the non-linear value? So from view-space z value --> screen-space z value.

Shader code:


float LinearizeDepth(float depth)
{
  float near= 0.1; // camera z near
  float far = 100.0; // camera z far
  float z = depth * 2.0 - 1.0; // Back to NDC 
  return (2.0 * near) / (far + near - z * (far - near));	
}

void main()
{             
    float depth = LinearizeDepth(gl_FragCoord.z);
    // <-- Some equation that transforms it back to the original gl_FragCoord.z value?
    color = vec4(vec3(depth), 1.0f);
}

Any insight would be appreciated.

Advertisement

You should be able to just do the inverse operations in the exact reverse order to do that (+ becomes -, / becomes * and apply operations in reverse order)

o3o

Hey Waterlimon,

That makes sense though, although I never really polished any of my algebraic skills (should probably start doing that soon).

I've switched the operators, and applied the operations in reverse order. I then return the (supposed to be) original value of the depth buffer, but the visual output is an all white terrain where the depth value does not seem to get any less than 1.0 (used to do that) so it's probably not correct:


float near= 0.1; // camera z near
float far = 100.0; // camera z far
float z = depth * 2.0 - 1.0; // Back to NDC 
float dep = (2.0 * near) / (far + near - z * (far - near));	
float v = (far + near / (far - near + dep)) * (2.0 / near);
return (v + 1.0) / 2; // Back to [0,1] range.

Is there any error I made with the inversion of the function?

You can also just apply your original projection matrix and then divide the result by w

Retrieving the result via the original projection matrix is an option yes, but for educational purposes I was interested in the equation.

I managed to retrieve the inverse equation for the linear -> non-linear case:


z_non_linear = -((near + far) * z_linear - (2 * near)) / ((near - far) * z_linear)

Testing the equation on a single test value should eventually return the same value which it does:


z = 0.9, near = 0.1, far = 100
z_linear = 0.2 / (100.1 - 0.9 * 99.9) = 0.01962708537782143   // from non-linear -> linear
z_non_linear = -(100.1 * 0.01962708537782143 - 0.2) / (-99.9 * 0.01962708537782143) = 0.9   // from linear -> non-linear
So the equation works out :) Although I'm not sure if there are any more simplifications left to make the function a bit more aesthetically pleasing?
Thanks for the help


Retrieving the result via the original projection matrix is an option yes, but for educational purposes I was interested in the equation.
This is obtained by using the original projection matrix, and just taking the z component. As long as you know how your projection matrix is constructed, then you would have the symbolic equivalent of your equation quickly and easily!

Yeah, I'll be sure to invest some time into properly understanding the projection matrix's inner workings. That'll save me questioning this kind of stuff smile.png

Try to multiply view matrix with projection matrix by hand and see the 4th row of the result- the w component construting row. You will notice it just contains the third view matrix row in the very resulted matrix 4th row:

view matrix projection matrix

[a,b,c,p1 ]

[a,b,c,p2 ]

[vx,vy,vz,vp] BLA

[0,0,0,1 ] [0,0,1,0]

if we consider column matricies and multiplication order , then we multiply view column with projection row.

first component in 4th row of result:

[a,a,vx,0]*[0,0,1,0]=vx

second component in 4th row of result:

[b,b,vy,0]*[0,0,1,0]=vy

third component in 4th row of result:

[c,c,vz,0]*[0,0,1,0]=vz

fourth component in 4th row of result:

[p1,p2,vp,1]*[0,0,1,0]=vp

thus, bottom 4th row of view*projection matrix is actualy the third row of the view matrix.

If a vector gets tranformed by this matrix, it actualy remembers its previous pre-projection transformations z component in its final w component. That is the linear z- the w komponent.

And the final vector that gets rasterized (depth tested and so on )is (x/w,y/w,z/w,w) - this should be glFragCoord value, but of course interpolated across triangle after vertex function.

This topic is closed to new replies.

Advertisement