3DGS三维重建全流程实战:从数据采集到训练调参
2026/9/16 4:38:21 网站建设 项目流程

1. 为什么大家都在说3DGS,它究竟解决了什么

先回答一个很多人刚接触时都会问的基础问题:3D Gaussian Splatting(下面我直接叫它3DGS)到底是什么?它能干什么?

简单说,3DGS是一种把真实场景变成“可实时渲染的数字三维模型”的技术。你拿手机或相机绕着某个物体、某个房间、某段街道拍一组照片,喂给3DGS的训练程序,它就能重建出一个和真实场景几乎一致的三维空间。这个空间可以用鼠标自由旋转、缩放,也能输出成视频或网格模型,放到Unity、Unreal里继续做开发。它在2023年刚出来的时候,最震撼人的一点是:重建质量接近甚至超过当时主流的NeRF方案,但训练速度快了几十倍,渲染速度更是拉到实时(普通消费级显卡就能跑30帧以上)。

我自己的实际感受是,NeRF时代你训练一个场景动辄几个小时,推理一张图也得几百毫秒,基本只能离线出图;3DGS把同样的活压缩到十几分钟到半小时,推理直接实时。这个量级的差距,意味着它从“实验室玩具”变成了“能落地的工具”,所以才会在视觉特效、游戏资产制作、建筑扫描、电商展示这些方向迅速火起来。

这篇文章我打算从头到尾带你跑通一次完整的3DGS使用流程:工具怎么选、环境怎么搭、数据怎么拍、训练怎么调参、结果怎么导出。无论你是第一次接触,还是有过NeRF基础想迁移到3DGS,按这个顺序走下来基本不会卡壳。

2. 跑通之前先想清楚:工具链选型与运行环境

3DGS不是一个单独的软件,它是一整套算法的统称。市面上有好几套开源实现,最主流的是原版项目(graphdeco-inria/gaussian-splatting),另外还有nerfstudio里的splatfacto、taichi生态里的gsplat等。我给新手的第一建议永远是:先跑原版,不要贪多。

2.1 原版仓库为什么是必修课

原版仓库是论文作者公开的实现,参数命名、训练流程、导出脚本都是“标准答案”。它的主要构成是这样的:

  • 用PyTorch做神经网络层面的计算;
  • 自带一个自定义的CUDA光栅化器,负责把三维高斯投影成屏幕上的颜色;
  • 依赖COLMAP做初始化,也就是先从照片里估算相机位置和稀疏点云;
  • 训练完成后,可以渲染视频、导出点云,或者配合第三方脚本转网格。

选择原版的逻辑很简单:社区里的教程、提问、衍生工具几乎都围绕它展开,你在使用中遇到八成的报错都能搜到解决方案。而nerfstudio的封装做得更友好,适合后续做数据管理、模型对比,但它把很多细节藏起来了,一旦出了问题你很难定位根因。所以我的建议是“原版打基础,nerfstudio做生产”。

2.2 硬件和软件环境的硬性条件

说下我自己的部署环境,你对照参考:

  • 操作系统:Windows 11(原版仓库对Windows、Linux都支持,但Windows需要手动配置更多环境变量,后面会细说)
  • 显卡:NVIDIA RTX 3060 12GB显存起步。显存直接决定你最多能训练多复杂的场景,12GB玩小物体和室内房间够用,室外大场景建议上24GB
  • CPU:训练过程中CPU用于数据加载和预处理,至少6核,影响没那么大
  • 内存:16GB是底线,32GB更稳。COLMAP特征提取阶段偶尔会吃掉不少内存

软件方面,最核心的是CUDA和PyTorch的版本匹配。原版仓库要求CUDA计算能力在6.0以上,实测下来CUDA 11.8配合PyTorch 2.0.0是最稳的组合。如果你装的是CUDA 12.x,也能跑,但需要手动调整编译参数,新手没必要第一关就给自己加难度。

有一个非常容易忽略的点:安装完仓库后,别忘了初始化子模块(submodule),否则diff-gaussian-rasterization和simple-knn这两个核心组件目录是空的,一编译就报错。网上很多人卡在“ModuleNotFoundError: No module named 'diff_gaussian_rasterization'”,八成就是子模块没拉下来。

# 拉取仓库并初始化子模块 git clone https://github.com/graphdeco-inria/gaussian-splatting.git cd gaussian-splatting git submodule update --init --recursive

2.3 COLMAP的安装路径与坑

COLMAP是3DGS训练前的必经步骤,它的作用是从你的照片中恢复出每张照片的相机位姿,同时生成一簇稀疏三维点。这个稀疏点云就是三维高斯的“种子”,训练程序会从这些点出发不断生长和修剪。

