在Vulkan中翻转视口

12

从规范上来看:

VK_KHR_maintenance1

允许在 slink::VkViewport::height 字段中指定负高度,以执行从裁剪空间到帧缓冲空间变换的y轴反转。这使得应用程序可以避免在还针对其他API的着色器中使用 gl_Position.y = -gl_Position.y。

我的物理设备支持版本 1.0.42,并且我已经启用了 VK_KHR_maintenance1。 当我在 VkViewport 结构中使用负高度时,我不会收到任何验证错误/警告。

vkCmdSetViewport(vkContext.commandBuffer, 0, 1, &viewport);

然而,我在屏幕上也没有看到任何东西,它是黑色的。如果我去掉viewport中的负值,一切都会如预期渲染。我需要做些什么来使用VK_KHR_maintenance1扩展翻转viewport吗?我只是在屏幕上渲染一个红色四边形,使用全屏viewport。

VkViewport viewport = {};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = 1024.0f;
viewport.height = -1024.0f;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
vkCmdSetViewport(vkContext.commandBuffer, 0, 1, &viewport);

顶点着色器:

void main() {
        vec4 positions[3] = {
        vec4(-1.0, -1.0, 0.0, 1.0),
        vec4(3.0, -1.0, 0.0, 1.0),
        vec4(-1.0, 3.0, 0.0, 1.0)
    };
    gl_Position = positions[gl_VertexIndex % 3];
}

片段着色器:

void main() {
    outFragColor = vec4(1,0,0,1);
}

“然而,我在屏幕上也没有看到任何东西。”你正在渲染什么?你的VS是什么样子的?你的投影矩阵是什么样子的?viewport包含什么?等等。如果没有看到你的代码,我们无法知道发生了什么事情。 - Nicol Bolas
@NicolBolas 添加了顶点着色器和片段着色器。 - hidayat
1个回答

12

只需仔细算一下数学。您拥有所有数字,并且规范明确地阐述了所有内容。

帧缓冲空间顶点坐标的 Y 分量方程为:

Yf = (Py / 2) Yd + Oy
Oy = viewport.y + viewport.height / 2.0
Py = viewport.height

Yf为帧缓冲空间的Y分量,Yd为NDC空间的Y分量。

如果插入高度为1024的正数,你会得到什么数字?当Yd为-1时,Yf为0.0。当Yd为3时,Yf为2048。

在Vulkan中,“自然”视口变换通过将-1放在viewport.x / y处,将1放在viewport.width / height处来映射NDC空间。并且请记住:Vulkan的图像具有左上角原点,正值向下和向右。因此,Yf为0.0在顶部,Yf为2048在屏幕底部之外。因此,三角形在视口的可见区域内(部分)。

既然如此,在使用负高度时会发生什么?数学不会改变;只是结果不同。当Yd为-1时,就像以前一样,Yf为0.0。当Yd为3时,Yf为-2048。

那看起来像什么?嗯,视口的含义没有改变;只是数字不同。这意味着帧缓冲图像的可见区域仍在左上角,向下移动。因此,值为-2048就在视口上方

简而言之,负高度会翻转视口。但这会在视口原点翻转,而不是在屏幕中心翻转。视口原点在左上角向下和向右。因此,翻转将翻转先前可见的所有内容移出屏幕。您现在正在查看屏幕的不同区域,在您的情况下为空白。

如果要将所有内容围绕视口中心翻转,则需要调整viewport.y以补偿负高度。具体来说,它需要是正高度。


谢谢,非常好的解释,我以为 Vulkan 在输入负高度时会翻转视口并将原点移动到场景后面 :) - hidayat
3
尼古拉·波拉斯提到的公式可以在Vulkan 1.2.148,第23章中找到。 - mkl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接