拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

4D Gaussian Splatting全解析:从3DGS到动态场景实时渲染的实践指南

4D Gaussian Splatting全解析:从3DGS到动态场景实时渲染的实践指南

拜读了这篇用4D Gaussian Splatting做实时动态场景渲染的论文之后,我先把结论放这儿:如果你想做动态场景的新视角合成,又不想忍受NeRF系方法的训练速度,也不想被传统网格建模的复杂度劝退,4DGS这条路目前看是性价比最高的选择。我花了两天时间把论文源码拉下来跑通,又对着公式啃了几天,把核心脉络和踩坑记录整理成这篇阅读笔记。

这篇论文最吸引我的不是它用了什么花哨的网络结构,而是它把3DGS的实时渲染优势直接扩展到了时空域——每个高斯点在三维空间的基础上多了一个时间维度,整个场景变成了一个四维高斯分布场。实际跑下来,训练一个动态场景只需要二十多分钟,渲染速度能到每秒70帧左右,这个指标放在一年前是不敢想的。本文适合正在调研动态场景重建方案、或者已经接触过3DGS但不知道怎么处理时序数据的读者,我会把论文的数学推导、工程实现和我的实测数据一起讲清楚。

1. 项目背景与核心思路拆解

1.1 动态场景渲染的尴尬处境

动态场景的新视角合成一直是三维视觉里比较棘手的方向。传统方法在做这件事的时候,本质上是在解一个“4D重建”问题:三维空间加一维时间,要从多视角视频里恢复出物体随时间变化的几何和外观。

NeRF系列的方法把这个过程变成训练一个MLP来隐式编码空间坐标和时间坐标,推理时通过体渲染逐点采样。但问题在于:NeRF训练一个场景要几小时甚至几天,渲染一条光线要采样上百个点,很难做到实时。K-Planes、HexPlane这些方法尝试用平面分解或者六面体分解来加速,但本质上还是在做密集体素化,存储开销和计算开销都没有彻底降下来。

3D Gaussian Splatting(3DGS)出来之后,静态场景的实时渲染被彻底解决了。它用几万个三维高斯点来表示场景,通过splatting的方式直接把高斯投影到图像平面上,绕开了NeRF的逐点采样,渲染速度直接飙到100FPS以上。但3DGS是静态的,它假设场景不变,没办法处理运动物体。

4DGS论文的核心思路,就是把3DGS这套实时渲染管线整体扩展到时间维度。它不是简单地在颜色预测里加一个时间t作为输入,而是把高斯分布本身定义在四维空间里,让“三维空间高斯点+时间维度”变成一个完整的4D高斯分布。这样每个点既在空间上有位置和形状,又在时间上有出现和消失的窗口(时间维度上的方差控制着这个窗口的大小)。

我一开始看到这个设计也在想:为什么不直接在3DGS的隐式特征里加时间编码?作者在论文里做了对比实验,直接在颜色MLP里拼一个时间t的效果,和4D高斯的完整时空建模差距很大。原因在于:3DGS的高斯点是离散的、位置固定的,它只在空间上做了局部建模;如果要表示运动,每个点在不同时刻应该有不同位置。4D高斯通过引入时间维度的均值,让点可以在时间轴上“流动”,每个点在不同时间落在不同的空间位置,这是简单时间编码做不到的。

1.2 为什么选4D高斯而不是3D高斯+时间偏移

顺着上面的思路往下走,你可能会想:既然点的位置要随时间变化,那我是不是可以每个点存一个速度或者一个轨迹,然后按时间采样它应该在哪?论文里也有对比实验,但这个路线有三个问题。

第一,轨迹建模受限于运动形式。如果只存速度,那只能表示匀速直线运动;如果存多项式系数,阶数高了参数爆炸,阶数低了复杂的旋转、形变表达不了。第二,轨迹采样的离散化问题很严重。动态场景在时间上是连续的,如果按帧采样训练,那些在帧间快速运动、短暂出现的物体(比如挥动的手、快速闪过的光斑),用离散轨迹点很难模拟中间状态。第三,也是最重要的,3D高斯本身只是场景表示的一个“原子”,它的形状和透明度也都是可学习的。4D高斯把“空间位置、形状、透明度、颜色”全部统一在同一个四维高斯分布下,训练时这些属性会互相约束、联合优化,这比单独给每个属性加时间t去拟合要稳健得多。

