工程深度

3DGS 如何工作:Gaussian、投影、排序与可微训练

用工程直觉和必要公式拆解 3D Gaussian Splatting 的 primitive、协方差、屏幕投影、球谐颜色、Alpha 合成与密度控制。

#3DGS · #Gaussian Splatting · #可微渲染 · #GPU · #计算机图形学2026/3DGS-RENDERING-PRINCIPLES

3DGS 的核心可以压缩成一句话:用一组可学习的三维椭球描述场景,把可见椭球投影成屏幕上的二维椭圆,再按前后关系进行透明度合成。

它的速度并不是因为 Gaussian 天生神奇,而是表示、优化和 GPU rasterization 被设计成了同一条路径:训练时渲染的就是部署时要显示的显式 primitive,不需要在每个像素的光线上反复查询大型神经网络。

理解这条路径后,许多概念会自然连起来:协方差控制椭球形状,球谐函数控制视角相关颜色,排序和 Alpha 合成产生像素,梯度再把图像误差传回每个 Gaussian。3DGS 官方项目页

从一个三维 Gaussian 开始

一个未归一化的三维 Gaussian 可以写成:

G(x) = exp(−½(x − μ)TΣ−1(x − μ))

其中:

  • x 是空间中的查询位置;
  • μ 是 Gaussian 中心;
  • Σ 是 3×3 协方差矩阵,决定椭球朝向和三个主轴的长度。

距离中心越远,贡献按指数衰减。和无限小的点相比,一个 Gaussian 能覆盖一片空间;和固定体素相比,它可以任意旋转和拉伸,更容易沿真实表面分布。

三维 Gaussian 的中心、主轴、协方差、透明度和球谐颜色属性
Gaussian 是一组为了图像重建而优化的属性,不天然包含三角拓扑、材质和碰撞信息。

为什么不用直接优化完整协方差矩阵

任意 3×3 数值矩阵不一定是有效协方差。原始 3DGS 将它分解为旋转与尺度:

Σ = RSSTRT

R 通常由四元数参数化,S 是三个方向的尺度。这样既便于优化,也能保证协方差保持半正定。工程实现通常还会对尺度、透明度等原始参数应用激活函数,避免直接优化时越过有效范围。

颜色为什么会随视角变化

每个 Gaussian 可以只保存固定 RGB,但真实场景中的高光、反射和细薄结构会随观察方向变化。原始 3DGS 使用球谐函数(Spherical Harmonics,SH)把观察方向映射成颜色修正。

可以把它理解成一个低阶方向函数:相机从不同方向看同一个 Gaussian,得到略有不同的颜色。SH 阶数越高,可表达的方向变化越复杂,但每个 Gaussian 要保存的系数也更多。

必须注意:**SH 记录的是训练数据里出现过的视角相关外观,不等于理解了真实材质。**它可能近似一个高光,却不知道光源在哪里,也不能保证换灯光后产生正确反射。

三维椭球怎样投影到屏幕

渲染器先把 Gaussian 中心从世界空间变换到相机空间,再通过相机投影落到屏幕坐标。三维协方差也要变成二维协方差,决定屏幕上椭圆的方向和大小。

在局部仿射近似下,可以把屏幕空间协方差概括为:

Σ′ = J W Σ WTJT

W 表示世界到相机的线性变换,J 是透视投影在 Gaussian 中心附近的 Jacobian。这个二维协方差随后转换为 conic 形式,渲染器就能快速判断一个像素离椭圆中心多远、当前 Gaussian 对它贡献多少。

“在中心附近线性化”也是误差来源。Gaussian 很大、靠近相机或位于宽视场边缘时,真实透视投影不再接近一个简单二维椭圆。Mip-Splatting 处理跨尺度 aliasing;Exact-GS 则直接针对 affine approximation 与投影一致性提出更精确的 splatting。Mip-Splatting Exact-GS

为什么渲染前必须排序

透明物体合成与顺序有关。沿相机方向从前向后排列 Gaussian 后,一个像素的颜色可以写成:

C = Σi Ti αi ci,   Ti = Πj<i(1 − αj)

αᵢ 是该 Gaussian 在当前像素的有效透明度,cᵢ 是方向相关颜色,Tᵢ 表示光线到达它之前还剩多少透射率。前面的 Gaussian 已经接近不透明时,后面的贡献可以提前停止计算。

原始 rasterizer 不会为每个像素独立整理所有 Gaussian。它将屏幕切成 tiles,为每个覆盖到的 tile 生成 Gaussian 实例,按 tile 和深度排序,再由 tile 内像素并行合成。这样减少了调度成本,却意味着深度排序是 primitive/tile 层面的近似,不是严格的每像素表面交点排序。

