【问题标题】:Vulkan swapchain format UNORM vs SRGB?Vulkan 交换链格式 UNORM 与 SRGB?
【发布时间】:2021-09-17 14:17:47
【问题描述】:

在 Vulkan 程序中,片段着色器通常向每个红/蓝/绿通道输出 0.0 到 1.0 范围内的单精度浮点颜色,然后将这些颜色写入(混合到)交换链图像,然后呈现在屏幕上。浮点值根据交换链图像的格式(在创建交换链时指定)编码为位。

当我将交换链格式从 VK_FORMAT_B8G8R8A8_UNORM 更改为 VK_FORMAT_B8G8R8A8_SRGB 时,我观察到帧的整体亮度大大增加,并且还有一些轻微的颜色偏移。

我对 SRGB 格式的理解是,它很像 UNORM 格式,只是浮点值到 8 位整数的映射有所不同,因此它在某些区域具有更高的颜色分辨率而在其他区域具有更低的颜色分辨率,但是“预编码”RGB 浮点值的实际含义保持不变。

所以我对亮度增加有点惊讶。我对 SRGB 编码的理解是错误的吗?和/或与 UNORM 相比,这种亮度增加是预期的吗?

或者也许我有一个错误并且预计不会增加亮度?

更新:

我观察到,如果我使用 SRGB 交换链图像并且还以 VK_FORMAT_B8G8R8A8_SRGB 格式而不是 VK_FORMAT_B8G8R8A8_UNORM 加载我的图像/纹理,那么额外的亮度就会消失。它看起来就像我使用 VK_FORMAT_B8G8R8A8_UNORM 交换链图像并以 VK_FORMAT_B8G8R8A8_UNORM 格式加载我的图像/纹理一样。

另外,如果我将交换链图像放入 VK_FORMAT_B8G8R8A8_UNORM 格式,然后使用 VK_FORMAT_B8G8R8A8_SRGB 加载图像/纹理,帧看起来特别暗/几乎是黑色。

弄清正在发生的事情会有所帮助。

【问题讨论】:

    标签: vulkan


    【解决方案1】:

    这是色彩空间和显示问题。

    假定片段着色器在线性 RGB 颜色空间中写入值。因此,如果您要渲染到具有线性 RGB 颜色空间 (UNORM) 的图像,则您的 FS 生成的值将被直接解释。当您渲染到具有 sRGB 颜色空间的图像时,您正在将值从一个空间(线性)写入另一个空间 (sRGB)。因此,这些值会自动转换为 sRGB 颜色空间。这与将位置从模型空间转换到世界空间或其他没有什么不同。

    不同的是,您一直在错误地查看场景。看,你的交换链的VkSurfaceFormat::colorSpace 值是VK_COLOR_SPACE_SRGB_NONLINEAR_KHR 的可能性非常大。

    VkSurfaceFormat::colorspace 告诉您显示引擎将如何解释您呈现的交换链图像中的像素数据。这不是您提供的设置;这是显示引擎,它告诉您它将如何解释您发送给它的值。

    我说它是 sRGB 的“可能性非常好”,因为在扩展之外,这是 唯一可能的值。不管你喜欢与否,你都在渲染到一个 sRGB 显示设备。

    因此,如果您写入 UNORM 图像,显示设备将读取数据的实际位并解释它们就像它们在 sRGB 颜色空间中一样。仅当您的片段着色器编写的数据自身在 sRGB 颜色空间中时,此操作才有意义。

    但是,FS 通常不是这样生成数据的。您计算的光照计算仅对线性 RGB 颜色空间中的颜色值有意义。因此,除非您编写 FS 在计算最终颜色值后故意进行 sRGB 转换,否则您的所有结果都在线性 RGB 颜色空间中的可能性很大。这就是您一直在写入帧缓冲区的内容。

    然后显示引擎对其进行处理

    通过使用 sRGB 图像作为目标,您可以强制将颜色空间从线性 RGB 转换为 sRGB,然后显示引擎会将其解释为 sRGB 值。这意味着您的光照方程最终会产生正确的结果

    未能执行gamma-correct rendering properly (including the source texture images which are almost certainly also in the sRGB colorspace, as this is the default colorspace for most image editors. The exceptions would be for things like gloss-maps, normal maps, or other images that aren't storing "colors".) 会导致出现linear light attention appears more correct than quadratic attenuation, even though quadratic is how reality works 的情况。

    【讨论】:

    • 因此,如果我正确理解了您的答案和其他答案,我应该使用 SRGB 作为表面格式并在 UNORM 中加载图像/纹理?
    • @AndrewTomazos:大多数图像不在线性色彩空间中,除非您明确地以这种方式构建它。那些用于非图片之类的东西:法线贴图、光泽贴图等。
    • 我发现如果我将采样器图像加载到 VK_FORMAT_R8G8B8A8_SRGB 中,将 4 字节 RGBA 像素 libpng 放入主机缓冲区,然后将我的表面格式也放入 VK_FORMAT_B8G8R8A8_SRGB,图像我简单地将采样像素传递到 FS 输出时在屏幕上看到 - 与我在系统图像查看器中看到的非常相似。我想我们暂时将其视为暂时的胜利。 :)
    【解决方案2】:

    这是伽马校正。

    使用带有 VK_FORMAT_B8G8R8A8_SRGB 的交换链可以利用将伽马校正作为渲染管道的最后一步的能力。这会在幕后自动为您发生。

    这是您唯一希望进行 gamma 校正的地方。 确保您的着色器没有应用 gamma 校正。您可能会将其视为:

    color = pow(color, vec3(1.0/2.2));
    

    如果您的交换链进行了 gamma 校正,则无需在着色器中进行。

    大多数图像都是 SRGB(图片、颜色纹理等)。线性图像用于特定数据,例如蓝噪声纹理或高度图。

    • 使用 VK_FORMAT_R8G8B8A8_SRGB 加载 SRGB 图像
    • 使用 VK_FORMAT_R8G8B8A8_UNORM 加载 LINEAR 图像

    如果遵循上述规则,则不需要着色器转换。

    【讨论】:

    • 非 srgb 图像的示例是法线贴图、噪声贴图 - 这些应以 unorm 后缀加载。用户将看到的任何与颜色相关的地图都应保存为 srgb - 这些应以 srgb 后缀加载。
    • 如果我用 srgb 加载图像然后将其传递给片段着色器,vulkan 会自动将图像从 srgb 非线性空间转移到线性空间吗?或者我仍然需要明确地这样做,例如 pow(color, vec3(2.2)) 。
    • @wangsy 我修改了我的答案,使其更加具体和清晰。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-02-23
    • 1970-01-01
    • 2020-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多