Probe-Based Global Illumination

发布时间 2023-11-22 14:28:11作者: UWATech

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


一、全局光照的流派

在实时渲染中,全局光照(Global Illumination,以下简称GI)始终是一个难题,GI可以拆分成高光和漫反射,高光反射暂时不在本篇的讨论范围,因此我们会主要讨论各向同性的漫反射的GI。纵观近些年的分享,大方向无非是从离线烘焙到实时更新,而在这个大方向中又有两个流派:统一流派和拆分流派。

统一化流派的主旨在于:来上一发,所有光照相关的计算统统搞定,比较经典的就是光子映射和路径追踪,以及它们的离线烘焙阉割版——光照贴图,光照贴图牺牲了实时性以及空间性,让动态的物体和粒子、云雾等体积渲染无法收益,以换取更高的性能。不可拆分流程最大的好处就是物理正确性,基本上是来一发就解决所有问题,中间不需要美术人员进行任何额外处理,仅通过外部调参就能达到很好的效果;而可拆分流派就显得麻烦许多,所谓可拆分,“拆分”的是什么呢?从统计学意义上讲,我们可以将光照的分布区分为高中低频。譬如阳光打到地面是亮黄色的,而天空是蓝色的,那么整个场景“打眼一看”就是亮处暖色而暗处冷色,剩下的高频则表现为局部的AO、纹理等。使用多种技术方案解决问题,这就是拆分流的策略。

拆分流可以认为是对工程和硬件性能的妥协。因为没有离线渲染那样宽松的时间限制,所以不得不接受一些效果上的牺牲。本文主要讨论的基于探针的GI方案就是一个面向低频的工程解。

二、古老的GI:Unity Light Probe

Light Probe是最古老也是最耳熟能详的Probe方案。其允许用户在场景中自定义摆放,并通过四面体插值的方法取周围4个Probe的结果的加权平均,而Probe的结果是直接静态烘焙的(理论上完全支持动态烘焙,只是Unity没有做而已),这是一个传统且可行的集静态与动态于一身的廉价GI方案。

 

这种方法的弊端也很明显,生成四面体运算量较高,对实时不友好,以及查找四面体对GPU运算不友好。第一点很明显,毕竟如果Probe要随意摆放,那么就需要进行空间搜索去生成四面体,这个工作在运行时基本不可能做到。同时,由于四面体的分布并不是均匀的,所以希望GPU以O(1)的复杂度且无分支的查找,也几乎并不可能,所以在Unity中是依靠CPU把查找结果计算出并传递的,这样做使得本就性能不足的CPU雪上加霜,并且需要随机查找的体积渲染类的GI就没有办法得到解决,这是另一个硬伤。

关于如何解决Light Probe的问题,在本文的后半部分再展开讨论,我们先着重讨论一下Light Probe使用的球谐函数的色彩储存方法。

三、Spherical Harmonic Lighting

球谐光照(Spherical Harmonic Lighting)以下简称SH,这并不是个新鲜词汇,其实现方案在知乎上也有大量的讲解,我们这里着实没必要再展开讨论。

但就算不知道它的原理,只需要知道有了这么一个“给它向量就能返回均匀且低频的值”的好东西以后,我们可以做很多好玩的事情了。想要计算一个点的正确的Diffuse GI,基本上需要收集这个点的Albedo、Normal、Position和Emission等信息,计算出点的光照结果后,以Probe到这个点的向量作为半球的法线进行蒙特卡洛半球积分,和传统路径追踪的累计方法完全一样,只不过在运算上我们只有一个采样点的结果,所以就需要用每个射线的PDF作为权重,与采样结果相乘累计到球谐上,这种方法是否是无偏的?当然不是,但是SH本身就偏的很离谱,所以我们并不是很在乎,只要结果大方向对了就行了。把这个采样过程在整个球面上均匀地分布并执行多次,最后得到的结果就是球谐的结果了。

而现在的情况是,纯烘焙的Light Probe功能太凄惨,而纯实时的又需要依赖实时光线追踪,性能开销十分昂贵且对硬件要求高,那么这个过程是否可以进行进一步细分以取得一个折中的结果呢?答案显然是可以的。做过路径追踪渲染器的朋友都知道,一般在采样率不高的情况下会有相当多的噪点,噪点的出现来自采样不足导致的求积分的结果与正确结果有偏差,而SH也是会存在这样的问题的。当场景复杂时,噪点往往很多,而当场景非常简单时,可能噪点就很少以至于根本没有噪点(比如在纯白色天光下渲染一个球),毕竟积分的误差当然是由需要被积分的函数的复杂程度来决定,天下乌鸦一般黑,所以这个结论对SH同样适用。那么再回到SH上,我们就需要认识到,哪些信息是高频的,哪些又是低频的,是否能够提前烘焙高频信息,并把低频信息放到运行时,以实现一个半实时半静态的GI呢?这就是Precomputed Radiance Transfer。

四、Precomputed Radiance Transfer

Precomputed Radiance Transfer,以下简称PRT,这种方法的策略就是在离线环境从每个Probe发射大量的射线,以预计算整个场景的Albedo Emission等色彩信息,同时再发出少量射线,将碰撞到的采样点的位置和法线储存下来,在运行时计算这些采样点的受光情况(天光、太阳光、局部光等),将这些光照信息乘到提前烘焙好的Albedo,并叠加Emission,最后得到一个看起来像那么回事的Plausible的结果。这种方法就是拆分了色彩部分的计算和灯光可见度的计算,在静态场景下支持动态的灯光渲染,是一种半实时的方案。