这解释了几个现象:

  • 两个大 Gaussian 深度交叉时,可能出现 popping;
  • 透明、薄片和近景特别容易暴露排序近似;
  • Gaussian 过大不仅损害几何,也会制造巨量 overdraw;
  • 文件里 Gaussian 数量相同,屏幕覆盖面积不同,渲染成本可能完全不同。

可微渲染怎样反过来训练场景

3DGS 从训练图像和位姿到可微渲染、图像损失、梯度更新和密度控制的闭环
优化参数只解决“现有 Gaussian 往哪里走”;densification 和 pruning 还要决定“需要多少 Gaussian”。

每次迭代大致执行:

  1. 选择一台训练相机和对应真实图像;
  2. 用当前 Gaussian 渲染预测图;
  3. 计算像素损失,原始实现组合 L1 与 D-SSIM;
  4. 通过可微 rasterizer 将梯度传回位置、尺度、旋转、透明度和 SH;
  5. 周期性调整 Gaussian 数量和透明度。

gsplat 将这套 CUDA rasterization 和常见训练策略做成了更便于研究与集成的开源库,并提供稀疏梯度、分布式训练和多种 rasterization 选项。它很适合用来阅读现代 3DGS 工程实现,但其 API 和性能特性仍应以当前官方文档为准。gsplat 论文 gsplat 文档

Densification:为什么训练中会“长出”新 Gaussian

固定数量的 primitive 很难同时拟合大平面和细枝。原始 3DGS 根据位置梯度等信号识别重建不足的区域,再进行两类操作:

  • 小 Gaussian 对局部细节不够时,可以 clone 到附近;
  • 大 Gaussian 承担过多结构时,可以 split 成更小的 Gaussian。

同时,透明度很低、过大或不再有贡献的 primitive 会被 prune。训练还会阶段性重置透明度,避免早期不透明 Gaussian 长期挡住后方区域。

这是一套有效但带有启发式色彩的控制系统。阈值改变会影响质量、Gaussian 数量、显存和训练稳定性;后来的 MCMC、anchor 和压缩方法,很多都在重新设计“何时长点、何时删点、属性如何共享”。

为什么训练图很漂亮,新视角仍会坏

优化目标只能约束输入相机看到的内容。以下区域天然证据不足:

  • 所有相机都没拍到的背面;
  • 只有极少视角覆盖的边界和角落;
  • 被动态物体长期挡住的背景;
  • 镜面、透明和重复纹理区域;
  • 相机位姿本身错误的图像。

模型可能用“从训练视角看起来正确”的浮动 Gaussian 解释这些像素。换一个新角度,伪解就会暴露。这不是简单增加迭代次数能解决的问题,而是观测不足和表示先验的问题。

显存和速度应该怎样估算

只统计 Gaussian 数量不够。实际成本至少包括:

  • 每个 Gaussian 的位置、尺度、旋转、透明度和 SH 属性;
  • 优化器状态与梯度,训练成本远高于只读渲染;
  • 每帧 tile-instance 列表和排序临时缓冲;
  • 屏幕分辨率、Gaussian 平均覆盖面积与 overdraw;
  • 双眼、多相机或多层合成带来的重复工作;
  • 解压、上传和格式转换的峰值内存。

因此“论文报告 100 FPS”不能直接推导“移动 VR 可用”。必须同时说明 GPU、分辨率、场景、Gaussian 数量、渲染器、是否双眼以及测量的是纯 rasterization 还是完整应用帧。

从原理推导出的工程结论

采集覆盖比盲目调参重要

没有图像证据的区域无法靠优化可靠恢复。先修复相机轨迹、曝光和覆盖,再讨论损失函数。

大 Gaussian 是质量和性能的双重风险

它们可能跨越真实深度边界,近看形成软片,还会覆盖更多 tiles 和像素。生产资产应检查尺度分布与屏幕 footprint,而不是只删“看起来飘”的点。

SH 不是 PBR 材质

它适合复现观察方向相关外观,不提供可编辑粗糙度、金属度和真实光照响应。需要重光照时,应增加材质分解或保留 Mesh/PBR 层。

Splat 不是可靠碰撞面

透明度合成给出的是视觉遮挡,不自动定义封闭表面。游戏、AR 和机器人任务需要深度、Mesh、SDF 或其他几何代理。

下一篇将把这些原理落到完整实践:怎样拍摄、怎样判断 COLMAP 是否成功、怎样用 Splatfacto 训练,以及为什么导出 PLY 后还没有完成交付。

参考资料