目录
零、写在前面
最近从头开始学习3dgs技术,看完3dgs文章后梳理了一下,整个梳理和理解都是以实现为目的进行的,目前还没看作者的代码细节,只通过文章叙述来理解了一下整个算法的思路。
先上一个pipeline图
![](https://i-blog.csdnimg.cn/direct/0e68edf7eaec4eb5aa02a9551c791739.png)
3dgs的算法主要有三个部分:
-
场景的3dgs表示;
-
高斯球的快速可微光栅化渲染器的实现,首先是要可微,其次是要快速;
-
高斯球的自适应操作,包括删除,分裂和复制三种类型的操作;
一、3dgs场景表示
3dgs场景的基本元素是高斯球,也就是一个3d的高斯分布,作为变量的属性值包括μ(高斯分布的均值),Σ(高斯分布的协方差矩阵,分解为缩放矩阵S和旋转矩阵R,Σ = RSS^TR^T,S由一个3d vector表示,R由四元数表示),α(高斯球的透明度opacity),c(高斯球的颜色)。但实际上颜色c是由球谐函数SH表达的,这样能产生视角相关的颜色变化,而要优化的属性值就成了球谐函数的系数
(对于球谐函数表达本文并没有叙述细节,需要另外查询资料来补充细节)。这几个属性值都是相互独立的,在优化过程中比较重要的是求微分(可微渲染),在求微分时属性值的相互独立能够让链式求导更加容易和清晰。
二、对于可微光栅化渲染器的正向渲染与梯度反向传播
主要分为三部分,1. 世界坐标系转变到屏幕空间;2. 着色与混合(blending);3.Gaussian splats光栅化。
-
坐标变换
去中心化的3d高斯分布 G(x) = e^(-1/2 * (x)^T * (Σ^-1) * (x));正向渲染:对于每个高斯球的坐标变换分解为对中心点μ和对去中心化高斯分布分别进行坐标变换。viewing transformation记作矩阵W,projective transformation的仿射变换近似记作矩阵J(Jacobian),此时变换后的协方差矩阵Σ' = JWΣW^TJ^T,变换后的中心点 μ' = JWμ; (细节实现可以再看EWA volueme splatting确认一下)另外如前所述,Σ = RSS^TR^T是由一个3d vector s得到缩放矩阵S和一个四元数q得到旋转矩阵R。反向梯度传播:paper的附录A有很详细的推导,公式太多就不敲了,如果要实现直接去翻译公式就好,导数的解析形式减少了自动微分的大量开销 -
着色与混合模型
-
着色:根据给定的viewing transformation,求得球谐函数的值作为高斯球的颜色 (需要另外查询资料来补充细节);
-
混合:成像模型 C=Sum_(i∈N){c_i * α_i * Multi_(j=1)_(i-1){1-α_j}};这里c_i是上一步着色中得到的颜色;α_i是由投影到2d的高斯分布G'(x)乘透明度α得到,α可以和颜色一样是球谐函数表示,也可以是一个一维的变量;而i∈N表示我们是在对有序的N个高斯球进行blending,这里的N个有序高斯球由下文中Gaussian splats光栅化获得。
-
-
Gaussian splats 可微光栅化渲染
三个关键词,tile-based, fast, differentiable-
划分16 x 16个tile,只保留与视锥相交超过99%的高斯球(为了方便应该是直接筛除中心靠近近平面和远离视锥的高斯球)
-
对于跨过多个tile的高斯球多实例化,每个实例赋予在view space的深度和tile ID结合的key值,将key值使用GPU Radix sort进行排序 (排序这块儿需要再查资料确认细节)(这里应该是用高斯球的中心深度做一个简单的深度测试,后续光栅化和混合用的就是这个深度,理论上讲对于每个像素来讲深度测试可能并不准确,不过感觉应该是一个计算量的trade off,而且应该应该是为了光栅可微的一个近似),然后每个tile生成一个由近到远的高斯球序列
-
正向渲染时,对每个tile启用一个线程block,先合力将高斯球数据加载到共享内存,然后每个tile分别对每个像素遍历高斯球序列,累计颜色和透明度直到透明度达到阈值,累计方式见前面的成像模型。每个像素储存其最终透明度和深度,用于后续梯度反向传播时的计算。
-
梯度反向传播时,复用每个tile的高斯球序列,但是要从后往前遍历。对于每个像素,从小于等于其深度的高斯球开始进行相交测试和梯度计算。在计算梯度时某些系数需要用到当前已累积的透明度值,这里通过剥离在处理的高斯球的透明度得到当前的累积透明度。 (梯度计算的公式细节仍需要仔细推到一下,文中没有写的很明确)
-
三、高斯球的自适应操作
对于高斯球的操作是没100次(可调整)迭代会进行一次,所做操作是依据待优化变量和变量的梯度进行的
-
删除
删除透明度α太低(小于阈值),或体积太大,或偏离太远的高斯球 -
加密
∇_p大于阈值时(view space中位置的梯度),表示高斯球不能很好的表示该重建区域,需要进行加密-
cloneunder-reconstruction情况,也就是高斯球体积较小不足以表达该区域,需要通过复制一份高斯球,并沿着位置梯度方向来放置新的高斯球
-
splitover-reconstruction情况,也就是高斯球体积较大无法表达细节,需要将原来的高斯球分裂成两个1/1.6大小的高斯球,位置由PDF采样进行初始化 (不太懂这个是什么意思,概率分布相关?)
-
-
特殊处理
相机附近的一些悬浮高斯球可能会让结果陷入不好的解而使得整体优化停滞(体表达的通病),在3dgs的方法里面具体表现就是会不受控制的高斯球加密,所以每3000次迭代,需要将这些高斯球的透明度α置为接近与0
四、最后附上一个整体的算法流程,对应文初的pipeline
五、优化的几个细节
1.主要方法是Stochastic Gradient Descent;
2.对于α使用sigmoid激活函数来约束它到[0,1)并获得光滑梯度;
3.对于协方差矩阵的scale使用指数激活函数进行约束;
4.协方差矩阵的初值设为各向同性高斯分布,而且轴长等于距离最近的三个点的均值;
5.对于中心位置μ的学习率使用标准指数衰减策略;
目标函数 L = (1 - 𝜆)L_1 + 𝜆L_D-SSIM,
𝜆取0.2
;
6.实际优化时会先用低分辨率进行,1/4分辨率迭代250次,然后上采样到1/2分辨率迭代到500次,然后再上采样到原始分辨率进行后续优化;
7.球谐函数系数的优化也是会随着迭代次数增加来添加表达细节,这部分需要等了解完球谐函数以后再返回来确认细节
7.球谐函数系数的优化也是会随着迭代次数增加来添加表达细节,这部分需要等了解完球谐函数以后再返回来确认细节
六、结尾的碎碎念
现在年纪大了脑子不太好了,看很多文章都需要一边看一边写点东西记录才能慢慢理解,到理解差不多了就顺手把阅读笔记整理下发出来了,方便后期查阅。
文中很多东西都是我个人的一个粗浅理解,可能比较片面而且甚至可能是错误理解,希望大佬们不吝赐教,能够帮忙指正。
后续的话还会看一些3dgs相关的工作,主要集中在效果改进,动态场景构建,结果编辑和数字人应用这几个方面。