Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Projection matrices: What should depth map to?

I'm running into contradictions while attempting to build a projection matrix for Vulkan, and have yet to find an explanation for how the projection matrix should map Z from input vector to the output. Mapping x and y is straightforward. My understanding is that OpenGL Projection matrices should map the near frustum plane to -1, and far to +1. Vulkan to 0 and +1 respectively. The mapping should be logarithmic, allowing greater precision in the near field.

The examples below use near (n) = 1, far (f) = 100. Here's a plot of z mapping using a matrix I constructed to the Vulkan spec. It produces errors in rendering, but produces the correct result as I understand it:

lambda z: (f / (f-n) * z - f*n/(f-n)) / z enter image description here

A plot of the most common OpenGL projection I've found online, which should map from -1 to +1:

lambda z: ((-f+n)/(f-n)*z - 2*f*n/(f-n))/-z enter image description here

And here's one generated from a lib I use, for OpenGL (cgmath in Rust): enter image description here

I'm unable to build a proper Vulkan projection matrix (Of which I've found none via Google) unless I understand what z should map to. I suspect this is due to an implicit correction done post projection matrix by the shader that actually maps to the ranges I listed, but if so, I don't know what range to feed into it via the proj mat.

like image 727
Turtles Are Cute Avatar asked Oct 19 '18 13:10

Turtles Are Cute


People also ask

What is the point of the projection matrix?

First projection matrices are used to transform vertices or 3D points, not vectors. Using a projection matrix to transform vector doesn't make any sense. These matrices are used to project vertices of 3D objects onto the screen in order to create images of these objects that follow the rules of perspective.

What is depth precision?

Depth precision is a pain in the ass that every graphics programmer has to struggle with sooner or later. Many articles and papers have been written on the topic, and a variety of different depth buffer formats and setups are found across different games, engines, and devices.

What is the matrix M for orthographic projection?

In computer graphics, one of the most common matrices used for orthographic projection can be defined by a 6-tuple, (left, right, bottom, top, near, far), which defines the clipping planes. These planes form a box with the minimum corner at (left, bottom, -near) and the maximum corner at (right, top, -far).


1 Answers

The mapping should be logarithmic, allowing greater precision in the near field.

Actually, if you don't do any tricks, the mapping will be hyperbolic, not logarithmic. The key point about the hyperbolic mapping is that you can actually interpolate it linear in screen space (which is a very nice property when you want to do some Z buffer optimizations like Hierarchical Z).

A plot of the most common OpenGL projection I've found online, which should map from -1 to +1:

lambda z: ((-f+n)/(f-n)*z - 2*f*n/(f-n))/-z 

Nope. You have a sign error in the first term, it should be

(-(f+n)/(f-n)*z - 2*f*n/(f-n))/-z 

Hence, your plot is just wrong. With the corrected formula, you would get a plot similiar to your cgmath rust library.

But the important bit is something else: you are plotting the wrong thing! Note the -z on denominator in that formula? Classic GL convention has always been to use a right handed eye space, but left handed window space. As a result, a classic GL projection matrix projects along -z direction. The parameters n and f are still given as distances along the viewing direction, though. This means, that the actual clip planes will be at z_eye = -n and z_eye=-f in eye space. What you plotted in your graph is the range behind the camera, and you will see the second branch of the hyperbole, the one which is usually clipped ayway, and which will map to outside of the [-1,1] interval.

If you plot the mapping for n=5 and f=100, you would get: plot of hyperbolic Z function

Note that OpenGL's project into -z direction is pure convention, it is not enforced by anything, so you can use +z projection matrix as well.

I'm unable to build a proper Vulkan projection matrix (Of which I've found none via Google) unless I understand what z should map to. Here's a plot of z mapping using a matrix I constructed to the Vulkan spec. It produces errors in rendering, but produces the correct result as I understand it:

lambda z: (f / (f-n) * z - f*n/(f-n)) / z 

Not sure what errors you're seeing with this, but the mapping is correct if you

  1. assume vulkan's default [0,1] z clip conventions, and
  2. want a projection direction along +z in eye space.

Btw. vulkans [0,1] clip convention also makes it possible to better use the precisions when using a floating point depth attachment: By reversing the mapping so that the near plane is mapped to 1, and the far plane mapped to 0, you can improve precision considerably. Have a look at this nvidia devblog article for more details.

like image 122
derhass Avatar answered Sep 27 '22 15:09

derhass