ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

3D Gaussian Splatting从原理到实操:三维重建与实时渲染新范式

3D Gaussian Splatting从原理到实操:三维重建与实时渲染新范式

1. 从“点云到网格”到“直接画出来”:先搞懂它到底在做什么

这两年如果关注三维视觉或计算机图形学,很难绕开3D Gaussian Splatting这个词。我最初听到这个名词是在一次内部技术分享上,同事放了一段城市级扫描重建的Demo,视角丝滑切换、光影连续,实时渲染帧率拉满。我当时以为是某种优化过的NeRF,结果他纠正我说这玩意儿叫3D Gaussian Splatting,训练和渲染速度比NeRF快出几个量级,而且不需要神经网络的隐式表达,直接在场景里“画高斯基元”。听完当时就觉得,这事情值得仔细研究。

先说人话版本:3D Gaussian Splatting(后面简称3DGS)是一种显式的三维场景表示和渲染方法。它把现实世界或数字场景表示成一大堆“椭球状的高斯函数”,每个高斯拥有自己的位置、形状、颜色、透明度,然后用一套并行光栅化流程直接投影到屏幕上,按从近到远的顺序排序混合,实时得到照片级画面。与NeRF那种训练一个MLP网络通过体渲染逐点查询颜色的思路不同,3DGS走的是类似“泼溅”(Splatting)的老传统路线,只是它把所有细节都做成了可微的、可优化的组件,配合相机位姿和图像输入,就能通过梯度下降把场景重建出来。

这套东西解决了什么问题?一句话概括:在保持NeRF级别画质的前提下,把训练时间从小时级压到分钟级,把实时渲染帧率从“看幻灯片”拉到上百帧。它非常适合做实景重建、自动驾驶仿真数据生成、电商三维展示、数字人资产制作这些需要“又快又真”的场景。

这篇文章主要面向三类读者:想快速了解3DGS原理和流程的产品或技术负责人;打算用开源3DGS框架做重建实验的研究生或工程师;以及想评估这个技术是否适合自己的业务、但不想啃原始论文的从业者。我会尽量用大白话把原理讲清楚,然后用一个完整的实操流程演示怎么从一堆照片得到可交互的三维场景,最后把我在实际测试中踩过的坑和排查思路全部列出来。

2. 为什么是“高斯”而不是“体素”或“点云”:核心思路拆解

2.1 从隐式到显式的路线转变

NeRF系列的套路是“隐式表示”:场景被编码进一个MLP网络的权重里,渲染时对每个像素发出一条光线,沿着光线采样几十个空间点,逐个输入网络查询颜色和密度,再做积分合成。这套逻辑在画质上很能打,尤其是复杂光照和遮挡关系,但代价是两个致命的性能瓶颈:每条光线要查询几十次网络,训练时更是每个像素都要反复前向传播;而且网络是全局编码,无法做到真正的实时交互。

3DGS的思路完全反过来。它把场景“物化”成一组显式的高斯椭球,每个椭球都是独立个体,可以直接操作、裁剪、编辑。渲染时不需要对每个像素发射光线,而是把每个椭球投影到屏幕上形成一个椭圆斑,按深度排序后做alpha混合。整个过程本质上是“按序画叠加的半透明圆斑”,GPU的并行度极高,所以实时渲染非常轻松。

为什么偏偏是高斯基元而不是更简单的点云或更常见的三角网格?关键在“可微性”和“覆盖性”。点云没有体积,投影后是离散点,覆盖不了连续表面,而且无法表达半透明或散射效果;三角网格需要拓扑结构,重建时从离散点生成网格本身就有大量约束和误差。高斯基元自带一个“软半径”,每个椭球有一定空间范围,可以互相重叠、彼此融合,天然适合表达连续表面、反射高光、薄片结构,甚至毛发这类复杂几何。而且高斯函数的数学性质很好——它的形状和半径可以被优化,投影后的二维高斯也有解析形式,这使得梯度可以端到端回传,训练稳定。

2.2 一个三维高斯里到底装了什么东西

每个三维高斯由以下几组参数定义。位置(均值)是一个三维向量,决定了这个椭球在场景中的空间坐标。协方差矩阵是一个3x3的矩阵,决定了椭球的形状、缩放和旋转朝向。但直接优化协方差矩阵容易在训练中变成非半正定矩阵导致数值爆炸,所以实际实现中用“缩放向量+旋转四元数”来间接表达,再通过公式组合成协方差矩阵。每个高斯还有一个不透明度值,控制这个椭球对最终颜色的贡献权重。每个高斯还带一组球谐系数,用来表达视角相关的颜色变化,原理和NeRF里的球谐光照类似。低阶球谐(比如0到2阶)就能表达不错的反射高光效果。

