跳转至

色彩空间

参考 Unity,通常使用 Linear 和 Gamma 两种色彩空间。

Gamma

Gamma 是一个非线性变换,一般定义为

\[ V_{\text{out}} = A V_{\text{in}}^\gamma \]

通常情况下,\(A=1\) 且输入和输出都在 \([0,1]\) 范围

  • \(0 < \gamma < 1\) 时,称为 Gamma 压缩、Gamma 校正。
  • \(\gamma > 1\) 时,称为 Gamma 展开、去 Gamma 校正。

显示器

早期的阴极射线管显示器(CRT Display)输出亮度 \(I\) 与输入电压 \(u\) 的关系为

\[ I=u^{\gamma} \]

其中 \(\gamma > 1\)。所以,要对输入信号做一次 Gamma 校正

\[ u_{\text{o}} = u_{\text{i}}^{1/\gamma} \]

才能将输入正确地显示出来。

图像编码

人眼感知到的亮度 \(I_1\) 和物理世界的亮度 \(I_2\) 大致也有一个指数关系

\[ I_1 = I_2^{\gamma} \]

其中 \(\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 色彩空间,被广泛使用。

公式

\[ C_{\text{linear}} = \begin{cases} \dfrac{C_\text{srgb}}{12.92}&, C_\text{srgb} \le 0.04045\\ \left (\dfrac{C_\text{srgb} + 0.055}{1.055} \right)^{2.4}&, C_\text{srgb} > 0.04045 \end{cases} \]
\[ C_\text{srgb}=\begin{cases} 12.92 C_\text{linear}&, C_\text{linear} \le 0.0031308\\ 1.055 \left( C_\text{linear}^{1/2.4} \right) - 0.055&, C_\text{linear} > 0.0031308 \end{cases} \]

指数函数 \(x^{1/2.4}\) 在靠近 \(0\) 的地方斜率趋于无穷,会放大一些微弱的噪声,所以靠近 \(0\) 的地方使用线性函数代替。

近似

完整的分段函数比较复杂,通常用 2.2 的 Gamma 近似

\[ C_\text{linear} = C_\text{srgb}^{2.2} \]
\[ C_\text{srgb} = C_\text{linear}^{1/2.2} \]

在不特别说明的情况下,大家说的 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 的渲染看上去会亮一点。


相关文章