目录
surfel,可以理解成贴在世界空间中某个 mesh 上的圆片(有点像贴花),在几何上定义为:顶点 + 法线 + 半径
在不同的实现方案中,surfel 承担的角色可能会不一样;对于一般的 Surfel GI 方案而言,往往是基于 radiosity 方法:往场景里的 mesh 疯狂贴圆片(surfel),这些 surfels 用来探测光照并缓存起来,和 probe 非常相似。但是由于其是贴在物体表面上,而不是像 probe 放置在空间中,因此光照效果要更高质量些。
因为渲染的像素往往就是物体表面的像素,而这些像素更加接近于贴在表面的 surfel 而非接近于置于空气中的 probe。
在 SIGGRAPH 2021 的 GIBS(Global Illumination based on Surfels)实现中,就是使用 surfels 来探测并缓存光照来实现 indirect diffuse lighting(注:不包含 direct lighting 也不包含 specular)
传统 path tracing 就是让每个 pixel 去做 ray tracing,而用 surfel 做 ray tracing 的开销会大大减少:因为 surfel 比 pixel 的数量远远要少;并且通过控制 surfels 的数量,还可以弹性控制性能和效果的平衡。
本文将主要基于 SIGGRAPH 2021 GIBS 方案来讲解 Surfel,中间也结合一些 wicked engine 的实现方式,以供扩宽思路。
我们希望 surfel 是持久化存储的,也就是说即使 surfel 不在屏幕范围内,其生命周期仍可以不结束,这样就不会白白丢掉 surfels 累积的计算。
也就是说相对于 SSGI 方法,Surfel GI 还可以保留有屏幕外的信息。
场景中的 mesh 可能会发生各种诸如移动变化,而 surfel 通过记录 transform id 和 local position(而非世界坐标),从而可以附着于动态的 mesh 表面上。
在具体实践中,surfel 数据其实内部属性还会进一步分离:
首先,有些属性经常需要被查询(热数据),而另一些属性则相对没那么频繁被用到(冷数据),这会对 cache 更加友好;其次,有些属性块需要进行 double buffering 来减少共享冲突,增加效率,而不是对所有属性都进行 double buffering。
但 surfels 不可能无限增多(存储空间有限),还需要有一定的回收机制,判断一个 surfel 是否应当回收取决于因素(启发式 heuristic):
数据结构实现:
接下来就是如何生成 surfel 的问题。
SIGGRAPH 2021 GIBS 采用了基于屏幕的 surfel 放置方案:将屏幕划分为 16*16 个 tiles,每个 tile 覆盖的 surfels 数量如果太少了,则在该 tile 中最少 surfel 的屏幕像素点来生成新的 surfel。
为了生成 surfel,我们需要根据访问屏幕像素对应的 G-Buffer 属性(transform ID,world normal,depth),并以此来初始化 surfel 的几何结构:
此外,对于 Skinned Mesh 情况则需要额外处理,因为它和一般的 rigid geometry 不同,它的表面是会形变的,不方便贴 surfel 上去。
解决方式:贴在权重最大的骨骼上而不是 mesh 上,因此针对 Skinned Mesh 情况,要使用 bone id 而不是 transform id;尽管这种解决方式不准确,但是实际表现出的效果是可接受的。
此外,我们还希望生成的 surfel 符合 LOD 思想(即远处的 surfel 没必要那么高质量那么精确),规定其在屏幕空间的投影大小需要大致相同,也就是说场景远处的 surfel 半径会很大,而近处的 surfel 半径会很小。
目的:希望通过一个空间加速结构,输入一个世界坐标可以快速查询到邻近的 surfels
难点:如何合理安排一个存储结构,既能保持高效查询,又能不耗费太多存储
列表可采用 index + offset 表示,而非使用固定长度的列表,这样可以进一步节省存储空间
理想情况下,1 个 surfel 的半径大小不应该超过 1 个 grid 的大小,否则可能会覆盖到过多的 grid,因此为了避免场景远处生成的 surfel 半径过大覆盖了太多 grids,SIGGRAPH 2021 GIBS 采用了 grid 的变种方案:
一般来说在每帧下,每个 surfel 需要生成相同次数的 ray,但是我们可以对比较重要的 surfel 多生成些 rays。
Modified Exponential Moving Average Estimator, MSME [2019] :如果 surfel 的 irradiance 变化很小,那么说明基本收敛,则少生成些 ray;如果 irradiance 变化很大,那么应该在本次加多些 ray 的次数。这样在同等性能下,可以实现更快适应场景变化。
GIBS 采用了 MSME 的实现方式,引入长期平均值(mean)和短期平均值(short mean),用这两者的相差体现 irradiance 的变化程度。
实际上在当前帧,一个 surfel 要发出 ray 的数量应综合取决于以下因素:
重要性采样,即让 ray 更大概率地指向 pdf 比较大的方向,除于 pdf(归一化)后得到更快收敛的积分值。pdf 越接近原分布函数,则越快收敛。
BRDF pdf:使用 cosine PDF 来做 diffuse 的 importance sampling
Lighting pdf:利用球面方向映射八面体的方式,来记录各立体角方向的历史累积 irradiance,做成一张 radial irradiance map
根据 pdf 选取 texel 时,可使用层次化积分优化(即将 irradiance map 做成 mipmap,先遍历大步再遍历越来越小的步),原本遍历所有 texels 的复杂度 \(O(n)\) 便可以降低为 \(O(logn)\):
每个 surfel 对应生成了若干个 rays(根据 importance sampling 来生成方向和 pdf)后,我们就需要开始收集光照并计算出 surfel 的 irradiance。
注:因为我们要做的是 indirect lighting 效果,因此 surfel 的 irradiance 只包含间接光照,而并不包含直接光照。
对每个生成的 ray,从 surfel 出发,射出后得到第一个 hit point:
一次间接光照:从 hit point 出发,再往光源发射 shadow ray(若到达光源前命中物体,则说明可见性 \(V\) 为 0,即该光源没有贡献光照),计算出 hit point 的 direct lighting
\[E_{dir}(x) = \sum_{j = 0}^{lightNum}{E_{light[j]}(x)* V(x \to {light[j]})}
\]
wicked engine 在实现一次间接光照的时候,并没有对所有光源计算光照贡献之和;而是采用了多光源俄罗斯轮盘赌的方式,随机从光源列表抽一个光源出来计算贡献,并将这个光源的光照贡献乘于光源数量来作为结果。
\[E_{indir}(x) = \frac{\sum_{k=0}^{nearSurfelNum} \mathrm{DistWeight}(x_{nearSurfels[k]},x) * E_{nearSurfels[k]}}{ \sum_{k=0}^{nearSurfelNum}\mathrm{DistWeight}(x_{nearSurfels[k]},x) }
\]
接着就能计算出本次 ray 的 radiance:
\[L(i) = \frac{(E_{dir}(x_i)+E_{indir}(x_i)) * \mathrm{albedo}(x_i)}{\pi}
\]
其中,\(i\) 代表了第几个 ray,\(x_i\) 代表了第 \(i\) 个 ray 的 hit point 位置。
当所有的 ray 都计算出 radiance 后,就可以计算出 surfel 在本帧的 irradiance:
\[E_{surfel} = \sum_{i=0}^{rayCount} \frac{L(i) \cos(\theta)/pdf(i)}{rayCount}
\]
比较粗暴的方法就是使用固定的历史 irradiance 混合权重(一般为 0.8~0.9),但可以考虑根据 estimator 的评估来作为 temporal 的混合权重参考:
例如 irradiance 变化程度很大时,我们认为历史帧参考意义不大,因为历史混合权重应当小些;相反,就证明基本收敛,就调大些历史混合权重。
当 surfel irradiance 变化过大时(远远没有收敛,例如发生在刚生成新的 surfel 时),我们可以多多参考附近的 surfel irradiance,相当于做了一次空间滤波。
实际操作就是利用空间加速结构来快速查询该 surfel 周围的其它 surfels,将它们的 irradiance 按以下因素来加权混合:
生成 surfels:基于屏幕空间
更新 surfels:对 surfels 容器进行遍历
ray tracing:对 ray buffer 遍历
integrate:对 surfels 容器遍历
pixel 着色:基于屏幕空间
Surfel GI 的优势:
Surfel GI 的缺点:
ps:PathTracing和自己的实现对比图(仅对比间接光照效果,无直接光)
手机扫一扫
移动阅读更方便
你可能感兴趣的文章