• Create Account

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

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

7 replies to this topic

### #1JoeyDewd  Members

1180
Like
0Likes
Like

Posted 20 July 2014 - 02:38 PM

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.

### #2Waterlimon  Members

4373
Like
3Likes
Like

Posted 20 July 2014 - 02:58 PM

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

### #3JoeyDewd  Members

1180
Like
0Likes
Like

Posted 20 July 2014 - 03:37 PM

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?

### #4MJP  Moderators

18204
Like
1Likes
Like

Posted 20 July 2014 - 04:58 PM

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

### #5JoeyDewd  Members

1180
Like
0Likes
Like

Posted 21 July 2014 - 04:29 AM

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

### #6Jason Z  Members

6416
Like
1Likes
Like

Posted 22 July 2014 - 04:52 AM

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!

Jason Zink :: DirectX MVP

Direct3D 11 engine on CodePlex: Hieroglyph 3

Games: Lunar Rift

### #7JoeyDewd  Members

1180
Like
0Likes
Like

Posted 22 July 2014 - 04:55 AM

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

### #8JohnnyCode  Members

1059
Like
0Likes
Like

Posted 22 July 2014 - 07:13 PM

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.

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.