1. 从NeRF到3DGS:一场渲染效率的降维打击
如果你这两年一直在关注三维重建或者神经渲染方向,大概率已经被两个词反复刷屏:NeRF和3D Gaussian Splatting(后面我统一叫3DGS)。NeRF刚出来那会儿,整个圈子都沸腾了,用几张贴图就能重建出带光照、带视角依赖的复杂场景,效果确实惊艳。但真正上手跑过的人都知道,NeRF的训练和渲染速度是个硬伤——训练一个场景动辄几个小时甚至一两天,渲染一帧也要好几秒,想拿来做实时交互基本是做梦。
3DGS在2023年SIGGRAPH上横空出世,直接把这个问题按在地上摩擦。它用一组三维高斯椭球来显式表达场景,配合基于瓦片的光栅化渲染管线,在保持甚至超越NeRF画质的前提下,把训练时间压缩到几分钟,渲染速度拉到100 FPS以上。这个跨度不是优化,是换了一条赛道。
我写这篇东西,是想把3DGS到底解决了什么问题讲透。不是那种论文摘要式的复述,而是从实际做项目、跑代码、调参数的角度,把它的核心思路、关键设计、实操要点和踩坑经验都摊开来说。如果你正在做三维重建、SLAM建图、数字孪生,或者单纯想搞清楚3DGS凭什么这么火,这篇应该能帮你省下不少翻文档的时间。
2. 3DGS到底解决了哪些核心痛点
2.1 NeRF的三大瓶颈:慢、隐式、难编辑
要理解3DGS的价值,得先看清楚NeRF的局限。NeRF的核心思路是:用一个多层感知机(MLP)把空间坐标和视角方向映射到颜色和密度,然后通过体渲染积分得到像素值。这个设计很优雅,但问题也很明显。
第一是训练慢。因为每条光线要采样几十上百个点,每个点都要过一遍MLP,计算量巨大。原始NeRF在单卡上训练一个场景要一到两天,就算后续的Instant-NGP用哈希编码把训练压到几分钟,渲染质量又会有波动,而且对显存的占用依然不友好。
第二是隐式表达。场景信息全部藏在MLP的权重里,你没法直接拿到“这个物体在哪里”“那个表面长什么样”。想对场景做编辑、分割、物理仿真,都得绕一大圈。做SLAM的时候,你想把建图和定位耦合起来,隐式表达会让优化变得非常别扭。
第三是渲染和训练耦合。NeRF的渲染过程本身就是网络前向传播,想加速就得改网络结构或者做烘焙,灵活性差。而且体渲染的采样策略对结果影响很大,调参成本高。
2.2 3DGS的核心突破:显式表达加可微光栅化
3DGS换了个思路。它不再用隐式网络,而是用一堆三维高斯椭球来显式表示场景。每个高斯有自己的位置、协方差矩阵(决定形状和朝向)、不透明度,以及用球谐函数表示的颜色。渲染的时候,把这些高斯投影到屏幕空间,按深度排序,然后用alpha混合的方式合成像素。
这个设计直接解决了NeRF的几个痛点:
- 训练快:没有MLP前向传播,梯度可以直接传到每个高斯的参数上,优化效率高得多。官方代码在A6000上训练一个场景大概几分钟到十几分钟。
- 渲染快:基于瓦片的光栅化管线,充分利用GPU并行能力,轻松跑到实时帧率。
- 显式可编辑:每个高斯都是独立的图元,你可以删、可以改、可以加,做场景编辑和下游任务方便很多。
- 表达能力强:高斯椭球可以自适应地拉伸、旋转,对薄结构、高频细节的拟合能力比点云强不少。
注意:3DGS不是万能的。它对反光、透明、镜面反射这类材质的处理仍然有局限,因为球谐函数只能表达视角依赖的颜色,没法真正模拟光线传输。这是它和NeRF共同的短板,也是后续很多工作的改进方向。
2.3 和SLAM、CUDA生态的关系
热词里出现了SLAM、CUDA这些词,说明很多人关心3DGS在机器人和工程落地中的位置。简单说,3DGS给SLAM提供了一种新的建图表达方式。传统SLAM用点云或体素建图,稠密度和渲染质量有限;3DGS可以在线增量式地构建高质量地图,同时支持新视角合成。像SplaTAM、Gaussian-SLAM这些工作就是往这个方向走的。
CUDA则是绕不开的底层依赖。3DGS的光栅化器是用CUDA写的,训练和渲染都依赖GPU。你装环境的时候,CUDA版本、PyTorch版本、显卡架构三者必须匹配,否则轻则报错,重则编译失败。后面我会专门讲这块的坑。
3. 核心原理拆解:高斯椭球是怎么工作的
3.1 三维高斯分布的数学表达
一个三维高斯分布由均值向量和协方差矩阵定义。在3DGS里,均值就是高斯中心的位置,协方差矩阵控制它的形状和朝向。为了保证协方差矩阵始终是半正定的,论文把它分解成旋转矩阵和缩放矩阵的乘积:Σ = R S S^T R^T。旋转用四元数表示,缩放用三个轴的长度表示。这样优化的时候不会出现非法矩阵。
这个分解很关键。你直接优化协方差矩阵的六个元素,很容易优化出非正定的矩阵,导致渲染出错。拆成旋转和缩放之后,每个参数都有明确的物理意义,梯度下降也更稳定。
3.2 从三维到二维的投影
渲染的时候,每个高斯要从世界坐标投影到相机坐标,再投影到屏幕空间。投影后的二维协方差矩阵可以用雅可比矩阵做近似计算。这里有个细节:投影后的高斯在屏幕空间也是一个椭圆,光栅化器会计算每个像素被这个椭圆覆盖的程度,然后做alpha混合。
3.3 自适应密度控制
3DGS训练过程中会动态调整高斯的数量和分布。具体来说,每隔一定迭代次数,它会检查每个高斯的梯度大小和位置。梯度大的高斯说明该区域重建不足,需要分裂成两个更小的高斯;梯度小但体积大的高斯说明该区域过表达,需要克隆或者删除。这个机制让高斯分布能自适应地贴合场景几何,既保证细节又控制数量。
3.4 球谐函数与视角依赖颜色
每个高斯用球谐函数系数来表示颜色。球谐函数是一组定义在球面上的基函数,阶数越高,能表达的视角依赖越复杂。3DGS默认用三阶球谐,每个颜色通道16个系数,总共48个系数。这样从不同角度看同一个高斯,颜色会变化,能模拟一定的镜面反射和高光效果。
实操心得:如果你的场景主要是漫反射材质,可以把球谐阶数降到0阶或1阶,能显著减少参数量和显存占用,训练也更快。我试过在室内场景用0阶球谐,画质损失很小,但显存省了将近三分之一。
4. 实操环境搭建:CUDA、PyTorch与依赖的版本博弈
4.1 显卡、驱动、CUDA版本的匹配逻辑
3DGS对CUDA版本很敏感。官方代码要求CUDA 11.6以上,但不同版本的PyTorch对CUDA的支持范围不同。你得先确定显卡型号,再查它支持的CUDA版本,然后选对应的PyTorch,最后装匹配的CUDA Toolkit。
举个例子,RTX 4060 Ti是Ada Lovelace架构,计算能力8.9,需要CUDA 11.8以上才能充分发挥。如果你装的是CUDA 11.6,可能能跑但性能打折,甚至编译报错。RTX 50系显卡(比如5070)是Blackwell架构,计算能力sm_120,需要CUDA 12.8以上,老版本的PyTorch根本不支持。
我整理了一个常见显卡和CUDA版本的对应关系,供你参考:
| 显卡系列 | 架构 | 计算能力 | 最低CUDA版本 | 推荐CUDA版本 |
|---|---|---|---|---|
| RTX 20系 | Turing | 7.5 | 10.0 | 11.8 |
| RTX 30系 | Ampere | 8.6 | 11.1 | 11.8 / 12.1 |
| RTX 40系 | Ada Lovelace | 8.9 | 11.8 | 12.1 / 12.4 |
| RTX 50系 | Blackwell | 12.0 | 12.8 | 12.8+ |
4.2 用conda隔离环境的具体步骤
我强烈建议用conda建独立环境,别在系统Python里瞎折腾。步骤如下:
conda create -n gs python=3.10 conda activate gs然后装PyTorch。注意要去PyTorch官网查对应的CUDA版本命令,别直接pip install torch,那样装的是CPU版或者不匹配的版本。比如CUDA 12.1对应的命令是:
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121接着装3DGS的依赖:
pip install plyfile tqdm pip install submodules/diff-gaussian-rasterization pip install submodules/simple-knn这两个submodule是3DGS的核心CUDA扩展,编译的时候会调用nvcc。如果报错说找不到cuda.h,说明CUDA Toolkit没装或者环境变量没配好。
4.3 编译CUDA扩展时的常见报错与解决
最常见的报错是“nvcc not found”或者“CUDA_HOME not set”。解决办法是确认CUDA Toolkit装好了,然后把路径加到环境变量:
export CUDA_HOME=/usr/local/cuda-12.1 export PATH=$CUDA_HOME/bin:$PATH export LD_LIBRARY_PATH=$CUDA_HOME/lib64:$LD_LIBRARY_PATH另一个坑是gcc版本。CUDA 12.1最高支持gcc 12,如果你系统gcc是13,编译会报错。可以用conda装一个低版本gcc:
conda install -c conda-forge gcc=12 gxx=12注意:如果你在WSL2里跑3DGS,记得装WSL专用的NVIDIA驱动,不是在WSL里装Linux驱动。CUDA Toolkit可以在WSL里装,但驱动必须用Windows端的。这个搞反了会一直报“no CUDA-capable device detected”。
5. 数据准备与训练流程:从拍照到出模型
5.1 自己制作数据集的完整流程
3DGS需要的是多视角图像加相机位姿。位姿可以用COLMAP做SfM得到。流程如下:
- 拍摄:围绕物体或场景拍一圈,重叠度至少60%,光照尽量均匀,避免运动模糊。
- COLMAP重建:用COLMAP做稀疏重建,得到相机内参、外参和稀疏点云。
- 格式转换:3DGS官方代码需要特定目录结构,包括images文件夹和sparse/0文件夹下的COLMAP输出。
- 训练:运行train.py,指定数据路径和输出路径。
拍摄的时候有个经验:别用广角镜头凑太近,边缘畸变会让COLMAP匹配失败。我一般用50mm等效焦距,光圈f/8左右,保证景深足够。如果场景纹理少(比如白墙),可以贴一些临时标记点辅助匹配。
5.2 训练参数的关键配置
3DGS的训练脚本有一堆参数,我挑几个最影响结果的讲:
- iterations:默认30000。一般场景够了,复杂场景可以加到40000。
- position_lr_init:位置学习率初始值,默认0.00016。调大收敛快但容易震荡。
- densify_until_iter:控制密度增长到多少迭代停止,默认15000。太早停会导致细节不足。
- densify_grad_threshold:梯度阈值,默认0.0002。调小会增加高斯数量,画质提升但显存吃紧。
我一般先用默认参数跑一遍,看结果再针对性调。如果发现某些区域糊,就降低densify_grad_threshold;如果高斯数量爆炸,就提高阈值或者提前停止密度增长。
5.3 训练过程中的显存监控与优化
3DGS训练时显存占用会随着高斯数量增长而上升。一个中等场景大概需要8到12GB显存,复杂场景可能超过24GB。如果显存不够,可以:
- 降低图像分辨率(用
-r 2参数下采样) - 减少球谐阶数
- 提高densify_grad_threshold,控制高斯数量
- 用更小的batch,但3DGS本身是全图训练的,这个不太好调
实操心得:训练过程中可以用
nvidia-smi -l 1实时监控显存。如果发现显存快满了,赶紧中断,调参数重跑,别等OOM了再后悔。我有一次跑一个室外大场景,没注意显存,跑到两万迭代崩了,白等一个小时。
6. 常见问题与排查技巧实录
6.1 编译与运行时报错速查
| 报错信息 | 可能原因 | 解决办法 |
|---|---|---|
| nvcc not found | CUDA Toolkit未安装或PATH未配置 | 安装CUDA Toolkit并配置环境变量 |
| CUDA_HOME not set | 环境变量缺失 | export CUDA_HOME=/usr/local/cuda-xx |
| no kernel image available | 显卡计算能力与编译架构不匹配 | 设置TORCH_CUDA_ARCH_LIST或升级CUDA |
| out of memory | 显存不足 | 降低分辨率、减少高斯数量、换更大显存显卡 |
| COLMAP failed to match | 图像重叠度低或纹理少 | 重拍、增加重叠、贴标记点 |
| 渲染结果有漂浮物 | 高斯未收敛或密度控制不当 | 增加训练迭代、调整密度阈值 |
6.2 画质问题的排查思路
如果训练出来的模型有雾状漂浮物,通常是背景高斯没被正确约束。可以检查一下训练数据里有没有拍到纯天空或纯背景,这些区域容易产生大量低不透明度的高斯。解决办法是在数据准备阶段把背景裁掉,或者训练时加一个背景正则项。
如果模型边缘有锯齿,可能是球谐阶数不够或者高斯数量不足。试着提高球谐阶数到3阶,或者降低densify_grad_threshold让更多高斯生成。
如果某些视角渲染出来模糊,检查COLMAP的位姿是否准确。位姿误差会直接导致渲染错位。可以用COLMAP的model_analyzer看重投影误差,一般要控制在1像素以内。
6.3 跨版本CUDA迁移的注意事项
有时候你在一台机器上编译好了,换一台机器跑,CUDA版本不同,就得重新编译。因为diff-gaussian-rasterization是编译成.so文件的,和CUDA版本绑定。迁移的时候,把整个conda环境打包带走不一定管用,最好在新机器上重新建环境、重新编译。
如果非要用同一个环境跨机器,确保两台机器的CUDA版本、驱动版本、显卡架构一致。否则就老老实实重装。
7. 3DGS在SLAM和机器人方向的落地思考
SLAM方向对3DGS的兴趣主要在于建图质量。传统视觉SLAM建出来的是稀疏点云,做导航够用,但做渲染和交互就差远了。3DGS可以在线构建稠密的高质量地图,同时支持新视角合成,这对AR/VR、机器人仿真很有价值。
不过目前3DGS做SLAM还有几个挑战。一是训练速度虽然比NeRF快,但要做到实时增量式建图还是有压力。二是位姿优化和地图优化的耦合需要仔细设计,不然容易漂移。三是显存占用大,嵌入式平台跑不动。
我个人的判断是,3DGS在SLAM里的落地会先从离线建图和后处理开始,逐步往在线方向走。短期内,用3DGS做地图的精细重建和可视化,用传统SLAM做定位,是比较务实的方案。
最后分享一个小技巧:如果你只是想快速体验3DGS的效果,不想自己拍数据,可以用官方提供的预训练模型或者公开数据集(比如Mip-NeRF 360)。跑通流程之后再换自己的数据,能省很多调试时间。另外,训练的时候记得开
--eval模式,这样会留出测试集,方便你客观评估画质,而不是只靠肉眼看。