3

Problem:

Vulkan right handed coordinate system became left handed coordinate system after applying projection matrix. How can I make it consistent with Vulkan coordinate system?

Details:

I know that Vulkan is right handed coordinate system where

  • X+ points toward right
  • Y+ points toward down
  • Z+ points toward inside the screen

I've this line in the vertex shader: https://github.com/AndreaCatania/HelloVulkan/blob/master/shaders/shader.vert#L23

gl_Position = scene.cameraProjection * scene.cameraView * meshUBO.model * vec4(vertexPosition, 1.0);

At this point: https://github.com/AndreaCatania/HelloVulkan/blob/master/main.cpp#L62-L68 I'm defining the position of camera at center of scene and the position of box at (4, 4, -10) World space

The result is this:

enter image description here

As you can see in the picture above I'm getting Z- that point inside the screen but it should be positive.

Is it expected and I need to add something more or I did something wrong?

Useful part of code:

Projection calculation: https://github.com/AndreaCatania/HelloVulkan/blob/master/VisualServer.cpp#L88-L98

void Camera::reloadProjection(){
    projection = glm::perspectiveRH_ZO(FOV, aspect, near, far);
    isProjectionDirty = false;
}

Camera UBO fill: https://github.com/AndreaCatania/HelloVulkan/blob/master/VisualServer.cpp#L403-L414

    SceneUniformBufferObject sceneUBO = {};
    sceneUBO.cameraView = camera.transform;
    sceneUBO.cameraProjection = camera.getProjection();
Andrea Catania
  • 1,361
  • 3
  • 21
  • 37
  • As soon as everything draws correctly, coordinate system does not really matter. Vulkan doesn't impose any coordinate system on the scene (there is no default projection), only on image/attachment/screen space. Projection matrix and world space is defined for Your convenience and if it suits You, it's ok. If You want to keep all coordinates persistent, then You need to change projection matrix. – Ekzuzy Mar 18 '18 at 13:25
  • It doesn't matter from the vulkan prospective but is mandatory IMO have a correct coordinate system where is possible define things in the scene and get expected behaviour – Andrea Catania Mar 19 '18 at 10:07
  • Yes, of course. But Vulkan doesn't make You use any specific coordinate system. So it is up to You to choose one that is convenient for You. Great explanation on how to create a projection matrix is presented in the "Introduction to 3D Game Programming with DirectX" book by Frank D. Luna. Based on this and on typical OpenGL's transformations I've prepared a [code](https://github.com/PacktPublishing/Vulkan-Cookbook/blob/master/Library/Source%20Files/10%20Helper%20Recipes/04%20Preparing%20a%20perspective%20projection%20matrix.cpp) that generates a projection matrix. – Ekzuzy Mar 19 '18 at 10:35
  • So if You want to create a matrix that is consistent with the rest of Vulkan's coordinate systems, I think it shouldn't be hard to change matrix generating code to suit Your needs. And did You use glm's `GLM_FORCE_DEPTH_ZERO_TO_ONE` definition as described [here](https://github.com/SaschaWillems/Vulkan/issues/45)? – Ekzuzy Mar 19 '18 at 10:52
  • Yes I did it. I fixed my issue here the commit: https://github.com/AndreaCatania/HelloVulkan/commit/d892988a1a9a4d40a03b2c667edcc837648164ea Thanks you for the comments! – Andrea Catania Mar 19 '18 at 14:18

2 Answers2

3

I do not use or know Vulcan but perspective projection matrix (at lest in OpenGL) is looking in the Z- direction which inverts one axis of your coordinate system. That inverts the winding rule of the coordinate system.

If you want to preserve original winding than just invert Z axis vector in the matrix for more info see:

So just scale the Z axis by -1 either by some analogy to glScale(1.0,1.0,-1.0); or by direct matrix cells access.

