实时光线追踪 联合双边滤波 单帧降噪


Summary:

  • 为什么实时光线追踪(RTRT)需要降噪
  • 用联合双边滤波对RTRT的结果进行降噪(附伪代码和结果)
  • Deferred Hybrid Ray Tracing 和 naive Path Tracing之间的区别
  • 论文Edge-Avoiding À-TrousWavelet Transform for fast Global Illumination Filtering 的

Deferred Hybrid Real-Time Ray-Tracing Overview, 图源 [4]

为什么需要降噪?

光线追踪的效果非常真实:

光线追踪一个例子

光追生成软阴影

比如,渲染Cornell Box,不需要复杂的技术,只用光线追踪就可以实现软阴影、color bleeding、全局光照等效果。下图是我之前实现的Path Tracing算法

img

然而,对于这个比较小的256x256的场景,在CPU下 SPP = 128时,一帧画面需要多达40s时间渲染:

image

就算是用GPU进行优化,也达不到实时(30fps)的要求。


于是,实时光追采取的方案是:用较小的SPP(比如1SPP)迅速渲染出每一帧的带有大量噪点画面,然后利用单帧降噪(联合双边滤波)和多帧降噪(Temporal Accumulation)提升画面质量:

image-20210529102937056

这套Denoising方案可以得到比较不错的结果:

image-20210529103156895

联合双边滤波

说到降噪,能想到最简单的方案之一就是高斯滤波,然而高斯滤波只是单纯的加权平均一块区域的像素值,在降噪的同时,也模糊了不同物体之间的边界:

高斯模糊

于是就有了双边滤波,不仅考虑两个像素之间的距离,还考虑了两个像素之间的颜色差。双边滤波假设:如果两个颜色之间的颜色差别较大,那他们就属于两个物体,那么,在滤波时不应该对彼此有贡献:

双边滤波

然而,对于上面的图片,降噪结果貌似还可以;但是对于1SPP生成的实时光追图片,双边滤波的效果就不行了,因为噪点太多了,双边滤波的假设并不成立。


观察高斯模糊和双边滤波,究其本质,是在用不同的度量(距离,颜色)来计算两个像素之间的相似性。但是由于实时光追的图片中大量噪声的存在,颜色这个度量本身是有噪声的,所以颜色并不是一个好的度量。

那么有没有其他不带噪声的度量呢?有的,比如G-buffer中的Normal, Position(世界坐标系下的位置), Depth等等:

场景

Normal

Position

image-20210605104116531

假设我们的联合双边滤波考虑了像素之间的距离 $|\mathbf{i}-\mathbf{j}|^{2}$,像素之间颜色的差别 $|\widetilde{C}[\mathrm{i}]-\widetilde{C}[\mathrm{j}]|^{2}$ 和 法线方向的差别:

那最终 $i,j$ 像素彼此之间的贡献值就是:

其中 $\widetilde{C}$ 是光线追踪的带有噪声的结果,$\sigma_*$是超参。联合双边滤波的伪代码如下:

def JBF(frame, normal): //frame 原始噪声图像,normal G-buffer中的法线
	filtered = new [][]; //和 frame大小一样的数组
	for each pixel (x,y):
		sum_of_weighted_values = 0
		sum_of_weights = 0
		for adjacent pixel (i,j):
			weight = compute_J((x,y), (i,j), normal, frame) //按照上式计算的J
			sum_of_weights += weight
			sum_of_weighted_values += weight * frame(i,j)
		
		filtered[x][y] = sum_of_weighted_values / sum_of_weights;
		
	return filtered;

原始噪声图像:

box-input

联合双边降噪结果:

box-filter

更多降噪技术 A-Trous Wavelet

在联合双边滤波的基础上,有很多更快更先进的滤波算法。这里对 Edge-Avoiding À-TrousWavelet Transform for fast Global Illumination Filtering进行了实现,这篇文章结合了A-trous算法和联合双边滤波,通过多趟较小的滤波器模拟大滤波核的卷积操作。效果如下,在我的电脑上速度达到了联合双边滤波的8-10倍。

box-atrous

补充知识: Deferred Hybrid Ray Tracing

Deferred Shading

联合双边滤波需要G-buffer中的信息,G-buffer是Deferred Shading中的一个概念。

一般而言,最简单的Shading方式是Forward Shading,它的伪代码可以这么写:

1. Vertex Shading

2. Rasterization
for each triangle:
	for each pixel in triangle:
		if pass depth test:
			3. fragment shading
			color = fragment_shader(...); // fragment_shader需要做 1)片元属性插值 2)Lighting着色
			frame_buffer[pixid] = color;

Forward Shading有很明显的缺点:太慢了,调用了太多次fragment_shader,其中大部分调用是不必要的,因为会被后续更靠前的fragment覆盖,一个最极端的例子就是从场景最后方的三角形开始往前渲染。

为了解决这个问题,Deferred Shading出现了,它的伪代码可以这么表示

1. Vertex Shading

2. Rasterization
for each triangle:
	for each pixel in triangle:
		if pass depth test:
			3. write to G-buffer
			normal, position, albedo, ... = fragment_geometry_shader(...);
			g_buffer_normal[pixid] = normal;
			g_buffer_position[pixid] = position;
			g_buffer_albedo[pixid] = albedo;
			
3. Deferred Shading
for each pixel:
	frame_buffer[pixid] = fragment_lighting_shader(
			g_buffer_normal[pixid],
			g_buffer_position[pixid],
			g_buffer_albedo[pixid],
            ...
       );
			

其核心思想是将昂贵的光照计算移到后面,使得 光照计算的次数 与 场景中的物体个数无关,只与像素个数有关。

deferred_overview

更多Deferred Shading的知识可以参考 [1] [2] [3]。

Hybrid Rendering

Path Tracing算法是一种纯粹的蒙塔卡罗光线追踪算法,和光栅化可以说是没有任何关系;但由上文可知,联合双边滤波降噪所需的G-Buffer是光栅化的产物,光线追踪中为什么又有光栅化呢?

现在,为了尽量加快速度,工业界常用的算法并不是纯粹的光线追踪算法,而是一种混合了光栅化和光线追踪的算法,Hybrid Rendering。

image-20210529102754498

Hybrid Rendering 会先进行一遍光栅化得到G-buffer,再从G-buffer的每个像素出发,进行光线追踪,整个过程如下图所示:

图源 [4]

Acknowledgement

这篇文章部分内容来源于GAMES202 闫令琪 ,文中的部分截图来自于课中使用的幻灯片,非常感谢闫老师深入浅出的课程以及实现框架的助教们。

参考文献

  1. learn opengl 延迟着色法
  2. 【原】实时渲染中常用的几种Rendering Path
  3. 游戏引擎中的光照算法
  4. Deferred Hybrid Path Tracing, RayTracing Gem

文章作者: GeT Left
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 GeT Left !
  目录