色彩空间¶
参考 Unity,通常使用 Linear 和 Gamma 两种色彩空间。
Gamma¶
Gamma 是一个非线性变换,一般定义为
通常情况下,\(A=1\) 且输入和输出都在 \([0,1]\) 范围
- \(0 < \gamma < 1\) 时,称为 Gamma 压缩、Gamma 校正。
- \(\gamma > 1\) 时,称为 Gamma 展开、去 Gamma 校正。
显示器¶
早期的阴极射线管显示器(CRT Display)输出亮度 \(I\) 与输入电压 \(u\) 的关系为
其中 \(\gamma > 1\)。所以,要对输入信号做一次 Gamma 校正
才能将输入正确地显示出来。
图像编码¶
人眼感知到的亮度 \(I_1\) 和物理世界的亮度 \(I_2\) 大致也有一个指数关系
其中 \(\gamma > 1\)。这个公式和 CRT 显示器的很像,但这只是个巧合。从公式可以看出,当 \(I_2\) 较小时,\(I_1\) 变化得较慢。换句话说,人眼能感知到更多暗色的变化,所以在编码图像时,应该给暗色更高的精度。
编码 | 原理 | 是否需要 Gamma |
---|---|---|
UNORM | 用 n 位无符号整数表示小数 0.0 - 1.0,整数 \(x\) 表示小数 \(x/(2^n-1)\) | 在将 0.0 - 1.0 的颜色值编码为整数前,应该先进行 Gamma 校正,把暗色的范围变大,使得编码后有更多整数表示暗色,更多地保留暗色的变化 |
FLOAT | IEEE-754 或类似表示方法 | 没必要,可能还会起反作用 1 |
CIE XYZ¶
这就是所谓的 Linear Color Space。颜色的三维坐标(X、Y、Z)与物理光能量之间是线性关系。如果将光源的能量按比例放大或缩小,那么 CIE XYZ 的坐标也会以同样的比例变化。
sRGB¶
sRGB 是惠普和微软开发的一种标准 RGB 色彩空间,被广泛使用。
公式¶
指数函数 \(x^{1/2.4}\) 在靠近 \(0\) 的地方斜率趋于无穷,会放大一些微弱的噪声,所以靠近 \(0\) 的地方使用线性函数代替。
近似¶
完整的分段函数比较复杂,通常用 2.2 的 Gamma 近似
在不特别说明的情况下,大家说的 Gamma 指的都是 sRGB 这个 2.2。sRGB 到 Linear 就是「去 Gamma 校正」,Linear 到 sRGB 就是「Gamma 校正」。
DXGI_FORMAT¶
DXGI_FORMAT 中带有 _SRGB
后缀的格式表示其保存的是 sRGB 颜色。在 Shader 中采样这些格式的纹理时,会自动将 sRGB 颜色转为 Linear 颜色。在 Shader 中向这些格式的 RTV 渲染时,会默认 SV_Target
输出的是 Linear 颜色,然后自动转换为 sRGB 颜色再保存。转换颜色时,使用的是准确的分段公式,而不是 Gamma 2.2 的近似公式。
只有整数格式(UNORM)才有 _SRGB
后缀。没有 _SRGB
后缀的格式也能保存 sRGB 颜色,只是 Linear 和 sRGB 的转换需要自己进行。
Swap Chain¶
Back Buffer 格式 | 色彩空间 | 备注 |
---|---|---|
整数 DXGI_FORMAT_*_UNORM (不能带有 _SRGB 后缀) | 必须写入 sRGB 颜色 | 可以创建 DXGI_FORMAT_*_SRGB 类型的 RTV,这样就能自动将 SV_Target 输出的 Linear 颜色转为 sRGB 了 |
浮点数 | 必须写入 Linear 颜色 |
表格中创建 RTV 的方法是只适用于 Back Buffer 的特例,其他资源必须创建为 DXGI_FORMAT_*_TYPELESS
才能实现。2
RenderDoc¶
RenderDoc 的 Texture Viewer 上有个 Gamma 按钮。如果当前查看的纹理内容是 sRGB 的,就选中它,如果内容是 Linear 的,就关闭它。3
渲染差异¶
从数学和物理的角度来看,Gamma (sRGB) Color Space 渲染是不准确的,差异主要体现在光照和透明混合上。4 另外,Linear Space 的渲染看上去会亮一点。
相关文章