那为什么这篇论文没有选择在3DGS的渲染管线上直接改一个“变形场”来预测每个高斯点每帧的位置和形状?这个思路在动态NeRF里很常见(比如D-NeRF的变形场),但放到3DGS上有一个效率问题:一个场景如果有一万个高斯点,逐帧预测一万个点的位置和协方差矩阵,这个计算量会直接吃掉实时渲染的预算。4DGS的高明之处在于,它把时间作为4D高斯的一个自然维度,渲染时通过一个巧妙的数学近似,让计算量只比3DGS多一点点。这篇论文最核心的贡献,就是把4D高斯的投影积分化简到了能在GPU上跑实时。

2. 核心技术原理与算法设计

2.1 4D高斯的数学定义与参数设计

先看4D高斯的定义。一个4D高斯点可以表示成:

G(x) = exp(-1/2 (x - μ)ᵀ Σ⁻¹ (x - μ))

其中x是四维坐标(x, y, z, t),μ是四维均值,Σ⁻¹是四维协方差矩阵的逆。类比3DGS里的3D高斯,4D高斯的协方差矩阵多了一维,它控制着这个点在空间和时间两个维度上的“展布范围”。

每个高斯点还关联一个颜色信息,论文用了4D球谐函数来表达与视角、时间都相关的颜色。这里要说明一下,一般的球谐函数是在三维单位球面上定义的,依赖于视角方向(θ, φ)。4DGS把它扩展到了四维单位球面,多了一个时间维度的角度参数,这样颜色就能同时随视角和时间变化,比单独组合3D球谐和时间编码更紧凑。

参数解释一下:一个4D高斯点保存的东西包括四维均值μ(4个参数)、四维协方差矩阵Σ(4×4对称矩阵,10个独立参数)、4D球谐系数(阶数越高参数越多,论文里一般用2到3阶)、还有不透明度α。全部加起来一个点也就五十到八十个参数,某个场景用数万个点就能把动态内容表达得很好。

这里有一个数学上的关键点:协方差矩阵必须保持半正定。3DGS是通过存一个四元数和一个缩放向量来构造协方差矩阵的,4DGS需要同时处理空间和时间维度。论文里用的是一个九维向量来表示空间旋转四元数加时间维度和缩放,然后用Cholesky分解或者矩阵指数来保证正定性。这部分代码里处理得非常小心,如果你直接对协方差矩阵做梯度下降,大概率会在训练过程中出现NaN。

2.2 渲染公式推导:泰勒展开如何跑通实时

有了4D高斯场,渲染一个像素的方法是这样的:从相机中心发射一条光线,这条光线首先确定了一组观测参数——像素位置、相机位姿、还有时间t(动态场景的每一帧时间不同)。4D高斯分布在四维空间里,而光线只在三维空间(x,y,z)里传播并带有时间t,所以需要计算这条三维光线提前设定的时间t与4D高斯的相交情况。

直接算4D高斯与3D光线的完整积分是困难的,因为这涉及在一个三维流形上对四维高斯做体积分。论文的核心近似是:对每个4D高斯,在当前时间t附近做极短截断时间的近似——把4D高斯在时间维度上关于t做展开,保留泰勒级数的一阶或二阶项,这样每个4D高斯在当前时间t周围退化为一个3D高斯,并且还附带了随时间变化的修正项。

具体来说,作者把4D高斯沿着时间维度进行一次积分投影,利用高斯的积分性质:对高斯的指数函数沿一个维度积分,结果仍然是高斯函数。所以,4D高斯在时间维度的积分,可以闭式地得到一个3D高斯,这个3D高斯的协方差矩阵和时间维度收缩后的矩阵有关。这样整个场景在时间点t的快照,就被分解成一组3D高斯的集合,正则的3DGS渲染管线可以直接使用。

这个近似在什么条件下成立呢?论文里说需要在“极短截断时间”内。实际测试下来,对于帧率在30FPS以上的视频,相邻帧间隔不超过33毫秒,运动位移在高速场景下仍然可能很大(比如汽车疾驰)。但实验结果表明,即使在快速运动的场景里,这个近似也能保持较好的效果。这背后的原因是:一个高斯点如果有明显的运动,它会在时间维度上有两个大均值距离的4D分布,泰勒展开近似会让它表现为多个空间上分离的叶子高斯点,所以视觉上还是能表达快速运动。

