从手机视频到 Web 场景:用 Splatfacto 跑通 3DGS
从采集、COLMAP 位姿求解、Nerfstudio Splatfacto 训练,到 SuperSplat 清理、压缩和 Web 交付,完整说明每一步的输入、输出与排错方法。
跑通 3DGS 的目标不应只是“终端里开始训练”,而是得到一个能被检查、清理、压缩并交给目标设备的资产。本篇选择 Nerfstudio 的 Splatfacto 作为训练主线,原因不是它在所有指标上最好,而是数据处理、训练、Viewer 和导出被组织在同一套 CLI 中,比较适合第一次建立端到端认识。
最终流水线是:
复现说明:本文最初只整理官方流程;2026 年 8 月 9 日已在 GeekX 的 RTX 4090 主机上补跑训练、留出视角评估和 PLY 导出。下文同时给出数据快照异常、一次性 CUDA 编译时间与测量边界,实测结果只代表这台机器上的一次基线运行。
先明确输入、输出和硬件边界
本篇分两条路径:
- CUDA 训练路径:下载官方
poster数据验证环境,再处理自己的视频并训练 Splatfacto; - 无训练路径:拿已有的 3DGS PLY/SPZ,在浏览器中完成查看、清理、转换和 Viewer 导出。
Splatfacto 当前依赖 gsplat 的 CUDA rasterization。官方文档把默认 splatfacto 描述为大约 6GB 显存档位,把 splatfacto-big 描述为约 12GB;它们是配置级参考,不是对所有场景的显存保证。Splatfacto 文档
准备以下空间比只看模型大小更稳妥:原始视频、抽帧图像、COLMAP 数据库与稀疏模型、训练 checkpoint、导出 PLY,以及浏览器编辑时的内存峰值。
GeekX 实测:RTX 4090 跑完 30,000 步需要多久
这次实测采用 Nerfstudio 1.1.5 的默认 splatfacto,没有为了数字好看关闭评估或缩短训练。运行环境如下:
| 项目 | 本次配置 |
|---|---|
| 主机 | WSL2 Ubuntu;Intel Core i5-13600KF,系统向 WSL 暴露 14 个逻辑 CPU |
| GPU | NVIDIA GeForce RTX 4090 24GB;驱动 591.86 |
| 软件 | Python 3.10、PyTorch 2.9.1+cu128、Nerfstudio 1.1.5、gsplat 1.4.0、CUDA Toolkit 12.9 |
| 数据 | Nerfstudio 团队在 Hugging Face 发布的 poster 快照;100 张可用图 |
| 训练/测试 | 固定随机种子 42;90 张训练、10 张测试;自动使用 2 倍降采样图,单张 540×960 |
| 配置 | 默认 splatfacto;30,000 步;每 100 步单图评估、每 1,000 步全测试集评估 |
这里有一个必须公开的数据问题:该 poster 快照的 transforms.json 引用了 226 帧,但四档图像目录各只有同样的 100 张,缺少 126 个被引用文件。本站没有伪装成完整 226 帧数据,而是保留原始快照,另生成只包含现存文件的 100 帧 transforms.json;其 SHA256 为 40d0baebf2a79c09992761cbb388ca65808a851cde662027fdd6157b261deaed。
正式命令是:
ns-train splatfacto \
--data data/nerfstudio/poster-hf100 \
--output-dir outputs \
--experiment-name poster-hf100-baseline \
--timestamp rtx4090-default-30000 \
--vis tensorboard \
--max-num-iterations 30000
训练、显存与功耗结果
| 指标 | 实测结果 |
|---|---|
| 30,000 步墙钟时间 | 6 分 41.52 秒 |
| 训练进程最大主存 RSS | 2,657,844 KiB,约 2.54 GiB |
nvidia-smi 设备显存峰值 |
4,452 MiB |
| GPU 平均利用率 / P95 | 37.19% / 60% |
| 平均功耗 / 峰值功耗 | 149.53 W / 195.36 W |
| 最高温度 | 55°C |
GPU 数据按 1 秒采样,并截取训练命令的 401.52 秒墙钟窗口,共 401 个有效样本。显存是 nvidia-smi 看到的整卡占用,不是 PyTorch 进程独占值;WSL 与桌面已有约 2,259 MiB 基线,因此不能把 4,452 MiB 解读成模型独占显存。较低的平均利用率也有合理原因:这个小场景每步只有约 7–13 ms,但训练中穿插了数据调度、定期整图评估和 checkpoint 写入,RTX 4090 并非持续满载。
第一次启动还有另一笔容易被忽略的成本:gsplat 为 RTX 4090 的 sm_89 架构编译 CUDA 前向和反向光栅化内核。本站冷启动的“编译 + 10 步烟雾测试”耗时 7 分 34.40 秒;正式训练复用编译缓存,所以上表没有把一次性编译时间算进训练。
留出视角质量与导出资产

