目录
一、Color Grading
1.1 概念
1.2 内置在Unity/UE4
二、HDR、LDR与VDR
2.1 LDR
2.2 HDR
2.3 VDR
三、Tone Mapping
3.1 概念
3.2 算法发展史
3.3 ACES算法
四、常见的颜色空间标准
参考
Color Grading(颜色分级、颜色校正)俗称调色,是游戏后期处理中最常见也是必要的一个环节。它能够改变或矫正最终图像的颜色和亮度,你可以把它想象成在Instagram这样的软件中应用了滤镜。
Unity下的颜色分级一般可以分为三种模式:
UE4通过后期盒子编辑调色模块参数。
首先我们来理解DR——Dynamic Range(动态范围):Dynamic Range是一种用数学方式来描述某个给定场景的亮度层次范围的技术术语。指图像中所包含的从“最亮”至“最暗”的比值,也就是图像从“最亮”到“最暗”之间灰度划分的等级数;动态范围越大,所能表示的层次越丰富,所包含的色彩空间也越广。
LDR,低动态范围图像。LDR图片是8位图片,我们身边的电子图片,几乎都是LDR。它所采用的色彩模型是目前通用的图像描述模型——RGB模型。每种色彩都可以用三原色(红、绿、蓝)加上适当的亮度来表示,三原色的亮度梯度各为256级。也就是说,LDR的颜色一共能有256^3=16,777,216种。但LDR也只能算是对现实颜色进行压缩,看似丰富,但真和现实中的颜色比,还是局限了许多,一旦需要进行调色等后续加工,便会因为颜色不够而难以进行。常用LDR图片储存格式有jpg/png等。
要想增加颜色?这便诞生了HDR。单通道位数超过8位,便可称为HDR,常见有12位和16位。
通常显示器被限制为只能显示值为0.0到1.0间的颜色,但是在光照方程中却没有这个限制。通过使片段的颜色超过1.0,我们有了一个更大的颜色范围,这被称作HDR(High Dynamic Range, 高动态范围)。有了HDR,亮的东西可以变得非常亮,暗的东西可以变得非常暗,而且充满细节,同时还让我们能够根据光源的真实强度指定它的强度,而这在LDR渲染中是不能实现的,因为他们会被上限约束在1.0。常用的HDR图片储存格式有hdr/tif/exr/raw等。
LDR就是一个确定的范围,HDR是一个宽广的范围。即便两个都是HDR的,但它们的范围仍可能不同。因此有人把这个称为Variable Dynamic Range(VDR),可变动态范围,因为此H不一定是彼H。所以,即便在一个HDR世界,也仍然需要tone mapping来改变动态范围。
色调映射(Tone Mapping)指的是转换HDR值到LDR,是HDR渲染的最终步骤。使用更大范围的颜色值渲染能够获取大范围的黑暗与明亮的场景细节,但最后需要将所有HDR值转换成在[0.0, 1.0]范围的LDR,因为显示器只能显示在0.0到1.0范围之内的颜色,我们肯定要做一些转换从而使得当前的HDR颜色值符合显示器的范围。
Tone Mapping这个概念出现以来,有很多的色调映射算法,这些算法致力于在转换过程中保留尽可能多的HDR细节。随着GPU、游戏和电影特效的突飞猛进,Tone Mapping也经历了一系列的进化历程。例如基于经验模型的Reinhard Tone Mapping,到后来基于Tone Mapping的结果来进行拟合出来的Filmic Tone Mapping,再到现在比较常用的Academy Color Encoding System(AECS)等方法,具体可以参考文章:Tone mapping进化论,文章中有很多代码与算法论文的链接。
美国电影艺术与科学学会发明了Academy Color Encoding System(ACES),是一套颜色编码系统,或者说是一个新的颜色空间。它是一个通用的数据交换格式,一方面可以不同的输入设备转成ACES,另一方面可以把ACES在不同的显示设备上正确显示。不管你是LDR,还是HDR,都可以在ACES里表达出来。这就直接解决了VDR的问题,不同设备间都可以互通数据。
更好的地方是,这个函数的输出是线性空间的,所以要接到LDR的设备,只要做一次sRGB校正。要接到HDR10的设备,只要做一次Rec 2020颜色矩阵乘法。Tone Mapping部分是通用的,这也是比它之前提出的几个算法都好的地方。
然而对于实时渲染来说,没必要用全套ACES。因为第一,没有什么“输入设备”。渲染出来的HDR图像就是个线性的数据,所以直接就在ACES空间中。而输出的时候需要一次Tone Mapping,转到LDR或另一个HDR。也就是说,我们只要ACES里的非常小的一条路径,而不是纷繁复杂的整套体系。那么这条路径有多小呢?只要几行代码。
float3 ACESToneMapping(float3 color, float adapted_lum)
{
const float A = 2.51f;
const float B = 0.03f;
const float C = 2.43f;
const float D = 0.59f;
const float E = 0.14f;
color *= adapted_lum;
return (color * (A * color + B)) / (color * (C * color + D) + E);
}
看着很像Uncharted 2的做法吧,都是多项式拟合。但是式子比Uncharted的简单,并不需要算两个多项式并相除,只要算一个,一次搞定。它的曲线是这样的。
在历史上,电影行业和各种银幕制造商共同制定了很多颜色空间标准。与我们最相关的几种颜色空间标准如下:
以下给出了上述四种颜色空间标准的各个参数(Transfer Function Parameters指的是正向转换函数的参数,即从线性颜色值转换成非线性信号值的伽马压缩转换函数的参数,完整表格可以参见wiki):
可以看到,我们最常打交道的sRGB实际只占了CIE 1931颜色空间的大约35.9%区域,而sRGB也是游戏开发领域使用最为广泛的一个颜色空间,这意味着大部分游戏的显示色域范围被大大约束了。当然,原因主要还是因为LDR显示器仍然是目前最为普遍的显示设备,而sRGB是LDR显示器的行业标准,也就是说LDR设备无法重现出上图中sRGB三角区域外部的所有颜色。与之相比,Rec. 2020的色域范围就大大提升了,大约可以占到整个CIE 1931颜色空间的75.8%,而它也是未来趋势的颜色空间标准。
Color Grading | Unity Manual Effects
Color Grading and Filmic Tonemapper | Unreal Engine Documentation
HDR - LearnOpenGL CN
Tone mapping进化论 - 知乎
Unity PostProcess的使用 - 知乎
HDR,ToneMapping,Bloom之间的关系 - 知乎