COLMAP的安装方式按系统分:

  • Windows:直接下载官方预编译的Windows版本,解压后把bin目录加到系统PATH
  • Linux:用apt或者从源码编译都行,源码编译耗时较长,建议直接用发行版自带的版本,但注意版本别太老,至少3.7以上

真实使用中,COLMAP能不能正确识别你的照片,取决于一个细节:照片的EXIF信息里有没有焦距数据。手机和相机拍摄的照片通常都有,但如果你用的是网图、截图或某些软件导出的图片,这一步大概率会失败。解决办法是先用转换工具把图片标准化,同时用脚本统一重命名,避免中文路径和乱码。

还有一点,COLMAP对非常多的照片(超过500张)做特征匹配时,内存占用会飙得很高。如果不想换大内存,可以在特征提取和匹配阶段适当降低精度,或者把输入图片缩小。

3. 比训练更影响出片率的第一步:数据采集规范

很多人第一次跑3DGS,训练出来的结果模糊、有漂浮物、视角一转就崩,第一反应是调参数,实际上八成问题出在数据采集。

我强调一句可能不太好听但很真实的话:3DGS这技术,是“数据决定上限,算法决定下限”。同样的训练代码,你喂给它高质量覆盖均匀的照片和随便扫一圈的照片,最终效果天差地别。

3.1 拍摄目标的基本要求

如果你想重建单个物体(比如一个雕塑、一双鞋、一件产品),拍摄时注意以下几点:

  • 背景尽量干净、简洁,纯色背景最好。3DGS会把背景也重建出来,背景纹理太复杂会分散训练资源,导致主体细节不足
  • 物体放在漫反射材质表面,不要用镜面反射的桌子。反光会干扰特征点匹配和颜色学习,这是很多人踩过坑的地方
  • 相机绕着物体转一圈,保持同样的高度和同样的距离,每隔一个固定角度拍一张,相邻两张照片的重叠区域最好在60%以上
  • 光线要稳定均匀。不要开闪光灯,不要在窗户边拍(阳光会随时间变化),最好是固定光源下的室内环境

如果你要重建的是整个房间或室外小场景,规则类似,但要多注意一个点:不要只绕着外围拍一圈,还要从不同高度、不同前进方向拍摄多组轨迹。简单说,你需要覆盖目标空间的各个面,而不是只拍一个环。

3.2 视频抽帧还是连拍:我推荐后者

3DGS社区常见的做法是“录一段视频,然后抽帧”。但根据我的实际对比,除非运动控制得特别好,否则视频抽帧是下策。

原因很简单:视频帧率通常是30fps,你拿着手机走动拍摄,相邻帧之间的位移可能只有几厘米,特征匹配时会产生大量的高度相似图像,不仅浪费训练时间,还可能让优化陷入局部最优。而且手持拍摄必然有抖动,轻微运动模糊对特征提取的影响很大。

我更推荐的做法是:用手持稳定器或直接手持,以每秒一张左右的节奏连拍,每拍一张略微移动一点位置。这样得到的照片数量在30到100张之间,重叠度和视角变化都比较理想。

如果只能录视频,抽帧间隔至少每隔5帧取1帧,并且在抽帧前先做一次去模糊处理。

3.3 传感器设置与防范过曝

照片的质量指标里,最影响3DGS的是曝光和清晰度,而不是像素大小。2000万像素的模糊照片远不如1000万像素的清晰照片。

我总结了一套比较稳妥的参数模板:

  • 快门速度不低于1/200秒,避免手抖模糊
  • 光圈调到f/4到f/8之间,保证景深足够大,整个物体都在焦内
  • ISO越低越好,优先保证画面干净,噪点会直接影响后续颜色学习
  • 关闭HDR模式。HDR会改变局部的亮度关系,多张合成的照片里可能出现不一致的光照,训练出来的场景看起来会“发虚”

拍照前先把相机的风格模式调到“标准”或“自然”,不要用鲜艳模式,饱和度太高会让重建出的颜色失真。

4. 训练参数的每一步都应该知道在干什么

环境搭好、数据拍好之后,进入正式的训练环节。原版仓库的训练入口是一个convert.py加一个train.py。convert.py负责调用COLMAP处理你输入的图片目录,train.py负责读取处理好的数据开始训练。

完整跑一趟的命令大概是这样的:

# 第一步:对图片进行COLMAP重建 python convert.py -s /path/to/your/dataset # 第二步:开始训练 python train.py -s /path/to/your/dataset -m /path/to/output

convert.py跑完之后,会在数据集目录下生成一个sparse文件夹,里面是COLMAP计算出的相机位姿和稀疏点云。这一步如果报错,优先检查图片格式(支持jpg/png,建议jpg)和图片数量(不要少于10张)。

