Home > noBug > 色彩空间错误导致图像亮度增加

色彩空间错误导致图像亮度增加
Color Space Vulkan Bug

正确处理sRGB色彩空间——记一次Vulkan图像过亮问题的排查


一、问题背景与现象

1.1 项目架构

最近完成了一个基于Vulkan的混合渲染程序,核心架构包含计算管线和图形管线:

  • 计算管线:使用 VK_FORMAT_R32G32B32A32_SFLOAT 格式的纹理作为输入/输出
  • 图形管线:通过相同格式的纹理进行采样,在片段着色器中将其渲染到屏幕

1.2 异常现象

程序运行时,屏幕上显示的图像比原始素材显著更亮,且在明暗过渡区域出现不自然的光晕效果。

image description

图 1:图片过亮


二、问题排查过程

2.1 初步怀疑:Gamma校正问题

观察到图像过亮后,首先怀疑Gamma校正未正确应用。以下是关键检查步骤:

(1) 检查图像加载逻辑

// 原始图像加载代码(发现问题)
int width, height, channels;
stbi_uc* pixels = stbi_load("example2048.png", &width, &height, &channels, STBI_rgb_alpha);
  • 问题发现STBI_rgb_alpha 将PNG的sRGB值直接读入内存,未执行sRGB→Linear转换
  • 根本原因:图像数据以sRGB格式存储,但被当作线性值处理

(2) 验证交换链配置

// 交换链颜色空间配置(默认值)
VkColorSpaceKHR colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
  • 关键发现:交换链自动执行Linear→sRGB转换,但输入的纹理数据已经是sRGB。

image description

图 2:色彩空间转换错误

三、技术原理:为什么冗余转换导致过亮?

3.1 sRGB与Linear空间的数学关系

sRGB采用近似Gamma 2.2的非线性编码(分段函数):

\[C_{sRGB} = \begin{cases} 12.92C_{linear}, & C_{linear} \leq 0.0031308 \\ 1.055C_{linear}^{1/2.4} - 0.055, & \text{otherwise} \end{cases}\]

3.2 冗余转换的数学推导

假设原始sRGB值为 0.5(实际对应Linear值约为 0.214):

  1. 错误处理流程
    • 加载时未转换:C_linear(误) = 0.5
    • 交换链自动转换:C_{final} = 1.055 × 0.5^{1/2.4} - 0.055 ≈ 0.735
  2. 正确值应为:0.5(不执行任何转换)

视觉差异:0.735比0.5亮度提升47%,导致整体画面过亮。


四、解决方案与评估

4.1 方案一:修改交换链色彩空间

// 使用PASS_THROUGH_EXT禁用自动转换
VkColorSpaceKHR colorSpace = VK_COLOR_SPACE_PASS_THROUGH_EXT;

4.2 方案二:片段着色器手动转换

// 在片段着色器中添加转换逻辑
vec3 sRGBToLinear(vec3 c) {
    return mix(c/12.92, pow((c+0.055)/1.055, vec3(2.4)), step(0.04045, c));
}
void main() {
    vec4 color = texture(sampler, uv);
    color.rgb = sRGBToLinear(color.rgb);
    outColor = vec4(color.rgb, 1.0);
}

先将颜色从sRGB空间转换到线性空间,颜色输出时,硬件再将颜色从线性空间转换到正确的sRGB空间。

五、延伸知识:常见色彩空间与应用

Vulkan 色彩空间对应关系

色彩空间 Gamma曲线 主要应用场景 Vulkan对应枚举值
sRGB ~2.2 Web图像、消费级显示器 VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
Linear 1.0 物理光照计算、HDR渲染 VK_COLOR_SPACE_PASS_THROUGH_EXT
Adobe RGB 2.2 专业摄影、印刷出版 无直接对应值
DCI-P3 2.6 数字影院、高端视频制作 VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT
Rec.2020 混合 8K/4K HDR电视 VK_COLOR_SPACE_BT2020_LINEAR_EXT
VK_COLOR_SPACE_BT2020_NONLINEAR_EXT
scRGB(线性) 1.0 HDR合成、科学可视化 VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT

六、EasyVulkan中的色彩空间

在EasyVulkan中,可以在交换链创建前指定色彩空间:

swapchainManager->setPreferredColorSpace(VK_COLOR_SPACE_SRGB_NONLINEAR_KHR);
swapchainManager->createSwapchain(800, 600);

交换链创建时,会优先创建具有指定format和color_space的交换链图像,如果不存在同时满足的情况,则会优先选择color_space,然后匹配合适的fromat。

// First try to find a format with our preferred color space and SRGB format
for (const auto& availableFormat : availableFormats) {
    if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB &&
        availableFormat.colorSpace == m_preferredColorSpace) {
        return availableFormat;
    }
}

// If not found, try to find any format with our preferred color space
for (const auto& availableFormat : availableFormats) {
    if (availableFormat.colorSpace == m_preferredColorSpace) {
        return availableFormat;
    }
}