这一大堆参数加起来,一个高斯大约需要几十个浮点数。一个中等规模的场景大概用几十万到上百万个高斯基元,听起来数量很大,但对显存来说完全可接受,因为渲染时只需要做投影和排序,不需要存储庞大的占用空间。

为什么说它是“可微泼溅”?因为从三维高斯到屏幕上的椭圆斑,中间是一个完整的可微计算链。每个椭球的中心点通过相机内外参投影到像素坐标,协方差矩阵经过雅可比变换得到二维协方差,从而得到一个二维高斯分布,也就是图像上的椭圆。然后椭圆按深度排序、按透明度混合。整条链路每个环节都能求导,所以输入图片的像素误差就可以一路反传回来,更新所有高斯的参数、颜色系数,甚至相机位姿本身。这就是所谓“端到端优化”的真正含义。

2.3 自适应控制:谁告诉你场景里需要多少个高斯

一个值得注意的细节是,3DGS并不需要预先生成固定数量的高斯。算法会在训练过程中动态增删和分裂高斯基元,这个机制叫作自适应控制。

直观理解就是:初始阶段先把稀疏点云(通常由SFM生成)变成一个初始高斯集合,每个初始点生成一个高斯;训练过程中,如果某个区域的高斯梯度很大,说明这个高斯不足以覆盖当前几何细节,就需要对它做“克隆”或“分裂”。克隆是在同一个位置复制一份,适合处理欠覆盖的情况;分裂是把一个大椭球切成两个小椭球,适合处理“太胖、跨度过大”的情况。同时,不透明度太低或者体积太扁的高斯会被周期性剔除,保证资源不浪费。

这个“自己生长、自己修剪”的机制,让3DGS在重建复杂场景时表现得非常从容——平坦墙面只需要少量大高斯基元,复杂植被和细小结构会自动长出密集的小高斯基元。整个优化过程在训练中动态调整基元分布,而不是像传统网格重建那样先定分辨率再费劲适应几何。

3. 从照片到实时场景:实操全流程与关键参数

3.1 采集数据的“下限”与“上限”

3DGS的输入非常简单——一组同一场景不同视角的照片,以及每张照片对应的相机内外参数。相机参数有两种来源:一是采集时用手机或相机的GPS、陀螺仪等记录粗略位姿,再用全局优化工具(比如博主常说的COLMAP)做稀疏重建精确估计;二是直接用已有数据集的定位信息,比如用NeRF社区常用的那几组公共数据集,参数都现成。

但不要低估数据采集的影响。我实测下来,照片的清晰度、重叠率、光照一致性对结果的影响,远大于模型框架本身的差异。一组质量好的采集数据,即使只用中等配置的显卡也能训练出很好的结果;数据不行,高端显卡也救不回来。推荐原则是:相邻照片的重叠率尽量保持在70%以上;绕场景一圈的覆盖角度要均匀,尤其是俯仰角,只拍平视和只拍俯视都不行;避免运动模糊和过曝高光;固定光圈快门,尽量保证整个采集过程光照稳定。也就是说,晴天正午的硬阴影和室内顶灯的硬暗部,都会对重建造成额外负担。

有条件的话,采集时顺手放几个标记物(棋盘格或Aruco码)会大幅提升COLMAP重建的鲁棒性。如果场景很大或环境纹理稀少,比如面对大面积白墙,采集时故意放一些有明显纹理特征的物体,或者在不同位置放置临时标记物,可以帮助特征匹配成功。

3.2 稀疏重建:拿到相机参数的关键一步

拿到图片后,第一步是跑稀疏重建,用COLMAP估计相机位姿并生成稀疏点云。这一步的具体操作是:把图片路径整理成标准数据集目录结构,然后用COLMAP依次执行特征提取、特征匹配、稀疏建图,最后将结果导出为模型文件夹或二进制文件。

实际跑起来有几个直接影响成功率的小细节。特征提取时,如果图片分辨率过高,建议先统一缩放到合理尺寸,控制在几百万像素级别,速度会快很多,而且对精度影响不大。匹配阶段,对连续视频抽帧得到的图片,可以使用顺序匹配模式(Sequential Matching),匹配效率会成倍提升;如果是乱序的真实照片,用词汇树匹配。稀疏建图是整条链路里最容易失败的环节,失败的表现通常是估计出的相机位置乱七八糟,或某一半图片全部丢失。排查时优先考虑:图片是否有足够的特征重叠、是否包含大量过曝区域、相机内参是否恒定。某一台手机如果开了镜头切换,不同焦段的照片混在一起,COLMAP经常直接崩掉。

3.3 训练参数怎么定

训练这一步用开源框架跑一个Python脚本即可完成。框架主要包括数据预处理、迭代优化、自适应控制三个环节,数据预处理其实就是把COLMAP输出的稀疏点云转换成初始高斯基元集合。