train.py开始训练后,你会看到一个进度条,默认迭代次数是30000次,每100次输出一次当前loss。不同配置的显卡,整个训练耗时从10分钟到1小时不等。

4.1 iterations、sh_degree 和 loss曲线怎么看

  • iterations:总迭代次数。默认30000,对于大多数室内室外场景都够用。如果你的目标物体纹理非常简单(一面白墙),可以降到20000,省时间。反过来,如果场景特别复杂、照片超过200张,可以适当增加迭代次数到40000-50000。但不要盲目加,迭代越多对显存和时间的消耗越大,而收益在后期几乎是平的

  • sh_degree:球谐函数的最大阶数,默认是3。球谐函数在这里负责表达高光、反射这类随视角变化的颜色特征。阶数越高,表达越精细,但计算量和显存占用也越高。我的经验是,大多数场景保持默认3就够了。如果你重建的是哑光材质物体,改成2反而收敛更快

  • loss曲线:训练日志里会输出一个组合loss,它由L1颜色损失、SSIM损失和体积正则化损失组成。看曲线趋势比看绝对值重要。正常情况下,loss在最初几千次迭代里会急速下降,随后进入缓慢下降阶段。如果loss曲线震荡剧烈甚至上升,说明学习率可能过大,或者数据本身有问题(比如图片顺序混乱、位姿估计失败)

4.2 densify相关参数:从稀疏到稠密的核心逻辑

3DGS里的三维高斯一开始是很稀疏的,训练过程会动态地“分裂”和“克隆”高斯点,来适应场景的几何细节。这个过程叫densification,它受几个关键参数控制:

  • densify_from_iter / densify_until_iter:分别在哪个迭代开始和结束稠密化。默认是500开始、15000结束。在500次迭代之前,模型只做基础的位姿和外观调整,过早开始稠密化会导致高斯点分布混乱
  • densify_grad_threshold:触发高斯点分裂的梯度阈值,默认0.0002。这个值可以理解成“敏感度”,值越小,一点微小误差就会触发分裂,适合纹理细节特别多的场景;值越大,整体越稳定但细节可能丢失
  • densification_interval:每多少次迭代做一次稠密化,默认是100。这个值一般不用动,改小了会大幅增加计算量

我自己的调参经验是:当场景里出现明显的“糊掉”区域(比如物体边缘发虚),优先降低densify_grad_threshold到0.0001试试,而不是盲目增加总迭代次数,效果来得更直接。

4.3 显存不足时的降级方案

训练过程中最常见的报错就是CUDA out of memory,尤其是12GB显存跑大场景的时候。

几个有效的降级手段:

  • 降低max_sh_degree参数(比如从3降到2),球谐的计算是显存大头之一
  • 减小训练图像分辨率。convert.py里默认会把图片short side缩放到1024(参数是--resize),你可以把输出分辨率降到800甚至640,对最终效果影响可控,显存却能省下来不少
  • 如果是在Windows上训练,关闭其他占用显存的应用,尤其是浏览器里开了大量硬件加速页面,会莫名吃掉几百MB显存

实在不行还可以用分块训练的思路,把场景拆成几个区域分别训练,再用后处理工具合并。这个操作复杂度高一些,建议先把前两个方案用到位再考虑。

5. 效果不理想时,如何判断问题出在数据还是超参数

训练跑完之后,立刻想知道效果好不好,有两条路:一条是直接看训练日志里loss数字,另一条是打开输出目录里的test文件夹,看渲染出来的测试视角图片。前者的参考意义有限,最重要的是后者——测试集图片是训练时没有见过的相机视角,渲染图如果在这上面清晰、稳定,说明模型泛化能力没问题。

原版仓库在训练过程中会定期保存测试视角的渲染图,输出目录的结构大概长这样:

  • output/点云文件.ply(训练完成后的最终模型文件)
  • output/test/iterations_30000/(测试视角渲染图)
  • output/train/iterations_30000/(训练视角渲染图)

把测试图里每一张和原图并排对比,基本能判断出问题类型。

5.1 典型伪影A:漂浮物

打开渲染图,如果看到场景周围有一团一团半透明的“烟雾”或细碎亮点,术语叫floaters。这是3DGS最典型的失败模式,原因通常是:相机位姿估计有误,或者某些高斯点被放到了本来没有实体的区域,靠透明度来“欺骗”loss函数。

对应的处理优先级是:先检查COLMAP是否成功匹配了所有图片(用COLMAP GUI打开project)——如果某几张图没有被正确匹配,重新补拍或删掉它们;如果位姿没问题,则考虑调高densify_grad_threshold,降低高斯点分裂的频率,减少在错误位置生成点。

5.2 典型伪影B:物体边缘闪烁

