Tone Mapping(色调映射)简介

2025年1月8日 1651点热度 11人点赞 1条评论

Tone Mapping是Post Processing过程中的一个重要处理阶段,我们知道,LDR的颜色为0-255,也记作0-1。超出1或低于0的颜色,就是HDR颜色。想显示多出去的颜色,就要把HDR压缩为LDR,这种映射关系,即色调映射(Tone Mapping)

实际上tone mapping自古以来一直都有,不是计算机图形学的专利。早期因为颜料的对比度有限,达芬奇等高手会把需要表达的内容用很有限的颜色画出来,即便色彩不真实。而刚发明电影的时候,胶片能表达的亮度范围有限,所以摄影师会把高亮区域和阴影区域向中等亮度方向压缩,发展出了S曲线的映射关系。这些实际上都是tone mapping。

随着2000年之后,GPU、游戏和电影特效的突飞猛进,Tone mapping的概念慢慢的形成并不断地优化完善。

Reinhard tone mapping

2002年,有篇论文叫Photographic Tone Reproduction for Digital Images,提出了如何构造一个从HDR映射到LDR的方法。该方法也用其作者的名字命名为Reinhard tone mapping。该方法解决了线性映射会造成最亮的区域和最暗的区域信息丢失的问题。

Reinhard tone mapping非常简单,只有三行代码。

vec3 ReinhardToneMapping(vec3 color, float adapted_lum)
{
    const float MIDDLE_GREY = 1.0;
    color *= MIDDLE_GREY / adapted_lum;
    return color / (1.0 + color);
}

其中color是线性的HDR颜色,adapted_lum是根据整个画面统计出来的亮度。MIDDLE_GREY表示把什么值定义成灰。这个值就是纯粹的经验参数,根据需要调整。Reinhard的曲线是这样的,总体趋势呈S型。

%title插图%num

这种tone mapping的方法更多地来自于经验,优点是简单直接,把亮的变暗,暗的变亮。这样暗处和亮处细节就都出来了。但缺点也很明显,就是灰暗。

CryEngine tone mapping

2007年,孤岛危机(Crysis)的CryEngine,为了克服Reinhard tone mapping灰暗的缺点,使用了一个新的tone mapping的方法,前面提到了tone mapping呈S型曲线,CryEngine则直接用一个exp来模拟S型曲线。

vec3 CEToneMapping(vec3 color, float adapted_lum)
{
    return 1.0 - exp(-adapted_lum * color);
}

CryEngine的曲线中间的区域更偏向于小的方向。

%title插图%num

这个方法相比于Reinhard tone mapping有更大的对比度,颜色也更鲜艳一些,虽然还是有点灰。CryEngine的方法在于快速,并且视觉效果优于Reinhard。但是这个方法纯粹就是凑了一个S型曲线函数,没有改进的空间。

Filmic tone mapping

2010年,Uncharted 2(神秘海域 2)公开了它的tone mapping方法,称为Filmic tone mapping。这个方法的本质是用原图和让艺术家用专业照相软件模拟胶片感觉的图像去做曲线拟合,得到一个高次曲线的表达式。这样的表达式应用到渲染中,就能在很大程度上自动接近人工调整的结果。

Filmic tone mapping的曲线是这样的,依旧呈S型,但增长的区域很长。

%title插图%num

从结果来看,对比度更大,而且完全消除了灰蒙的感觉,代码也更复杂了。

vec3 Uncharted2HelperFunc(vec3 x)
{
    const float A = 0.22;
    const float B = 0.30;
    const float C = 0.10;
    const float D = 0.20;
    const float E = 0.01;
    const float F = 0.30;
    return ((x * (A * x + C * B) + vec3(D * E)) / (x * (A * x + B) + vec3(D * F))) - vec3(E / F);
}

vec3 Uncharted2ToneMapping(vec3 color, float adapted_lum)
{
    const float WHITE = 11.2;
    return Uncharted2HelperFunc(color * 1.6 * adapted_lum) / Uncharted2HelperFunc(vec3(WHITE));
}

其中A、B、C、D、E、F是多项式的系数,WHITE是个经验参数,表示白色的位置。因为Filmic tone mapping的优异表现,大部分游戏都切换到了这个方法。

Academy Color Encoding System(ACES)

2014年,在大家以为Filmic tone mapping会统治很长时间的时候,美国电影艺术与科学学会发布了Academy Color Encoding System(ACES)1.0版本,ACES是一套颜色编码系统,或者说是一个新的颜色空间,它是一个通用的数据交换格式,能够让不同设备互通数据,不管你是LDR,还是HDR,都可以在ACES里表达出来。

对于实时渲染来说,无需使用全套ACES。因为渲染出来的HDR图像就是个线性的数据,直接就在ACES空间中,输出也只需要一次tone mapping,转到LDR或另一个HDR。

vec3 ACESToneMapping(vec3 color, float adapted_lum)
{
    const float A = 2.51;
    const float B = 0.03;
    const float C = 2.43;
    const float D = 0.59;
    const float E = 0.14;

    color *= adapted_lum;
    return (color * (A * color + B)) / (color * (C * color + D) + vec3(E));
}

看起来和Uncharted 2的做法很像,都是多项式拟合。但比Uncharted的简单,并不需要计算两个多项式相除。

%title插图%num

ACES的曲线更加接近S型,即便很小的数值和接近1的数值也有梯度。这样能很好地保留暗处和亮处的细节。渲染后的效果比之前任何一个都要鲜艳, 也没有因此丢掉细节!当之无愧成为目前最好的tone mapping算法。

按照前面说的,ACES目的是解决所有设备之间的颜色空间转换问题。所以这个tone mapping不仅可以用于HDR到LDR的转换,还可以用于从一个HDR转到另一个HDR。也就是从根本上解决了VDR的问题。这个函数的输出是线性空间的,所以要接到LDR的设备,只要做一次sRGB校正。要接到HDR10的设备,只要做一次Rec 2020颜色矩阵乘法。Tone mapping部分是通用的,这也是比之前几个算法都好的地方。

总结

以上4种有代表性的tone mapping算法,分别代表了各个时期的发展情况。在三维引擎的渲染管线中,将HDR用ToneMapping压缩成LDR,再用Bloom表现高光溢出的效果是Post Processing过程的常规做法,然而Tone Mapping不仅能用在实时渲染的Post Processing过程中,也可以用在HDR照相的处理上,相机得到HDR的线性数据后,通过tone mapping处理,会比传统相机效果好得多。

StackSnow

追风赶月莫停留,平芜尽处是春山。

文章评论

  • Avatar photo
    Good

    Good!!

    2025年1月8日