Spektre
  • 49,595
  • 11
  • 110
  • 380
  • Thanks you for response, now it's more clear. Do you mean that I should scale directly the projection matrix? because it doesn't works.. Also in order to change the coordinate system to set Y+ up X+ right and Z+ backwards (right handed) I have created a matrix that rotate it, but it doesn't works. Do I have to multiply it with projection or with the camera? Before multiply it I have to swap Coordinate system from Left Hand to Right Hand, Do I? – Andrea Catania Mar 19 '18 at 10:05
  • @AndreaCatania rotating will not do anything with this. Scaling the projection directly can change the depth buffer and NDC range behavior. As I do not use vulkan I do not now if you need to invert depth test and or flip the NDC Z range somehow so safer is to scale the view matrix (it is a part of modelview) The problem with that is that there the camera is in inverse matrix form so scale will probably not do it as you need to invert most likely row instead of column. Also after doing this your camera will be looking in the opposite way so turn it around. – Spektre Mar 19 '18 at 10:20
  • @AndreaCatania On second taught if you scalef `x` in projection and turn your camera around y by 180 deg it should work without the need to change NDC nor depth test. Yes you would still looking through `Z-` but the winding rule will be the same .... also if you want to go with the first option you still can use scaling even for rows if you transpose before and after ... but that is a lot of operations for simple inverting sign of 3 element in the matrix – Spektre Mar 19 '18 at 10:29
  • Thanks you for the clarification about that projection invert the Z axis. Also I went for the second solution where I was forced to change face orientation from Clockwise to counter clockwise. https://github.com/AndreaCatania/HelloVulkan/commit/d892988a1a9a4d40a03b2c667edcc837648164ea – Andrea Catania Mar 19 '18 at 14:18
  • @AndreaCatania that can be most likely flipped too in opengl by `glFrontFace(GL_CW);` or `GL_CCW` so in vulkan should be something similar too no need to redefine mesh ... – Spektre Mar 19 '18 at 14:52
  • Yes the same is for Vulkan, check this: https://github.com/AndreaCatania/HelloVulkan/commit/d892988a1a9a4d40a03b2c667edcc837648164ea#diff-c1ce24314e38f83cac3804abfb91411cR1221 – Andrea Catania Mar 19 '18 at 16:57
0

All the OpenGL left coordinate system vs Vulkan right coordinate system happens during the fragment shader in NDC space, it means your view matrix doesn't care.

If you are using glm, everything you do in world space or view space is done via a right handed coordinate system.

GLM, a very popular math library that every beginner uses, uses right-handed coordinate system by default.

Your view matrix must be set accordingly, the only way to get a right handed system with x from left to right and y from bottom to top is if to set your z looking direction looking down at the negative values. If you don't provide a right handed system to your glm::lookat call, glm will convert it with one of your axis getting flipped via a series of glm::cross see glm source code

the proper way:

glm::vec3 eye = glm::vec3(0, 0, 10);
glm::vec3 up = glm::vec3(0, 1, 0);
glm::vec3 center = glm::vec3(0, 0, 0);
// looking in the negative z direction
glm::mat4 viewMat = glm::lookAt(eye, up, center);

Personnaly I store all information for coordinate system conversion in the projection matrix because by default glm doest it for you for the z coordinate

from songho: http://www.songho.ca/opengl/gl_projectionmatrix.html

Note that the eye coordinates are defined in the right-handed coordinate system, but NDC uses the left-handed coordinate system. That is, the camera at the origin is looking along -Z axis in eye space, but it is looking along +Z axis in NDC. Since glFrustum() accepts only positive values of near and far distances, we need to NEGATE them during the construction of GL_PROJECTION matrix. Because we are looking at the negative z direction glm by default negate the sign.

It turns out that the y coordinate is flipped between vulkan and openGL so everything will get turned upside down. One way to resolve the problem is to negate the y values aswell:

glm::mat4 projection = glm::perspective(glm::radians(verticalFov), screenDimension.x / screenDimension.y, near, far);
// Vulkan NDC space points downward by default everything will get flipped
projection[1][1] \*= -1.0f;

If you follow the above step you must end up with something very similar to old openGL applications and with the up vector of your camera with the same sign than most 3D models.

Paltoquet
  • 1,184
  • 1
  • 10
  • 18