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

## Recommended Posts

JoeyDewd    1216

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.

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.

##### Share on other sites
Waterlimon    4398

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)

##### Share on other sites
JoeyDewd    1216

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?

##### Share on other sites
MJP    19754

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

##### Share on other sites
JoeyDewd    1216

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

##### Share on other sites
Jason Z    6434

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!

##### Share on other sites
JoeyDewd    1216

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

##### Share on other sites
JohnnyCode    1046

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.