GPU Gems 1 · Chapter 22. Color Controls
用 Levels、Curves、一维查找纹理、灰度点积与三乘三矩阵,把艺术家可控的色彩校正稳定地带进实时 pixel shader。
学习目标
- 能把 color correction 分解为逐通道 remap、查表与通道混合,并说明每一步的输入输出
- 能实现 Levels 与 Curves 的 shader 版本,选择合适的一维或三维资源保存艺术家控制
- 能用加权点积和三乘三矩阵完成灰度与色彩空间转换,并验证结果没有意外压暗或溢出
- 能在实时预算内调试 lookup texture、gamma、saturation 和矩阵顺序,保留可复现的视觉控制
色彩校正不是渲染完成后的静态修图,而是把像素从一个可见的颜色关系映射到另一个关系。它可以把不同资产的色彩统一到同一场景,也可以制造褪色胶片、交叉冲洗、红外和游戏 UI 的风格。关键是把艺术家熟悉的控制,转换成 pixel shader 能稳定执行的资源。
↡把像素颜色从一种通道关系、色彩空间或艺术风格映射到另一种结果的过程 的共同接口是逐像素输入和逐像素输出。输入可以来自 2D 纹理、3D 场景的 back buffer 或中间 render target;输出通常在 pixel shader 中完成,因此可以实时作用于完整画面。
1. 逐通道控制:Levels 先建立可解释的基线
↡使用 input black、input white、output black、output white 与 gamma 调整单个颜色通道动态范围的参数化校正 用少量参数描述每个通道的输入输出范围。将输入归一化后,可以先把 inBlack 到 inWhite 映射到零到一,再施加 gamma,最后恢复到目标输出范围:
float LevelChannel(float value, float inBlack, float inWhite, float gamma,
float outBlack, float outWhite) {
float normalized = saturate((value - inBlack) / max(inWhite - inBlack, 0.0001));
float shaped = pow(normalized, gamma);
return lerp(outBlack, outWhite, shaped);
}这里把颜色先放到零到一的 shader 表示;若工具以零到二百五十五的整数工作,就在 CPU 端预先归一化参数,避免每个像素重复做无意义的缩放。↡作用于归一化颜色值的幂函数参数,用于改变中间调的亮暗曲线 不同于黑白点:若三个通道的 gamma 不同,就不能把所有幂运算合并成一个共享标量,必须保留三条通道路径。
Levels 适合做曝光、对比度和动态范围的基础调整。它也适合作为资产校验的第一层:给输入 ramp,检查黑点是否真的变黑、白点是否真的到达目标、gamma 是否只改变曲线而没有偷偷改变端点。
2. Curves 与一维查找纹理:把艺术操作变成数据
↡由输入通道值索引输出通道值的纹理资源,常用一条一乘二百五十六的 RGB ramp 保存复杂的每通道曲线 是 Curves 的运行时表示。Curves 工具可以让艺术家绘制任意的输入到输出关系;shader 不必重现 spline,而是读取这条已经烘焙好的 ramp。通常准备一张一乘二百五十六的 RGB 图:黑到白的初始 ramp 经过 Curves 调整后,R、G、B 三个分量分别成为三个通道的映射。
float3 inputColor = tex2D(inSampler, IN.UV).xyz;
float3 outputColor;
outputColor.r = tex1D(colorCorrMap, inputColor.r).r;
outputColor.g = tex1D(colorCorrMap, inputColor.g).g;
outputColor.b = tex1D(colorCorrMap, inputColor.b).b;
return float4(outputColor, 1.0);查表的工程契约需要明确纹理格式、寻址模式、采样过滤和颜色空间。对于连续的颜色映射,线性过滤能在相邻 texel 之间插值;对于需要精确分段的风格化 map,则应验证过滤不会制造不想要的中间色。工具导出的 ramp 还要和运行时读取的通道顺序一致。
3. 通道混合:灰度、矩阵与三维映射
当输出通道依赖多个输入通道时,逐通道一维 map 不再够用。↡将 RGB 或其他通道值通过标准或自定义关系映射到另一种颜色表示的过程 可以用每个输出通道一个 dot product 表示;把三个 dot product 放在一起,就是一个三乘三矩阵乘法。
灰度是最小的例子。按照官方章节给出的 Rec 709 权重,可以写成:
float grayscale = dot(float3(0.222, 0.707, 0.071), inputColor);
float3 grayColor = float3(grayscale, grayscale, grayscale);权重总和为一,通常能保持整体能量尺度,但它也意味着纯蓝可能被压到很暗的灰度。游戏风格不一定要服从这个默认结果,因此应把权重作为显式艺术参数,而不是把它藏在 shader 常量里。
↡用一个三维输入坐标索引输出颜色的查找资源,可表达任意 RGB 到 RGB 关系但需要更多内存与制作工具 能表达最一般的 RGB 到 RGB 映射:
float3 outputColor = tex3D(colorSpaceSampler, inputColor);代价是三维纹理的内存、滤波和制作管线。若转换关系是线性的,优先把它写成三乘三矩阵:
float3x3 conversionMatrix;
float3 outputColor = mul(conversionMatrix, inputColor);矩阵不仅可以表达 RGB 到其他工业色彩空间,也可以表达饱和度、亮度和色轮旋转等控制。将多个线性操作预先在 CPU 端串接,可以减少 shader 中的中间值和纹理访问;但要记录矩阵乘法顺序,因为矩阵通常不满足交换律。
4. 运行时控制与验证策略
艺术家需要看到参数改变后的完整画面,程序员则需要知道每个参数改变的是哪条通道。建议把控制分成三层:先用 Levels 处理输入范围,再用一维 map 处理风格化的 per-channel remap,最后用 dot product 或矩阵执行需要通道混合的操作。若要做 grayscale,就在混合阶段明确写出权重;若要做 3D map,就将其视为独立资源并记录版本。
Color Controls Lab
先预测:gamma 与 saturation 改变的是同一件事吗?
观察:Levels 先压缩输入动态范围,gamma 改变曲线形状;saturation 只改变通道偏离 luminance 的幅度,grayscale 则把三个输出合成同一亮度。
实验中的示意像素不是完整色彩管理系统,但它能暴露三个高频错误:黑白点反转造成所有输出饱和,gamma 方向写反造成中间调过亮或过暗,以及 saturation 与 grayscale 顺序不清造成预览和 shader 不一致。发布前至少用黑、白、灰、纯红、纯绿、纯蓝和一条灰阶 ramp 验证,并在目标 framebuffer 的颜色空间中重复检查。
小结:把艺术控制编码成可验证的像素映射
- color correction 先区分逐通道 remap 与通道混合,再选择 shader 公式或纹理资源。
- Levels 用黑白点和 gamma 提供可解释的动态范围控制;Curves 更自由,适合烘焙到一维 lookup texture。
- 灰度可用加权 dot product;线性的色彩空间转换可用三乘三矩阵;任意 RGB 到 RGB 关系才值得考虑 3D color map。
- 运行时必须记录颜色空间、采样过滤、通道顺序和操作顺序,并用极端颜色与灰阶 ramp 回归。
练习
问题 1|改 Demo 代码 给定 inBlack=0.1、inWhite=0.9、gamma=1.0,输入通道值为 0.5。按 Levels 的归一化步骤计算输出的中间值;如果把 gamma 改为 2.0,中间调趋势如何变化?
问题 2|资源选择题 艺术家要复现三条任意 Curves 曲线,运行时每个像素只需要独立改变 R、G、B;应选择一维 lookup texture 还是三维 color map?说明制作和采样代价。
问题 3|独立实现题 为一个需要灰度预览、复古曲线和可调饱和度的实时画面设计 shader 与验证计划。请写出阶段顺序,并列出至少四个回归样本。
名词解释
本章出现的专业名词,用大白话再讲一遍。
- color correction
- 将像素颜色从一种通道关系、色彩空间或艺术风格映射到另一种结果的过程。
- Levels
- 使用黑白点、输出范围和 gamma 调整单个颜色通道动态范围的参数化校正。
- 1D lookup texture
- 由输入通道值索引输出通道值的一维纹理资源,常用于保存 Curves 映射。
- color-space conversion
- 将 RGB 或其他通道值通过标准或自定义关系映射到另一种颜色表示的过程。
- 3D color map
- 用三维输入坐标索引输出颜色的查找资源,可表达任意 RGB 到 RGB 关系。
- gamma
- 作用于归一化颜色值的幂函数参数,用于改变中间调的亮暗曲线。