这套方案有相当大的优化潜力。The Division在此之上又做了新的优化,通过收集一块区域内的采样点,并取这些采样点的平均值,计算出一个经过平均后的点作为一个共享的采样点:

 

 

 

 

 

 

这种直接用两层混合的操作,在以牺牲正确性为成本的同时,极大减少了需要储存和计算的采样点,对于性能十分有限的设备可以说是一个不错的策略。

五、Dynamic Diffuse Global Illumination

如果要求把PRT的静态部分也一并改成动态,实现完全动态的GI是否可以呢?NVIDIA推荐的DDGI方法就是利用硬件的求交加速,把上方PRT的烘焙过程简化到了实时,使得工程维护难度大幅度降低的同时,也支持了动态的场景和材质,而非仅仅支持光照,这是十分有诱惑力的。只是,目前的RTX效率和功能并不完善,甚至DirectX RayTracing这种软件接口和硬件之间还时常产生矛盾,本人在开发VEngine的RayTracing模块时就遇到了RTX显卡有自己的想法并拒绝了API的调用的情况,并且目前仍未修复,只能暂时绕过。这些缺陷只能等待硬件厂商慢慢迭代并配合微软等软件厂家制定一套跨硬件的通用接口,而对我们这种个人或小团体开发者来说,显然希望官方提供一个SDK直接接入项目,这也是不可行的方案,应该也是DDGI这类依赖硬件进步的功能目前最大的限制,一句话概括就是:未来可期。


DDGI

 

六、Volume Based Probe

既然说到了“支持动态场景”,那么一套对GPU友好的,支持视锥体积内任意坐标采样的(粒子、体积渲染等),不需要美术人员手动摆放的Probe系统就是必需的了。作为可微函数,球谐是可以直接被当做像普通的颜色一样扔到3D贴图中并使用QuadLinear插值采样的,那么在每个体素的中心放置一个采样点,就是一个十分简单粗暴的方法。这种方法还是有两个问题,第一个就是漏光会十分严重。譬如我们现在假设一片空间内放置了4x4x4个采样点:

 

很不幸的是,有一面墙正好覆盖在了Probe上,或者穿插在了两个格子之间:


发生严重漏光

 

在彩虹老师的指导下,我们学习到可以使用SDF解决这个问题,利用SDF求导计算某点的向“外”的向量,也就是法线,可以将Probe“推”出去,使其远离几何体,关于SDF求法线的相关文档:
RTXTime:Signed Distance Field(基础篇)

 


经过偏移后的Probe

 

由于采样时也是会发生插值产生的漏光,所以我们需要在采样时也来一发SDF采样,把采样点也挤出去,这就能解决大部分的漏光问题了。

同时,所有的Probe必须按规律摆放,所以即使在周围光照环境变化并不大或结构并不复杂的情况下也必须使用固定的计算密度,但这样在开放的场景中将会有巨大的浪费。一个较为可行的方案是把Bilateral Filter的思想用上,对那些不是特别重要的Probe降低采样率的同时使其收集混合周围的采样点,减少采样的不均匀。

七、GI Framework in Vector Engine

VEngine提供了一套强兼容性的GI方案,旨在使一套方案能够在不同项目和不同需求下工作。我们在几乎不需要修改源码的情况下,可以切换多种GI方案(Cascade Shadow Map,AO off,SSR off,Volumetric-Light on):


PRT in VEngine

 

 


DDGI in VEngine(DirectX RayTracing)

 

在采样上,我们实现了一套3D版Virtual Texture,通过将体素向外扩展一圈,实现开放场景的无缝采样;由于GI的运算开销往往较大,几乎不可能在一帧内计算完成,因此我们可以先开一张临时的图,再计算完毕后的数帧,将结果输送回Virtual 3D Texture,由于游戏中的光照一般不会有太大的变化,所以这种策略是完全可行的。

在SH Probe的生成方面,我们也是采用了开放式的设计,允许直接从硬盘或其他部分加载Albedo、Emission等Geometry信息,也允许使用实时的DirectX RayTracing封装计算GI,能够做到实时离线一体化工作。同时,RayTracing部分结合了引擎自带的Bindless Resource设计,在Mesh、Texture等资源时只需要一个ID,极大优化了求交效率,解决了目前商业引擎由于架构限制解决不了的问题。

在示例图中,故意采用了很薄的长方体模拟墙体(0.1m),完全没有表现出漏光,这归功于SDF在采样和生成时的使用。

八、Future

我们这套方案依然有很大的进步空间,首先,实时计算GI对于目前绝大多数中低端计算机来说依然是天方夜谭,同时如果是一个有动态场景的网游,可能就要求每个玩家都要在自己的计算机上重新计算一遍整个GI,那么将这个对延迟要求并不苛刻的繁重工作放到服务端就是一个非常可行的方案,VEngine在设计上也考虑到了这一点,把数据流的加载抽象出来,配合网络库就可以实现联机下载。

同时,上方也提到了,将SH均匀铺盖到体素空间是非常浪费的,而SH在相当多的情况下变化并不大,那么用神经网络作为数据存储的媒介,把SH信息训练到网络中,是一个可行度高的方案,只是目前VEngine目前还没有提供对于NN方面的支持,这些工作和研究会在日后慢慢展开。


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

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

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