Siren GI:用神经光照夺得图形学顶会竞赛的第一吧 您所在的位置:网站首页 pass层是什么意思 Siren GI:用神经光照夺得图形学顶会竞赛的第一吧

Siren GI:用神经光照夺得图形学顶会竞赛的第一吧

2023-03-09 17:43| 来源: 网络整理| 查看: 265

本文的渲染器成品,极速且无噪点https://www.zhihu.com/video/1607162172542459904零.写在前面(废话,可以跳过)

本文介绍了一种使用SIREN网络对路径追踪结果进行储存和压缩的算法,利用该算法可以将带烘培信息的实时渲染提升至离线渲染品质。

本文会稍微涉及到PathtracingDenoisingNerf,以及Siren NetworkUV Unwrap等内容。

2022年,可以说是鄙人人生中最糊涂的一年。论文失利,独立游戏项目推进缓慢,大三的学业也是最忙的,一个学期有四五门的数学。基本已经忘了这一年是怎么苟过来的。正是因为这种种失败,让人会急切地找补,毕竟人的存在感几乎是支撑生活的全部意义了,于是鄙人开始找各种比赛,寻找性价比比较高(投入比较少,获得的荣誉看起来比较高)的比赛。其中一个是和曾经喜欢过的团体有关联的比赛,也就是Shadertoy 这篇文章里提到的比赛;另外一个就是本文会描述的图形学顶会(应该算顶会吧,第三志愿应该也挺顶的吧)的High Performance Graphics 2022的学生竞赛。

很巧的是,HPG的学生竞赛的背景是Shadertoy,熟悉笔者的人可能知道,笔者的技术比较平庸,但是对于一些奇淫技巧有着奇怪的执着,像Shadertoy这样的限制性编程工具,这样的机会自然是不会放过的。

之所以文章拖到现在,有几个原因,一是项目真的很忙,基本全年无休每天就对着UE4了,自然也没有什么心情做其他的事情;二是日本的物价飞涨,最近几个月是真的很穷,吃了两个多月的泡面(物理,非比喻),每天摄入1000卡的热量,属实没什么心情写文章。但是考虑到今年的HPG快要开始了,所以还是在匆忙中写下了这些文字,一是记录一下自己糊涂的生活,二是希望里面的技术能给大伙带来一些灵感(当然希望今年的比赛大伙能手下留情(如果今年没有限制神经网络的话),毕竟这是笔者为数不多的收入来源了)。

一.目录

零.写在前面

一.目录

二.杂言

三.路径追踪的优化

1.低先验降噪

2.高先验降噪

3.过拟合

四.网络的优化

1.光照贴图

2.神经光照贴图

3.采样管线小结

五.SIREN GI

1.Siren是什么

2.SIREN GI的结构

3.目标的修正

六.光照UV

1.UV拆解算法

2.技术细节

七.结论

二.杂言

这个比赛的主题是,将给定的Shadertoy(路径追踪的着色器,以及给定了一个场景),尽可能地优化到和GroundTruth一致。同时时耗不能超过2spp。

原比赛链接:HPG2022

这是俺

但是笔者的英文很烂

以及本文的开源的着色器链接:https://www.shadertoy.com/view/7scfR7

不过要注意编译时间可能会比较久,大概30秒左右。

GT和本文渲染器的比较

在进入下一个环节之前,我们先来看一下本文的渲染器有多离谱。左边是比赛方原渲染器运行一分钟后收敛的结果,右边是本文的渲染器的效果。在笔者按照预设的思路完成该渲染器时,就已经被这个效果完全震惊了。因为右边基本上比GroundTruth还GT,如果不是特别指出,我愿意相信右边的才是真正的GT(误)。