对 10 张未参与训练的图像运行 ns-eval,得到:
| 指标 | 均值 | 标准差 |
|---|---|---|
| PSNR | 33.4799 dB | 2.6119 |
| SSIM | 0.9589 | 0.0156 |
| LPIPS | 0.0869 | 0.0251 |
这次 ns-eval 还报告 4.36 FPS,但代码中的计时窗口包含模型输出、指标计算和 PNG 写盘,因此它是离线评估流水线吞吐,不是 Viewer、浏览器或 VR 的交互帧率。评估 10 张图的完整命令墙钟时间为 9.76 秒。
最终 checkpoint 内有 200,492 个 Gaussian。按默认导出规则过滤 851 个低透明点后,PLY 含 199,641 个 Gaussian,文件为 49,512,561 字节(约 47.2 MiB),导出耗时 11.27 秒;checkpoint 本身约 144.8 MiB。PLY SHA256 为 3c0505f6d8496e97d676bea38691f5437b580e58c2436e6a9b12f5c32dc36e1c。
这组结果说明:在 4090 上,小型静态场景的默认 3DGS 训练已经是分钟级;但它没有证明同一配置能以相同比例扩展到数百上千张高分辨率图片,也没有回答 Web/XR 端到端帧率。后两项还需要在目标资产和目标设备上单独测。
第一步:建立可追踪的运行环境
推荐为 Nerfstudio 单独创建环境,并在安装完成后记录版本:
conda create --name nerfstudio -y python=3.10
conda activate nerfstudio
python -m pip install --upgrade pip
# 先按 pytorch.org 的选择器安装与本机驱动匹配的 CUDA 版 PyTorch,
# 再安装 Nerfstudio。不要直接复用不明来源的 torch/cuda 组合。
pip install nerfstudio
python --version
python -c "import torch; print(torch.__version__, torch.version.cuda, torch.cuda.is_available())"
ns-train --help
如果最后一行 torch.cuda.is_available() 是 False,不要继续训练。优先检查:
- 是否安装成 CPU 版 PyTorch;
- NVIDIA 驱动是否能识别 GPU;
- PyTorch wheel 支持的 CUDA runtime 与驱动是否兼容;
gsplat首次编译 CUDA 扩展时是否缺少编译工具。
Nerfstudio 官方同时提供容器路径。Linux/Windows、远程 GPU 和本机 CUDA 环境差异较大,因此安装命令应以当前官方安装页为准,而不是把某个旧版本组合写死成永久答案。
第二步:先用官方数据验证环境
不要一开始就用自己的视频。先下载 Nerfstudio 的 poster capture,验证下载、数据解析、训练、Viewer 和导出链路:
ns-download-data nerfstudio --capture-name=poster
ns-train splatfacto --data data/nerfstudio/poster
训练启动后,终端会输出 Viewer 地址和结果目录。先确认:
- Viewer 能打开,不是只有终端 loss 在变化;
- 相机 frustums 分布合理;
- 场景方向、尺度和裁剪范围正常;
- 训练结束后输出目录里有
config.yml和 checkpoint。
远程机器上的 Viewer 默认需要访问 WebSocket 端口。不要把训练服务直接暴露到公网;使用 SSH 端口转发或受控代理。官方入门页说明了 Viewer 和远程端口的基本方式。Nerfstudio 首次训练
这一步成功,才能把自己数据的问题与环境问题分开。
第三步:正确采集自己的场景
推荐拍摄方式
- 锁定曝光、白平衡和焦距,关闭会频繁切换镜头的自动模式;
- 慢速移动,让相邻帧保持大量共同特征;
- 围绕物体拍两圈:平视一圈,略高或略低一圈;
- 房间场景沿可行走区域移动,并补拍门框、桌底、角落和遮挡边缘;
- 每个表面最好从多个角度被观察,不要只补一张孤立近景;
- 先完成整体覆盖,再拍局部,不要在一个细节处停留很久后突然跳到远处。
应避免的内容
- 大面积镜子、透明玻璃、纯色墙和重复栅格;
- 持续移动的人群、车辆、树叶和屏幕;
- 运动模糊、数字变焦、自动曝光明显跳变;
- 原地旋转拍全景却没有平移;
- 距离表面太近,导致不同帧几乎没有共同区域。
视频分辨率越高不一定越好。COLMAP 需要可重复匹配的清晰特征;训练还要缓存或处理完整图像。第一次可以使用一段稳定的短视频,先验证流程再增加覆盖。
第四步:从视频恢复相机位姿
将视频放在工作目录,例如 captures/room.mp4:
ns-process-data video \
--data captures/room.mp4 \
--output-dir data/room
ns-process-data 会调用 FFmpeg 处理视频,并使用 COLMAP 求解相机位姿和稀疏点。官方也支持图片目录、Polycam、Record3D、KIRI、RealityCapture 等入口;不同入口是否携带现成位姿,会显著影响处理时间。自定义数据文档
处理结束后,不要只检查命令退出码。应查看生成的相机和稀疏点:
- 已注册图片比例是否足够高;
- 相机轨迹是否连续,是否出现一组远离主体的相机;
- 稀疏点是否形成可辨认结构;
- 是否出现两个本应相同却被拆开的空间;
- 相机是否都挤在一点,说明视频缺少视差。
如果大量图片未注册,按顺序尝试:减少模糊帧、提高相邻帧重叠、移除曝光跳变和动态帧、增加带纹理的观察角度,然后重新求解。不要先用更长训练掩盖错误位姿。
第五步:训练 Splatfacto
ns-train splatfacto --data data/room
第一次保持默认配置,先建立基线。训练过程中从多个角度观察:
- 大片长针状 Gaussian 是否从边界伸出;
- 天空、镜面和动态物体周围是否产生漂浮物;
- 从训练视角附近看很好,稍微侧移是否迅速崩坏;
- Gaussian 数量是否持续增长而质量不再改善;
- 近景是否由巨大软椭圆组成。
默认结果出来后,才有理由调整配置。Splatfacto 官方文档提供了透明度裁剪、densification 后是否继续裁剪、尺度正则等开关;它们是质量—数量—稳定性的取舍,不应当作为“万能高质量参数”整段复制。
用未参与训练的视角验收
最可靠的测试是预留一部分相机或另拍一段相近轨迹,不参与训练。只看训练图附近的效果,会把过拟合误认为高质量重建。
至少保存这些结果:
- 原始视频和采集说明;
- 处理后的数据与相机注册统计;
config.yml、checkpoint 和命令记录;- 代表性成功视角与失败视角;
- 导出前的 Gaussian 数量和文件大小。
第六步:导出标准 3DGS PLY
训练结果目录中找到 config.yml,再执行:
ns-export gaussian-splat \
--load-config outputs/room/splatfacto/SESSION/config.yml \
--output-dir exports/room
实际目录名由运行配置和版本决定,不要照抄 SESSION。可以先检查:
find outputs -name config.yml
ns-export gaussian-splat --help
导出的 PLY 与普通点云 PLY 不同。它需要包含位置、尺度、旋转、透明度和球谐颜色等字段。把普通 XYZRGB 点云改名为 .ply 并不会变成 Gaussian Splat,SuperSplat 也会拒绝缺少这些属性的文件。Nerfstudio 导出说明
第七步:在 SuperSplat 中清理资产
打开 SuperSplat Editor,把导出的 PLY 拖入浏览器。普通编辑需要 WebGL 2;SOG 和独立 Viewer 导出需要 WebGPU。SuperSplat 导入导出文档
建议按以下顺序处理:
- 保存编辑项目:先保存
.ssproj,它与交付用 PLY 不是一回事; - 从外向内检查:先删除远处离群点和采集者,再处理主体边缘;
- 多角度确认选择:选择可能沿深度穿过场景,不要只从一个视角删除;
- 调整坐标:统一朝上方向、原点和尺度,记录目标引擎坐标系;
- 检查 SH:不同 Viewer 的 SH 阶数、曝光和 tone mapping 会造成颜色差异;
- 降低负担:删除无贡献区域,必要时减少 SH bands 或生成 LOD;
- 保存主档与交付档:保留高质量 PLY 主档,另导出 Web 使用格式。
第八步:选择交付格式
| 格式 | 适合用途 | 主要取舍 |
|---|---|---|
| PLY | 训练输出、交换与高质量主档 | 生态广,但文件大,字段约定并不完全统一 |
| compressed PLY | 编辑器和部分 Viewer | 量化后更小,兼容性取决于实现 |
| SPZ | 移动端和跨工具压缩交换 | Niantic 开源格式,需检查版本和坐标系 |
| SOG | PlayCanvas Web runtime | 压缩和流式友好,但属于特定运行时生态 |
| 独立 HTML/ZIP | 快速分享和自托管演示 | HTML 内嵌方便但有 Base64 体积开销;ZIP 需 HTTP 服务 |
Niantic 的 SPZ 开源实现说明,典型 SPZ 相比对应 PLY 约小一个数量级,同时也特别处理坐标系与球谐系数旋转;这个比例是该项目给出的格式级描述,不是所有资产的固定保证。SPZ 仓库
SuperSplat 当前推荐 SOG 用于 runtime;大资产还可以生成多级 LOD 和分块流式版本。具体阈值和支持格式会随工具更新,应以导出时的官方文档为准。
第九步:导出可自托管 Web Viewer
在 SuperSplat 中选择 File → Export → Viewer App…,可以导出:
- 单个 HTML:模型以 Base64 内嵌,双击即可查看,但体积会增加;
- ZIP Package:HTML、脚本、样式和 SOG 分开,加载更高效,需要通过 HTTP 服务访问。
本地检查 ZIP:
cd exported-viewer
python -m http.server 8080
然后访问 http://127.0.0.1:8080/。官方 Viewer 支持在具备相应能力的设备上进入 WebXR AR/VR,但“能进入 XR 模式”不代表大型资产已经达到头显帧率。Self-Hosting Viewer
无 NVIDIA GPU 时能做什么
如果只想学习资产和渲染流程,可以跳过训练:
- 获取许可证允许使用的有效 3DGS PLY、SPZ 或 SOG 示例;
- 在 SuperSplat 中观察 Gaussian 中心、尺度、SH 和透明度;
- 练习裁剪、变换、减少 SH 和格式转换;
- 导出 HTML/ZIP,在桌面和手机浏览器检查首屏、交互和内存;
- 对同一资产比较 PLY、SPZ、SOG 的文件大小与目标 Viewer 兼容性。
这条路径不能替代训练经验,但能先建立“3DGS 是资产,不只是一张视频”的认识。
开源项目与许可证边界
| 项目 | 角色 | 许可注意事项 |
|---|---|---|
| graphdeco-inria/gaussian-splatting | 原始参考实现 | 官方许可证限制为研究/评估和非商业使用,不能简单称为宽松商业开源 |
| nerfstudio-project/gsplat | CUDA rasterization 库 | Apache-2.0;仍需检查组合项目与 CUDA 依赖 |
| Nerfstudio / Splatfacto | 数据、训练、Viewer、导出 | 适合作为工程入口;方法和 API 会持续变化 |
| playcanvas/supersplat | 浏览器编辑器 | MIT;托管平台的部分服务不是开源组件 |
| nianticlabs/spz | 压缩格式与编解码 | MIT;不同版本和坐标系仍需验证互操作 |
尤其要注意依赖链:一个仓库自身使用 MIT 或 Apache-2.0,不代表它下载的数据集、人体模型、预训练权重和继承的 rasterizer 都拥有同样许可。商业产品应逐项形成许可证清单。原始 3DGS 许可证
最终验收清单
- 输入照片清晰、曝光稳定、覆盖完整;
- COLMAP 注册比例和相机轨迹已人工检查;
- 至少用未参与训练的视角检查泛化;
- 记录训练环境、命令、配置和失败区域;
- PLY 在第二个 Viewer 中也能正确打开;
- 坐标、尺度、SH 和背景色在目标引擎中一致;
- Web 端记录文件大小、首屏时间、显存/内存和交互帧率;
- 近景、快速转头和移动端都经过测试;
- 训练代码、依赖、数据和模型权重许可证已分别确认。
完成这些步骤,你得到的才不只是一个训练结果,而是一个有来源、有主档、有交付格式、有失败说明的 3DGS 资产。下一篇会回到研究演进,解释几何、动态、压缩和生成式方法分别在补哪一块短板。