移动端全局光照演变的思考与实践

本文出自腾讯游戏开发精粹三 ”移动端全局光照演变的思考与实践“。

20.1 引言

        伴随着现代化图形技术的发展演讲,现实场景与虚拟游戏世界间的界限变得越来越模糊。开发者致力于打造不断接近真实感的宏大的游戏场景,而对于如何让身处其中的玩家更为身临其境,全局光照(Global Illumination,简称 GI)技术则是贯穿视觉体验的关键一环。

20.1 什么是全局光照

        同时光照是指同时考虑来自光源的直接光照,以及经由其他物体的反射或折射后的间接光照,从而大幅提升画面的真实感的一种高级渲染技术。

20.2 静态光照烘培

        在全局光照的实现中,静态全局光照是一种比较传统和常用的方案,主要应用于静态场景和静态光照的情境下,一般会结合光照贴图和光照探针来实现。

20.2.1 光照贴图

光照贴图(LIght map) 是一种纹理贴图,他存储了场景中物体表面的全局光照信息。光照贴图的一个样例应用于场景如图20.2所示。

        如图20.3所示,场景中的一个立方体通过2UV展开后将不同区域收到的光照存储在光照贴图中,运行时通过光栅化后的光照贴图2UV坐标在光照贴图中查找对应的光照数据进行光照的重建。

光照贴图的生成的过程如图20.4 所示 。

20.2.2 光照探针

        光照探针是指摆放在场景的自由空间中一系列的探针(如图20.5所示)它收集了来自各个方向的光照数据,动态物体(当然也包括静态物体)。通过查询器附近的探针,并根据距离权重进行插值就可以计算出动态物体自身的关照数据。光照探针的数据存储远远小于光照贴图的,但是光照探针对漏光比较敏感,需要通过采样偏移或者可见性检测等方式减少漏光。

20.2.3 静态光照烘培的局限性与优势

静态光照烘培由如下的局限性。

  • 烘培时间过长。光照贴图由于要离线计算很多数据,尤其是场景巨大和物件超多时,烘培效率会急剧下降。以开放世界的游戏距离,烘培几公里的地图可能需要十几小时。而离线烘培是要先对每个物体进行UV展开,并且还要存储光照贴图的数据。当物件越来越多时,数据量呈线性增长,因此难以支持起超大场景的渲染,如图 20.6所示。

  • 无法支持动态光源,场景和光照都是不能变化的。

        虽然静态光照贴图有些使用上的局限,但是凭借低廉的运行时开销,目前时移动端的使用最广泛的方法,基本可以覆盖所有的设备。

20.3 基于预计算传输的全局光照

        在静态烘培的基础上,结合解耦光照传输和光照重建的过程,衍生出一种半动态GI的方案,也就是预计算传输的全局光照,简称为PRT GI。 PRT GI 可以做到在游戏过程中动态的修改光照的效果,比如实现动态的昼夜变换的效果。

20.3.1 如何让光照动起来

烘培多套光照贴图是一种办法,但是给本就不大的存储空间和超长的烘培时间带来更大的压力,并且还无法实现任意时间端的动态光照效果。本节将介绍一种基于预计算的半动态光照技术,可以实现游戏中昼夜变换的光源效果,如图20.7所示。

20.3.2 预计算的辐射传输

        预计算辐射传输是一种将光照的传输的过程和最终着色点的光照计算解耦的方法。在离线节点计算光照在物体表面发生弹射和光源的可见性信息,然后在运行时将可见性信息和光照本身的数据相乘,最后得到着色点的光照数据,如图20.8所示。

基于预计算的光照可以分为离线预计算和运行时光照重建两个过程,如图 20.9 所示。离线预计算与静态烘培的方法一致,只是烘培的是预计算的传输系数而不是最终的光照数据。运行时光照重建与光照贴图的使用略有不同,它不是简单地根据 2UV 进行纹理采样直接获取光照数据,而是先将实时变化的光照和预计算的系数相乘得到最终的光照数据,然后再应用到物体光照计算的着色过程中。

20.3.3 数据存储的优化

        对于预计算的光照数据存储,由于要考虑运行时来自各个方向的光源,所以此时我们需要一些基函数(可以采用不同的基函数形式,如图 20.10 所示)来拟合这些方向上的数据。

此外,还可以使用多种方式存储预计算的可见性数据。比如,以光照贴图形式存储 transfer vector(传输向量), 以probe(探针)形式存储tansfer matrix (传输矩阵)等。图 20.11 所示的是不同形式的数据的存储占用情况,将预计算数据存储到光照探针(ILC 是 UE 中一种稀疏的光照探针的组织结构,VLM 的全称是 volumetric lightmap)中是数据量最少的。

但是光照探针在效果上会出现类似细节表达不全,漏光等问题,因此,如果想要达到存储和效果的平衡,可以考虑同时在场景中使用2中模式的混合存储(少量的光照贴图形式的预计算存储配合光照探针形式的预计算的存储)。

20.3.4 基于预计算的半动态GI的局限性

基于预计算传输的方法的局限性如下:

  • 烘培时间过长。人力和资源成本仍然较高。基于预计算辐射传输实现的GI本质上还是拖离不了预计算,因此烘焙的时间还是会很长,需要很多硬件资源和美术人员的等待时间。

  • 功能有限,不支持动态变化的场景需求。场景或者材质的变化都会导致光照的传输过程发生变化,因此这种方法无法处理动态变化的场景需求,尤其是对于有 UGC 需求的游戏;无法支持自发光(Emissive)和聚光灯等复杂光源的位置和角度变化。

20.4 动态全局光照SmartGI

为了更进一步实现动态全局光照效果,不限于PRT GI的动态光照变化,还能实现动态的场景物体变化的效果,比如 UGC 玩法,我们还需要一种完全动态 GI 方案,本节会重点介绍动态 GI 的实现方案。

20.4.1 移动端全动态 GI 方案的挑战

由于移动端硬件能力的局限性,在移动端实现全动态 GI 方案的难度远高于在PC端和主机端;另外,在PC端和主机端虽然也实现了很多动态 GI, 但是仍然存在各自在效果,能力和性能方面的短板。

20.4.2 已有全动态 GI 方案的分析

如图 20.12所示,我们对部分全动态 GI 方案 进行了罗列。

  • Voxel GI (VXGI)是一种将场景体素化,然后利用体素信息做光照缓存的方案。体素的精度是其比较大的问题,精度不够就不好表现精细的光照效果,精度太高会面临存储和性能的压力。

  • SSGI 是一种基于屏幕空间的GI算法,在计算每个光照的间接光照信息前会计算像素的直接光照的结果,然后通过屏幕空间的 ray marching(射线步进)技术收集着色点的间接光照信息,从而实现全局光照的效果。它的局限是,无法获取屏幕外的物体的反射信息,这会给准确性造成一些影响。

  • RSM 相对于 SSGI 来讲可以弥补离屏数据缺失导致的效果问题,他的实现原理类似于我们计算的阴影贴图,只是不单纯地记录深度信息,还要记录 反照率(albedo),法线(normal), 和直接光照信息,给这些名字起了名字叫做 reflective shadow map(反射阴影贴图),简称RSM。有了这些信息就可以给其他物体提供二次反弹的光照计算的数据了。但是RSM最大的局限是,其处理的光源类型非常有限(平行光),很难对应其他复杂类型的光源。

  • Surfel GI 将场景进行离散图元化,然后利用这些离散的图元进行光照缓存。目前这种方案的实现主要基于相机看到的屏幕空间生成的图元,这种方式在相机没有看到的地方就无法生成有效的图元信息,从而造成效果上的错误。但是可以通过预生成的方式生成图元,不过又会引发数据存储的问题和离线烘培带来的效率的问题。

  • DDGI(Dynamic Diffuse Global Illumination), 他是 RTXGI 的核心算法,整体实现时通过在场景中生成动态探针,然后利用光线追踪进行探针的位置调整以达到比较准确的光照效果。这种方案首先需要硬件光线追踪的支持(目前也有一些利用屏幕空间生成的算法,但是局限性和Surfel GI 类似),其次从名称中可以看到,其光照特性主要是处理漫反射产生的全局光照效果。

  • ReSTIR GI 是一种利用 ReSTIR 算法做路径跟踪采样的优化的 GI 技术,实现的光照效果非常准确,但是严格的硬件能力要求使其无法在大部分设备上运行。

  • Lumen 是 UE5 中一种使用 meshcard 做光照缓存和使用距离场做射线检测的 GI 算法,在性能和效果上都有不错的表现,但是由于要满足 meshcard 生成策略, 所以需要有严格的场景资产需求;另外,由于需要在 meshcard 中存储光照数据,所以内存和带宽的开销使其无法在移动端高效运行。

20.4.3 使用混合架构实现全动态 GI 的基本框架

SmartGI 在对以往实行抽象的和总结后,将GI框架设计成光照缓存(Surface Cache)和光照数据采集(Final Gather)两部分(如图20.13所示)

  • 光照缓存:用简单的低成本的形式表达场景的结构,并且用起来缓存光照数据。

  • 光照数据采集: 将光照数据采集过程抽象出来,可以支持多种软硬件光线追踪方案,可以解决性能和兼容性问题。

20.4.4 使用屏幕空间数据做光照缓存

  • 屏幕空间缓存

用上一帧渲染的场景数据作为当前帧的光照缓存,使用高精度的贴图保存精细的光照缓存,可以为GI提供更精细的反弹效果,多次采样屏幕空间数据的实例如图20.14所示。

  • 利用HIZ快速进行相交检测

射线的相交检测的伪代码如图20.15所示。

这里的实现与利用HIZ实现遮挡提出方案有所不同,我们对 depth mipmap(深度分级纹理) 的生成不但支持最近策略,用户可以进行设置。这里要简要介绍一下使用最近策略去做ray marching(射线步进)的过程,射线的起点和终点如图20.16所示。

下面是迭代过程的演示,如图 20.17 所示。

20.4.5 使用体素化数据做光照缓存

使用体素化数据做光照缓存需要考虑以下几个方面的问题。

1. 体素缓存

场景的体素化的主要由2个过程构成: 一是利用光栅化的方法实现场景的体素化,二是对体素化的数据进行直接光照和间接光照的注入过程。

使用光栅化实现体素化时要将相机沿着XYZ三个轴向做三次光栅化渲染,并且要关掉遮挡剔除,背面剔除等属性,并且在体素化的像素着色器中将物体的反照率,法线,自发光存储在三维纹理中,如图20.18所示。

2. 体素化数据的存储

如果将体素化数据用一张简单的三维纹理来存储的话,显存占用会非常大,因此我们使用 VXGI 2.0 的多级 Clipmap(剪贴图)方法(如图 20.19 所示),距离相机越近,体素的半径越小,距离相机越远,体素的半径越大。这样会实现近处精度较高,远处精度较低的效果。

3. 利用 Clipmap 快速进行相交检测

Cone Tracing(圆锥追踪) 是一种对光线采集的概括方法,使用圆锥代替单根射线,把半球空间概括为几个圆锥去近似收集间接光照。其还可以和体素有很好的结合,根据传播距离和圆锥的夹角快速计算出需要采样的面积,从而快速选择 mip 级别和 trace step(追踪步长)。如图 20.20 所示,Co 是射线起点,Cd 是射线方向,θ 是圆锥追踪的角度,t 是当前总体步进距离,d 是当前单个步长的步进距离。可以看到随着步数的增加,t 在不断增加,d 也在不断增加,整体的迭代越来越快。这里我们可以根据 d 的大小选择在哪一级的 Clipmap 中进行采样。

20.4.6 使用离散图元做光照缓存

使用离散的图元做光照缓存需要考虑一下几个方面的问题。

1. 图元缓存

图元(Surfel)本质上是一种表面缓存(Surface Cache)的存储结构,单个图元的样式如图20.21所示。他将整个场景表面离散化,如图20.22所示,将中心点的表面信息近似扩散为整个图元的信息。一个基本图元的数据至少应包括 : 中心点的坐标,半径,法线,反照率等。为了满足各种特性的开发需要,也可以自行添加例如 primID, instanceID,BRDF等相关参数,但基础的存储结构不宜过大。

2. 基于屏幕空间的图元生成

在图元生成时,将屏幕空间中每 16像素 x 16像素分为一块,每个块记录其256个像素中具有最小图元覆盖率的那个像素的位置和覆盖率(如该像素有 3个图元覆盖,那么覆盖率是 3)。在每次生成过程中遍历这些块记录的最小覆盖率,若小于人为给定的覆盖率阈值,则以该块的最小覆盖率的像素为中心生成新的图元(如图 20.23 所示)。基于该点的屏幕空间位置,从GBuffer中获取反照率、法线等信息,并结合深度信息重建该点的世界空间坐标,同时更新该块的最小覆盖率。若每个块的覆盖率都足够大,则停止生成。确保屏幕空间中每个像素都被覆盖,且不会生成过多的图元。

3. 图元数据的存储与查询

我们采用类似空间哈希的方案存储图元,将空间划分为三维网格,在网格中记录内部的图元索引,通过索引查找缓冲区内的数据。对于空间网格划分而言,由于图元是依附于物体表面的,有很多网格是空的,因此可以增加一个网格压缩的通道,在缓冲区中舍弃内部没有信息的网格,以减小带宽占用。图 20.24 所示为图元分布的热度图(红、绿、蓝、黄的热度依次递减)

20.4.7 多光照缓存的收集

光照数据收集的流程如图 20.25 所示。
(1)生成屏幕空间探针。
(2)为每个探针根据重要性采样生成光线数据。
(3)使用屏幕空间算法进行射线探测和光照采集。
(4)重新整理射线数据,将未命中的光线重新打包压缩到一起。
(5)使用 SDF 或者光线查询查找命中的图元,并且做光照的采集工作。
(6)重复步骤(4)。
(7)使用 SDF、光线查询或圆锥追踪查找射线命中的体素,并且采集光照
数据。
(8)对屏幕空间探针做时空滤波。
(9)对屏幕上的像素进行光照插值,分别生成 diffuse(漫反射)的间接光照
和 glossy(高光)的间接光照。

(10)使用 denoiser(降噪器)对光照数据进行去噪。
(11)将漫反射和高光的间接光照数据应用到最终场景。

20.4.8 全动态 GI 的性能优化

我们在移动端动态GI的过程中使用了多种优化方法和策略,如图20.26所示,主要分为两大类:一是算法层面的优化,二是硬件指令和带宽方面的优化。

下面重点介绍下核心的优化方法。

  • 光照缓存的分帧,局部更新与滚动更新。游戏的场景不是全视野的全量变化,经常是某些区域或者物体在变化,这时候不需要每帧都去重新生成场景的光照缓存数据,而是局部更新变化的区域就可以了。还有一种情况是,静态地移动可能会导致需要更新光照缓存的数据,但是镜头移动通常也是只有部分区域需要更新数据(刚刚远离我们的数据和刚刚进入缓存范围的数据),如图20.27所示。此时也可以只更新变化的数据。

  • 减少为光照缓存做光照注入时的射线求交的计算。射线求交是一个非常耗时的操作,因此需要想办法减少射线求交的计算。因此需要想办法减少射线的求交的计算,在为每个光照缓存做光照注入(例如对体素注入直接光照,如图20.28所示)时通常需要计算光照缓存与光照的可见性,利用光线追踪是一种办法,但是耗时。不过在直接光照的情况下计算有一种办法,就是利用阴影贴图去做光照的可见性计算。因此在做光照缓存的光照计算时,也可以使用这种办法,从而避免或者减少射线的求交。

  • 减少光照数据采集时射线求交的计算,使单个像素的平均射线数量小于1。我们再收集光照时,不是每个像素每帧都会发射一条或者多条射线,而是利用棋盘算法(如图20.29所示),让一个Tile(瓦片)发射一组射线,而一个Tile的大小是 16像素 x 16像素,每个 Tile 每帧发射64条射线,这样平均到每个像素是 1/4 条射线。

  • 合理高效地利用每条射线,融合多种重要性采样。首次通过物体表面的BRDF再法线附近生成更多的射线信息,再后面的射线生成时,会根据历史上哪些射线方向获取了有效光照的信息,从而在这些方向上后续发射更多的射线去收集光照。为了进一步提高射线样本的质量,使用多重重要性采样融合基于材质的采样和基于光照的采样,让样本更符合light transport分布,如图20.30所示。

  • 利用 SDF (Signed Distance Field, 符号距离场)加速软件射线求交的过程。距离场表示空间中每个点到其最近表面的距离,如图 20.31 所示。 而当我们有了空间中的每个点的SDF后,就可以每次步进 d=SDF距离,从而快速步进,如图 20.32 所示,直至打到交点,这就是使用SDF进行软件光线追踪的基本原理。使用SDF之后,对于空旷的场景,射线步进的性能比基于Clipmap的圆锥追踪的性能高很多。

  • 针对移动端优化的轻量级降噪管线。传统的 SVGF (Spatio-Temporal Variance-Guided Filter)对图像信号的降噪主要通过空间滤波和时序滤波进行。在空间上,SVGF 采用了联合双边滤波(Joint Bilateral Filtering),将相邻像素加权混合到目标像素;在时序上,当前帧的目标像素通过传统的运动矢量找到历史帧对应的屏幕位置,从而可以采样该位置对应的4个历史像素(因为这个屏幕位置往往不是正好在像素中心),并加权混合到当前帧目标像素。SVGF 的核心思想是,空间上或时序上相邻的像素在光照分布上是极度相似的,因此目标像素可以通过混合空间上或时序上相邻的像素来达到复用样本减缓噪声的效果,并利用无噪声的 GBuffer 作为复用的指导,尽量避免复用到光照分布差异过大的像素样本。这些经典降噪算法应用在 PC 端是行之有效的,而应用在移动端是非常困难的,主要瓶颈是移动端带宽。为此,我们舍弃了图像信号的空间滤波,同时在时序滤波上并没有像传统 SVGF 算法那样直接利用完整的 GBuffer 进行时序复用的指导,因为读取当前帧和历史帧的各种 GBuffer 信息(包含但不限于法线、深度、粗糙度)会导致过多的带宽开销。取而代之,我们采用了轻量级的降噪管线,仅使用少量 GBuffer 信息(当前帧的 ID、深度、光照变化率和历史帧的 ID、深度、历史累积帧数)作为时序复用的指导,如图 20.33 所示。

在该轻量级降噪管线中,我们还采用了以下优化手段:

1. 使用线性模式采样 1 次纹理,而非使用最近邻模式采样 4 次纹理。对线性采样后的历史像素信息(而非对 4 个历史像素)进行时序复用,可以进一步减少采样纹理的次数,但会牺牲小部分降噪质量,适用于低端机型。
2. 通过历史累积帧数来决定历史像素的混合系数。过低的历史像素的混合系数会导致噪声,而过高的历史像素的混合系数会导致延迟。而我们根据历史像素的历史累积帧数来决定 4 个历史像素的混合系数,这样停留在屏幕上越久的历史像素的混合系数就可以更高,从而避免噪声;刚进屏幕不久
的历史像素的混合系数就可以更低,避免引入延迟。
3. 基于物体 ID 的检测方法。时序滤波往往会产生鬼影(Ghosting)现象,常规解决方法是方差钳制(Variance Clamping),但是这个方法往往需要读取目标像素附近至少 3 像素×3 像素的颜色值来获取方差值,会增加相当多的带宽开销。为此,我们使用了基于物体 ID 的检测方法,通过检测目标像素 ID 与历史像素 ID 是否相同,来决定是否混合该历史像素。并且我们所使用的 ID 信息只需要做比较操作,因此可以采用哈希的方式将 32 位完整 ID 映射至 8 位 ID,以减少读取 ID 信息的带宽开销。
4. 考虑使用多种不同的运动矢量进行搭配。针对不同路径类型的信号,可以设计额外的运动矢量。并通过一定方式分别评估多个运动矢量的置信度,再以置信度为指导进行多个运动矢量对应历史样本的混合,从而提高特定路径类型信号的降噪质量,但这也会引入一定的额外性能开销,可以根据
效果与性能的平衡来决定是否启用多种运动矢量。
5. 使用移动端性能优化工具持续优化指令和带宽开销。如图 20.34 所示,通过对比 Shader ALU Capacity Utilized 和 Time ALUs Working 两个参数,可以找到 divergence(分歧)比较严重的通道,即 GPU 不进行复杂的分支预测,当 warp divergence 过多时,会造成阻塞,大大影响性能。pass 的情况各有不同,但问题主要存在于 ALU 和带宽两个方面,主要的优化策略是控制分支减少分歧及优化内存读取和纹理采样。

经过一系列算法优化和移动端指令带宽优化后,我们的方案可以在高端移动端设备上面运行到 60 FPS 以上(GI 消耗在 4ms 以下,部分场景还可以控制到 2~3ms),在中端设备上可以控制在 30 FPS 以上(千元机)。


图 20.35 所示为使用移动端性能分析工具得到的实机性能数据,可以看到红框内的 GI 消耗基本在 4ms 左右。

图 20.36 所示为手机中的截图,可以看到,搭载我们 GI 方案的程序达到了满帧 60 FPS,如果不锁帧率,实际可以达到 90~120FPS。

20.4.9 全动态 GI 的渲染效果

在 SmartGI 的全动态光照渲染技术下,我们可以实现符合各种游戏类型场景的光照效果。考量渲染效果的评估点如图 20.37 所示。

下面从以下几个方面进行具体分析。

1. 与离线路径追踪效果的对比

如图 20.38 所示,上图为路径追踪的渲染效果,下图为 SmartGI 的渲染效果。可以看到,SmartGI 的渲染效果与路径追踪的渲染效果相当接近。

2. 开放世界中的道路、森林、植被、自然光照

如图 20.39 所示,可以看到,SmartGI 能够支持开放世界中各种复杂环境的全局光照渲染效果。

3. 复杂高光(Glossy)材质

如图 20.40 所示,可以看到,上图蓝色幕布上面的金属镶边产生的高光效果,以及下图地铁场景中机器人反射的高光效果。

4. 动态体积雾

如图 20.41 所示,可以看到场景的动态体积雾渲染效果。

5. 复杂光源场景

我们可以做到对任意形状和类型的光源提供支持,不局限于平行光、点光等虚拟光源。如图 20.42 所示,可以将上图左边的幕布作为光源,照亮整个场景,也可以使用魔方或者小动物的模型作为光源。

20.5 未来的展望与思考

回顾 GI 的发展,可以发现,在不同的阶段会有适应于对应时期算力的 GI 算法出现。随着算法和移动端硬件的发展,相信未来还会有更高质量、更高性能的 GI 算法出现。

20.5.1 GI 算法的持续迭代

GI 算法一直在高速迭代,不断涌现出各种更先进的采样算法(如图 20.43 所示,左图为原始的路径追踪的效果,右图为结合基于神经网络的全局光照的效果),给 GI 计算光照收敛效果带来更快的速度。

20.5.2 移动端硬件能力的持续演变和提升

移动芯片厂商也一直在研发更高性能的设备和更复杂的硬件特性,比如硬件光线追踪、移动端 AI 加速单元、移动端超分等,这会给 GI 开发者带来更广阔的发挥空间,开发出之前无法想象的效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值