微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

顶点位置的 Vulkan 默认坐标系统

如何解决顶点位置的 Vulkan 默认坐标系统

我正在通过玩具渲染器研究 Vulkan 坐标系。 我对顶点位置的坐标感到困惑。 在线 Vulkan 信息,例如: https://matthewwellings.com/blog/the-new-vulkan-coordinate-system/

...提到+X 是对的,+Y 是向下的,而+Z 又回来了。

在我的渲染器中,+Z 指向前方,我不知道为什么。 我有一个这样定义的三角形:

    // ccw is facing forward
    std::vector<PosColorVertex> vertexBuffer = {
        {{ 0.0f,-1.0f,0.0f},{1.0f,0.0f,0.0f}},{{-1.0f,1.0f,{0.0f,{{ 1.0f,-5.0f},1.0f}},};

那个 -5(Z) 将顶点移回屏幕。这样做应该是 +5。

坐标系好像是这样的:

enter image description here

如果我把相机放在原点,它看起来像这样:

enter image description here

一个镜头,相机远离三角形(视图由 Z 上的 -4 平移)。

enter image description here

一些相关代码。模型和视图矩阵都是恒等的。

VS:

 outColor = inColor;
 gl_Position = ubo.projectionMatrix * ubo.viewMatrix * ubo.modelMatrix * vec4(inPos.xyz,1.0);

FS:

outFragColor = vec4(inColor,1.0);

投影为:

glm::perspective(glm::radians(60.0f),w/h,0.1,256.0);

解决方法

剪辑和标准化设备坐标

剪辑坐标是我们从顶点着色器获得的坐标。归一化设备坐标 (NDC) 相同,但除以 w。有两种常用的用户选项(惯用左手和惯用右手):

NDH

“向上”的含义实际上取决于您。但是,如果您希望它与几乎所有的演示引擎兼容,那么在视口转换之后,您希望“up”表示 -y(因此在 NDH 中,您的“up”应该是 -y 在原版视口变换的情况下,或者它应该是 +y 以防视口变换稍后翻转它)。

在帧缓冲区\图像坐标中选择“向上”总是 -y 是因为几乎所有呈现引擎上的表面坐标都假定原点为左上:

surface coordinates

许多图像文件格式也假定相同。

世界坐标

世界坐标完全由您决定。通过顶点着色器,您可以将世界坐标转换为 Vulkan 可以处理的剪辑坐标。

您是通过您的 glm::perspective 完成的。

让我们先看看我们有什么:

std::vector<PosColorVertex> vertexBuffer = {
    {{ 0.0f,-1.0f,0.0f},{1.0f,0.0f,0.0f}},{{-1.0f,1.0f,{0.0f,{{ 1.0f,-5.0f},1.0f}},};

现在,这又是高度取决于解释这实际上意味着什么。我们需要另一个“向上”的参考方向。

为了保持理智,我们可能希望“向上”朝着 y 增加的方向发展。所以这意味着我们在底部有一些红角。我们在左上角有一个绿色的角落。我们在右上角有一个蓝色的角落。或者我认为这是作者的意图。

另外为了保持理智,我们更喜欢右手坐标系。所以,如果我们选择 +y 表示“向上”而 +x 表示“正确”,那么 -z 必须是“前面” (并且 +z 必须“返回”):

World coordinates

(这是匹配的,例如 Blender 如何拥有世界坐标。)不过现在我们让自己陷入了困境。我们的 z 是负数,而不是 NDH 要求的正数。与“向上”相比,我们的 y 指向与 NDH 不同的方向。无论我们做什么转换,我们都需要使这些匹配。

glm::perspective() 做什么

glm::perspective() 主要确实让它看起来很好,透视。但为此,它需要做几个假设。

最简单的是深度。在 NDC 的 Vulkan 中,它是零比一。方便的是GLM_FORCE_DEPTH_ZERO_TO_ONE。这指示 perspective() 它应该将您的 nearfar 分别映射到 01。 (默认值是 -1 比 1,除非手动更正,否则在 Vulkan 中不起作用。)xy 仍然总是 -1 比 1。

第二个选择是惯用手。右手是默认的。左撇子需要GLM_FORCE_LEFT_HANDED。或者它可以显式用于单个函数,例如perspectiveRH()。这有点误导。这实际上意味着“右手”意味着“前面”是-z。 “左手”意味着投影假设“前面”是 z 的正方向:

enter image description here

第三个选择是什么是“向上”。 glm::perspective() 实际上对此没有任何作用,并且 y 的极性在整个变换过程中保持不变。如果我们想让 +y 表示“向上”,我们需要手动执行此操作。我们可以使用视口翻转功能,也可以将其烘焙到视图投影矩阵中:proj[1][1] = -proj[1][1]

enter image description here

如何测试这些东西

测试这个实际上非常简单。此代码可用于:

#include <cmath>
#include <iostream>

#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
//#define GLM_FORCE_LEFT_HANDED
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

int main(){
#ifdef GLM_FORCE_LEFT_HANDED
    const float near = 1.0f;
    const float far = 2.0f;
#else //right-handed
    const float near = -1.0f;
    const float far = -2.0f;
#endif

    glm::vec3 right { 1.0f,near};
    glm::vec3 left  {-1.0f,near};
    glm::vec3 up    { 0.0f,near};
    glm::vec3 down  { 0.0f,near};
    glm::vec3 front { 0.0f,(far-near) + near };
    glm::vec3 back  { 0.0f,(near-far) + near };


    const auto xform = glm::perspective(glm::radians(60.0f),std::abs(near),std::abs(far));

    auto r = xform * glm::vec4(right,1.0f);
    auto l = xform * glm::vec4(left,1.0f);
    auto u = xform * glm::vec4(up,1.0f);
    auto d = xform * glm::vec4(down,1.0f);
    auto f = xform * glm::vec4(front,1.0f);
    auto b = xform * glm::vec4(back,1.0f);


    std::cout << "Right to clip: ("  << r.x << "," << r.y << "," << r.z << "," << r.w << ")\n";
    std::cout << "Left to clip: ("   << l.x << "," << l.y << "," << l.z << "," << l.w << ")\n";
    std::cout << "Up to clip: ("     << u.x << "," << u.y << "," << u.z << "," << u.w << ")\n";
    std::cout << "Down to clip: ("   << d.x << "," << d.y << "," << d.z << "," << d.w << ")\n";
    std::cout << "Front to clip: ("  << f.x << "," << f.y << "," << f.z << "," << f.w << ")\n";
    std::cout << "Back to clip: ("   << b.x << "," << b.y << "," << b.z << "," << b.w << ")\n";
}

对于双手习惯设置,我们得到:

Right to clip: (1.73205,1)
Left to clip: (-1.73205,1)
Up to clip: (0,1.73205,1)
Down to clip: (0,-1.73205,1)
Front to clip: (0,2,2)
Back to clip: (0,-2,0)

UPS,这一切都被剪掉了。但无论如何“正确”和“向上”都是正数。所以是的,我们可能希望以某种方式翻转 y 以与演示引擎坐标兼容。 “前”为(0,1)方向,“后”停止存在。

注意代码中的变化是世界坐标中使用的 z 的方向。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。