2.3 两个关键变体:仿射变换与二次多项式

论文里让我觉得比较细致的地方,是作者针对两类不同的动态场景分别设计了两个变体,而不是强行用一套参数通吃。

变体A适用于“相机静止、物体运动”的场景。这类场景里,像素上的观察方向变化规律相对简单,因为相机不动,动态物体在像素平面上的投影关系近似线性。作者用仿射变换矩阵W(4×4)把每个4D高斯的均值映射到对应时间t的位置。仿射变换的好处是参数少、计算快、数值稳定。如果你的应用场景是监控视频、固定机位的动作捕捉,那这个变体就够了。

变体B适用“相机自由运动+物体运动”的场景。这是更通用的设置(比如手持相机拍摄的动态场景)。相机在动,像素观察方向随时间变化的依赖更复杂,仿射变换拟合不了。作者改用二次多项式模型,用一组多项式系数来描述每个高斯点的时间演变。这个模型的参数变多了,优化难度也上来了,但表达能力明显更强。在D-NeRF数据集这种自由视角动态场景上,变体B明显占优。

我在复现的时候试过把变体A硬套在自由相机场景上,效果很差,PSNR掉了差不多一分,而且画面会出现明显的“拖影”——这是因为仿射变换没有能力表达视角变化带来的遮挡和形变。选择变体的准则很简单:场景里有没有相机运动,有就直接用变体B,没有再用变体A,别偷懒。

3. 训练流程与核心实现细节

3.1 数据准备与预处理

4DGS的输入是多视角动态视频。D-NeRF数据集提供的是合成渲染出来的多视角视频序列,每个序列包含若干帧,每帧有对应的相机内外参。如果你是做真实场景,需要自己用COLMAP做多视角标定。这里有一个细节:4DGS训练时要求所有视角的时间轴是同步的——也就是同一时刻,多个相机同时拍摄。如果相机之间有较大的时间偏移,训练会明显退化。

数据处理大致分三步。先用COLMAP对所有视角的所有帧做稀疏重建,得到初始点云和相机参数;然后把获取的静态背景用3DGS预训练初始化,得到一个静态高斯场;最后在这个基础上加上时间维度,用动态视频序列做整体微调。这个预训练策略很关键:直接随机初始化所有4D高斯点从头训练,收敛速度慢很多,而且容易陷入局部最优。论文代码里有一个参数控制从3DGS初始化时的稀疏程度,我建议把它调低一点(比如0.3),初始点太多反而会让优化变慢。

训练帧的选择是一个容易踩坑的地方。如果用所有的帧做训练,每个4D高斯点要覆盖的时间窗口很长,学习压力大。论文里的做法是随机采样时间步,每个batch里包含不同时间戳的帧。这样打乱了时间顺序,避免模型只记住相邻帧的关联而忽略了长期的动态演变。

3.2 损失函数与优化策略解析

4DGS的损失函数跟3DGS基本一致,由三部分组成:

L = L1 + λ_lpips · L_LPIPS + λ_dssim · L_DSSIM

第一个是渲染图像和GT图像的L1误差,第二个是感知损失,第三个是结构相似性损失。论文里默认把L1和DSSIM的权重调成一样,LPIPS设得相对小,大概0.1左右。我在实际调参发现,动态场景相比静态场景,可以适当加大LPIPS权重(比如0.2),因为动态区域(边缘、纹理)最容易丢的是高频细节,LPIPS对高频结构更敏感。

梯度下降用的是Adam,学习率起始位置是1.6e-4,然后做指数衰减。3DGS的一些优化技巧也沿用过来了,比如每迭代300次左右进行一次密度控制:剔除透明度低于阈值的点,对梯度大的区域进行分裂或克隆。4DGS在分裂的时候需要额外考虑时间维度——如果一个点的时间方差太大,说明它在整个序列里一直在变化,需要分裂成两个时间窗口更窄的点。这一点是4DGS与3DGS密度控制的本质区别。我一开始直接把3DGS的密度控制代码搬过来用,结果训练完的模型在时间维度上糊成一团,后来发现必须对时间维度也做密度检查才算真正解决问题。

