Kerbl B, Kopanas G, Leimkühler T, et al. 3d gaussian splatting for real-time radiance field rendering[J]. ACM Transactions on Graphics (ToG), 2023, 42(4): 1-14.
3D Gaussian Splatting 是 Siggraph 2023 的 Best Paper,法国团队在会议上展示了其实现的最先进的场景渲染。该方法在训练时间和高质量的实时渲染之间实现了很好的权衡,在重建质量之高的情况下还能接入传统光栅化,优化速度也相当快。自从 3D Gaussian Splatting 横空出世,整个 NeRF 圈波涛汹涌,有望显著加速一系列 NeRF 类的工作 1。
3D Gaussian Splatting 和 NeRF 一样,所做的任务也是新视图的合成,但没有使用 ray tracing 的渲染方式,而是更偏向于光栅化的渲染。通过构建以协方差为主导的 3D Gaussian 点云,用 Splatting 的方式进行渲染,实现了高分辨率的实时渲染。
3D Gaussian Splatting 论文直接阅读会比较困难,本节参考 [NeRF坑浮沉记]3D Gaussian Splatting入门:如何表达几何,对需要用到的预备知识进行梳理。
更多参考资料如下:
传统的几何显式表达使用点云、体素、网格等方式,可以沿着存储空间遍历场景中的所有元素。几何隐式表达则是使用函数或神经网络来表示几何信息,而不是使用显式的几何对象。在经典的隐式几何表达 —— NeRF 中,使用一个多层感知机作为几何的隐式表示方法,输入三维空间坐标和观测视角,就可以输出对应点的几何密度和颜色。渲染场景时把光线上的一系列采样点加权积起来,就可以渲染得到一个像素颜色。
从表达方式来看,几何的隐式表达可以分为体积类表达和表面类表达两种:
目前市面上大部分大型游戏在计算机中都是使用三角网格进行三维显式几何表达,能够借助 GPU 进行快速渲染。但三角网格由于三角形之间或三个顶点之间都是离散的,强行优化网格也只能是顶点粒度的,难免维度爆炸。
NeRF 系列方法大多使用光线追踪 (ray marching),速度自然没法和成熟的显式几何+光栅化比。而NeRF 在参数优化时一个参数变化对几何的优化可能是一大块也可能是一个小细节。
而 3D Gaussian 使用的椭球形表示可以作为小型可微空间进行优化,不同 Gaussian 之间则能够像三角网格一样并行光栅化渲染。可以看成是在可微和离散之间做了一个微妙平衡。其实早期的 3D 游戏也曾尝试过以椭球为基础元素进行建模 2:
说到 3D Gaussian 3,第一反应是:
N
μ
x
,
σ
x
,
μ
y
,
σ
y
,
μ
z
,
σ
z
(
x
,
y
,
z
)
=
1
(
2
π
)
3
σ
x
σ
y
σ
z
exp
(
−
(
x
−
μ
x
)
2
2
σ
x
2
−
(
y
−
μ
y
)
2
2
σ
y
2
−
(
z
−
μ
z
)
2
2
σ
z
2
)
N_{\mu_x, \sigma_x, \mu_y, \sigma_y, \mu_z, \sigma_z}(x, y, z)=\frac{1}{(\sqrt{2 \pi})^3 \sigma_x \sigma_y \sigma_z} \exp \left(-\frac{\left(x-\mu_x\right)^2}{2 \sigma_x{ }^2}-\frac{\left(y-\mu_y\right)^2}{2 \sigma_y{ }^2}-\frac{\left(z-\mu_z\right)^2}{2 \sigma_z{ }^2}\right)
Nμx,σx,μy,σy,μz,σz(x,y,z)=(2π)3σxσyσz1exp(−2σx2(x−μx)2−2σy2(y−μy)2−2σz2(z−μz)2)
但这其实是分量
x
,
y
,
z
x, y, z
x,y,z 互相独立的特例,此时的模型坐标系和世界坐标系平行。不过这种特例可以帮助理解 3D Gaussian:试想一个实心椭球,将它沿某一条轴切开后会得到一个椭圆,这个椭圆其实是无限大的,每一层的密度都不相同,这个切面就是二维高斯分布的等线图:
3D Gaussian 也叫三维高斯分布或三维正态分布,是统计学中一种特殊的多维正态分布,通常用来建模具有连续性随机性的现象,如图像处理、统计建模、机器学习等领域。3D Gaussian 能够涵盖空间中任意形状的椭球,包括平移、旋转。3D Gaussian 在三维空间中定义了一个概率分布,标准模型数学表示如下:
G
s
(
x
)
=
1
(
2
π
)
3
det
(
Σ
)
e
x
p
(
−
1
2
(
x
−
μ
)
T
Σ
−
1
(
x
−
μ
)
)
G_s(x)=\frac{1}{(\sqrt{2 \pi})^3 \operatorname{det}(\Sigma)}exp \left( {-\frac{1}{2}(x-\mu)^T \Sigma^{-1}(x-\mu)} \right)
Gs(x)=(2π)3det(Σ)1exp(−21(x−μ)TΣ−1(x−μ))
其中
x
=
[
a
,
b
,
c
]
T
x=[a, b, c]^T
x=[a,b,c]T 是三维坐标向量;
μ
\mu
μ 是椭球的中心,控制椭球的位置平移;
Σ
=
[
σ
a
2
Cov
(
a
,
b
)
Cov
(
a
,
c
)
Cov
(
b
,
a
)
σ
b
2
Cov
(
b
,
c
)
Cov
(
c
,
a
)
Cov
(
c
,
b
)
σ
c
2
]
\Sigma=\left[σ2aCov(a,b)Cov(a,c)Cov(b,a)σ2bCov(b,c)Cov(c,a)Cov(c,b)σ2c\right]
Σ=
σa2Cov(b,a)Cov(c,a)Cov(a,b)σb2Cov(c,b)Cov(a,c)Cov(b,c)σc2
是协方差矩阵,控制椭球在三维方向的伸缩和旋转。
论文中的 3D Gaussian 表示去掉了指数部分前面的尺度系数(因为系数不影响椭球的几何形状)和均值(因为每个点云有自己的位置
p
p
p ),方便旋转放缩。于是 3D Gaussian 表示如下:
G
(
x
)
=
e
x
p
(
−
1
2
x
T
Σ
−
1
x
)
G(x)=exp \left({-\frac{1}{2}x^T \Sigma^{-1}x} \right)
G(x)=exp(−21xTΣ−1x)
因此只要确定了
Σ
\Sigma
Σ 就可以确定一个椭球的形状。但需要注意的是,三维高斯分布的协方差矩阵
Σ
\Sigma
Σ 必须是半正定的才有数学意义。论文中使用的是各向异性
Σ
\Sigma
Σ,即在不同方向上具有不同方差值。各向异性协方差更有利于优化,因为它能够适应不同方向的变化,具有更精确的建模、更好的参数优化和更紧凑的表示。
在介绍 Splatting 之前需要先介绍一下 光栅化 (Rasterization) :光栅化是将图形或图像的矢量数据转换为像素数据,从而能够在计算机屏幕上显示的过程。通过将图形转换为像素级别,计算机可以更容易地处理和显示图形,同时确保图像在屏幕上以高速率绘制。
光栅化时,先将三角网格映射到二维平面,然后使用采样函数获取三角形内的像素情况 4,再根据走样情况(如锯齿 Jaggies、摩尔纹 Moire Patterns、车轮效应 Wagon Wheel Illusion 等)进行反走样 5,最后使用顶点片元着色 6 或其他方法进行像素点着色 7。
光栅化是实现计算机屏幕上图形显示和渲染的关键步骤,能够以非常高的速度生成图像,适用于实时渲染,例如视频游戏和模拟器。
Splatting 是一种用于光栅化 3D 对象(如前文讨论的椭球)的技术。这些 3D 对象被映射到投影平面后得到的 2D 图形称为 splat,类似于一个点、圆、矩形或其他形状,就像雪球打在墙上留下的印记,能量从中心向外扩散并减弱。这个过程可以在 GPU 上并行处理,因为每个 Splat 之间是独立的。
Splatting 可以将三维空间中的点投影到二维图像平面上,这些投影的数据点以某种方式在图像上产生视觉效果,从而呈现在最终的渲染图像中。Splatting 通常并不是由传统的图形硬件管线 8 自动执行,而是需要相对复杂的 GPU 编程,通常由图形开发者在需要时自行实现。
整个模型的输入是 SfM 点云表示的一组静态场景的照片和相应机位,使用点云初始化一组 3D Gaussian 模型。 3D Gaussians 能够合理紧凑地表示三维场景,颜色由球谐系数 SH 表示。 通过交叉优化 3D Gaussian 模型参数和自适应控制得到场景表示。本文方法高效的关键在于 3D Gaussian 的光栅化,通过各向异性的 Splatting 和 α \alpha α-blending 技术,能够快速合成渲染图像。
引入 3D Gaussian 来表示场景,将其初始化为摄像机标定产生的 SfM 稀疏 点云。这样既能保持辐射场的理想特性以便优化,又能避免在空白空间中进行不必要的计算。
3D Gaussian 的 光栅化 (rasterization) 过程如下:先使用 Splatting 技术将 3D Gaussian 投影到 2D 图像,然后使用标准的 α \alpha α-blending 技术快速合成渲染图像。这意味着 3D Gaussian 的三维场景表示可以转换为二维图像,以供渲染和可视化。
α \alpha α-blending:一种用于合成两幅或多幅图像以获得混合效果的图像处理技术。在 α \alpha α-blending 中,每幅图像都被赋予一个权重参数 α \alpha α ( 0 ≤ α ≤ 1 ) (0 \leq \alpha \leq 1) (0≤α≤1),表示每幅图像在最终合成中的贡献程度,0 表示完全使用第一幅图像,1 表示完全使用第二幅图像,而在 0 到 1 之间的中间值则表示混合两幅图像。
α \alpha α-blending 可以用于各种图像处理任务,包括图像叠加、过渡效果、颜色校正等,以实现图像的合成与编辑。一个典型应用就是创建图像淡入淡出效果,其中一个图像逐渐淡出,同时另一个图像逐渐淡入,通过调整 𝛼 参数的值来实现平滑过渡。𝛼-blending 也可以用于将两个图像合成成一个,以产生具有新特性的图像,如合成景深效果、融合不同光照条件下的图像等。
交叉优化 (interleaved optimization) 3D Gaussian 的属性和 Gaussians 的自适应 密度控制 (density control),尤其是优化 各向异性协方差 (anisotropic covariance),以实现场景的精确表示。
可以优化的 3D Gaussian 点云属性包括:三维位置 p p p、透明度 α \alpha α(其实就是 α \alpha α-blending 中的权重参数)、各向异性协方差 Σ \Sigma Σ、球谐系数 SH (spherical harmonic coefficients) 等。
使用 GPU 进行快速、可微的渲染,支持各向异性 Splatting 和快速反向传播。因此能够加速训练和实时渲染。
鉴于 3D Gaussian 可微、便于投影到二维 splats、显示地支持快速渲染等优点,用其作为场景表示。使用一个三维的满协方差矩阵初始化 3D Gaussian:
G
(
x
)
=
e
x
p
(
−
1
2
x
T
Σ
−
1
x
)
G(x)=exp \left({-\frac{1}{2}x^T \Sigma^{-1}x} \right)
G(x)=exp(−21xTΣ−1x)
光栅化时需要将三维场景变换到二维空间,作者希望 3D Gaussian 在变换中保持分布(不然光栅完和高斯没关系的话一直以来的努力都白费了),这难免需要更多限制。View 变换
W
W
W 主要是旋转和平移,都是 仿射 (affine) 的 9,不会有问题;但 Project 变换不是仿射的,于是文中用
J
J
J 矩阵变换替代 Project 变换,
J
J
J 是 Project 变换的仿射近似。于是得到摄像机坐标下的协方差矩阵
Σ
′
\Sigma^{\prime}
Σ′:
Σ
′
=
J
W
Σ
W
T
J
T
\Sigma^{\prime}=J W \Sigma W^T J^T
Σ′=JWΣWTJT
但
Σ
\Sigma
Σ 不能直接优化,因为
Σ
\Sigma
Σ 必须是 半正定 (semi-definite) 的 3D Gaussian 才有意义。于是文中将
Σ
\Sigma
Σ 表示为球变换到椭球的过程:将球按轴向先放缩
S
S
S 再旋转
R
R
R(注意顺序,是先放缩再旋转,椭球的对称轴一直是模型空间的 xyz 轴,互相垂直,详见视频讲解 17:15
)。因此
Σ
\Sigma
Σ 初始化如下:
Σ
=
R
S
S
T
R
T
\Sigma=R S S^T R^T
Σ=RSSTRT
于是在训练过程中使用梯度下降对参数
Σ
\Sigma
Σ 进行优化时需要继续将梯度传递到
S
S
S 和
R
R
R 进行优化。
如前文所述,各向异性协方差更适合优化:能够优化 3D Gaussian 以适应场景中不同形状的几何形状,从而产生一个紧凑的表示。如下图所示,将 3D Gaussian 渲染的图像缩小 60% 后细节仍然清晰可见,说明其对复杂细节较强的拟合能力:
本文的核心是对 3D Gaussian 的优化,优化的目的是创建一组密集的 3D Gaussian 以精确地表示场景。优化的参数包括:三维位置 p p p、透明度 α \alpha α、各向异性协方差 Σ \Sigma Σ 和球谐系数 SH (spherical harmonic coefficients) 。这些 ⌈ \lceil ⌈参数的优化 ⌋ \rfloor ⌋ 和 ⌈ \lceil ⌈自适应控制高斯模型 ⌋ \rfloor ⌋ 交替进行。
由于 3D 投影到 2D 的模糊性,渲染图像中的几何体可能会被放错位置。因此,优化过程中需要能够创建几何体,并且在错误位置处销毁或移动几何体。
参数优化使用 SGD 连续迭代完成,每一轮迭代时都会渲染图像并将其与真实的训练视图做比较。
α
\alpha
α 使用 Sigmoid 激活函数来限制 (0, 1) 的范围;
Σ
\Sigma
Σ 使用指数激活函数激活;
p
p
p 使用指数衰减调度技术 (exponential decay scheduling technique) 进行优化。模型的损失函数是 L1 与 D-SSIM 项的组合:
L
=
(
1
−
λ
)
L
1
+
λ
L
D
−
S
S
I
M
\mathcal{L}=(1-\lambda) \mathcal{L}_1+\lambda \mathcal{L}_{\mathrm{D}-\mathrm{SSIM}}
L=(1−λ)L1+λLD−SSIM
在 3D Gaussian Splatting 中,场景表示是通过多个高斯模型叠加而成的。在早期迭代次数较少时,会出现 重建不足 (under-reconstruction) 的问题,即高斯模型没有完全覆盖小规模的几何体,此时需要复制高斯模型进行覆盖;在后期迭代次数较多时,会出现 重建过度 (over-reconstruction) 的问题,即高斯模型超出小规模几何体的范围,此时需要将该高斯模型一分为二。这就是自适应控制 Gaussians:
从初始化 Gaussians 为稀疏的 SfM 点云开始,通过 自适应地控制高斯模型的数量和它们在单位体积上的密度,逐渐从稀疏的高斯模型集合过渡到更密集且能够更好地表示场景的集合。该过程主要关注 under-reconstruction 和 over-reconstruction 的区域,即具有较大的视图空间位置梯度的区域。直观理解来看,是因为这些区域尚未完全重建好,因此优化算法试图移动高斯函数以进行修正。under-reconstruction 和 over-reconstruction 的区别是 over-reconstruction 区域的 Gaussian 方差大,因为数据的变化幅度较大。对于视图空间的位置梯度大于阈值 𝜏pos 的区域,需要对该高斯模型进行 稠密化 (densify) 操作:
然而,这种体积表示法在优化过程中可能会因为相机附近的漂浮物而陷入困境,从而导致高斯密度不合理的增加。一个有效的调节方法是每 3000 次迭代就将 α \alpha α 值设为接近零,然后再根据上述优化过程逐渐提高 α \alpha α 值。此外,每 100 次迭代就剔除透明的高斯分布(即 α \alpha α 小于阈值 ϵ α \epsilon_{\alpha} ϵα)以限制高斯模型的总数量。
直接 Splatting 显然没法实现高帧率的渲染效果,下面介绍文中是如何进行光栅化的。Gaussians 快速可微光栅化是为了快速实现整体渲染和排序,从而实现近似 α \alpha α-blending 并且不再限制能够接收梯度的 splats 的数量。为了达到目的,对 Gaussian splats 进行分块 (tile) 处理,将该光栅化过程命名为 基于分块的光栅化 (tile-based rasterization)。
首先将 2D 屏幕分割成 16×16 个 tile,然后为每个 tile 筛选视锥体 (view frustum) 内的 3D Gaussian:
根据每个 Gaussian 重叠的 tile 数量来实例化,为其分配 key 值(key 值结合了该 Gaussian 所在 tile 的 ID 和对应视域的深度);
使用 GPU Radix sort 根据 key 值对 Gaussians 进行排序(其实就是按高斯模型到图像平面的深度值);
传统光栅化渲染管线只能逐像素进行,将三角网格分解为像素,并经过颜色插值、深度测试等一系列的像素处理步骤,最终将渲染结果呈现在屏幕上。
将排好序的 Gaussians 从近到远向对应 tile 上做 Splatting。然后在每个 tile 上对高斯模型留下的 splat 做堆叠(类似 α \alpha α-blending,累积 α \alpha α 和 c \bold c c),直到所有像素的不透明度都饱和( α \alpha α=1);
为每个 tile 都单独开了一个线程快,因此每个 tile 上的光栅化过程可以并行执行。图像中某一个像素不透明度达到饱和后,线程块就停止对该像素的处理。
优化参数时,按每个 tile 堆叠的 splat 对应的 Gaussians 的顺序反向传播;
本节就是 3D Gaussian Splatting 的渲染过程。在合成新视图时,只需要指定一个视角,就可以使用视锥体筛选 3D Gaussian 点云,然后光栅化得到 2D 图像。
实验中使用了 13 个真实场景,来自以下数据集:
文中使用 PSNR、L-PIPS、SSIM 作为模型的评价指标,将 3D Gaussian Splatting 和 Mip-Nerf360、InstantNGP、Plenoxels 做了对比。定量结果见下表:
定性结果如图:
图像质量随迭代轮次逐渐提高,背景中伪影逐渐减少:
不同细节处理对应 PSNR 指标:
初始化 SfM 点云和随机初始化的效果对比:
自适应控制 Gaussians 的 densification 操作效果对比:
各向同性和各向异性 Gaussian 效果对比:
3D Gaussian Splatting 是一种用于实时渲染的光栅化技术,同 NeRF 一样,可以对从小图像样本集中学习到的逼真场景进行极其快速的渲染。但 3D Gaussian Splatting 本质与 NeRF 还是有比较大的区别,前者更偏向于光栅化的渲染,后者则有点类似于 ray tracing 的渲染方式。并且由于 3D Gaussian Splatting 的生成结果是 point cloud 的显式表达,因此在有遮挡物体建模、生成静态场景的动画等情境下更占优势。
3D Gaussian Splatting 无疑是继 InstantNGP 后,一种更高效的显 - 隐式相结合的 3D 空间表达方式。它的优势在于可以提供类似于点云的可存储的显式表达,对于各种设备更加友好。并且光栅化的渲染方式不需要光线追踪、路径追踪、Diffusion 等技术,就可以直接将模型信息转换为图像,相较于 ray tracing 的渲染方式也更加快。
但 3D Gaussian Splatting 也存在一些缺点:视角不足区域和反光区域容易出现伪影、未使用正则化、数百万个高斯需要几十个 G 的显存。
3D Gaussian Splatting 使用 Pytorch 框架,并自定义 CUDA 核用于光栅化,以及使用 NVIDIA CUB 排序例程进行快速基数排序。
(这里选用 A5000 显卡,因为 Cuda 版本要求 11.8,之前一直使用的 3090 显卡最高支持 11.5 版本。)
实验记录:
克隆仓库时,因为使用了 --recursive
选项递归处理目录,因此费时较长,耐心等待即可;
执行 environment.yml
脚本创建完虚拟环境后,使用 conda activate
指令无法激活环境。改为 source activate
即可;
下载可用的数据集,并将其上传至正确的位置以供调用。复现时使用的是 Tanks&Temples dataset 下的火车 (train) 场景;
如果想要训练时呈现实时渲染效果,需要执行 Interactive Viewers 下 SIBR 相关配置,详见 3D Gaussian Splatting for Real-Time Radiance Field Rendering论文中代码复现及排错过程;
训练时如果直接执行 Running 下的指令默认将所用可用图片都用来训练,如果想要评价模型(Evaluation 部分)需要划分测试集,即执行 train.py
时加上 --eval
选项;
训练完成后,output 文件夹下会保存 7k 和 3w 次迭代时对应的结果。再执行 render.py
后就会生成 3w 次迭代时 train 和 test 的渲染图像;
如果想要将渲染图像生成 mp4,需要执行 Colab 中最后两条 ffmpeg
指令:
ffmpeg -framerate 3 -i output/ac1d9199-0/test/ours_30000/renders/%05d.png -vf "pad=ceil(iw/2)*2:ceil(ih/2)*2" -c:v libx264 -r 3 -pix_fmt yuv420p output/ac1d9199-0/test/ours_30000/renders.mp4
ffmpeg -framerate 3 -i output/ac1d9199-0/test/ours_30000/gt/%05d.png -vf "pad=ceil(iw/2)*2:ceil(ih/2)*2" -c:v libx264 -r 3 -pix_fmt yuv420p output/ac1d9199-0/test/ours_30000/gt.mp4
执行 ffmpeg
指令期间出现 ffmpeg: error while loading shared libraries: libopenh264.so.5: cannot open shared object file: No such file or directory
问题,更新 ffmpeg 即可 10:conda update ffmpeg
;
实验结果:
实验证明,3D Gaussian Splatting 确实又快又准。仅仅经过十几分钟 3w 轮迭代,就可以完成高质量的三维重建。火车场景的真实视图和渲染新视图对比如下:
真实视图
渲染视图