Some computer graphics techniques require comparing pixel depths. Since the hardware already calculates and stores pixel depths, it would be ideal if we read the depths from the hardware depth buffer instead of calculating it ourselves and using extra storage.

When using perspective projection, the pixel depths stored in the hardware depth buffer are non-linear. That means that the distance between 0.1 and 0.2 is not the same that between 0.8 and 0.9.

This post is about explaining how to convert a non-linear pixel depth (as a result of a symmetrical perspective projection) to the view space z, which is linear. The formula is easy. The explanation is more complicated. If you just want the formula, look at the GLSL code at the end of this post.

The strategy of the explanation is first to explain the process that converts a view space position to a pixel (non-linear) depth and then derive the inverse process.

View space position to pixel depth

Pixel depth to view space z

The view space position is obtained by multiplying the inverse projection matrix by the clip space position. The equation is:

We are only interested in finding view space z. From the third row we get:

Remember that we don’t know the clip space w. From the fourth row we get:

Finally, we isolate the view space z:

Here GLSL code:

uniform mat4 inv_projection_matrix;
uniform sampler2D depth_texture;

float view_space_z_from_depth(float depth)
{
	float ndz = depth*2 - 1; // To normalized device z (From [0, 1] to [-1, 1])
	return -1.0f/(inv_projection_matrix[2][3]*ndz + inv_projection_matrix[3][3]);
}

in vec2 tex_coord;

void main()
{
	float depth = texture(depth_texture, tex_coord);
	float z_vs = view_space_z_from_depth(depth);
	...
}