图形引擎实战:一种典型的屏幕空间全局光照方案设计

背景

在3D游戏中,光照是影响整个画面感受最重要的因素。可以说,整个游戏行业的发展一定程度上也受图形学光照模拟计算的发展而推动。

随着玩家与制作人对游戏画面要求的提高,所谓的全局光照效果成了游戏开发工作中必不可少的一项内容。同时,全局光照技术的应用也确实使得3D渲染画面感受更加真实自然。这里,我们以一个简单的单一方向光场景为例。

画面中只有一个方向光源,以及两个不同颜色的自发光球体,整个场景使用普通的BlinnPhong光照模型渲染,只计算直接光照。可以看出,虽然模型制作上很精致,但是整个画面给人一种很干、不真实的感觉。

在不使用全局光照技术的情况下,我们可以通过增加辅光、环境光、以及使用后处理等方式让画面看起来好一点,比如上述画面,当我们多加一个其他方向的方向光源,使用环境光照和Bloom后处理效果后是这样。

可以发现,画面亮了一些,原本没有直接光照射的地方被辅光和环境光照亮了,但是仍然不够自然。很明显的,自发光的球有光晕,但是却照不到附近的表面,面和面之间的光照比较平,不真实。

如果将辅光换成某种全局光照方案(这里使用本文讨论的屏幕空间全局光照SSGI),如下图。

同样的,原本没有直接光照射的地方会变亮,同时两个自发光的小球发出的光影响了附近的表面,表面和表面之间的颜色似乎有一定程度的影响(注意阴影中的细节以及距离接近的表面,例如旗帜和墙壁)。画面整体变得稍微自然了。这里的画面提升,主要来自于间接光照。我们经常说的全局光照,一般都是在讨论如何模拟这个间接光照,也就是光照射到物体表面后一部分被反弹,转而照射到其他表面上的过程。

经过多年的探索,间接光照模拟目前已经有多种方案,大部分都有自己适合的应用场景,例如桌面端用的较多的体素圆锥体追踪全局光照(VXGI)、光照传播体积(LPV)、屏幕空间全局光照(SSGI)等实时方案,而移动端由于性能限制,一般使用基于烘焙的LightProbe或者LightMap来实现。本文主要讨论一种典型SSGI设计,即在屏幕空间利用ray marching对光进行逆向追踪从而获取间接光照的方案。

核心思路

本SSGI方案的核心思路比较简单,主要分为以下几步:

1. 在后处理阶段利用深度Buffer还原当前屏幕所有像素的像机空间位置,即光线回溯起点,如果当前像素是天空则跳过不处理;

2. 根据法线Buffer计算得到所有像素在相机空间下的法线方向;

3. 基于法线方向在外表面半球中选取光线回溯方向,在此过程中,我们需要注意两点,一是ray marching过程的性能消耗与光线数量直接相关,因此非必要情况下,每个像素使用一个方向进行ray marching就可以了,通过实际测试发现,单一方向ray marching基本满足间接光效果需求;二是为了处理离散方向带来的噪点,我们可以在确定ray marching方向的时候引入噪声因素(例如白噪声和URP Random.hlsl中的交错梯度噪声等,如下图),后期多帧混合可以提高间接光结果的柔和度;

4. 根据光线起点、方向、投影矩阵、回溯最大距离、步进次数等必要参数在屏幕空间进行ray marching,每一次步进需要判断当前测试点深度与当前测试点对应UV的屏幕深度(场景深度),如果前者远于后者,说明光线穿过了物体表面,出现了交叉;

5. 检测到交叉之后,可以直接认为当前测试点为间接光一次反弹点,但是如果需要更准确的结果,可以在前后两个测试点之间继续进行二分查找,通过二分迭代次数或者深度差确定退出时机。另外,大多数情况下,计算一次反弹的间接光结果就足够了,多次反弹计算的效果提升相比性能消耗增加不具性价比;

6. 最终利用反弹点UV获取ColorBuffer上的颜色,即作为当前像素的间接光来源,需要注意的是,如果是延迟渲染,这里的ColorBuffer使用光照渲染之后的结果效果会更自然。下图是全屏像素进行一次光线反弹计算后获得的结果,其中黑色部分是整个ray marching计算中始终未击中表面的像素。

7. 基于ray marching的结果,我们迭代若干次降分辨率高斯模糊(类似于Bloom)以获得柔和的结果;

在模糊过程中,采样点权重计算与高斯模糊不同,其通过采样点与中心点的深度差和表面方向差计算得到,即采样点与中心点深度差越大、面方向差别越大,对应采样点权重越小。

8. 最后一步将降噪后的间接光结果与原始ColorBuffer叠加即可。如果是在前向渲染路径下,则间接光结果只能做一些图像处理后直接叠加,如果是在延迟渲染路径下,则可以使用GBuffer获得每个像素点的Albedo和Specular,从而进行简单的光照计算混合以获得更好的效果。

其他要点

1. 光线回溯方向的确定

基于表面法线方向,无论计算几个方向的间接光来源,通过Cosine-weighted算法获得光线回溯方向进行重要性采样,可以得到较好的收敛效果。

2. 光线回溯失败情况下的处理

由于噪声、相机运动的存在,屏幕上像素点不一定总能在光线回溯的过程中击中表面,从而导致间接光结果不稳定,容易出现噪点。对于这个问题,我们可以通过两种方式来减轻其影响。(1)我们可以设定一个默认的反射球,当光线回溯失败时,使用像素点对应的世界空间法线进行反射探针采样,用采样到的结果作为间接光来源;(2)我们可以将每帧的间接光结果缓存下来,配合MotionVector,当某一像素点的光线回溯失败时,使用MotionVector计算上一帧的像素UV,然后从缓存的间接光结果上采样对应的间接光作为当前帧间接光来源。

3. 遮蔽估计

存在深度Buffer与法线Buffer的情况下,我们可以很容易估计屏幕空间某一像素点空间位置的遮蔽情况,从而在全局光照中增加AO效果。具体的思路是,在像素点屏幕空间周围360°范围内平均采样一定数量的方向(示例代码中为16个采样点,同时引入了噪声作为反射计算的法线来改变每个像素点的采样方向),分别还原各采样方向上设定距离的屏幕空间点对应的世界空间位置,并计算得到要估计的点到各采样点的世界空间方向向量,通过计算该方向向量与要估计的点的世界空间法线的余弦值,即可估计对应方向上的遮蔽情况。大致的原理可以理解为,每一个检测点到周围的采样点的方向矢量越接近于检测点的法线方向,则该方向上遮蔽越强。

结果如下图所示,很明显,余弦值求和越大的地方遮蔽越大,对应图上越亮(第一张是计算得到的原始结果,第二张是经过模糊之后的结果)。

4. 移动端的应用性

SSGI方案与其他全局光照方案的一个不同点是,SSGI在整个计算过程中,可以很容易通过一些参数的调整,从而降低其性能消耗。因此,在移动端应用实时SSGI方案是可以尝试的,就像某些情况下应用体积云和屏幕空间反射一样。在实际测试中,我们也发现基于以下的性能配置,在小米12上使用SSGI方案基本可以满足性能需求。

1)屏幕空间ray marching以及遮蔽检测在3~4倍降采样下进行;

2)深度采样是最频繁的操作,预先拷贝一份相同降采样倍率的深度图可以提高深度采样性能;

3)移动端每个像素只建议采样一个光线路径,一次光线反弹;

4)设定好合适的光线回溯最大距离以及最大步进次数;

5)不使用二分法或者很少的次数迭代获取精确击中点;

6)对于在移动端使用延迟渲染的项目,天然比较合适尝试使用SSGI。

欢迎加入我们!

感兴趣的同学可以投递简历至:CYouEngine@cyou-inc.com

#我的求职思考##搜狐畅游##校招##求职##引擎开发工程师#
全部评论
deferred shading里常用的技术了,对于forward没有gbuffer就不行了。真的能上手机吗
点赞 回复
分享
发布于 04-09 15:41 江苏

相关推荐

6 7 评论
分享
牛客网
牛客企业服务