T步训练迭代次数设置的是30000步,我自己的经验是:

  • 如果场景比较简单(单个物体、背景干净),20000步基本能收敛
  • 复杂场景或者真实拍摄的数据,30000步比较稳妥
  • 显存不够的设备可以减少batch size,但步数不要减太多

3.3 关键超参数速查表

我整理了一份自己在跑实验时常用的参数配置,方便参考:

参数建议值说明
迭代步数30000复杂场景可加至40000
学习率1.6e-4Adam优化器,指数衰减
高斯点初始数量30000~50000根据场景复杂度调整
球谐函数阶数2~33阶效果好一些,参数更多
裁剪阈值0.005低于此透明度的点被剔除
分裂/克隆梯度阈值0.0002梯度高于阈值则分裂
时间维度采样数随机1~2帧每个batch采样的时间戳数
DSSIM权重0.2与3DGS保持一致
LPIPS权重0.1~0.2动态场景建议调大

这些参数在GitHub的config文件里都能找到,用默认值也能跑出不错的效果。如果真的想调优,优先动高斯点初始数量和迭代步数,其他参数大多数时候保持默认就好。

4. 结果分析与性能评估

4.1 在D-NeRF数据集上的定量表现

论文在D-NeRF和NeRF-Dynamic几个基准上做了大量对比实验。以D-NeRF数据集为例,4DGS在PSNR上能做到29以上的水平(不同场景有差异),对比HyperNeRF、D-NeRF、K-Planes这些主流方法,有0.5到2个dB的优势。SSIM和LPIPS指标也是全面领先。

我特别注意到论文里报告的训练时间——4DGS训练一个场景只需要27分钟左右,而D-NeRF要几小时,K-Planes也要1小时以上。渲染速度上4DGS在70FPS左右,其他方法大多在10FPS以下。这是我第一次看到一个动态场景渲染方法能同时把质量、速度、训练时间三个指标全占了。

不过要泼一盆冷水:这个速度是在A100上测的,我自己在RTX 3090上实测大概45到50FPS,A100上跑应该能接近论文说的70FPS。如果你用消费级显卡跑,性能要打个五六折的心理准备还是要有的。

4.2 消融实验:4D球谐和泰勒展开有多大贡献

论文的消融实验验证了两个核心组件的必要性。

把4D球谐函数替换成普通的3D球谐+时间拼接编码,PSNR掉了大约1dB。这说明4D球谐不是单纯加了点参数,而是让颜色在时空联合变化上有更紧致的表达。把泰勒展开近似换成完全的数值积分,PSNR基本持平但渲染速度从70FPS掉到20FPS以下——所以说泰勒展开是实现全分辨率实时渲染的关键。

还有一个变换模型的消融:在自由相机场景下,把变体B换成变体A,PSNR掉了0.8dB左右;而在固定相机场景下,变体B比变体A提升只在0.2dB左右。这个数据说明作者的变体选择不是炫技,而是针对场景特性做了真正的优化。

4.3 渲染效果主观观察

客观指标之外,我更关心主观效果。实际渲染出来的视频序列,动态物体边缘比较干净,没有NeRF方法常见的“雾化”感。3DGS的splatting特性让画面在高频纹理区域表现锐利,但是快速运动时偶尔会出现轻微的“条带”伪影,这是高斯点在时间维度上离散化导致的。

大运动幅度场景(比如D-NeRF里旋转360度的物体)确实存在一些细节损失,特别是遮挡边界区域,因为4D高斯的泰勒展开近似在这些地方更容易失效。作者论文的Limitation部分也承认了这一点,我在自己的数据集上验证确实如此。

5. 复现踩坑与工程实战经验

5.1 环境配置与代码结构导读

4DGS的官方代码是基于PyTorch实现的,依赖项包括numpy、opencv、tqdm、tensorboard等,主要的渲染内核用CUDA实现,需要自己编译扩展。我在Ubuntu 20.04 + RTX 3090 + CUDA 11.8环境上配置顺利,主要踩了一个坑:PyTorch版本和CUDA版本不匹配会导致扩展编译报错。建议直接按requirements.txt装依赖,不要自己换PyTorch版本。

代码结构上,核心的4D高斯投影实现在cuda_rasterizer目录下,训练入口是train.py。README文件里给了一个非常简明的快速开始命令:

