games202:六,实时光线追踪RTRT:Temporal Filtering、联合双边滤波、Outlier Removal 、SVGF、RAE
RTRT现状
光线追踪能实现软阴影、反射、ao、diffuse全局光照等接近真实的效果,随着18年NVIDIA的RTX(硬件)架构发布,光线追踪在实时领域也可以进行了。
- RTX----一种硬件架构,每秒可以计算100亿根光线与包围盒的求交和加速,实际应用中大概每帧可以计算1spp(sample per pixel),但在games101里我们都知道1spp的结果根本不能看,全是noise,而RTRT最关键技术就是通过实时的方法来为1spp的光追结果做降噪。(因此rtx的光追技术并没有变化,重点在denoiseing)
- 1SPP path tracing = 1 rasterization + 1 ray visibility + 1secondary bounce + 1secondary visibility
下边左图:1spp过程示意;右图:1spp结果+denoiseing
「注意这里的降噪与DLSS无关,DLSS可以与RTRT同时进行,本质是提高分辨率,不是降噪」
这里的降噪,我们既想要高质量又想要实现实时(<2ms),那很多方法比如:切变滤波(Sheared filtering)系列(SF,AAF,FSF,MAAF)、一些离线滤波(IPP,BM3D,APR……电影用)、深度学习(CNN、Autoencoder—时间不行)等降噪方法就都不适用了。「OpyiX API上用了他们的一个简化版深度学习方法,时间几十ms」因此针对RTRT的降噪很难,我们这节课介绍两种思路:Temporal Accumulation和Spatial Filtering。
实时降噪方法
一,Temporal Filtering时间降噪方法
Temporal Accumulation----时间累积的滤波
- 核心思路:
- 认为前一帧永远是滤波后的结果,并且每帧是连续的
- 用motion vectors去寻找上一帧的位置并复用结果
- 通过复用等同于增加spp(递归增加,累积多帧的结果)
- G-Buffer:Geometry buffer,(延迟渲染中获取的)屏幕空间信息,包括深度图、法线、albedo、世界坐标、直接光照结果等。
1,back projection法计算motion vectors
- 获取屏幕某点世界坐标s:
- 在G-Buffer里保存世界坐标s,从屏幕中直接读取
- 将屏幕空间的某点坐标加上深度坐标,用公式变换到世界坐标 s = M − 1 V − 1 P − 1 E − 1 x s = M^{-1}V^{-1}P^{-1}E^{-1}x s=M−1V−1P−1E−1x,相当于MVP的逆运算,其中E是viewport变换,x是屏幕坐标
- 计算该点在上一帧的世界坐标: s ′ = T − 1 s s' = T^{-1}s s′=T−1s,这里的T是上下帧的世界坐标的变换
- 获取该点在上一帧的屏幕坐标
x
′
=
E
′
P
′
V
′
M
′
s
′
x' = E'P'V'M's'
x′=E′P′V′M′s′
「与光流法不同,光流是仅凭两帧结果的内容找对应,本方法是计算的对应,绝对真实;并且光流速度也不行」
2,将上一帧与当前帧结果线性混合
「 C ˉ \bar C Cˉ表示已经滤波过的, C ~ \widetilde C C 表示还没滤波」
C
ˉ
(
i
)
=
F
i
l
t
e
r
[
C
~
(
i
)
]
\bar C^{(i)} = Filter[ \widetilde C^{(i)}]
Cˉ(i)=Filter[C
(i)](空间降噪)
C
ˉ
(
i
)
=
α
C
ˉ
(
i
)
+
(
1
−
α
)
C
ˉ
(
i
−
1
)
\bar C^{(i)} = α\bar C^{(i)} + (1-α)\bar C^{(i-1)}
Cˉ(i)=αCˉ(i)+(1−α)Cˉ(i−1)(时间降噪)
「一般α=0.1~0.2,也就是大部分用上一帧的结果」
最终效果如下图。「注意:滤波不会改变能量守恒,降噪前的图看起来暗是因为显示器将结果clamp到0-1了,如果是HDR显示器结果不会这么暗。」
算法缺陷/问题temporal failure
-
Switching Scenes:如果切换场景或者灯光、镜头(电影转场)等突然变化,本方法需要先累积一些“上一帧结果”,那第一帧显然效果就不好,
-
Screen Space Issue:本帧物体点在上一帧没有或超出屏幕范围了,比如经典的walking backwards in a hallway后退走、disocclusion上一帧被遮挡的物体本帧突然出现等,会出现legging的问题,如下图
-
Shading Failure:对于静态物体动态shading的情况,由于motion vector为0,阴影和反射等常常会滞后产生拖尾现象(detached shadows),如下图光源改变阴影错误及物体移动,地面反射滞后。
缺陷解决方法
- clamping:当上一帧的结果与当前帧差距过大时,把上一帧结果clamp到当前帧结果的近似处(类似均值方差小范围误差内),再做混合。
- detection:加一步检测以确定是否使用上一帧的结果。给场景中物体的加一个Object ID以区分颜色,如果ID突变,则判定出现temporal failure,这时去调整α(可以选非0即1,也可以根据id的值调整系数)。但这样会重新引入噪声(之前的累积没用上了),如下图。
- tips:
- Temporal Filtering是受Temporal Anti-Aliasing(TAA)启发
- 可以部分解决temporal failure的paper:Temporally Reliable Motion Vectors for Real-Time Ray Tracing,和该论文导读:EG2021 | 计算更加可信的运动向量,从根本上解决鬼影等问题
二,Spatial Filtering 空间降噪方法
上边讲了时间降噪的方法,但空间降噪还没讲,本节就是空间降噪的方法。 C ˉ ( i ) = F i l t e r [ C ~ ( i ) ] \bar C^{(i)} = Filter[ \widetilde C^{(i)}] Cˉ(i)=Filter[C (i)]里的Filter,滤波核是K。
对于RTRT的应用场景噪音多是高频信息,因此低通滤波就可以,最简单的是使用高斯滤波,但高斯完的图像边缘也变糊了,损失了部分有用的高频信息,由此我们引入更复杂的滤波核。
2.1 bilateral filtering双边滤波
核心思路:颜色变化大的地方认为是边界、贡献越小。
公式如下,与高斯滤波核相似,只是加入了颜色权重,其中
I
(
)
I()
I()是像素颜色:
- 效果:
- 然而事实上,颜色差异大不能区分两点是在边界还是噪声,这是双边滤波的固有问题,到SVG部分讲解决方法。
2.2 Joint/Cross bilateral filtering联合双边滤波
- 高斯滤波权重----像素距离;
- 双边滤波权重----位置距离+颜色差;
- 联合双边滤波权重----G-buffer信息(不采样光栅化免费得到并且没有噪音)辅助加权(用什么信息可以主观设置—记得归一化,深度、法线、坐标、object id、颜色等都行)+ 任何距离衰减函数(高斯、cos、Exponential等都行)
- tips:
- G-buffer的每个信息由其对应的σ控制重要程度,需要调参(不黑盒)
- G-buffer的信息怎么用没有标准,有时需要探索一个合理的指导维度
2.2.1 滤波核优化加速
- Separate Passes:滤波核本来是NxN大小,但全遍历一次开销大;可以把滤波核拆分至水平和竖直方向的两个一维滤波核,改为遍历2*N次。「注意只有高斯核可拆分,其他滤波核有损失,不过半径小(小于32)的话也看不出来」
∫ ∫ F ( x 0 , y 0 ) G 2 D ( x 0 − x , y 0 − y ) d x d y = ∫ { ∫ F ( x 0 , y 0 ) G 1 D ( x 0 − x ) d x ) } G 1 D ( y 0 − y ) d y \int\int F(x_0,y_0)G_{2D}(x_0-x,y_0-y)dxdy = \int \{\int F(x_0,y_0)G_{1D}(x_0-x)dx)\}G_{1D}(y_0-y)dy ∫∫F(x0,y0)G2D(x0−x,y0−y)dxdy=∫{∫F(x0,y0)G1D(x0−x)dx)}G1D(y0−y)dy
-
****:
-
Progressively Growing Sizes(A-Trous Wavelet):把一个核拆成多个小核做多pass计算,这些小核逐步增长滤波步长间隔
在这个方法下,采样间隔是2的i次方可以不发生“走样”,原因是在第2个pass上的采样间隔相当于对信号进行搬移,正好搬移的间隔就是上一次pass留下来的最高频率(右图),然后再一次滤波。
)
- tips:
- 快速傅里叶变换在GPU优化很差,不如cpu速度,不如咱们讲的这两种优化
- 实际中常常第一个pass是不太干净的,导致之后的搬移也有点问题,可以最后再做一次小滤波
增加G-buffer信息又会增加计算量,因此需要对滤波核进行优化。
2.3 Outlier Removal 噪点移除(and temporal clamping)
Outlier指采样中出现的一些亮度极其高的噪点,滤波后它的亮度会贡献到周围导致形成亮斑,所以应该在滤波前使用clamp的方式将这些Outlier去除。(去除后能量不守恒,但也这么做了)。
-
具体方法:
- 对每个像素取周围7x7的均值μ和方差σ
- 取一个范围比如 [ μ − k σ , μ + k σ ] [μ-kσ,μ+kσ] [μ−kσ,μ+kσ],k取1~3,认为这个范围外的样本是异常的
- 把异常值clamp到上述区间内
-
tips
- 对于第一节的Temporal Filtering里的clamp也采用这种办法, C ( i ) = α C ˉ ( i ) + ( 1 − α ) C ( i − 1 ) C^{(i)} = α\bar C^{(i)} + (1-α)C^{(i-1)} C(i)=αCˉ(i)+(1−α)C(i−1)—> c l a m p ( C ( i − 1 ) , μ − k σ , μ + k σ ) clamp(C^{(i-1)},μ-kσ,μ+kσ) clamp(C(i−1),μ−kσ,μ+kσ)(把上一帧结果clamp到当前帧)
- 工业界的clamp会更复杂,可能在某种颜色空间上进行,并且范围可能是3d的颜色空间等----TAA的实现
- 为避免很小的光源被去掉,可以前渲染没有光源的场景再加光源
2.4 Spatiotemporal Variance-Guided Filtering(SVGF)时空差异引导滤波
SVGF比上述的时空降噪又多了偏差分析等内容。(也是联合双边滤波)
3 factor-Guided filtering:
-
1,深度
w z = e x p ( − ∣ z ( p ) − z ( q ) ∣ σ z ∣ ∇ z ( p ) ⋅ ( p − q ) ∣ + ε ) w_z = exp(-\frac{\vert z(p)-z(q) \vert}{σ_z\vert \nabla z(p) \cdot (p-q) \vert + ε}) wz=exp(−σz∣∇z(p)⋅(p−q)∣+ε∣z(p)−z(q)∣)
分母的 ε ε ε是防止除0用, σ z σ_z σz是控制深度的影响程度。 ∇ z ( p ) \nabla z(p) ∇z(p)是指深度的梯度(沿着法线、垂直法线),这样如果两点有深度差异但实际在同一个侧面时(下图),可以通过分母减少深度差异带来的权重。
-
2,法线
w n = m a x ( 0 , n ( p ) ⋅ n ( q ) ) σ n w_n = max(0,n(p)\cdot n(q))^{σ_n} wn=max(0,n(p)⋅n(q))σn
σ n σ_n σn是控制法线权重的放大系数。「如果使用法线贴图,则用贴图应用前的法线」 -
3,亮度(luminance)
w l = e x p ( − ∣ l i ( p ) − l i ( q ) ∣ σ l g 3 × 3 ( V a r ( l i ( p ) ) ) + ε ) w_l = exp(-\frac{\vert l_i(p)- l_i(q) \vert}{σ_l\sqrt{g_{3×3}(Var(l_i(p)))} + ε}) wl=exp(−σlg3×3(Var(li(p)))+ε∣li(p)−li(q)∣)
这里的亮度通过颜色得出的灰度判断,但容易受噪点异常干扰,就需要这里的Variance。 -
Variance计算:
- 计算7x7内的方差
- 按时域的方法,通过motion vector计算上一帧对应像素的方差,取平均(时域上平滑)
- g 3 × 3 g_{3×3} g3×3表示在周围3x3内再做一次空间平均滤波
就是spatial filter → temporal filter → spatial filter 从而得到P点精准的variance
问题----会造成over blur
2.5 Recurrent AutoEncoder(RAE)
基于神经网络对蒙特卡罗路径追踪的结果进行reconstruction,在时域上基于Gbuffer和1SPP的渲染图降噪。
如图AutoEncoder(U-Net)加上每层对参数复用,就相当于用了上一帧的结果。至于中间过程是不可知的(但未来发展可能会更好)
目前rae有了更多发展,但速度还是不足以在RTRT中使用(但这里说明了SIGGRAPH的看重方向,也许是未来大势)
一些工业界解决方案
三,Anti-Aliasing抗锯齿/反走样
3.1,TAA(Temporal Anti-Aliasing)时域抗锯齿
(先有taa才有上边的时域滤波的,几乎一样的思路)
「抗锯齿的根本解决方案还是要增加采样数」
方法:把每个像素分为4个部分,在每部分的固定位置逐帧轮换采样并复用上一帧上一位置的结果,相当于在时域上把采样率增加了2x2。「注意实际中这里每个部分的采样位置固定比随机效果更好」如果场景物体移动了,如同上边的时域滤波方法----用clamp。
3.2,MSAA(Multisample)、SSAA(Supersampling)
- SSAA相当于把场景按照几倍的分辨率采样完了在降采样的方式(暴力采样,效率线性上升)
- MSAA同一结构体只采样一次,还可以复用采样点,以这种方式增加采样数(游戏里可以设置渲染百分比,120%、200%等,就是MSAA。但代价也大)
3.3,image based AA
- 发展:FXAA->MLAA(Morphological AA)->SMAA(Enhanced subpixel morphological AA)
SMAA原理如下图(和FXAA好像啊),速度大概1ms,效果也不错,工业界常用
tips:G-buffer千万不能做抗锯齿,应该保留锯齿,因为要保留信息
四,Temporal Super Resolution时域超分辨率
DLSS1.0:信息只有图像,结果全靠猜(提前学一些常见的场景)
DLSS2.0:有Temporal信息(核心思想就是应用TAA)
但DLSS2.0也有问题:如果有temporal failure,则不能clamp了(这样就变得很糊,与超分违背),因此对temporal信息有更高的要求。所以DLSS2.0告诉的其实是“上一帧的对应信息应该怎么用(smarter than clamping)”这点
- DLSS是NVIDIA的,其他厂商类似功能:
- AMD:FidelityFX Super Resolution
- Facebook:Neural Supersampling for Real-time Rendering (像DLSS1.0,工业界不用)
五,提升shading效率
5.1 Deferred Shading延迟渲染
延迟渲染是一种节省shading时间的方法。
-
一般光栅化过程:三角形->fragments->深度测试->着色->像素;在极端情况下每个fragment都要shade一次,复杂度是O(fragment * light)-----因为深度测试是逐渐更新的,很多fragment到最后其实是不可见的
-
那能不能只渲染可见的fragment呢?可以:把场景光栅化2次,第一次不shading,只写入深度缓存;第二次才根据深度shading----depthprepass(这是建立在光栅化rasterization比shading开销小的基础上),复杂度是O(visible.frag * light)
「不能做加采样AA,只能做TAA或屏幕空间AA」
5.2 Tiled Shading(forward+)
原理:在 Deferred Shading的基础上,把屏幕分成比如32x32个小块,然后分别shading。
- 亮点:节省对每个光源的计算(因为一般的光源都有关于距离平方的衰减,范围可以理解为下图的圆),可以只计算影响到该Tile的光源,复杂度是O(visible.frag * avg.light-pretile)
5.3 Clustered Shading
原理:在 Tiled Shading把屏幕分块的基础上,在根据深度对其切片(3d grid网格),如下图
- 亮点:进一步减少每个网格需要计算的光源,复杂度是O(visible.frag * avg.light-precluster)
六,LoD(Level of Detail)
lod是在不同计算层级下选择不同层级的方式,以减少计算。(Mipmap也是)在工业界也叫做“cascaded”(级联)。
-
应用举例:
- Cascaded shadow maps----相机近处用高分辨率 shadow maps,远处用低分辨率的,高低分辨率转换处有一个过渡区域需要overlapping和blending,如下图
- Cascaded LPV(Light Propagation Volumes光传播体积)----越远格子越大
- geometric LoD—一系列高模低模,根据距离远近选择,或通过计算动态的使三角形大于1像素。(UE5Nanite的实现原理----动态选取LOD)
- Cascaded shadow maps----相机近处用高分辨率 shadow maps,远处用低分辨率的,高低分辨率转换处有一个过渡区域需要overlapping和blending,如下图
-
难点:
- 切换levels的时候缝隙怎么办?
- 动态加载不同levels时缓存、带宽怎么动态调度?
- 用几何纹理(games102)做geometry LoD比三角形面更简单
- 使用clipping和culling会更快
七,Global Illumination Solutions
各种GI方法都有各自的问题,除了真的做RTRT,但又太慢了。因此工业界会使用hybrid solutions综合解决方案:先做SSR得到近似的GI,在SSR不行的地方用硬件或软件的方式做ray tracing。
- 软件方式:近处用高质量SDF(shader里做tracing),远处用低质量SDF覆盖整个场景;用RSM处理很强的平行光或点光;Probes探针存储光照计算(Dynamic Diffuse GI,DDGI)
- 硬件方式:用简化模型替代高模做ray tracing;RTRT结合Probes----RTXGI
- 上边加粗的四条就是UE5的Lumen
老师的课程完结总结
-
老师更喜欢思考理论算法,而不是工程实现----放弃思考等于自杀;但实现不重要吗?NO!非常重要(相信我们大多数人受自身硬软件限制都更适合做应用,而不是科研)
-
还有很多没讲的话题:
- SDF贴纹理
- 透明物体和order-independent transparency
- 粒子渲染
- 后处理
- 生成和应用随机数、蓝噪声(blue noise)
- Foveated rendering比如VR中人眼看的地方投入更多算力
- Probe based GI
- ReSTIR,Neural Radiance Caching等先进技术
- 多光源理论和light cuts、VPL
- Participating media,SSSSS
- Hair表现
-
GAMES203广告:3d vision结合了计算机视觉和图形学,分三块:3d重建、3d信息表示、3d信息识别(语义)
-
老师自己的GAMES2XX离线渲染课程广告:
- 采样和光线传播理论
- appearance modeling
- 先进研究
参考资料
1,GAMES202高质量实时渲染-个人笔记:实时光线追踪
2,GAMES202 笔记-实时光线追踪
3,Spatiotemporal Variance-Guided Filter, 向实时光线追踪迈进