![8944d113c50df8510acb4b6a76c17f39.png](https://i-blog.csdnimg.cn/blog_migrate/69b31cc03a952e0af78fdc1548b5b76a.jpeg)
简介
本文为Sébastien Hillaire在Eurographics Symposium on Rendering 2020发表的论文A Scalable and Production Ready Sky and Atmosphere Rendering Technique的导读。据论文介绍,文章中的方法已经在Fortnite和UE4中实装。
论文作者Sébastien Hillaire应该很多人都会熟悉,之前在Frostbite的部门工作,Frostbite的体积渲染的Siggraph course(Towards Unified and Physically-Based Volumetric Lighting in Frostbite)就是他做的。现在到了Epic,负责的渲染相关工作。
这篇论文的提供的思路非常棒,简单来说使用了一定程度的近似,最后达到的效果为:
- 在保证效果跟path tracing的GT接近的情况下,比以前的预计算LUT,实时采样LUT(Bruneton08)的效率只稍微慢一点;
- 有一定的LUT要素,但是LUT完全可以实时计算(大气参数可以实时修改,而且不是分帧更新之类的伪实时,是真的一帧搞定);
- 在iPhone6s上可以在每帧1ms以内搞定(包括了LUT计算);
- 做了一些近似假设,从而可以用数值方法计算所有高阶散射之和;而不是像之前的方法只计算一定的阶数就结束。在大气稀薄的情况下(高阶散射更加重要)效果提升非常明显。
总结一下,在效率、效果方面,可以说是把以前的方法拉起来全部A了。
![5eddc7aa681e3d79e310f1212a638858.png](https://i-blog.csdnimg.cn/blog_migrate/0f76767c83ecc06ac1969714a25780fd.jpeg)
当然因为采用了一定的近似,在结论部分作者也提供了一个明显偏差的情况,即大气设置非常厚重(比地球密55倍)时会出现偏差。
但是即便存在这个缺点,该方法依然是比之前的方法存在大量的优越性的。
作者官网以及paper链接:
https://sebh.github.io/publications/sebh.github.io https://sebh.github.io/publications/egsr2020.pdfsebh.github.io我是第一次写这种论文导读类型的文章,欢迎指正。
前置知识
在阅读这篇论文前,读者应该对老方法有一定的认识,如果能读懂Bruneton08的论文,本篇论文就非常好理解了。另外Bruneton在17年做了一个新的开源实现,使用了C++做验证实现(相同的glsl代码通过宏定义的方式编译成C++代码),并且代码做了非常多的注释(甚至有图),看了代码就基本上不用看那篇论文了。代码被作者托管在http://github.io上,点开就可以直接看带图的注释,链接如下:
https://ebruneton.github.io/precomputed_atmospheric_scattering/ebruneton.github.io知乎上也有大佬对大气散射相关原理的解析,这里我推荐一篇 @Lele Feng 大佬的文章,最早的时候就是这篇文章让我明白了大气散射的原理:
https://zhuanlan.zhihu.com/p/36498679zhuanlan.zhihu.com此外,论文中前4章也一定程度上说明了大气散射的原理,可以作为补充。
下一章开始说明Hillaire20的内容。因为本文主要为讲解论文的创新点,因此对于大气散射的原理、计算方法等不会做过多说明。这里假设读者已经完全理解了Scattering、Absorption、Rayleigh、Mie、Henyey-Greenstein、Phase Function、Transmittance等名词的意思,并且清楚大气散射的计算方法。如果发现自己看不懂,建议先阅读上述内容。
另外,开始之前我对一些论文中的名词做出本篇论文语境下个人的解释,可能存在偏差。
Extinction Coefficient
Phase Function ,
Transmittance,
Scattering,
In-Scattering,
Luminance,
此外,
论文方法
论文中介绍具体方法的第5章分别讲述了:
- 对大气散射特性的观察结论,大意即为天空中大部分的散射现象是低频的,除了靠近地平线部分会变得高频。
- Transmittance LUT的计算,其实就是老方法,不提
- Sky-View LUT
- Aerial Perspective LUT
- Multiple Scattering LUT 重点
Sky-View LUT
在实际的渲染流程中,首先根据相机位置渲染至一张低分辨率的Sky-View LUT上,后期再合成到Render Target中。
Sky-View LUT中包含了当前相机位置接收到的各个角度的Luminance。计算时根据像素对应的视线方向直接做Raymarch得到结果(这里每一步Raymarch中的每一步采样都包含了所有阶的Scattering,如何计算看后文),论文中的例子如下图。
![cf2e0d28e5734ab8cbd53f472d73c846.png](https://i-blog.csdnimg.cn/blog_migrate/3a930244e49e504b99f48c84c45f5cd6.jpeg)
如上面提到,在靠近地平线部分大气散射会变得高频,因此UV的映射不是线性,而是靠近地平线部分会分配更多像素,具体公式在论文中写明了,这里就不复读了。
在论文中的测试,PC上只需要200*100的分辨率效果就足够。另外太阳本身是不会渲染在图里的,因为属于高频特征。会在后面再合成上去。
需要注意的是,这里跟Bruneton08已经不一样了,Bruneton08中不会有任何实时的Raymarch,都是LUT查找搞定;而这篇论文中,不管怎么样,在这一步实时Raymarch都是少不了的(其他的Raymarch步骤可以看情况跳过),这也是相比老方法会稍微慢一点的原因。
Aerial Perspective LUT
除了背景的大气,相机和远处物体之间的大气散射也是画面的重要组成部分。不过其实也是老方法了。
- 分割Camera Frustum(和Cluster Rendering中的分割一样),计算每个格子到Camera方向的In-Scattering和Transmittance,保存在Volume Texture中;
- 在Volume Texture的z轴方向上根据Transmittance累加In-Scattering,使得每一个单元格保存的是该单元格到Camera的Luminance;
- 在Opaque渲染之后,做一次Post Processing,采样上述的Volume Texture,对场景中的物体添加Aerial Perspective;
- Transparent物体渲染时在VS中采样Volume Texture添加Aerial Perspective。
Multiple Scattering LUT
这篇论文中最神奇的地方就是Multiple Scattering的计算了。在Bruneton08中,Multiple Scattering的计算需要一阶一阶网上算,比如1阶Scattering、Luminance,然后计算2阶Scattering、Luminance……
论文作者在计算Multiple Scattering时,认为高阶散射非常低频,从此出发使用了一些简化,包括:
- 大于等于2阶的散射,将Rayleigh散射和Mie散射视为为各向同性的散射
- 计算某点的大于2阶的Scattering时,认为该点周围任意一点的Illuminance与其相同
第1点的意思是,对于1阶散射,我们认为Rayleigh、Mie散射是正常的,但是在之后的阶数,我们认为相位函数对于任何方向都是相同的值
第2点,原文的描述有点神秘,写的是“Neighbouring points”,在问了作者之后其实意思是在计算某点的Scattering时,空间中任意一点的某一阶的Scattering都视作和该点相同。因为参与计算的点需要与该点有Line of sight,从整个地球的视角来看确实是Neighboring points。这一条近似看上去是非常粗略的,但是it works。
实际流程中,Multiple Scattering LUT的计算可以一个pass输出到一张32*32的2D LUT完成。只需要2D的原因是因为上述简化中我们使用了各向同性Phase Function,不需要存储相机跟光照方向的关系,只需要存储不同高度、不同光照角度的情况即可。
在pass中,论文中提到,首先计算
其中
转移函数
第二步,在上述计算完毕后,计算当前像素的Transfer Function
使用
这里可能会对
根据简化1、2,我们认为在计算当前像素时,
即
细心的读者可以发现,相比(2)式,(7)式中省去了地面反射。这里我咨询了作者,作者表示确实是忽略了多重散射照射到地面形成反射的贡献。不过保留的话也没办法化简出这么一个
多重散射结果
有了
根据数学公式,因为
最后,就可以将
![52aa2d3fc71b311c5b753ffe083ff288.png](https://i-blog.csdnimg.cn/blog_migrate/b8a5b928af2421b5b1182a4b6afd4f0e.jpeg)
论文中提到,在PC或者iPhone6s上,该LUT的计算都是在64个方向上做Raymarch,每个方向20个step得到,乍一看很多,但是LUT大小只有32*32,问题不大,论文后面给出的测试iPhone6s上也只消耗0.12ms。
最终计算
根据上文,我们可以得到任意点的所有阶数的Scattering之和,包括一阶Scattering和高阶Scattering之和(
在计算某个方向上的Luminance时,一路积分过去就行。这也是上文中Sky-View LUT中每个像素的Luminance计算方法。
![915a55ba72f5251bc2586db6e32f14ae.png](https://i-blog.csdnimg.cn/blog_migrate/1b8a04e0b8a0d0714986520d87a7c76d.jpeg)
![5a98a182297b1e1e269d84366c24f598.png](https://i-blog.csdnimg.cn/blog_migrate/5451c5447da7f9d542294a63bbade1de.jpeg)
总结
这篇论文中,作者通过做出一些对结果影响不大的简化假设,大幅度地降低了Multiple Scattering计算的消耗,并且使用数值方法计算无穷阶Scattering之和变为可能。这毫无疑问是需要非常多的实践经验、数学直觉和大胆尝试才能做到的,让人不得不佩服。
此外在工程方面,这篇文章也是尽可能压缩了各个LUT的大小以得到最好的效率,包括200*100的Sky-View LUT、32*32的Multiple Scattering LUT等等。尽管最后写成论文的就是表中的几个数字,但是这些数据毫无疑问是经过了大量试验得到的。最终在iPhone6s上能跑到1ms以内更是amazing。并且不需要任何的3D贴图,就在VS-PS里直接算就完事儿了。
本文只是挑选了我认为最具亮点的一部分来讲。论文原文中还有许多细节内容,如根据高度自动切换Raymarch方法和Sky-View LUT等,建议有兴趣的读者详读一遍。