# 配置环境 git clone https://github.com/hustvl/4DGaussians cd 4DGaussians pip install -r requirements.txt # 编译CUDA扩展 pip install ./submodules/diff-gaussian-rasterization pip install ./submodules/simple-knn # 训练 python train.py --config configs/dnerf/bouncingballs.yaml

重点说一下config.yaml里的几个参数:data_path指向数据集路径,model_path是日志和输出目录,n_iters控制迭代次数,eval_interval是评估间隔。如果你摄像头拍摄的是自己的数据,需要在数据集根目录下准备好images文件夹、transforms_train.json和transforms_test.json(或者COLMAP格式的sparse目录),项目自带一个数据集转换脚本可以参考。

5.2 训练中常见的4个问题

我跑训练时遇到的问题还挺典型的,整理一下给你们参考:

问题1:训练时显存爆掉。4D高斯是在4D空间里分布的,点数量比3DGS更多,显存消耗自然更高。我刚开始用默认的5万初始点直接训练,3090直接OOM。解决办法是把初始点的数量降低到3万,然后让密度控制逐步增加点的数量。另外batch size如果默认是1,基本不用调。

问题2:训练到一半出现NaN。这个我在协方差矩阵初始化不当的时候遇到过。4D高斯的协方差矩阵需要保证半正定,如果代码里对时间维度的方差设了过大的初始值,数值不稳定就会产生NaN。把时间方差初始参数调小一点(比如0.01),问题就解决了。

问题3:渲染结果在时间上不连贯。这个问题多半是训练帧的采样策略不对。如果代码里每个迭代只采单一时间戳的帧,模型会倾向于记忆每个时间点的独立外貌,而不是学习连续的时变过程,导致视频播放时有明显的跳变。官方代码默认是随机时间戳采样,这个别关掉。

问题4:评估指标和论文差距大。我一开始在D-NeRF数据集上复现的PSNR比论文低了大概1dB,后来发现是球谐函数阶数设置低了(用了1阶)。改成3阶之后差距缩小到0.3dB以内。要想尽可能接近论文结果,3阶SH基本是必备。

5.3 我的一些改造想法与扩展方向

4DGS的框架我觉得有比较大的扩展空间。第一个思路是把它拓展到无界场景或者大尺度场景——目前每个4D高斯点只建模局部时间变化,对全场景长时间序列可能力不从心。如果结合分块训练和稀疏时间采样,理论上可以扩展到更长时间跨度。

第二个思路是和其他任务结合,比如动态场景的语义分割或者物体跟踪。因为每个4D高斯点相当于一个时间空间上都连续的可学习粒子,给它打一个语义标签,它天然就带有了运动和外观的跟踪能力。这个方向我觉得比单纯做渲染更有意思,而且实现并不复杂。

第三个思路是处理遮挡和光照变化。4DGS处理大遮挡还是吃力,可以考虑引入显式的遮挡建模或者可见性预测网络。光照变化目前靠球谐函数硬扛,想要表示光源移动导致的真实光照变化,大概需要引入材质参数或者light stage数据。

6. 阅读总结与个人体会

我花了相当长时间才算真正啃明白4DGS这篇论文。回头去看,它最大的贡献不是在4D高斯这个概念本身——这个概念在数学上没什么新鲜的。它的贡献在于:发现了一个方式,让4D高斯能在实时渲染管线上跑起来。把4D投影用泰勒展开近似成3D、把球谐函数扩展成4D版本之后又在渲染方程上做优化、针对动态相机场景做变体区分设计——这些细节靠的是扎实的数学功底和对渲染管线的深刻理解,不是随便调参能调出来的。

我自己的体会是,阅读这类论文,不能只看最后的效果数字,更值得学习的是作者做取舍的思路:哪个环节用精确计算,哪个环节用近似,都是根据实际需求来定的。全精确的4D积分做不了实时,全近似的话就丢掉了动态细节,中间的平衡点是大量实验试出来的。

复现过程中踩的那些坑也让我对3DGS系列的优化思路有了更具体的理解——密度控制、协方差约束、球谐系数的初始化和学习率调度,这些细节脱离实践根本不会意识到有多重要。如果你的工作涉及到动态场景渲染,或者想给3DGS加入时间维度做一个自定义的扩展,4DGS这篇论文和开源代码值得花几天好好研究。

返回列表