训练时最核心的参数有:迭代次数,常见的默认值是3万步,但这个值需要根据场景复杂度调整;学习率控制,位置和高斯基元形状的学习率通常是分阶段衰减的;稠密化阈值,决定何时触发克隆或分裂;球谐阶数,决定视角相关颜色的表达能力,默认用3阶球谐,对大多数实景足够了。显存充裕且场景复杂时可以将迭代次数适当提升,但要注意过拟合风险——训练轮次过多时,高斯基元会“过拟合”到特定视角,出现碎斑或闪烁。

我个人常用的配置是:先用默认参数跑一轮完整训练,观察测试视角的PSNR和视觉效果,然后针对明显模糊或空洞的区域,加大或减小最大基元数量上限,再跑一轮。多轮微调比一次狂调参数效果可控得多。

3.4 训练完成后的资产导出与实时渲染

训练完成的模型通常保存为一个后缀名包含“splat”的文件或者一个包含点云和属性参数的目录。这个模型可以在多个端侧实时渲染,比如手机App、Web端或游戏引擎。

Web端渲染是我在业务落地时最常用的方式。有个开源的前端渲染器可以直接加载训练好的splat文件,支持在浏览器里拖动旋转缩放,而且对普通笔记本GPU就能跑到60帧以上。部署方式就是把模型文件放到服务器,前端通过API加载即可。如果使用游戏引擎,比如Unity或虚幻引擎,官方插件也基本都支持直接导入该格式。需要注意的坑是:splat文件大小通常比同等画质的视频或纹理网格大不少,一个中等场景可能就有几百MB到1GB以上。做Web展示时必须考虑加载策略,比如先加载低分辨率版本、分块流式加载,或者用后端做量化压缩。我在实际项目里见过只图方便把1GB模型直接丢到网页上,结果首屏等了半天的教训。

4. 我踩过的坑:后期处理、训练速度与画质的矛盾

4.1 背景分割到底要不要做

如果拍摄对象是单个物体而不是完整场景,背景处理是个绕不开的话题。比如拍一台汽车、一件商品或一个人物,如果背景里有大量干扰物,重建出来的三维场景会包含很多无关高斯基元,不仅浪费资源,还会影响前景画质。

做法一般有两种:一是采集时用纯色幕布,拍完再后期抠图,输入网络的图片都是无背景的PNG序列;二是先用语义分割把每张图的掩码求出来,训练时只对前景区域回传梯度。我在实测中发现,第二种方式更灵活,但对掩码质量要求极高。掩码边缘一旦出现锯齿或空洞,训练结果里就会出现漂浮的半透明残影,后期反而更难处理。个人经验是:能控制采集环境就尽量控制,纯色幕布加简单的色键抠图,得到的结果往往比算法分割更干净。用语义分割做掩码,至少要把模型的分割阈值调到合适的值,并做形态学开闭运算填充空洞。

4.2 训练速度为什么时快时慢

同样是一个房间级场景,有些人说训练只要10分钟,我跑出来要半小时,差距主要在三个地方。第一是高斯基元数量。自适应控制机制会根据复杂度和分辨率自动调整基元数量,如果场景纹理特别丰富,基元数量会涨到几百万个,每一轮迭代的代价成倍上升。第二是图像分辨率。默认训练分辨率如果直接拉到原图尺寸,显存和计算量都吃不消。常见做法是先用一个较小的分辨率保证稳定,再加载到高分辨率做精细恢复。第三是迭代次数。默认3万步是平衡点,具体场景最好先小步预跑试一下,比如先跑5000步看损失收敛情况,再决定是增加还是减少。

我还遇到过一种比较隐蔽的慢:基元数量虽然不多,但球谐阶数设置得过高。高阶球谐意味着每个高斯要存储和计算更多系数,开销不是线性增长而是呈平方级增长。实景拍摄中,阳光直射的硬高光需要较高阶球谐才能表达,但大多数室内或阴天场景3阶球谐绰绰有余,再往上加很浪费。

4.3 画面闪烁和“漂浮物”的原因与处理

训练完看预览时,最常见的两类问题是画面闪烁和空间中漂浮的半透明碎片。

闪烁通常出现在观察视角变化比较大的位置。原因大概率是相邻高斯基元的深度排序不稳定:相机视角变化时,部分椭圆斑的先后顺序发生了频繁切换,颜色混合结果随之跳动。排查思路是检查球谐阶数是否过高导致过拟合、基元密度是否不足、相机位姿是否抖动。如果是自由视角视频,可以对相机轨迹做平滑处理,减轻排序跳变。

