为什么选择 3D 高斯椭球,而不是其他基元
因为高斯核有很好的数学性质:
- 仿射变换后高斯核仍然闭合(结果依然是高斯)
- 3D 降维到 2D 后(沿某一个轴积分)依然是高斯
3DGS 为什么是椭球(实心椭球)
协方差矩阵如何控制椭球形状
\(\Sigma = A \cdot I \cdot A^{T}\) 可以看成对一个单位球做仿射变换,最后得到椭球。
仿射变换:本质上就是一个旋转矩阵乘一个缩放矩阵 A = RS
因为这里的 \(\Sigma\) 是对称矩阵,而对称矩阵的特征向量矩阵是正交矩阵,所以 \(U^{T} = U^{-1}\)。
3D 高斯观测变换
3D 高斯投影到 2D 平面
每一次变换中,均值与协方差矩阵的变换表达式
从世界空间中的 3D 高斯出发,一路跟随视图变换和投影变换,到最终图像空间中的 2D 椭圆高斯,在每个阶段都给出
\[ X \sim \mathcal{N}(\mu, \Sigma) \quad\Rightarrow\quad X' \sim \mathcal{N}(\mu', \Sigma') \]
步骤 0:原始 3D 高斯分布(世界空间)
- 均值:\(\mu_0 \in \mathbb{R}^3\)
- 协方差:\(\Sigma_0 = R S^2 R^\top \in \mathbb{R}^{3 \times 3}\)
步骤 1:应用视图变换(相机外参),从世界坐标到相机坐标
令 \(W \in \mathbb{R}^{3 \times 3}\) 为视图变换的旋转部分,\(d \in \mathbb{R}^3\) 为平移,则
\[ \mathbf{x}' = W \mathbf{x} + d \quad\Rightarrow\quad \begin{cases} \mu_1 = W \mu_0 + d \\ \Sigma_1 = W \Sigma_0 W^\top \end{cases} \]
步骤 2:透视投影变换(相机内参)
用非线性函数把相机空间坐标 \(\mathbf{x} = (x, y, z)\) 映射到图像空间:
\[ (u, v) = m(\mathbf{x}) = \left( f_x \cdot \frac{x}{z} + c_x,\; f_y \cdot \frac{y}{z} + c_y \right) \]
投影后均值直接代入即可:
\[ \mu_2 = m(\mu_1) \]
投影后协方差因为是非线性变换,需要用 Jacobian 做一阶近似。先计算雅可比矩阵
\[ J = \left. \frac{\partial m(\mathbf{x})}{\partial \mathbf{x}} \right|_{\mathbf{x} = \mu_1} \in \mathbb{R}^{2 \times 3} \]
然后变换协方差矩阵
\[ \Sigma_2 = J \Sigma_1 J^\top \]
α Blending
Note:上图中假设了 \(I_{BK}\) 是黑色,也就是全为零。
\(\alpha\) Blending 在数学定义上是一个从远到近融合的过程。
数学上 α Blending 等同于体渲染
两者做法完全不同,前者速度比后者快很多,但公式可以互相对应。
注意:代码中实际是由近到远做 alpha blending
这一点容易让人疑惑——传统 alpha blending 要求从远到近,为什么 3DGS 的实现反过来?
传统 alpha blending 的前提:半透明物体需要按从远到近的顺序渲染,才能正确叠加颜色和不透明度。
3DGS 的特点:每个高斯点代表一个小的半透明面片,数量非常大,而单个点的不透明度通常很低。渲染时需要在性能和视觉效果之间平衡。
采用从近到远的原因:
- 性能优化:从近到远渲染可以利用提前终止(Early Termination)。当累计不透明度达到一定程度时,后续高斯点对结果影响很小,可以直接跳过,节省计算。
- 视觉误差可控:由于每个高斯点的不透明度很低,后续点对最终像素颜色的贡献很小,因此从近到远导致的误差在可接受范围内。
具体做法是维护累计不透明度 \(A\),初始化 \(A = 0\),对每个高斯点更新像素颜色并累加不透明度:
\[ \begin{aligned} C_{\text{out}} &\leftarrow C_{\text{out}} + (1 - A)\,\alpha_i\, C_i \\ A &\leftarrow A + (1 - A)\,\alpha_i \end{aligned} \]
当 \(A\) 接近 1 时,认为该像素已经完全不透明,可以提前终止后续计算。
在实现中,排序和渲染的流程如下:
- 排序高斯点:按从近到远的顺序排序。
- 渲染循环:遍历排序后的高斯点,累积颜色和不透明度。
- 提前终止:当 \(A\) 超过阈值(如 0.99)时跳出循环。
1 | float A = 0.0f; // 累计不透明度 |
为什么不采用从远到近:从远到近时,先处理的远处高斯点对累计不透明度贡献很小,无法有效提前终止,必须处理所有高斯点,计算量巨大;而换成从近到远带来的颜色混合误差,由于单点不透明度很低,是可以接受的。
小结:渲染顺序从近到远,混合方式采用累计不透明度 \(A\),通过提前终止减少计算量,在保证性能的前提下尽可能接近正确的渲染结果。
每个 3DGS 椭球的参数
| 参数 | 表示 | 维度 |
|---|---|---|
| 中心点位置 | \((x, y, z)\) | 3 |
| 旋转 | \(R\)(四元数) | 4 |
| 缩放 | \(S\) | 3 |
| 球谐函数系数 | SH | \(16 \times 3 = 48\) |
| 透明度 | \(\alpha\) | 1 |
总共 59 个系数决定一个 3D 高斯。
Densification 判据
判断是否需要分裂/克隆高斯时,用的是损失对 2D 投影均值的梯度在所有视角上的累积:
\[ \text{grad2d}_i = \sum_{t=1}^{T} \left\| \frac{\partial \mathcal{L}}{\partial \mathbf{m}_{2D,i}^{(t)}} \right\|_2 \]