ARTICLE DETAIL

资讯详情

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

3D高斯泼溅(3DGS)实战指南:从原理到实时三维重建与渲染

3D高斯泼溅(3DGS)实战指南:从原理到实时三维重建与渲染 1. 高斯泼溅到底是个什么东西第一次看到“高斯泼溅”这四个字我脑子里浮现的是颜料罐炸开的画面。实际上这个直觉离真相并不远——它确实是把一堆“颜料点”泼到空间里然后让这些点自己长成三维形状。3D Gaussian Splatting简称3DGS是2023年SIGGRAPH上冒出来的一个三维重建与渲染方案核心思路跟NeRF那一派完全不同。NeRF的做法是用一个小神经网络记住整个场景你给它一个空间坐标加一个观察方向它吐给你这个点是什么颜色、有多透明。渲染一张图得沿着每条光线采样几百个点每个点都跑一遍网络。一张1080p的图几百万次网络前向计算训练要几十小时渲染一帧要好几秒。慢是真的慢。3DGS换了个思路我不需要神经网络来隐式地记住场景我直接用几十万到几百万个三维高斯椭球来“显式”地表示它。每个高斯椭球有自己的位置、大小、旋转方向、颜色和不透明度。渲染的时候把这些椭球按照深度排序投影到屏幕上做alpha混合。整个过程是光栅化的不是光线步进。光栅化有多快你打游戏时的GPU每秒能光栅化几十亿个三角形这就是为什么3DGS能把渲染速度拉到实时。那“泼溅”这个词怎么来的因为每个高斯椭球投影到屏幕上之后不是一个硬边的小圆点而是一个有柔和衰减的“溅射”形状。多个高斯叠加在一起就形成了连续的表面和体积效果。你可以把它想象成用喷枪在空气中喷出一团彩色的雾雾的每个小颗粒都是一个高斯分布从不同角度看这些雾团组合出完整的物体。这个方案解决的核心问题是三维重建的“训练速度”和“渲染速度”能不能同时做到快。NeRF训练一个场景要几个小时到几天3DGS把训练压到几分钟到几十分钟NeRF渲染一帧要秒级3DGS做到毫秒级、实时交互。这就是标题里说的“速度提升一个数量级”——不是某一项指标提升10倍而是训练和渲染两个环节同时跨过了可用性的门槛。适合谁来了解这个做三维重建的研究生、搞数字孪生的工程师、做VR/AR内容的技术美术、想从照片生成3D模型的独立开发者甚至是对GPU计算感兴趣的爱好者。你不需要先精通NeRF但最好对三维空间变换、相机投影、GPU渲染管线有基本概念。下面我会从设计思路、核心细节、实操流程、踩坑经验四个层面把它拆开讲。2. 为什么是高斯为什么是泼溅2.1 从NeRF的瓶颈说起NeRF在2020年横空出世的时候大家惊为天人。用几张照片就能重建出带光照效果的精细三维场景这在以前是不可想象的。但用起来之后问题很快暴露训练慢、渲染慢、编辑难。训练慢是因为它把场景编码在一个多层感知机里每次梯度更新都要对整条光线上的采样点做反向传播。一个场景训练100万次迭代每次迭代要采样几千条光线每条光线几百个点计算量是天文数字。渲染慢是因为推理时同样要对每条光线做密集采样虽然可以用一些加速结构但本质上是串行的光线步进。编辑难是因为神经网络是个黑盒你想把场景里的椅子挪个位置或者把某个物体删掉几乎做不到。你只能重新训练或者用一些很别扭的变形方法。3DGS的作者们想我们能不能放弃神经网络这个隐式表示直接用一堆显式的几何基元来表示场景这些基元要满足几个条件第一可微这样才能从照片反向传播优化第二渲染要快最好能用GPU的光栅化管线第三表达能力要够强能表示精细的纹理和半透明效果。高斯分布恰好满足这些条件。一个三维高斯椭球有解析的投影公式投影到二维屏幕后还是一个高斯分布可以高效地做alpha混合。而且高斯是可微的所有参数——位置、协方差、颜色、不透明度——都能通过梯度下降来优化。2.2 显式表示带来的连锁优势用高斯椭球做显式表示带来的好处是连锁反应式的。训练快不需要对每条光线采样几百个点只需要把当前视角下可见的高斯投影到屏幕做一次光栅化计算损失反向传播。每次迭代的计算量跟高斯数量和屏幕分辨率成正比而不是跟光线采样数成正比。实测下来一个中等复杂度的场景用单张RTX 3090训练30分钟左右就能达到NeRF训练一整天的效果。渲染快光栅化是GPU的看家本领。几百万个高斯椭球按深度排序后做alpha混合在现代GPU上跑1080p分辨率可以轻松超过100帧。这意味着你可以戴着VR头显在重建的场景里自由走动延迟低到不会晕。可编辑每个高斯都是独立的你可以选中一组高斯把它们平移、旋转、缩放、改颜色甚至删除。这给场景编辑打开了大门。比如你重建了一个房间想把墙上的画换掉直接找到对应位置的高斯改颜色就行不需要重新训练。内存可控高斯数量可以根据场景复杂度自适应。简单场景几十万个高斯复杂场景几百万个。每个高斯存储位置、协方差、球谐系数、不透明度大概几百个字节。几百万个高斯占几百MB到几GB显存比NeRF的MLP权重加特征网格要直观得多。2.3 跟NeRF的对比不是替代是分工很多人问3DGS是不是要取代NeRF我的看法是它们在不同的场景下各有优势。维度NeRF3DGS表示方式隐式神经网络显式高斯椭球训练时间数小时到数天数分钟到数十分钟渲染速度秒级到分钟级毫秒级实时显存占用中等与网络规模相关较高与高斯数量相关编辑能力极弱强可增删改半透明效果天然支持支持但排序有挑战大场景扩展需要分块或哈希需要分层或裁剪实现复杂度高涉及神经网络中主要是光栅化NeRF在极稀疏视角、需要强先验的场景下仍有优势因为神经网络的隐式正则化能补全信息。3DGS在视角充足、需要实时交互和编辑的场景下碾压。实际项目中我经常用NeRF做小物体、少视角的重建用3DGS做房间、建筑、大场景的实时展示。2.4 为什么“泼溅”这个操作能成立“泼溅”的本质是各向异性高斯投影加alpha混合。每个三维高斯椭球有三个轴每个轴的长度和方向由协方差矩阵决定。投影到屏幕时根据相机视角椭球会变成一个椭圆。这个椭圆的形状、大小、方向都可以解析计算。渲染时把所有高斯按深度排序从远到近依次混合。每个像素的颜色是当前高斯颜色乘以它的不透明度加上后面所有高斯累积的颜色乘以(1-不透明度)。这跟传统的透明物体渲染是一样的只是“物体”变成了几十万个高斯。这里有个关键细节排序。因为高斯是半透明的必须按深度排序才能得到正确结果。但几十万个高斯每帧都排序开销不小。3DGS的工程实现里用了GPU的基数排序把排序也放到并行管线里才做到实时。如果排序出错会出现闪烁、颜色错乱、前后遮挡关系错误。这是实操中最常见的坑之一。3. 核心细节拆解从照片到高斯场3.1 输入准备照片和相机位姿3DGS的输入是一组照片和对应的相机位姿。相机位姿怎么来通常用COLMAP做运动恢复结构SfM得到每张照片的相机内参、外参以及一个稀疏的点云。这个稀疏点云非常重要它是高斯椭球的初始位置。为什么初始位置重要因为3DGS的优化是非凸的如果高斯初始位置离真实表面太远优化容易陷入局部最优重建结果会模糊或者有漂浮物。COLMAP的稀疏点云虽然点不多但位置基本在真实表面上给高斯一个很好的起点。实操中照片拍摄有几个要点第一覆盖要全物体或场景的各个角度都要拍到重叠率至少60%第二光照要均匀避免强烈的阴影和高光因为3DGS假设场景是静态的、漫反射为主的第三避免运动模糊快门速度要够快第四如果拍的是物体最好放在纯色背景上方便分割。3.2 高斯参数化每个高斯带什么信息每个三维高斯椭球包含以下参数位置三维坐标就是椭球的中心。协方差矩阵决定椭球的形状和方向。为了优化方便不直接优化协方差矩阵而是优化一个旋转四元数和一个缩放向量再组合成协方差。这样能保证协方差矩阵始终是半正定的。不透明度一个标量经过sigmoid激活后范围在0到1之间。颜色用球谐系数表示支持视角相关的颜色变化。球谐的阶数可以选0到3阶阶数越高视角相关效果越丰富但存储和计算也越大。通常用3阶每个高斯有16个系数RGB各16个。球谐系数是什么简单说它是一组基函数用来拟合“从不同方向看这个点颜色怎么变”。比如金属表面从不同角度看反光不一样球谐就能表达这种效果。如果场景全是漫反射0阶就够了但3DGS默认用3阶因为能捕捉高光。3.3 初始化从稀疏点云到高斯初始化时每个COLMAP稀疏点云的点变成一个高斯椭球。位置就是点的位置颜色用点的RGB不透明度初始化为一个较低的值协方差初始化为各向同性大小根据最近邻点的距离来定。这里有个经验初始高斯数量不要太多也不要太少。太少场景覆盖不全优化过程中需要分裂很多次太多显存吃紧训练变慢。通常COLMAP输出的稀疏点云有几万到几十万个点直接拿来初始化就行。3.4 可微光栅化渲染管线怎么走渲染一帧的流程视锥裁剪把相机看不到的高斯剔除。投影把三维高斯投影到二维屏幕得到二维高斯。排序按深度对所有可见高斯排序。分块把屏幕分成16x16的瓦片每个瓦片独立处理。混合每个瓦片内按深度顺序混合高斯计算最终像素颜色。这个管线跟传统的三角形光栅化很像区别在于“图元”是高斯而不是三角形混合是alpha混合而不是不透明覆盖。分块处理是为了并行化每个瓦片由一个线程块处理充分利用GPU的并行能力。3.5 损失函数与优化怎么让高斯长对损失函数是渲染图像和真实图像的L1距离加上一个D-SSIM项。L1负责像素级准确D-SSIM负责结构相似性。两者加权通常L1权重0.8D-SSIM权重0.2。优化过程中高斯会经历自适应密度控制每隔一定迭代次数检查每个高斯的梯度。如果某个高斯梯度很大说明它所在区域重建不好需要分裂成两个更小的高斯如果某个高斯不透明度很低说明它没用删掉如果某个高斯太小但梯度大说明需要克隆一个一样大的。这个自适应机制是3DGS能自动调整高斯分布的关键。没有它高斯要么太稀疏导致模糊要么太密集导致过拟合和显存爆炸。3.6 参数选择迭代次数、学习率、高斯上限实操中几个关键参数迭代次数通常30000次。10000次能看到大致形状30000次细节到位。超过30000次提升有限还容易过拟合。学习率位置学习率从0.00016开始指数衰减到0.0000016协方差学习率从0.001开始颜色学习率0.0025不透明度学习率0.05。这些是原论文的推荐值实测很稳。高斯上限根据显存定。24GB显存可以跑到500万到800万个高斯。超过上限就停止分裂只做优化。球谐阶数默认3阶。如果显存紧张降到1阶或0阶能省不少空间。4. 实操流程从零跑通一个3DGS项目4.1 环境搭建CUDA、PyTorch、依赖库3DGS的原生实现是基于CUDA的需要NVIDIA GPU。环境配置是第一个门槛。# 创建conda环境 conda create -n gs python3.10 conda activate gs # 安装PyTorch注意CUDA版本要跟驱动匹配 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 安装3DGS依赖 pip install plyfile tqdm pip install submodules/diff-gaussian-rasterization pip install submodules/simple-knndiff-gaussian-rasterization是核心的可微光栅化库编译需要CUDA Toolkit。如果编译报错多半是CUDA版本跟PyTorch不匹配。用nvcc --version和torch.version.cuda核对一下。注意Windows下编译这些子模块比较折腾建议用WSL2或者Linux。如果只有Windows可以找预编译的wheel或者用Docker镜像。4.2 数据准备拍照片、跑COLMAP拍照片的要点前面说了这里说COLMAP的命令行流程# 特征提取 colmap feature_extractor \ --database_path ./database.db \ --image_path ./images # 稀疏重建 colmap mapper \ --database_path ./database.db \ --image_path ./images \ --output_path ./sparse # 去畸变生成3DGS需要的格式 colmap image_undistorter \ --image_path ./images \ --input_path ./sparse/0 \ --output_path ./dense \ --output_type COLMAP跑完之后./dense目录下有images和sparse两个文件夹这就是3DGS的训练输入。如果COLMAP跑失败常见原因照片太少、重叠不够、纹理太弱。可以尝试调整--SiftExtraction.max_num_features增加特征点数量或者用--Mapper.ba_global_function_tolerance放宽优化容差。4.3 训练启动、监控、调参python train.py \ -s ./dense \ -m ./output \ --iterations 30000 \ --eval训练过程中./output目录下会生成point_cloud文件夹里面有PLY格式的高斯点云。每7000次迭代保存一次可以随时用可视化工具查看中间结果。监控训练看损失曲线L1损失应该稳步下降最后在0.01到0.03之间。如果损失震荡或者不降检查学习率、数据质量、COLMAP位姿是否准确。调参经验如果重建结果模糊增加迭代次数到40000或者降低位置学习率的衰减速度。如果有大量漂浮物降低不透明度学习率或者增加密度控制的阈值。如果显存不够减少高斯上限或者降低球谐阶数。4.4 可视化与导出看结果、导模型3DGS原生实现带一个SIBR可视化器可以实时查看训练好的场景。编译SIBR需要一些依赖比较麻烦。更简单的方法是用第三方工具比如gsplat或者SuperSplat直接加载PLY文件。导出模型PLY文件可以直接用MeshLab、CloudCompare打开。如果要导出网格需要做表面重建比如用泊松重建或者TSDF融合。但注意3DGS的高斯不是表面是体积直接做泊松重建效果一般。更好的方法是先渲染多视角深度图再融合成网格。4.5 自己制作数据集的注意事项如果你想用自己的照片做数据集几个关键点照片数量小物体50到100张房间200到500张建筑500到1000张。太少重建不全太多训练慢。拍摄轨迹围绕物体或场景走一圈保持相机高度和距离大致一致。不要只在一个角度拍。光照阴天或者室内均匀照明最好。避免直射阳光因为阴影会被重建进模型。背景如果只想要物体用纯色背景后期分割。如果背景也要确保背景有足够纹理否则COLMAP会失败。分辨率不要超过4K2K到4K足够。太高分辨率训练慢显存吃紧。5. 常见问题与排查技巧实录5.1 训练报错CUDA out of memory这是最常见的错误。原因高斯数量太多或者分辨率太高。解决方法降低--resolution比如从2降到4数字越大分辨率越低。减少高斯上限在代码里改--densify_grad_threshold从0.0002提高到0.0004减少分裂。降低球谐阶数从3降到1。用更小的batch但3DGS本身没有batch概念只能降分辨率。5.2 重建结果模糊、有雾原因高斯太大、不透明度太低、训练不足。排查看训练损失如果L1损失高于0.05说明训练不够增加迭代次数。看高斯数量如果只有几万个说明密度控制没生效检查--densify_until_iter是否设得太小。看COLMAP位姿如果位姿不准高斯会学到一个模糊的平均位置。重新跑COLMAP增加特征点。5.3 漂浮物和伪影原因COLMAP稀疏点云有噪声或者背景纹理太弱导致位姿估计错误。解决方法在COLMAP之后手动清理稀疏点云删掉明显偏离的点。训练时增加不透明度的正则化让没用的高斯自动消失。用--opacity_cull参数在训练后期剔除低不透明度的高斯。5.4 渲染速度慢原因高斯数量太多或者排序开销大。优化减少高斯数量用更激进的密度控制。用分层渲染远处用低分辨率近处用高分辨率。如果只是展示可以预渲染成视频或者用LOD。5.5 显存占用分析高斯数量球谐阶数显存占用约100万31.5 GB300万34.5 GB500万37.5 GB500万13.5 GB500万02.5 GB每个高斯3阶球谐有48个浮点数RGB各16加上位置3个、协方差7个、不透明度1个共59个浮点数约236字节。加上优化时的梯度、动量等实际占用是2到3倍。5.6 常见问题速查表问题可能原因解决方法CUDA out of memory高斯太多/分辨率太高降分辨率、降球谐阶数、提高分裂阈值重建模糊训练不足/位姿不准增加迭代、重跑COLMAP漂浮物稀疏点云噪声清理点云、增加不透明度正则渲染闪烁排序错误检查光栅化库版本、更新驱动COLMAP失败照片太少/纹理弱增加照片、用更好纹理颜色不对球谐阶数低提高到3阶训练不收敛学习率太高降低学习率、检查数据提示3DGS对GPU驱动版本敏感建议用较新的驱动。如果遇到奇怪的渲染错误先更新驱动。6. 3DGS的边界与我的实操体会3DGS不是万能的。它有几个明确的边界第一它假设场景是静态的。如果照片里有移动的人或车重建结果会有鬼影。解决办法是用分割掩码把动态物体去掉或者用多相机同时拍摄。第二它假设光照是固定的。如果拍摄时开了闪光灯或者光照变化大重建的颜色会不一致。解决办法是固定曝光和白平衡或者用HDR拍摄。第三它不直接输出网格。高斯是体积表示要转成网格需要额外步骤而且转换过程会损失细节。如果下游任务需要网格3DGS可能不是最佳选择。第四大场景需要分块。单个场景的高斯数量受显存限制城市级重建需要分块训练再合并合并时的接缝处理是个难题。我在实际项目里用3DGS做过几个场景一个雕塑的精细重建用了80张照片训练25分钟高斯数量120万渲染帧率在RTX 4090上跑4K分辨率有60帧一个房间的VR展示用了300张照片训练40分钟高斯数量350万在Quest 3上串流渲染延迟低于20毫秒。体验下来3DGS的“实时”是真的实时不是那种“优化后勉强能看”的实时。踩过的坑有一次拍照片时用了自动曝光结果不同照片亮度差异大重建出来的墙面颜色一块深一块浅。后来改成手动曝光问题解决。还有一次COLMAP跑出来的位姿有漂移导致重建结果扭曲重新拍了一组重叠率更高的照片才搞定。最后分享一个小技巧训练时用--eval模式把一部分照片留作测试集。训练完后看测试集的PSNR如果低于25dB说明重建质量一般需要检查数据或调参。如果高于30dB基本可以交付了。这个指标比肉眼看更客观能帮你快速判断重建是否成功。
返回列表