Vulkan 右手坐标系变为左手坐标系

Vulkan right handed coordinate system become Left handed

问题:

应用投影矩阵后,Vulkan 右手坐标系变为左手坐标系。如何使其与Vulkan坐标系一致?

详情:

我知道Vulkan是右手坐标系

我在顶点着色器中有这一行:https://github.com/AndreaCatania/HelloVulkan/blob/master/shaders/shader.vert#L23

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

此时:https://github.com/AndreaCatania/HelloVulkan/blob/master/main.cpp#L62-L68我正在定义相机在场景中心的位置和盒子在 (4, 4, -10) World 的位置 space

结果是这样的:

正如您在上图中看到的那样,我得到 Z- 点在屏幕内,但它应该是正数。

这是预期的,我需要添加更多内容还是我做错了什么?

有用的部分代码:

投影计算:https://github.com/AndreaCatania/HelloVulkan/blob/master/VisualServer.cpp#L88-L98

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

相机 UBO 填充:https://github.com/AndreaCatania/HelloVulkan/blob/master/VisualServer.cpp#L403-L414

    SceneUniformBufferObject sceneUBO = {};
    sceneUBO.cameraView = camera.transform;
    sceneUBO.cameraProjection = camera.getProjection();

我不使用或不知道 Vulcan,但 透视投影矩阵(至少在 OpenGL 中)正在寻找 Z- 反转坐标系的一个轴的方向。这反转了坐标系的缠绕规则。

如果您想保留原始绕组而不是仅反转矩阵中的 Z 轴向量以获取更多信息,请参阅:

所以只需通过 -1 缩放 Z 轴,或者通过与 glScale(1.0,1.0,-1.0); 的某种类比,或者通过直接矩阵单元格访问。

所有 OpenGL 左坐标系与 Vulkan 右坐标系都发生在 NDC 的片段着色器期间 space,这意味着您的视图矩阵不关心。

如果您使用 glm,您在世界 space 或视图 space 中所做的一切都是通过右手坐标系完成的。

GLM,一个每个初学者都使用的非常流行的数学库,默认使用right-handed坐标系。

必须相应地设置您的视图矩阵,获得 x 从左到右、y 从下到上的右手系统的唯一方法是设置您的 z 方向向下看负值。如果您没有为您的 glm::lookat 调用提供右手系统,glm 将通过一系列 glm::cross 将其转换为您的一个轴翻转,请参阅 glm 源代码

正确的方法:

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);

我个人将坐标系转换的所有信息存储在投影矩阵中,因为默认情况下 glm 会为您分配 z 坐标

来自 songho: http://www.songho.ca/opengl/gl_projectionmatrix.html

注意眼睛坐标是在right-handed坐标系中定义的,但是NDC使用的是left-handed坐标系。也就是说,原点的相机在眼睛 space 中是沿 -Z 轴看,但在 NDC 中是沿 +Z 轴看。由于 glFrustum() 只接受近距离和远距离的正值,我们需要在 GL_PROJECTION 矩阵的构造过程中将它们取反。 因为我们看的是负z方向glm默认取负号。

事实证明,y 坐标在 vulkan 和 openGL 之间翻转,所以一切都会颠倒过来。解决问题的一种方法是同时否定 y 值:

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;

如果您按照上述步骤操作,您最终得到的结果一定与旧的 openGL 应用程序非常相似,并且您的相机的向上矢量与大多数 3D 模型具有相同的符号。