MAKE · WRITE · SHARE

Base-色彩空间与线性渲染

同样是 RGB 数字,放进不同色彩空间,可能代表不同的光。颜色发灰、混色偏暗或特效强度异常,很多时候源自数字解释方式不一致。

这里把 2020 年的 Gamma 笔记与 2022 年的色彩空间笔记合并:先分清坐标与编码,再沿“纹理输入—光照计算—显示输出”追踪一次颜色。Gamma 不是一种美术滤镜,也不能用一次 pow 解决所有色彩管理问题。

相同黑白输入在编码空间和线性光空间平均后的差别

1. 色彩模型与色彩空间

RGB 是用红绿蓝三个分量描述颜色的模型;sRGB 则进一步规定了原色、白点和传递函数。不同 RGB 空间里的 (1,0,0) 不一定是相同的红。

HSV 是对 RGB 数值的另一种组织,便于按色相、饱和度和明度操作,但不是感知均匀空间,也不自动解决跨色域转换。将一个像素转成 HSV 再转回 RGB,不能把 sRGB 变成 Display P3。

“线性”描述的是数值与光强的关系。可以谈线性 sRGB、线性 Display P3;只说“Linear”还不足以确定原色和白点。原笔记里“线性空间不是色彩空间”的表述过于简化。

电磁波谱与可见光范围,帮助区分光谱分布和 RGB 编码

原笔记配图:电磁波谱与可见光范围,帮助区分光谱分布和 RGB 编码。

不同色域在色度图中的覆盖范围

原笔记配图:不同色域在色度图中的覆盖范围;色域、白点和传递函数需要分别约定。

2. sRGB 不是严格的 2.2 次幂

对于归一化的 sRGB 通道值 c,解码为线性光值 l 的标准形式是分段函数:

1
2
c <= 0.04045 : l = c / 12.92
否则 : l = ((c + 0.055) / 1.055) ^ 2.4

反向编码在 l <= 0.0031308 时使用 12.92*l,其余使用 1.055*l^(1/2.4)-0.055。pow(c,2.2) 只是近似,在暗部尤其不能当作精确标准。

历史显示设备的响应和有限位深下的编码效率都有关系,但不宜把整套流程解释成“眼睛非线性,所以随便反向校正”。今天首先需要遵循资产与输出端约定的传递函数。

旧资料中的 OETF 与 EOTF 曲线对照

原笔记配图:旧资料中的 OETF 与 EOTF 曲线对照。它们描述编码与显示响应,不能把不同标准都当成同一个 2.2 次幂。

Gamma 2.2 与其逆函数的近似示意

原笔记配图:Gamma 2.2 与其逆函数的近似示意;图中的 0.218 来自 0.5 的 2.2 次幂,精确 sRGB 解码约为 0.214。

3. 为什么混色要在线性光下做

以黑白各占一半的平均为例。线性平均是 0.5,编码成 sRGB 后约为 0.735。如果先在 sRGB 编码值里计算 (0+1)/2=0.5,再解码,只有约 0.214 的线性光值。

后一种结果更暗。模糊、光照相加、透明合成与离线缩图都可能遇到同样问题。这个例子只比较数值平均;最终视觉还会受显示、曝光和周围颜色影响。

4. 一条正确的数据链

颜色纹理作为 sRGB 资源采样时,可由硬件执行解码;光照与材质运算在线性空间进行;输出至 sRGB 目标时,再按目标格式与管线要求编码。代码里重复手工解码或重复编码,会让结果再次出错。

法线、粗糙度、深度、Mask 等是数据纹理,通常不应走 sRGB 解码。Alpha 通道也不应简单照搬 RGB 的光度转换。颜色常量、顶点色和脚本上传的 HDR 值是否需要转换,则必须检查引擎入口约定。

1
2
颜色资产 → sRGB 解码 → 线性材质与光照 → 曝光/色调映射 → 输出编码
数据资产 → 保持数据含义 ────────────────┘

5. Gamma、曝光与 Tone Mapping 各管什么

曝光改变光能尺度;Tone Mapping 把较大动态范围映射到显示范围;输出传递函数负责数值编码。三者不是同一件事。修复颜色输入错误时,不应靠调曝光把画面勉强拉回来。

HDR 值也不意味着可以忽略色彩空间。大于 1 的值可能是某种线性强度,也可能是旧工具把显示编码颜色乘了一个倍率。迁移历史特效时,需要明确其原有制作语义。

6. 排查顺序

先关掉后处理,用已知数值的灰阶和颜色块检查输入;再确认纹理 sRGB 标记、渲染目标格式和显示输出。随后逐项恢复曝光、Tone Mapping 与调色。

若只有某类旧特效异常,应追踪其颜色常量来源,而不是全局改变项目颜色空间。若只有模糊或透明边缘异常,检查运算空间和预乘 Alpha 是否一致。

总结

颜色的数字必须带着解释方式一起使用。把颜色空间、传递函数与线性光计算分开,才能判断应在哪个入口解码、在哪个出口编码,以及哪些数据根本不应该参与颜色转换。

参考资料