在 Vulkan 中翻转视口

Flipping the viewport in Vulkan

来自规范:

VK_KHR_maintenance1

Allow negative height to be specified in the slink::VkViewport::height field to perform y-inversion of the clip-space to framebuffer-space transform. This allows apps to avoid having to use gl_Position.y = -gl_Position.y in shaders also targeting other APIs.

我的物理设备支持版本 1.0.42,并且我已启用 VK_KHR_maintenance1。 当我在 VkViewport 结构中执行负高度时,我没有得到任何验证 error/warning。

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

但是我也没有在屏幕上看到任何东西,它是黑色的,如果我删除 viewport 中的负值,一切都会按预期呈现。我是否需要执行任何其他操作才能使用 VK_KHR_maintenance1 extension 翻转视口?我只是在屏幕上渲染一个带有全屏视口的红色四边形。

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

只需完成数学运算即可。您拥有所有数字,并且规范明确列出了所有数字。

帧缓冲区 Y 分量的方程式-space 顶点坐标为:

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

其中 Yf 是帧缓冲区-space Y 分量,Yd 是 NDC-space Y 分量。

如果你输入 1024 的正数 height,你会得到什么数字?对于 -1 的 Yd,您会得到 0.0 的 Yf。对于 3 的 Yd,你得到 2048 的 Yf

在 Vulkan 中,“自然”视口变换映射 NDC-space,方法是将 -1 置于 viewport.x/y,将 1 置于 viewport.width/height。请记住:Vulkan 的图像从左上角开始,正值向下和向右。所以0.0的Yf在最上面,2048的Yf在屏幕底部。因此,三角形(部分)在视口的可见区域。

鉴于此,当您使用负数 height 时会发生什么?数学没有改变;只是结果。对于 -1 的 Yd,您会得到 0.0 的 Yf,就像以前一样。对于 3 的 Yd,您得到 -2048 的 Yf

那看起来像什么?好吧,视口的含义没有改变;只是数字。这意味着帧缓冲区图像的可见区域仍然在左上角,向下。因此,-2048 的值 高于 视口。

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

如果您想翻转视口 center 周围的所有内容,则需要调整 viewport.y 以补偿负高度。具体需要为正高度。