4

Tonemapping不够用了:Local Tonemapping方案总结

 8 months ago
source link: https://blog.uwa4d.com/archives/USparkle_LTM.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

【USparkle专栏】如果你深怀绝技,爱“搞点研究”,乐于分享也博采众长,我们期待你的加入,让智慧的火花碰撞交织,让知识的传递生生不息!


一、前言-Global/Local Tonemapping介绍

Physically Based Rendering(PBR)中,过去讨论得最多的是PBR Material。但随着时间的推移,当前讨论“PBR”已经不仅局限于Material,而是Material、Lighting、Camera的结合,即对应的是:材质如何物理地表现、光照如何物理地模拟、以及图像如何物理地生成的问题。

1.png

在设计上保持以“物理正确性”作为前提,是非常符合第一性原理的。比如说,我们将物理世界中的光照单位和强度照搬进游戏中,然后光源的参数就可以直接查询现实世界里该光源对应的强度,在整个系统足够物理的前提下,我们理应得到和现实世界差不多的效果。

但这只是一个理想化的假设,实际情况复杂的多,我们仅从PBR Camera的视角看:

我们按照物理方式渲染最终得到了一个将要呈现的“原始图像”(Scene-referred),其信号的动态范围和物理世界的动态范围相近。但由于我们现在的显示器的可呈现的动态范围和物理世界相比实在太小:

2.png

因此呈现的结果必然有信息丢失。

通过应用Global Tonemapping(全局色调映射),将Scene-referred映射到显示设备能够呈现出较好效果的区间内,我们熟知的“ACES曲线”就是如此:

3.png

关于Global Tonemapping的发展,建议查看叛逆者大佬的文章[1],做了非常出色的总结。

不过当画面的动态范围进一步增加的时候,Global Tonemapping开始不那么完美了,有些过亮或过暗的区域会开始丢失细节,个人在玩《赛博朋克2077》的时候就发现有部分丢细节的现象,特别是人坐在车内逛街的时候:

4.png
5.png

注意图中圈出来的红色区域,已经完全看不出细节了,在现实生活中肯定不是这样的。

2016年Bart Wronski的文章[2]对此问题进行非常详细的解释,建议先阅读后再往后看。

在摄影或电影界这个问题也是存在的:

6.png

如上左图照相机拍摄影子处欠曝,右图经过后期调整到比较接近人眼观察的效果。

不过由于照片是“静态”的,所以有各种前期或后期的解决方法,比如:

  • 人工补光,俗称“Hack the light”。
  • Graduated Filter(ND镜)
  • Bounce cards
  • Dodging & burning
  • Shadow/highlight调整(后期阶段)

但在游戏中这些前期调节手段并不好用。因为游戏输出的画面是动态的,输出什么内容和玩家控制的角色看到什么有关,也和当前灯光环境有关(比如昼夜流逝、天气变化等)。若以“Hack the light”为例,通常在某时刻某位置补光,在光照环境变化时就会露馅,而管理一大堆变化的Hack light会把项目变成灾难。

后期调节或许是在游戏中比较有可行性,我们想要一种手段可以降低画面整体的对比度,但同时又要能保持住局部区域的对比度。

二、《战神4》中的Gaussian Blending Local Tonemapping[2]

Bart Wronski的原文中提到为《God Of War》实现的Local Tonemapping方法思路是:

  • 对图像做一次Gaussian Blur。
  • 以Blur的结果作为判定局部区域亮度差异的依据,以此进行局部图像直方图拉伸或对比度调节。
7.png

Global Tonemapping

8.png

Global Tonemapping + Gaussian Blending Local Tonemapping

作者并没有在文中公开更多的实现细节,因为这个方法他认为只是:

it can work “ok”, but generates pretty bad halos.[3]

Halos Artifact
这里需要解释一下Halos Artifact。如果更改某个局部区域的亮度,那么这个局部区域如何划定呢?仅从图像的相邻程度来考虑的话容易将对局部区域的修改扩散到周边不期望的区域,以Photoshop中的“shadow/highlight”功能为例进行演示:

9.png

Wide dynamic range image

10.png

Adjust Shadow/Highlight

11.png

Halos Artifact

试图降低过亮区域的影响会扩散到亮度适中和较暗的区域。反过来也一样。

如果是静态图片,手调总是能出一个能看的效果。但游戏中运动的相机会使这个缺陷能被注意到。

要解决Halos Artifacts,用类似Bilateral Filter[4]的思路或许是一个好主意。

三、《对马岛之魂》中的Bilateral Grid Local Tonemapping[5]

Bilateral Filter可以通过加权来保持图像的边界,而对图像的其他部分做模糊,以降低Halo Artifact。

