UE5 Lumen实时全局光照系统简单分析

前言

  做这个调研分析的时间应该在去年(2022)十二月初的几天时间,主要是通过UE官方的直播讲解和在SIGGRAPH 2021(Radiance Caching for Real-Time Global Illumination)及2022(Lumen: Real-time Global Illumination in Unreal Engine 5)的两次分享进行学习了解。没有看具体源码,所以文章内容全部为个人理解,同时Lumen经过后续更新,似乎一些方法也已经被更换。

什么是Lumen

  Lumen是UE5的实时全局光照(Realtime Global Illumination)解决方案,核心为一套实时的软光线追踪系统,但同时也支持硬件光追,本文的关注点也在其软光追部分。


  简单来说Lumen实现实时GI的主要方式是就是在屏幕空间全局光照(SSGI)的基础上使用有向距离场(SDF)加速光线与场景求交,并通过Surface Cache存储材质光照信息,即基本过程如下:

  1. 使用SSGI进行光线追踪,得到一部分像素的全局光照信息
  2. 对于SSGI未命中的像素,通过使用屏幕空间探针插值来获得全局光照信息(屏幕空间探针发射多条射线对场景进行追踪)。这里也使用了稀疏的世界空间探针处理远光以降低噪点
  3. 光线追踪部分使用了场景的距离场进行求交,并通过Surface Cache获得光照信息(Surface Cache在运行时会有选择的进行更新)

SSGI

  屏幕空间全局光照的做法就是利用屏幕空间的深度、法线等信息进行光线追踪,Lumen中使用了层次Z缓冲(Hierarchical Z Buffer)的结构来加速求交。当然,SSGI最大的问题自然是无法获得屏幕外的信息(就像SSR实现的水面,低下头倒影也跟着屏幕上方被裁切掉了)。

屏幕空间探针

  只是SSGI并不能满足全局光照的需求,于是就还需要在场景中进行光线追踪。传统的方式是对屏幕上的每一个像素发射一条或多条射线与场景求交,而Lumen认为这种方式噪声相对较大,于是采用了在屏幕空间的物体表面每隔一定距离放置探针的方式,并在探针处追踪较多的光线,并对采样的Radiance进行缓存。探针先是稀疏均匀放置的,但如果某一像素不能使用周围探针进行插值,则会在该像素位置放置探针(如下图所示)。


最后探针会进行一个3x3的卷积降噪,并最终将光照结果插值到像素上。同时这里也使用了BRDF以及上一帧的光照信息进行重要性采样,从而提高了采样的效果。

世界空间探针

  只是屏幕空间探针仍然不能很好的解决远光问题,因此lumen引入了世界空间探针,在世界空间更稀疏的放置探针,但采样了更多方向的光。此时lumen将屏幕空间探针追踪距离设置为一个较短范围内,更远的距离使用世界空间探针,但为了防止漏光,世界空间探针追踪的方向可能和屏幕空间探针的方向不同,虽然这也引入了一定的偏差。

Mesh SDF与Surface Cache

  前边提到了需要在场景中进行光线追踪,Lumen采用有向距离场SDF得到空间一个点到物体表面的最近距离(当在物体内部时,距离为负),使用RayMarching快速获得光线与场景的交点。因为通常模型都是三角面表示的,因此模型对应的距离场需要额外计算,而且表示精度会有一定损失。Lumen为每个静态网格单独生成Mesh SDF,在模型导入的时候被生成。

Mesh SDF,可以看到场景中的物体表示是分离开的  

  SDF只提供了交点和法线,不能提供光线交点的材质光照等信息,同时也需要对一些昂贵的计算进行缓存,于是Lumen采用了Surface Cache来存储物体的材质光照信息。简单来说,一个网格体被多个轴对齐的Mesh Card捕捉并存储。


Surface Cache在运行时会不断更新,但并不是每帧都全部更新,而是根据与上次使用时间、上次更新时间相关的优先级选择其中的一部分。对于Surface Cache直接光照的计算会复用shadow maps,不能解决的部分向光源追踪光线判断是否在阴影中。间接光照则类似于上述讲到的屏幕空间探针,Lumen会以一定密度在表面放置探针并追踪光线,将结果插值至周围纹素。

Global SDF与Voxel Lighting

  上述内容理论上其实已经能够跑通了,但场景中通常存在大量的网格体,遍历所有Mesh SDF进行RayMarching代价过于昂贵,于是Lumen又为整个场景生成了一个Global SDF(动态物体与静态物体分别缓存,在运行时仅更新变化的部分)。

Global SDF,精度更低但求交更快  

相比于Mesh SDF, Global SDF精度要更低,Lumen选择在近距离追踪时采用Mesh SDF(在场景单元格内维护了从该单元格出发求交,所需求交的物体,因此只会求交一部分物体),在远距离追踪时采用Global SDF。但,这里就又出现了一个问题…Surface Cache是基于物体的,当光线与Global SDF求交时,找到了交点却不知道这是哪个网格体被击中了,无法对物体surface cache进行采样。因此Lumen又使用了Voxel Lighting,将相机周围体素化并将Surface Cache中的光照信息存储在里面,这样在与GDF求得交点后就可以对应至Voxel获得光照信息。

总结

  所以Lumen的核心思路就是在SSGI的基础上对SDF的表达的场景进行更快的光线追踪,同时选择性的更新场景中的光照信息以降低压力,并使用屏幕探针和世界探针来进一步降低噪声。其中使用了Mesh Card捕捉物体表面并存储在Surface Cache中,Surface Cache用来提供SDF无法提供的物体表面材质、光照信息,体素化场景则是用来在更大场景中提供光照信息。

(再次说明:这是经过个人理解的旧的Lumen方案,其中可能存在错误,参考见前言部分)