本文是误差最小的那个(

根据比赛方统计的结果,其他作品的精度也都十分离谱,其他作者的实力也是世界顶尖水准(第二第三名是PhD而且有一位是剑桥的博士,能与这些大神交手是我的荣幸,敬礼);但是可以看到本文的精度,要稍微地略胜一筹一点点(也就准两三倍而已)。但是这没有任何办法,因为本文的解决方案对于这个题目来说过于OP了(指OverPowered)。(基本上是想到的那一刻,笔者就已经觉得稳操胜券了(虽然有点胜之不武),剩下的就是工程质量上的较量了,当然熟悉笔者的人都知道,此人技术平庸,但是工匠起来确实不要命(日日夜夜地肝))

那么是怎么做到的呢,请听我一步步分析。

三.路径追踪的优化

对于一个简单的场景来说,场景Query结构的影响已经是线性的了,也就是无法过多的优化。因此在这个比赛背景下,

低先验降噪

几乎全部人的第一想法都会是,降噪。以下会通过输入输出来分析各种方法的利弊。

是的,降噪是一个很好的做法,对于纯数学先验的做法,比如SVGF[1][记得引用]来说,在一些场景能达到0.94的 SSIM。这当然很好,但是对于该比赛主题来说显然不够好,因为该算法是Temporal的,比赛方并没有明确指出会选取哪一帧进行比较(如果选取第一帧,那么Temporal的信息就会丢失),同时该算法依赖很重的GBuffer,以及很重的大核Filter,前者在Shadertoy中无法实现(无法全分辨率地实现,因为可以分屏,但是这里就不做讨论),而后者很耗,在webgl里不好优化。输入带噪光追结果,半个GBuffer,输出无噪结果。

SVGF的效果,很好,但是不够好!(在比赛背景下)

还有就是借助神经网络的降噪算法,比如KPCN[2],在降噪的时候比起SVGF可以带一些针对特定场景的先验,也就是可以针对该比赛用场景进行狠狠地优化(过拟合),来进行降噪。这种方法在特定的背景下可能优于SVGF。预处理过拟合场景作为先验,输入带噪光追结果,半个GBuffer,输出无噪结果。

KPCN的效果,很好,但是不够好!(在比赛背景下)

2.高先验降噪(小标题名字是笔者瞎写的,民科,请忽略)

既然已经想到了用场景先验信息来做,那么更直接的做法就是类似Nerf[3](Nerf太火了,本文不赘述了)一样过拟合该场景。预处理过拟合场景作为先验,输入第一Intersection位置,输出无噪结果(Radiance)。

Nerf论文里的图,用位置和视角信息预测Radiance Field

顺便啰嗦一下这个Nerf里提到的Positional Encoding(PE),实际上后面我们要提到的SIREN,他做到的提升笔者认为就是在做PE的工作(因为形式十分地类似),但是具体的不会在本文提到,本文以应用为主,为了后面忘记在这里提一嘴。

3.过拟合

很明显,最后一种结果更直接,没有中间商赚差价。因此经过简单分析,选用最后一种方法,也就是找到一个一个网络 F(\textbf{p},\textbf{v};\Theta) ,使得这个网络为该场景的Radiance Field表达,这里 \textbf{p} 是第一Intersection的位置, \textbf{v} 是当前视角(因为场景带有微弱的粗糙度,是anisotropic的,所以要算上视角),而 \Theta 则是我们需要优化的参数集。

四.网络的优化

这里我们只是完成了很前期的工作。因为F(\textbf{p},\textbf{v};\Theta) 这个网络,要达到在正常分辨率下实时运行同时精度高于SVGF的,基本是不可能的。当然这是基于经验进行分析的。但是无论如何,我们都需要对这个网络进行优化。首先我们知道,Shadertoy是有所谓预计算的,具体可以参考笔者之前的文章

里面详细说了笔者是如何把贴图带入Shadertoy的,里面用到了一个Buffer专门在第一帧进行预计算。

1.光照贴图

如何利用预计算就成了我们接下来要思考的问题。我们知道,通常游戏会使用一种叫光照贴图Lightmap的技术,将低频的光照储存到一张三通道的贴图中,并使用2UV进行Query。用公式来看是这样的关系: R=Lightmap(UV(Pos)) ,其中 UV: \mathbb{R}^{3} \rightarrow \mathbb{R}^{2} , Lightmap: \mathbb{R}^{2} \rightarrow ColorSpace 。

其中 Lightmap 函数是预储存的。

随便找的一个示意图,意思意思

2.神经光照贴图

理解了游戏中光照贴图的原理之后,我们也可以对原网络进行拆分。首先是照葫芦画瓢,把巨量的计算放到预计算的函数里:

R=Lightmap(UV(\textbf{p},\textbf{v})) ,但是这是几乎不可能做到的,因为我们很难找到一个 UV: \mathbb{R}^{3+3} \rightarrow \mathbb{R}^{2}

是六维映射到二维的函数(平时拆UV是三维到二维的已经很困难了,具体数学笔者不懂,但是应该是这样的)。

因此,Lightmap函数之前,只能运用到三维的 \textbf{p} 信息,而 \textbf{v} 作为一个稍微不怎么影响结果的信息,需要放到Lightmap之后。因此我们得到了一张神经光照贴图

R=F_{2}\left(\textbf{h}, \textbf{v}\right),\textbf{h} = Lightmap\left(UV\left(\textbf{p}\right)\right)

这里很明显,我们将神经网络成功分为了两个部分,一个部分是纯和 输入位置 \textbf{p} 相关的光照贴图部分笔者称为Lightmap Network,一个是和 \textbf{v} 视线角度相关的笔者称为Refine Network的网络。同时,根据神经网络的特点,底层网络学习的都应该是低频信息,高层网络学习的是高频信息(出处俺忘了,但是这是正确的,不是民科),这也完美符合了渲染中的特点:在中低粗糙度中,和视角 \textbf{v} 相关的都是高频信息(不难理解)。同时预计算的信息实际上是离散信息,靠可怜的插值拼凑而成的Latent space。但是因为我们把这一部分放到了网络底层,因此也可以避免了大部分信息丢失。

但是这里的 Lightmap 不能再是对ColorSpace的映射了,因为我们的网络希望保留更多的“预计算”的信息,于是需要更多的维度。这里笔者预留了32个维度,也就是相当于8个RBGA通道的Pixel。

3.采样管线小结

实际上因为同样的位置,也可能有差别较大的Radiance变化,这通常是因为法线的不连续导致的。因此在Lightmap的计算时需要加入法线信息。完整的采样管线如下:

R=F_{2}\left(\textbf{h}, \textbf{v};\Theta_{2}\right),\textbf{h} = Lightmap\left(UV\left(\textbf{p},\textbf{n}\right)\right)=F_{1}(\textbf{p},\textbf{n};\Theta_{1})

UV: \mathbb{R}^{3} \rightarrow \mathbb{R}^{2} , Lightmap: \mathbb{R}^{2} \rightarrow \mathbb{R}^{8\times4},F_{1}: \mathbb{R}^{3+3} \rightarrow \mathbb{R}^{32}, F_{2}: \mathbb{R}^{3 + 32} \rightarrow ColorSpace ,其中 F_{1},F_{2} 都是神经网络, \Theta_{1},\Theta_{2} 都是需要优化的超参数,而且 \Theta_{1} 需要大量参数,渲染效果的扛把子。当然训练的时候会在一起训练,在后面会提到。

五.SIREN GI

SIREN[4],一个简单,压缩比极高的网络。笔者很喜欢Siren。Siren很适合文中的任务,因为实际上哪怕是神经的光照贴图,也是符合图像的特征的,是极其连续且不那么高频的(除非UV拆很散,意思到了就行了)。因此本文使用SIREN作为Backbone。

1.Siren是什么

很久没碰公式了,这里简单地描述一下。简单来说就是把MLP的激活函数换成Sine等周期函数,并用特殊的初始化算法让网络优化提速。具体请参考SIREN[4]。

Siren很适合用于图像的神经表达上,同理,Lightmap也是类似图像的。

2.SIREN GI的结构

网络结构

这里只描述本文使用到的结构。为了阅读方便,这里就不赘述太多公式了,本文采用一种简化记法:

Layer_{i} = sin(w_{i}(W_{i}x_{i} +b_{i})),x_{i}=Layer_{i-1},W_{i} \in \mathbb{R}^{m \times n}, b_{i} \in \mathbb{R}^{m},w_{i} \in \mathbb{R}

记作 L_{i}:[n\rightarrow m, w_{i}]

对于MLP里的一层网络:

首先是网络 F_{1} ,

第一层 L_{1}: [6\rightarrow64, 20] ,输入是 concat(\textbf{p},\textbf{n}) ,注意这里要用特殊的初始化和特殊的 w

第二到第三层 L_{2},L_{3}: [64 \rightarrow64, 1] ,

最后一层 L_{4}: [64 \rightarrow32,1] 稍微压缩一下,

作为Lightmap的输出,刚好占用8个RGBA通道的像素。

然后是网络 F_{2} ,

先对 \textbf{v} 升维,第一层 L_{5}: [3\rightarrow32, 60] ,注意输入是\textbf{v} ,注意这里要用特殊的初始化和特殊的 w

第二到四层L_{6},L_{7},L_{8}: [64 \rightarrow64, 1] ,注意第二层的输入应该是 concat(\textbf{x}_{4},\textbf{x}_{5})

最后一层输出Radiance: L_{8}: [64\rightarrow3, 1] ,这一层输出Identity,不用Sine激活。

这里有一个小细节,就是sine函数的值域是-1到1,而像素亮度是0~正无穷的。因此我们不能直接拿Radiance的值作为目标进行训练(很难,因为假设最后一层是随机的参数,他在接受前一层-1,1的输入之后输出基本上也是-1,1的,当然强行这么做也是可以的,代价就是不准)。

为了严谨,我们把参数集的定义补上: \Theta_{1} = {W_{i}}_{1}^{4}\cup{b_{i}}_{1}^{4},\Theta_{2} = {W_{i}}_{5}^{8}\cup{b_{i}}_{5}^{8} ,本文使用pytorch以及Adabound[5]的优化器(补充一句,Adabound是目前笔者感觉所有非图像类型的regression任务最好的优化器)对参数集进行优化。

3.目标的修正

我们知道 color = albedo \otimes lightcolor + specular (这很民科,但是无所谓,意思意思),而 albedo 是在求交的时候就已知的,我们希望尽可能地归一化网络的目标,因此对Radiance进行以下处理(正常来说,需要把diffuse和specular分开两个网络(比如KPCN[2]里用到的),但是因为场景较小所以我们一起处理了):

Target =Ln(10\times Radiance \oslash (Albedo+0.5) + 1) ,

经过变换的图像偏白

这里的Albedo是和First Intersection同时获得的。0.5是因为光滑表面的Radiance有些和Albedo无关,比如Albedo是黑色的光滑表面,为了处理这一种情况直接alt一下。还有很多adhoc的数字就不逐一解释了。目标就是把整个场景的全部采样分布尽量拉到高斯分布,Ln是很常用的把偏锋拉正的做法。总之本文推荐的训练目标是这样的。

到目前为止,高大上(误)的神经网络部分就结束了,剩下的就是一些传统的图形学问题(悲)。

六.光照UV

回顾一下采样的管线

R=F_{2}\left(\textbf{h}, \textbf{v};\Theta_{2}\right),\textbf{h} = Lightmap\left(UV\left(\textbf{p},\textbf{n}\right)\right)=F_{1}(\textbf{p},\textbf{n};\Theta_{1}) ,目前我们欠缺的就是这个 UV 函数了。通常,拆UV是一门学问,对于美术来说,是一种手艺,对于工程师来说,是一种比较难的算法。但是幸好这次比赛的题目比较友好,并没有曲面。对于经典的康奈尔盒子来说,我们可以直接把Plane映射到2维的Plane上,因此我们可以只对场景中的其他物体设计以下算法。

1.UV拆解算法

全是方块将物体 Obj_{i} 拆分为若干个长方体 Box_{i,j} 将这些长方体进一步拆分为若干长方形 Quad_{i,j,k} 将这些长方形放到UV空间内,用2d的Box Bin Pack算法保证各自不重叠,同时注意Pixel Perfect(顶点必须在像素的中心)防止漏光。表示为 Quad_{i,j,k} \xrightarrow{UV} Quad'_{i,j,k} 。到此,我们得到了心心念念的 UV 函数。但是光有 UV 函数还不行,因为我们知道Shader是PerPixel运行的,哪怕是进行预计算的Lightmap的pass他也需要遵循这个规律。因此我们还需要 UV^{-1} 来完成UV空间到世界空间的转换。这个函数也很好求,本文不赘述了。小结一下,在预计算的Pass,我们使用 UV^{-1} 求得该Lightmap在当前像素点对应的世界空间的点。同时利用该点的信息作为神经网络的输入,进行推理,写入Lightmap。最终在渲染时,根据世界空间的点信息和 UV 函数读取Lightmap,进行最终渲染。由于这一步没有做Visiualizer,所以用其他uv unwrap的示意图代替了

2.技术细节

a. Shadertoy中有一个CubeMap的buffer,该buffer提供了6x1024x1024xRBGA的像素量,本文渲染器的Lightmap将储存在这个buffer中。同时要注意的是,理论上这是8个Lightmap,因为一个点需要32个通道信息(如果读者跳过了前面的文章的话可能会有点晕所以这里还是回顾一下),储存的也不是传统的亮度信息,而是神经网络的一个Latent space vector。

如果看了本文的Cubemap觉得太绕了可以去看这位大神做的一个shader:https://www.shadertoy.com/view/Wss3Ds

b. 实际上笔者并没有使用bin pack算法。因为Lightmap在均分后每个Quad的分辨率比较低。笔者使用了完全撞大运随机算法,也就是随机生成若干次次无重合的UV排布,选取空白最少的一次方案。实际上这样更快,而且不用思考,理论上也最优(渐进最优,因为并不是无限种排布)。但是要注意Quad是可以旋转的,我们只考虑两种旋转。

传统2d bin pack算法的示意图

c. UV 和 UV^{-1} 在shader里的表现形式是纯纯的硬编码函数,是由数学上的函数通过某种codegen生成的,有很多个if else(无所谓,编译器会出手优化)。也由于一个面,占用一个if else,同时也只有一种法线,因此让这个六维到二维的映射变得可能。

d. 实际上每一个物体的网络都是单独训练的。也就是说该场景有若干个结构相同但是参数不同的子网络构成(这个很工匠,实际上笔者训练了很久)。(怎么说了那么多个实际上,我都快不认识实际上这三个字了)

一张神经Lightmap七.结论

结论就是Siren能在Shadertoy里乱口。

如果传统的Lightmap玩腻了,说不定真的可以尝试这种神经光照贴图。他的优点很多

自适应性很强,永远能找到适合当前量的最优解十分地Flexible,能处理稍微高频的光线,原光照贴图只能做点diffuse的光照。如果再魔改一下,可以处理实时光源,相当于encode一下visibility。

当然也有缺点,实际上这个算法就是球谐函数的可变维度(还有分布)的版本。代价是更多的计算量。

但是用来做玩具是十分足够的,尤其是在Shadertoy里。

实际上最终渲染速度比原来的1spp还快,说明准度还能继续提升。最终结果的SSIM在0.95左右,可能是因为抗锯齿分摊了很多。参考下面的视频。也就是说,哪怕一个像素超级采样三次,也能满足原比赛的需求,属于是赢了又赢,不用全力都能赢,把赢分享给大家,相当于赢两次(所以笔者很喜欢Siren,但是还是要说希望在本文发出去之后大伙不要和我抢饭碗呜呜呜)

本渲染器240fps,而1spp是180fpshttps://www.zhihu.com/video/1607163093733404672

另外我们也在做一个清新可爱的横板独立游戏,欢迎来B站或者爱发电或者Youtube(偏个人账户)关注我们鸭:

求关注求关注鸭,在这里给大家磕一个啦(咚。

如果读者是游戏从业人员,也许会对以下的文章感兴趣:

(本项目上线了虚幻商城,有闲钱的可以支持一下哦ଘ(੭ˊᵕˋ)੭

这篇文章开源了一个全蓝图的光追渲染器,不妨下来看看哦。

感谢阅读。

给大家拜一个晚年(很晚的晚年)。

这篇文章花费了数十个小时,时间跨度了几个月,创作不易,希望读者们可以点赞支持一下ଘ(੭ˊᵕˋ)੭

PS:有日本的同行可以帮忙提供一下找工作上的资讯吗,本人技术上偏技美,对引擎不熟悉,也没有刷题,虽然比较熟悉深度学习但是还是希望进入游戏行业。

而且日本应届生的工资稍微比较低,是否有可能直接以社招之类的形式进入公司呢?

再次感谢了(磕头)

参考^Spatiotemporal Variance-Guided Filtering https://cg.ivd.kit.edu/publications/2017/svgf/svgf_preprint.pdf^abKernel-Predicting Convolutional Networks https://jannovak.info/publications/KPCN/KPCN.pdf^Nerf https://arxiv.org/abs/2003.08934^abSIREN https://arxiv.org/abs/2006.09661^Adabound https://github.com/Luolc/AdaBound


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有