转载自 https://zhuanlan.zhihu.com/p/21983679
这几年,随着拍摄设备、渲染方法和显示设备的发展,HDR慢慢会成为标配。照相机和摄像机可以捕捉到HDR的影响,渲染过程中可以产生HDR的画面。这些内容如果需要显示到LDR的设备上,就需要一个称为tone mapping的过程,把HDR变成LDR。现在高端的显示器和电视也可以直接显示出HDR的内容。然而和LDR不同之处在于,LDR就是一个确定的范围,HDR是一个非常宽广的概念。即便两个都是HDR的,但它们的范围仍可能不同。因此有人把这个称为Variable Dynamic Range(VDR),可变动态范围,因为此H不一定是彼H。所以,即便在一个HDR世界,也仍然需要tone mapping来改变动态范围。
Tone mapping的过去
实际上tone mapping自古以来一直都有,不是计算机图形学的专利。早期因为颜料的对比度有限,达芬奇等的高手会把需要表达的内容用很有限的颜色画出来,即便色彩不真实。而刚发明电影的时候,胶片能表达的亮度范围有限,所以摄影师会把高亮区域和阴影区域向中等亮度方向压缩,发展出了S曲线的映射关系。这些都是tone mapping。
到了计算机图形学发展的时代,最早只有LDR,颜色就是0-255。这个情况一直持续了很长很长时间。到了90年代后期,软硬件的发展才让HDR变成一个可以考虑的问题。所以HDR转LDR的需求也就浮现出来了。随着2000年之后,GPU、游戏和电影特效的突飞猛进,tone mapping也经历了一系列的进化历程。每一个时期的代表作往往有强烈的流派特征,所以这里就按照流派来总结一下tone mapping。
经验派
直到2002年,有篇论文叫Photographic Tone Reproduction for Digital Images,提出了如何构造一个从HDR映射到LDR的方法。该方法也用其作者的名字命名为Reinhard tone mapping。从下图可以看出,用了tone mapping operator后,图片细节得以保留,而线性映射会造成最亮的区域和最暗的区域信息丢失。注意灯和书本。
Reinhard tone mapping非常简单,用代码描述就三行。
float3 ReinhardToneMapping(float3 color, float adapted_lum)
{const float MIDDLE_GREY = 1;color *= MIDDLE_GREY / adapted_lum;return color / (1.0f + color);
}
其中color是线性的HDR颜色,adapted_lum是根据整个画面统计出来的亮度。MIDDLE_GREY表示把什么值定义成灰。这个值就是纯粹的magic number了,根据需要调整。Reinhard的曲线是这样的,可以看出总体形状是个S型。
拿一个KlayGE的场景为例,经过Reinhard tone mapping是这样的。
这种tone mapping的方法更多地来自于经验,没什么原理在后面。所以就姑且称它为经验派吧。它的优点是简单直接,把亮的变暗,暗的变量。这样暗处和亮处细节就都出来了。但缺点也很明显,就是灰暗。个个颜色都朝着灰色的方向被压缩了,画面像蒙了一层纱。
虽然有这样的缺点,但那几年一来用HDR渲染的游戏少,二来大家不知道别的方法,所以就一直用着Reinhard tone mapping。
粗暴派
到了2007年,孤岛危机(Crysis)的CryEngine 2,为了克服Reinhard灰暗的缺点,开始用了另一个tone mapping的方法。前面提到了tone mapping就是个S曲线,那么既然你要S曲线,我就搞出一个S曲线。这个方法更简单,只要一行,而且没有magic number。用一个exp来模拟S曲线。
float3 CEToneMapping(float3 color, float adapted_lum)
{return 1 - exp(-adapted_lum * color);
}
CE的曲线中间的区域更偏向于小的方向,这部分曲线也更陡。
这个方法得到的结果比Reinhard有更大的对比度,颜色更鲜艳一些,虽然还是有点灰。
CE的方法在于快速,并且视觉效果比Reinhard。但是这个方法纯粹就是凑一个函数,没人知道应该如何改进。属于粗暴地合成。
拟合派
到了2010年,Uncharted 2公开了它的tone mapping方法,称为Filmic tone mapping。当年我也写过一篇博客讲KlayGE切换到Filmic tone mapping的事情。这个方法的本质是把原图和让艺术家用专业照相软件模拟胶片的感觉,人肉tone mapping后的结果去做曲线拟合,得到一个高次曲线的表达式。这样的表达式应用到渲染结果后,就能在很大程度上自动接近人工调整的结果。
最后出来的曲线是这样的。总的来说也是S型,但增长的区域很长。
从结果看,对比度更大,而且完全消除了灰蒙的感觉。
而代码就有点复杂了:
float3 F(float3 x)
{const float A = 0.22f;const float B = 0.30f;const float C = 0.10f;const float D = 0.20f;const float E = 0.01f;const float F = 0.30f;return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F;
}float3 Uncharted2ToneMapping(float3 color, float adapted_lum)
{const float WHITE = 11.2f;return F(1.6f * adapted_lum * color) / F(WHITE);
}
那些ABCDEF都是多项式的系数,而WHITE是个magic number,表示白色的位置。这个方法开启了tone mapping的新路径,让人们知道了曲线拟合的好处。并且,其他颜色空间的变换,比如gamma矫正,也可以一起合并到这个曲线里来,一次搞定,不会增加额外开销。缺点就是运算量有点大,两个多项式的计算,并且相除。
因为Filmic tone mapping的优异表现,大部分游戏都切换到了这个方法。包括CE自己,也在某个时候完成了切换。
你们都是渣渣派
在大家以为Filmic tone mapping会统治很长时间的时候,江湖中来了一位异域高手。他认为,你们这帮搞游戏/实时图形的,都是渣渣。让我们电影业来教你们什么叫tone mapping。这位高手叫美国电影艺术与科学学会,就是颁布奥斯卡奖的那个机构。不要以为他们只是个评奖的单位,美国电影艺术与科学学会的第一宗旨就是提高电影艺术与科学的质量。
他们发明的东西叫Academy Color Encoding System(ACES),是一套颜色编码系统,或者说是一个新的颜色空间。它是一个通用的数据交换格式,一方面可以不同的输入设备转成ACES,另一方面可以把ACES在不同的显示设备上正确显示。不管你是LDR,还是HDR,都可以在ACES里表达出来。这就直接解决了VDR的问题,不同设备间都可以互通数据。
然而对于实时渲染来说,没必要用全套ACES。因为第一,没有什么“输入设备”。渲染出来的HDR图像就是个线性的数据,所以直接就在ACES空间中。而输出的时候需要一次tone mapping,转到LDR或另一个HDR。也就是说,我们只要ACES里的非常小的一条路径,而不是纷繁复杂的整套体系。
那么这条路径有多小呢?只要几行,系数来自于Krzysztof Narkowicz的博客文章。
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的简单,并不需要算两个多项式并相除,只要算一个,一次搞定。它的曲线是这样的。
S感很浓,并且比Uncharted的更向小的方向移,即便很小的数值和接近1的数值也有梯度。这样能很好地保留暗处和亮处的细节。至于视觉效果如何呢?看看这个。
可以看出来,比之前的任何一个都要鲜艳,并且没有因此丢掉细节!当之无愧成为目前最好的tone mapping算法。
更好的地方是,按照前面说的,ACES为的是解决所有设备之间的颜色空间转换问题。所以这个tone mapper不但可以用于HDR到LDR的转换,还可以用于从一个HDR转到另一个HDR。也就是从根本上解决了VDR的问题。这个函数的输出是线性空间的,所以要接到LDR的设备,只要做一次sRGB校正。要接到HDR10的设备,只要做一次Rec 2020颜色矩阵乘法。Tone mapping部分是通用的,这也是比之前几个算法都好的地方。
目前一些新的游戏,比如Rise of the Tomb Raider、UE 4.8,也都切换到ACES的tone mapping曲线。
总结
这里介绍了4种有代表性的tone mapping算法,分别代表了各个时期的发展情况。随着HDR显示设备的不断普及,游戏也会越来越多地需要支持输出HDR。所以从业人员最好在此之前先把前导技术都准备好,以免突然需要切换的时候措手不及。
另外,这个tone mapping算法也可以用在HDR照相的处理上。相机的到HDR的线性数据后,通过这个tone mapping处理,会比传统的Reinhard算法效果好得多。