旋转视角时,物体会边缘出现毛刺或闪烁,说明高斯基元过大,覆盖了多个深度层。最简单的处理是训练结束后对模型做一次后处理减薄(pruning),把透明度极低的高斯点删掉。原版仓库没有直接提供这个脚本,但社区里有很多pruning小工具,原理都是遍历.ply文件,剔除opacity小于0.1的高斯点。

5.3 典型伪影C:色彩发灰或过曝区域

如果整体颜色看起来灰蒙蒙,或高光区域一片死白,大概率是拍照阶段曝光参数不合适,或者图片里存在高动态范围场景没处理干净。回到数据阶段,重拍或者调整曝光比任何参数调优都有效。

一个能辅助定位问题的小技巧:把convert.py生成的稀疏点云导入到MeshLab里看一眼。如果点云本身已经乱七八糟、密集成团或大范围缺失,就不用花时间调训练参数了,直接回到采集和COLMAP环节解决。

5.4 用PSNR和SSIM做量化对比

原版仓库里有一个evaluate.py脚本,会在测试集上计算PSNR、SSIM和LPIPS三个指标。PSNR和SSIM越高、LPIPS越低,代表渲染质量和原图越接近。

但我要泼一盆冷水:这三个指标只能作为参考,不能完全依赖。PSNR对亮度偏差非常敏感,如果你拍的照片和渲染图存在轻微的全局色调差异,PSNR会很难看,但人眼实际感知可能非常好。所以我建议以肉眼判断为主,指标为辅。作为参考,一个训练正常的室内场景,PSNR大约在27到32之间,SSIM在0.85到0.95之间。低于这个区间,先查数据;高于这个区间,说明场景本身比较简单,模型做得不错。

6. 拿到模型之后:渲染导出与其他进阶玩法

训练完成后,你手里有一个.ply文件,它包含了几十万个高斯点的位置、颜色、旋转、缩放、透明度信息。这一步之后,你有很多种出路。

6.1 用训练好的模型渲染视频

原版仓库中有一个render.py脚本,它做的事是:根据训练时保存的相机轨迹,逐帧渲染并输出视频,实现“绕物体旋转”或者“穿越场景”的效果。

如果你想生成自定义的相机轨迹(而不是训练时的轨迹),官方脚本支持比较有限,可以用社区里的slerp插值脚本,在关键帧之间做球面线性插值,生成平滑的相机路径。我个人试过用它与原版render.py组合,输出一段10秒钟的物体环绕视频,效果比官方自带轨迹灵活得多。注意,生成的轨迹要保证相机始终朝向场景中心,并且没有穿过任何高斯基元,否则画面里会出现穿模。

6.2 把高斯点云转成传统网格模型

高斯点云可以直接用于实时渲染,但如果你需要把它导入到Blender、Maya或者游戏引擎里做物理碰撞计算,必须先把点云转成网格模型(.obj或.fbx)。

社区方案里比较成熟的有SuGaR,它能把3DGS训练的高斯点提取成带纹理的三角网格。SuGaR的使用体验是:对简单场景(单个物体)效果好,对复杂场景需要额外调参。你也可以在点云阶段用Poisson重建(Open3D里几个函数就能搞定),但出来的网格面数较多,需要配合简化工具减面。

还有一条值得提的路:把高斯点云直接导入到基于Web的渲染器(比如Three.js的gsplat插件)里,在网页端做实时交互展示。相比传统网格,这种方式省去了网格化的损失,画面质量和实时渲染速度都很好,很适合电商产品展示和线上展厅类的项目。

6.3 稀疏视角重建与连续场景扩展

最后聊一个扩展方向。原版3DGS做完一个场景后只能看这一个场景,但实际项目里经常需要“大范围连续场景”——比如一条商业街,或者一个厂房内部。两个做法:

  • 把大场景拆成若干个子场景分别重建,再用点云配准工具把它们对齐融合。融合的关键是相邻子区域要有30%以上的重叠拍摄区域,否则找不到共同特征点
  • 用稀疏视角重建的思路,只对关键位置拍照,结合先验知识让3DGS“脑补”中间内容。这是当前学术界的前沿方向,工业落地还有距离,但值得关注

我自己用3DGS做完一个器物级项目后的最大体会是:这技术真正难的不是写代码,而是工程化——从数据采集的环境设计,到COLMAP中间产物的质量检查,再到训练参数的微调,每一环都需要耐心试错。但正因为如此,一旦你完整跑通一遍,再看NeRF或者别的重建方案,会觉得世界一下子清晰了不少。

如果再让我给一个具体的下一步建议:项目跑完后,补一组不同光照条件下的测试图片,用测试集评估一下你的模型对光照变化的鲁棒性。这个实验会告诉你,你的模型离真实落地还有多远。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询