On origins and projection matrices

(Rudi Floren) #1

Hello everyone,

to discuss a maybe issue in a more formal manner I will share my findings here to have a better overview. Please correct me if I am wrong on parts. I will keep this post updated when new ideas pop up.

Different backends different origins:

  • Viewport origins
    • OpenGL, Metal and DirectX: (-1;-1) is in the bottom-left corner
    • Vulkans: (-1;-1) is in the top-left corner
  • Texture origins
    • OpenGL: Origin in the bottom-left corner
    • DirectX, Metal and Vulkan: Origin in the top-left corner

What did we do:
Our bandaid atm is to multiply the (1,1) cell of the projection matrix by -1 to invert y in the viewport.

But this is an implementation detail that can cause confusion. During camera creation, a dev should be able to pop in a standard projection matrix e.g. from nalgebra-glm or provided by VR libraries.
If we expose the projection matrix to provide methods like un/project_point in the future as nalgebra does, a flipped projections matrix can return not expected results as our world is generally Y-up not Y-down. If we provide these functions it certainly will be no problems as we can compensate for the inverted y-axis but if one uses the raw matrix and writes his own methods he needs to know that y is inverted in the matrix.

Different viewport origins among backends are currently no problem because gfx-hal instructs spirv-cross to flip y during shader compilation. But this is discouraged by spirv-cross as it can get messy [3].

Other ways to flip the viewport:

Multiply by -1 during camera gather:
During camera gathering before submitting the projection matrix to the uniform buffer, we can multiply the (1,1) cell by -1 to invert y in the viewport.

Would result in one additional computation per frame in comparison to storing the flipped matrix in the camera, but enables us to allow standard projection matrices and allow standard methods to un/project points and vectors.

Negative Height Viewport:
Khronos understood, after developer feedback, that a viewport with y-down is not great (in terms of difference from industry standards I assume) and added support for setting the viewport with a negative height to y-flip the image. This was first introduced for Vulkan 1.0 with VK_KHR_maintenance1 and has now been included into core for Vulkan 1.1

So depending on which Vulkan version we enforce as a minimum we need to check if VK_KHR_maintenance1 is available and then set a negative viewport height and move the viewport origin (per [1]).

For that, we should look up adoption of VK_KHR_maintenance1 or Vulkan 1.1 among drivers.

It seems at least Metal should not have a problem with negative height viewports [2] but I could not verify that.

gfx-hal currently does not support enabling specific device extensions and relies on Vulkan 1.0.
Thus this is not a solution for amethyst 0.11

position.y = -position.y in shaders:
We could also just invert y in our vertex shaders or in a post-processing pass but as [3] said this can get messy especially.

Backwards Compatibility
Nevertheless which solution we choose we need to make sure that we provide an Into implementation for nalgebra::Perspective3 and nalgebra::Orthographic3 to change the depth range to [0;1] (Zero-to-One)


[1] https://www.saschawillems.de/blog/2019/03/29/flipping-the-vulkan-viewport/
[2] gfx issue #2676
[3] https://github.com/KhronosGroup/SPIRV-Cross#clip-space-conventions

(Thomas Schaller) #2

Thanks @valkum for the extensive write-up!

Do I understand you correctly that the current behaviour is basically correct, but confusing in case the user interacts directly with the projection matrix?

That seems to be fine to me, at least I don’t think it’s a blocker for 0.11.

(Rudi Floren) #3

Thats correct. For now we did just apply the fix in our projection matrix and added an extensive amount of tests to the camera. We need to make sure that this is covered in the book. We allow the use of custom matrices for external generation (VR, AR, other experiments) so this should be clear to users.

1 Like