不过Bilateral Filter在Real-time上实现的性能开销并不低,后来论文Chen et al., 2007[6]中提出了更高效的Bilateral Grid。

Bilateral Grid的思路是通过升维来解决问题:将不同值的Pixel分离到不同的Slices上,再分别对每个Slices做Blur,由于每个Slices之间在空间上是分开的所以不会互相影响,这就自动完成了保持边界的效果。

12.png

以一张1D的输入图片为例(图a),构建的Grid能在空间上将原图的Edge分开(图b),然后可以对Grid和Slice做一些模糊滤波(图c),最后从高维的Grid中取回结果(图d)。

《Ghost of Tsushima》基于Bilateral Grid实现的Local Tonemapping流程如下:

构建Bilateral Grid:

  • 创建一个和屏幕对其的Volume(3D Texture),其Z轴刻度上分布着均匀的log-luminance(对亮度取log2)值。
  • Volume的每个片(Slices)都是一个和屏幕对其的Grid。其中填充内容为一个float2数值:(w_i * V_i,w_i),其中w_i为屏幕Pixel Sample的权重(见下一条),V_i为Sample的log-luminance。Grid的一个单位对应屏幕中一块区域,填充的最终结果为区域内所有Pixel Sample结果的累加。
  • Sample权重取决于Sample的log-luminance在Z坐标轴上的对应数值,数值通常会落在两个坐标刻度之间,因此每个Sample实际上对相邻的两个2Slices都会有贡献。
  • 接着对Volume的x和y方向,即2D Grid应用半径较大的Gaussian Blur,z方向应用半径较小的Blur(通常z的Blur会忽略)。
  • 最后对Volume进行trillinearly sampling并normalizing,得到的结果就是的bilaterally filtered log-luminance。

最后根据此公式进行出了亮度调节:

13.png

对这个公式的理解:

(B - M)是区域平均亮度相对于场景平均亮度的大小。表示局部区域的亮度。

(I_i - B)是Pixel亮度相对于区域平均亮度的大小。表示局部区域内各Pixel细节的亮度。

因此这个公式的含义就是:根据c调节局部区域亮度,根据d调节区域内各细节的亮度。

14.png

Global Tonemapping

15.png

Global Tonemapping + Bilateral Grid Local Tonemapping

Ringing Artifact
不过Bilateral方法有一个缺陷就是Ringing Artifact:

16.png

原文中也解释了这种现象的成因[7](我不解释才不是因为我没有完全理解ฅʕ•̫͡•ʔฅ 呢)。

解决方法是将Bilateral和Gaussian进行混合,在Halos和Ringing之间取一个平衡:

17.png

改动对比:

18.png

Bilateral Blur

19.png

40% Bilateral Blur + 60% Wide Gaussian Blur

四、《虚幻引擎5》中的Local Exposure[8]

2022年UE5发布了正式版,惊喜地发现官方实现了一套Local Tonemapping,官方叫Local Exposure。

UE5的Local Exposure基本是照着上面Bilateral Grid Local Tonemapping实现的,这里就不做重复解释原理了。

管线实现上主要由3个Pass组成:

20.png
  • Local Exposure,是生成Bilateral Grid的Pass,见“AddLocalExposurePass”的实现。
  • Local Exposure - Blurred Luminanc,包含降采样和Two Pass Gaussian Blur,为了解决前面提到的Ringing Artifact问题。
  • 最后在Tonemap Pass中合并计算。

2022年初UE5演示的《黑客帝国觉醒》就用了Local Exposure:

21.png

Local Exposure Off

22.png

Local Exposure On

五、Exposure Funsion Local Tonemapping

之前,Bart Wronski大佬时隔2016年的第一篇文章6年后,更新了他关于Local Tonemapping的第二篇文章[3],Exposure Funsion Local Tonemapping。

虽然Bilateral Grid Local Tonemapping在Halos和Ringing之间取得了不错的平衡,但通常Local Tonemapping的参数设置的比较极端时还是会出Halos Artifact,且容易将局部“抹平”,因此在实际使用中只能让美术不要调那么极端的参数。

本节介绍的Exposure Funsion则不会有这些缺陷,且进一步提升了局部对比度,下图是Bilateral Grid相比Exposure Funsion在保持局部对比度上的差异:

23.png

Bilateral Grid

24.png

Exposure Funsion

可以看到Bilateral Grid则会倾向于将图像边界平坦化,Exposure Funsion则保持了更多的局部对比度。

Exposure Funsion算法由Mertens et al[9]提出,是一种将多张图像(通常是不同曝光的图像)进行混合,但又保持细节、边界和最小化Halos的方法:

25.png

算法的原理,我们先从对图像的一些观察入手,我们想要Local Tonemapping:

26.png
  • 对于图像中的边界也就是相关性弱的图像区域,我们希望混合的范围需要变小以避免Halo(如图中红色圈部分)。
  • 对于相关性强的平坦的画面部分,我们希望混合的范围可以变大,以形成平滑自然的效果,使视觉上注意不到混合的发生(图中绿色圈部分)。

仔细思考一下,其实等价于:“对于图像中不同频率的部分,我们应该用不同的混合半径”,这就是Exposure Funsion的原理。

Exposure Funsion算法步骤:

27.png
  • 通过不同的曝光设置,得到需要进行混合的多张图像,数量越多越好,但通常3张足够了。
  • 对于每个需要混合的图像生成相应的亮度图。
  • 对亮度图创建Laplacian Pyramid(拉普拉斯金字塔)[10],直到某一层(可自定义)。
  • 创建混合权重图,权重图可以通过原图的一些指标来生成,如“饱和度、对比度或曝光度”,作者建议只使用曝光度以避免over-saturated或over-contrasty。
  • 对权重图创建Gaussian Pyramid(高斯金字塔)[10]
  • 开始遍历金字塔,先将金字塔顶层的Gaussian亮度图根据权重图混合(Laplacian Pyramid的最顶层等价于Gaussian亮度图)。
  • 金字塔往下遍历,将每层的Laplacian亮度图和对应的根据权重图进行混合。
  • 将混合后的金字塔还原回原图,就是想要的结果。在渲染管线中,这一步之后做Exposure和Global Tonemapping。

作者在原文中设定了不少控制参数,附带实现的Web Demo且完全开源!

六、《孤岛惊魂5》中的GI-Based Local Tonemapping[11]

前面介绍的都是图像后处理方法的Local Tonemaping,并且Exposure Funsion版本的Local Tonemapping在保持局部对比度和最小化Halos上都趋于完美。

不过我这里另外补充一个《Far Cry5》中使用的不基于图像后处理的方法:

Bilateral Blur可以用于Local Tonemapping/Local Exposure是因为:

  • 能够区分在屏幕2D空间中距离近,但是在场景3D空间中距离远的区域(减小Halos)。
  • Blur的结果能表示局部的平均亮度。

其实有一个现成的数据也能够近似满足这些条件,就是3D场景中的低频GI:Indirect Sunlight + Skylight(with Occlusion)。

28.png
29.png

事实上GI并不完全能反映区域的平均亮度,而且大场景的Skylight Occlusion精度也有限。

不过通常最需要Local Tonemapping的场景是 “室内室外互看”的时候,此时室内外GI的差异是足够大的。

实现步骤:

  • 计算当前场景的曝光度
  • 计算当前Pixel的GI Average Luminance(Indirect Sunlight + Skylight(with Occlusion))
  • 根据GI Average Luminance和场景曝光度的差异,对Lighting结果进行缩放,伪代码:
    30.png
31.png
32.png

原文表示虽然前后的图像没有产生太大的差异,但足以解决因为室外细节丢失影响玩法的困扰。

七、其他资料

好文推荐:胡渊明老师的文章 《用Taichi实现GPU图像处理:从入门到入魔》[12],文中对Bilateral Grid的实现过程做了更详细的解释。

八、总结

本文主要介绍了为什么需要Local Tonemapping,以及对目前为止在实时渲染里实现的方案进行了简单总结,更多细节还是建议亲自看看引用文献。如有错漏欢迎补充。

参考
[1] Tone mapping进化论

[2] abLocalized tonemapping – is global exposure and global tonemapping operator enough for video games?

[3] abExposure Fusion – local tonemapping for real-time rendering

[4] Bilateral Filter

[5] Real-Time Samurai Cinema Lighting, Atmosphere, and Tonemapping in Ghost of Tsushima

[6] Real-time Edge-Aware Image Processing with the Bilateral Grid

[7] Ringing Artifact

[8] Unreal Engine 5.0 release notes-Local Exposure

[9] Exposure Fusion

[10] Laplacian Pyramid and Gaussian Pyramid

[11] The Challenges of Rendering an Open World in Far Cry 5

[12] 用Taichi实现GPU图像处理:从入门到入魔


这是侑虎科技第1527篇文章,感谢作者PZZZB供稿。欢迎转发分享,未经作者授权请勿转载。如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。(QQ群:465082844)

作者主页:https://www.zhihu.com/people/puzzledboy

再次感谢PZZZB的分享,如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。(QQ群:465082844)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK