天天看点

ue4光追降噪_UE4.24 SSGI实现分析

SSGI简介

UE4.24的Roadmap里可以看到SSGI(Screen Space Global Illumination,屏幕空间全局光照)已经发布,这是一个非常廉价的实时GI方案,适合要求不那么高,不支持光追的硬件或性能敏感的应用场景。先建个简单的场景,镜花水月的看看效果对比:

ue4光追降噪_UE4.24 SSGI实现分析

可以看到左边是关闭SSGI之后,远半部BoxScene一片死黑;右边开启SSGI之后,远半部BoxScene内部已经被照亮。

SSGI的相关选项

SSGI开启选项

:控制变量为

r.SSGI.Enable ,0关闭SSGI,1开启SSGI,默认值为0 质量选项:

控制变量为

r.SSGI.Quality ,默认值为4,取值范围为{1,2,3,4},

值越大质量越高,当然性能会成反比

SSGI RT分辨率:

控制变量为

r.SSGI.HalfRes,0为否,1为是,默认值为0,

即关闭HalfResolution,用Half Resolution会提高SSGI的性能,使处理的像素减少到原来的1/4,但同时也会降低SSGI的质量,增加噪点

SSGI是否使用使用无漏光的SceneColor :

控制变量为

r.SSGI.LeakFreeReprojection

取值范围为{0,1} ,默认值为0。

这是SSGI选项中唯一一个会有点疑问的变量——实际上它并不能解决漏光,它确定的是:你要用上一帧的SceneColor或是用当前帧的TAA History来做为当前的SSGI的输入数据。因为TAA的History基于移动指数方式混合了更多的前帧数据,只是相对来说后者可能导致更多的漏光罢了。

SSGI的基本流程

ue4光追降噪_UE4.24 SSGI实现分析

SSGI的实现分为4步,共计7个Pass,其中第3步的Denoiser实现不表,其它部分共4个Pass,步骤如下:

  • 准备数据阶段(Reduction) :2个PASS,生成PreFrame的SceneColorTexture和HZBTexture的mipmap 。这一步有两个Pass,生成5个mipmap。生成mipmap的算法和单纯的纹理生成mipmap不同,不是简单的取上一级mip对应4个相邻像素的均值做为本级mip。SceneColorTexture在生成mipmap的时候,会从Depth Buffer中还原当前像素及相邻像素WorldPosition ,使用WorldPosition之间的距离平方衰减其Color影响。具体代码整理如下:
ue4光追降噪_UE4.24 SSGI实现分析

HZB Texture的mipmap生成则是存储上一级mip对应的4个相邻像素中的z的最大值。

ue4光追降噪_UE4.24 SSGI实现分析

上述的SceneColorTexture和HZB Texture均使用上一帧的数据做为输入。上一部分我们提到过

r.SSGI.LeakFreeReprojection

的值会影响到这两项数据的来源:如果

r.SSGI.LeakFreeReprojection为1,

则使用的是原生的上一帧的SceneColor,否则使用的是TAA的History。

SSGI在大的渲染流程中被调用早于Lighting相关的Pass,也早于TAA相关的PASS,大胆推测一下这么设计的原因:不使用本帧数据而使用上一帧的数据作为输入的目的,一是因为SSGI发生在Lighting之前,如果使用当前帧的数据则Buffer中无光照信息(但这不应该是大的问题,因为他们可以把此Pass放到Lighting相关Pass之后),二是因为使用前帧数据能有效的保证SSGI充分获得TAA的好处而不出现闪烁(SSGI Pass不能放在本帧的TAA PASS之后——它可能带来新的Aliasing问题,这样就需要在其后面添加新的AA PASS)。 评论有同学指出使用TAA History还有一个重要的作用:实现多次反弹 。最简单的场景:试想相机完全静止的时候,第三帧时获取到的第二帧History数据中带第一次反弹的信息,再次执行RayCast就相当于第二次反弹,以此类推,第N+1帧即可部分N次反弹的效果。如果直接使用的是上一帧的TAA之前SceneColor而非History,则就只会有单次反弹了……如果相机非完全静止,则History可能带来前帧累积错误反弹
  • SSGI Raycast : SSGI RayCast是在一个PASS里完成的,它同时计算了AO和Diffuse反弹,所以它的输出包含一份IndirectDiffuseOutput和一份AmbientOcclusionOutput。

这个PASS的实现有几个值得注意的地方,一一列举如下

1.它基本上沿袭了SSAO的光线发射方式,光线发射在屏幕空间的同轴圆。这儿为了只采样上半球的空间用了一个简单的tricky ,z = sqrt(1-x^2-y^2)) ..这样z始终保持取值范围为[0,1],即始终在正半球~

ue4光追降噪_UE4.24 SSGI实现分析

2.它的RayCast过程也颇为有趣,其RayCast过程不光是step不均匀(普通优化手段,用于产生非grid状的cast结果)。

UE4的Scene Space RayCast step是打包执行的,一次执行4步 ,并且每隔两步就增加其采样目标纹理的mip level。其中打包的部分可以理解为充分利用GPU的向量化执行特性的优化。。

代码经过整理且加上了注释,如下

ue4光追降噪_UE4.24 SSGI实现分析
每执行2步就增加mip level的做法带来一个结果:离光线原点越远的地方,采样到的mip level越低,结果越模糊,即离光线越远,采样到的原始scene color的范围越大(低级的mip相当于高级mip更大范围内像素颜色的加权平均值),其实挺合理(类似于ConeTracing的做法,缺点是SceneColor部分会带来漏光,而Ambient部分则是阴影泄漏)。
  • ScreenSpace Denoise :由SSGI Raycast带来的结果是充满噪点的,所以降噪就尤其重要 。UE4的SSGI所使用的降噪器和RTX使用的相同,实际上应该说实时RTX这一波应用,完全就是因为降噪技术的贡献才使其成为可能。SS Denoiser这一课题值得单独开篇去讲,这儿就此略过,补图看看降噪前后的效果对比。
ue4光追降噪_UE4.24 SSGI实现分析
  • 合成GI影响到DiffuseColor和AO, 混合降噪之后的间接Diffuse和AO到DeferredColor中 DeferredColor后续会继续叠加SSR、SkyLight、光照图、直接光照==其它贡献。混合过程过于简单,直接上代码
ue4光追降噪_UE4.24 SSGI实现分析

SSGI的一些不足之处

  • 不是无偏,所以结果不可能完全正确(主流的实时RTX实现也非无偏的,用低密度的spp+降噪达到较平滑较低方差的结果)
  • 无法反射屏幕外的像素,所以可能会有光和阴影的泄漏

SSGI 实现代码清单

C++部分

C++代码入口在RenderScreenSpaceDiffuseIndirect中。

Shader部分

SSRTPrevFrameReduction.usf(实现数据准备mip生成)

SSRTDiffuseIndirect.usf(具体的RayCast Pass的实现,其中raycast核心函数为

CastScreenSpaceRay

)

DiffuseIndirectComposite.usf(SSGI的合成实现)