漂浮物通常是训练时背景分得不干净产生的“无处安放”的高斯基元。这些基元不透明度不高,但会在某个视角下聚合成团。遇到这种情况我一般先检查输入图片是否有遗漏的遮挡物或反射面,比如玻璃幕墙、车漆倒影,这些区域本身就很难用高斯表达;其次再考虑把不透明度过低的基元直接剔除,或者在训练后期对透明度分布加一个先验惩罚,避免低透明度基元到处飘。

4.4 特别容易忽视的“相机位姿优化”开关

部分3DGS框架里有一个可选项叫“姿态优化”或“相机位姿优化”。默认情况下它是开启的,意思是训练过程中不仅优化高斯参数,还会微调相机位姿本身。这个开关对COLMAP质量差值比较大的场景很有帮助,能补正一部分位姿误差;但如果输入位姿本身就很准,或者场景比较小,开着反而可能让位姿漂移,导致训练结果出现局部模糊。

我的经验是:小场景、单物体拍摄时,建议把姿态优化关掉,训练更稳;大场景、手持相机连续拍摄时,打开姿态优化往往能显著改善边缘对齐问题。如果你的照片是用手机连拍或视频抽帧得到的,COLMAP估计的位姿偶尔会出现系统性误差,这时姿态优化是“兜底”的好工具。

5. 如果没有COLMAP:训练时的几条备用路线

有些场景拿不到足够特征点去跑COLMAP,比如纹理极少的白墙房间、透明材质较多的瓶子等。这种情况还有几条备用路线。

第一条:用结构光或深度相机直接采集深度图。比如用带深度传感的相机扫一圈,把每帧的彩色图和深度图对齐,生成带深度信息的视频序列。有了深度图,每个像素都能直接反投影成三维点,不需要COLMAP做特征匹配。把深度信息融合成密集点云后,再用点云注册的方式获得位姿,最后初始化高斯集合去训练。这条路对硬件有要求,但胜在稳定,基本不依赖场景纹理。

第二条:如果只有普通照片但没有位姿,也有一些前沿方案可以直接从单目视频训练三维高斯场景,跳过显式重建位姿的步骤。这类方法的思路是把相机位姿也当成网络需要预测的参数,一边训练高斯参数一边训练位姿参数,整体做自监督优化。我实测下来,这类方案在小场景、短轨迹上效果够用,但大范围或快速移动时会不稳定,目前更适合作为COLMAP失败时的补救措施,而不是首选项。

第三条:利用已有的网格模型或深度图直接初始化。如果你是做合成数据或游戏资产转换,模型本身有精确几何,可以直接从网格表面采样高斯基元,再渲染不同视角图像做自监督训练。这样做的好处是省掉了COLMAP这一整条链路,而且初始高斯分布非常均匀,训练收敛极快。缺点是对网格质量有要求,网格面数太低或拓扑有洞时,结果会出现空洞。

6. 从“能跑”到“跑好”:3DGS在业务中的落地判断

聊了这么多原理和实操,最后还是想谈一点业务层面的经验。很多团队的判断逻辑是:3DGS能实时渲染、画面又真,直接用就行。但落地时通常会遇到模型文件太大、手机端性能不够、动态场景支持不足这几道坎。

模型文件大是因为场景里高斯基元数量多,每一帧渲染都要读入全部基元数据,文件自然就大了。压缩方案目前有几条路线:对基元属性做矢量量化或熵编码,对颜色系数降阶后,在手机端解码时再插值恢复;按区块划分场景,只加载用户视野范围内的区块;以及用更紧凑的基元表示,比如有些变体用低秩近似来减少每个基元的属性维度。这些方案各有取舍。我在实际项目里通常的做法是:一个场景出三个版本——服务器端全精版、网页端中等压缩版、手机端低清晰度版,按设备能力自动选择。

动态场景方面,原始的3DGS只支持静态场景。如果你需要做人、车、动物这类可动内容的实时重建,可以考虑一些扩展版本,它们把场景分成背景和前景多组高斯,每一组各自运动,配合骨架驱动或每组独立的位姿变换,实现可控制的动态模型。这类方案目前还不够成熟,容易出现穿模或运动不自然,但方向是对的,值得持续关注。

另外想特别提醒的一点:3DGS不等于全自动。输入数据的质量直接决定了结果上限。如果拿一堆随手拍的模糊照片喂进去,再好的框架也救不回来。我在项目里经常说的一句话是“采集多花一小时,后期少愁一星期”。与其花大量精力在训练阶段调参修修补补,不如多花心思设计采集方案和拍摄路径。

就当前阶段而言,3DGS已经足够好用,但离“傻瓜化”还有距离。如果你打算在自己的场景里尝试这种技术,我的建议是先拿数据量最小的一个小场景跑通全流程,从采集、重建、训练到渲染走一遍,感受一下每个环节的体感,再逐步扩大场景规模。这个过程踩过的坑会让你对这个技术的理解深很多,比看十篇论文都有用。

返回列表