光线追踪高斯:当 3DGS 装上 BVH 与 RT Core
一份写给已经懂 NeRF / SDF / 线代但还没系统学过 3D Gaussian Splatting 的读者的「由浅入深」讲解。全文按 问题 → 数学 → 交互 → 代码 的循环展开,主线是 NVIDIA 在 2024–2025 推出的 3DGRT / 3DGUT / 3DGRUT,以及它们引爆的整片衍生工作。
🌫️ 3DGS:把场景表示成几百万颗椭球,光栅化投影 + per-tile sort + $\alpha$-blend。
📐 3DGUT:把 EWA 的 Jacobian 投影换成 unscented transform,代价 23%,换来任意畸变相机支持。
🔦 3DGRT:把每颗椭球包成 icosahedron 进 BVH,OptiX RT cores 光追。慢 ~$3\times$,换来二次光线(反射 / 折射 / 阴影 / 卷帘)。
♻️ 3DGRUT:上面两条混合——主光线走 UT 快,次光线走 RT 准。
§ 1复习:从 NeRF/SDF 走到 3D Gaussian Splatting
你已经熟悉 NeRF:一个把场景编码进 MLP 权重的"隐式辐射场",渲染靠沿光线 march 数百次 query。你大概也接触过 SDF:把场景压缩成"到表面的有符号距离",渲染靠 sphere tracing。3D Gaussian Splatting (3DGS) 走了第三条路:把场景表示成几百万颗带朝向、带颜色、带不透明度的椭球体粒子,渲染靠光栅化把它们 splat 到屏幕上。
1.1 表示与渲染的本质区别
| 维度 | NeRF (Mildenhall 2020) | 3DGS (Kerbl 2023) |
|---|---|---|
| 场景表示 | 隐式 MLP $F(\mathbf{x},\mathbf{d}) \to (\sigma, c)$ | 显式百万级椭球 $\{\mu, \Sigma, \alpha, c_{\text{SH}}\}$ |
| 渲染原语 | 沿射线 ray march,每像素 query MLP 64–256 次 | 椭球投影到屏幕 + per-tile 排序 + $\alpha$-blend |
| 训练时长 | Vanilla 1-2 天;Instant-NGP 数分钟 | ~30-50 分钟(M360 / T&T) |
| 渲染速度 | < 1 FPS @ 1080p | 30-100+ FPS @ 1080p |
| 可微性 | 全可微(autograd) | 全可微(splatting 解析梯度) |
| 可编辑性 | 差(权重无空间局部性) | 好(每颗高斯是独立可移动原子) |
| 二次光线 / 反射 | 原理可做,但慢得不可用 | 原生不支持——需要 3DGRT 等扩展 |
1.2 3DGS 的四个核心公式
把这四条记牢,后面所有"光追扩展"都是在动其中某一条。
① 参数化
第 $i$ 颗高斯:
$$ \theta_i = \big\{\;\mu_i \in \mathbb{R}^3,\; s_i \in \mathbb{R}^3_+,\; q_i \in \mathbb{S}^3,\; \alpha_i \in [0,1],\; \mathbf{c}_i \in \mathbb{R}^{3(\ell+1)^2}\;\big\} $$其中 $s_i$ 是三轴 scale,$q_i$ 是单位四元数(旋转),$\alpha_i$ 是 opacity,$\mathbf{c}_i$ 是 spherical-harmonics 系数(让颜色随观察方向变化)。
② 协方差分解
$$ \Sigma_i = R(q_i)\,S(s_i)\,S(s_i)^{\top}\,R(q_i)^{\top} $$把 6 维 PSD 矩阵的优化转成对 $(s, q)$ 共 7 维的优化——缩放 + 旋转足以参数化任意各向异性椭球。
③ EWA 投影:3D → 2D
$$ \mu'_i = \pi(\mu_i),\qquad \Sigma'_i = J\,W\,\Sigma_i\,W^{\top}\,J^{\top} $$$W$ 是 view 矩阵的 $3\!\times\!3$ 旋转部分,$J$ 是相机投影 $\pi$ 在 $\mu_i$ 处的 Jacobian。注意这一步是"在均值处的一阶 Taylor 展开"——这正是后面 3DGUT 要替换掉的地方。
④ Alpha-blending(front-to-back)
$$ C(\mathbf{x}) = \sum_{i=1}^{N} \mathbf{c}_i\,\alpha'_i\,T_i,\quad T_i = \prod_{j\lt i}(1-\alpha'_j),\quad \alpha'_i = \alpha_i \cdot \exp\!\Big(-\tfrac{1}{2}(\mathbf{x}-\mu'_i)^{\top}\Sigma'^{-1}_i(\mathbf{x}-\mu'_i)\Big) $$1.3 你的第一个 demo:沿射线的 alpha-blend
下面这个 demo 把 NeRF/3DGS 共享的 volume rendering integral 拆开给你看。沿一条横向射线放了若干颗 1D 高斯(你可以把它当成"3D 高斯被一条射线穿过时的横截面强度"),下面画出 transmittance $T(t)$ 一路衰减、最终颜色 $C$ 一颗颗累积的过程。拖动高斯的中心、改变它们的 opacity 与 sigma,看 alpha-blending 如何工作。
Demo A · 沿射线的 alpha-compositing
每颗高斯都把"自己的颜色"按 $\alpha_i T_i$ 贡献到最终像素。拖动顶部圆点移动高斯,下方滑块控制 sigma 与 opacity。
注意几个数学事实:(a) 加入越多高斯,$T$ 越快衰减到 0;(b) 一旦 $T \to 0$,后面的高斯就再不贡献了——这正是 ray tracing 时 "k-buffer / early-out" 的依据;(c) 公式对"沿射线积分"是天然适配的——这就是为什么 3DGS 能被自然地搬到 ray tracing 上。
§ 2光栅化高斯的三条软肋
把 3D Gaussian 投影到屏幕、按 tile 排序、再 alpha-blend——这条流水线在针孔相机 + 主光线 only 的假设下闪闪发光,但它有三个隐含的雷区。理解它们是理解 3DGRT/3DGUT 为什么必须存在的前提。
2.1 软肋一:非针孔相机让 EWA 投影崩坏
EWA 投影 $\Sigma'_i = J\Sigma_i J^{\top}$ 把非线性投影 $\pi(\cdot)$ 在 $\mu_i$ 处一阶 Taylor 展开。在标准针孔相机下,FOV $60^{\circ}$ 左右、Gaussian 又小,一阶 Taylor 的残差可以忽略。但是:
- 📷 鱼眼 / 全景相机:FOV $\ge 120^{\circ}$,$\pi$ 在 Gaussian 支撑范围内已经强烈弯曲,"投影还是椭球" 这个结论失效。
- 📐 Gaussian 横跨视场边缘:粒子一半在中心一半在 $90^{\circ}+$ 处,$\mu_i$ 处的 Jacobian 只代表局部斜率。
- 🚗 卷帘快门(rolling shutter):每一行的曝光时刻不同,等价于一个时间相关的 $\pi(\mathbf{x}, t)$,EWA 干脆不能表达。
下面这个 demo 把这件事画给你看:一颗 3D Gaussian,用真实非线性投影(在每个像素上各自算)与EWA 一阶近似(只在均值处算一次 Jacobian)画到屏幕上,对比两者的差距。把 FOV 推到 $160^{\circ}+$,看 EWA 的椭圆怎么和真实形状渐行渐远。
Demo B · 针孔 vs 鱼眼:EWA 在哪里崩
左:相机真实投影(每像素独立采样)。右:EWA 一阶近似(投影 + Jacobian 拉伸)。把 Gaussian 拖到视场边缘,或拉大 FOV,看红色 (EWA) 椭圆怎么从真实形状(蓝色 splat)偏离。
2.2 软肋二:per-tile sort 引发 popping
3DGS 把屏幕切成 16×16 的 tile,同一个 tile 里的所有像素共享同一次按深度的排序。这是为了让 GPU shader 能做整齐的并行 alpha-blend——但代价是:当相机微动时,某两颗 Gaussian 在 tile 内的"代表深度"突然互换,整个 tile 的所有像素同步发生颜色跳变。这就是著名的 popping artifact。
2.3 软肋三:光栅化里没有"光线"这个一等公民
反射、折射、阴影、Ambient Occlusion、参与介质——这些都需要从某个 3D 点再发射一条 ray。光栅化是前向投影:从 3D 几何到 2D 屏幕,是一条单向流。没有"光线 hit 之后从交点继续 trace"的概念。
实践上 3DGS 社区有各种 hack(虚拟相机做镜子、deferred shading 做反射 lobe……),但这些都是"绕着光线走"。要做真正的二次光线,你必须有 BVH,必须能从任意 3D 点向任意方向投射 ray——这正是 ray tracing 提供的。
§ 33DGRT:让光线真的穿过高斯
Moenne-Loccoz, Mirzaei, et al. — 3D Gaussian Ray Tracing: Fast Tracing of Particle Scenes, NVIDIA + U. Toronto, SIGGRAPH Asia 2024. 这是把整套光线追踪流水线(OptiX、BVH、RT cores、any-hit shader)接到 3DGS 上的开山之作。它要解决的核心问题是:BVH 的最小单元是三角形,可 3D Gaussian 是一团发着光的雾,怎么让它进 BVH?
3.1 Proxy primitive:拉伸的 icosahedron
答案优雅而务实:给每颗 Gaussian 套一个紧致的 20 面凸壳(icosahedron),把它的 20 个三角面交给 OptiX 的 RT cores。Hull 的顶点按协方差被拉伸:
$$ \mathbf{v}' = \sqrt{2\log(\sigma_i / \alpha_{\min})}\; S_i\,R_i^{\top}\,\mathbf{v} + \mu_i \qquad\text{(3DGRT Eq. 7)} $$其中 $\alpha_{\min}$ 通常取 0.01。这个系数保证:任意一条 ray 只要在 hull 外侧,它对该 Gaussian 的 alpha 贡献必小于 $\alpha_{\min}$。换句话说,截断误差可控。论文消融过 AABB(太松)、octahedron(够紧但三角形不够多 → 在 RT cores 上跑得不快)、icosahedron(甜点):icosahedron 是 $\text{bounding 紧致度} \times \text{三角形数}$ 的最优解。
3.2 沿射线的 response 与 t*
当一条 ray $\mathbf{r}(t) = \mathbf{o} + t\mathbf{d}$ 穿过 Gaussian $(\mu_i, \Sigma_i)$,沿途的响应是:
$$ g_i(\mathbf{r}(t)) = \exp\!\left(-\tfrac{1}{2}(\mathbf{r}(t)-\mu_i)^{\top}\Sigma_i^{-1}(\mathbf{r}(t)-\mu_i)\right) $$它是 $t$ 的二次型 exp,恰好有一个解析最大点。把 $\mathbf{o}_g = S_i^{-1}R_i^{\top}(\mathbf{o}-\mu_i)$、$\mathbf{d}_g = S_i^{-1}R_i^{\top}\mathbf{d}$ 代入:
$$ t^{*}_i = \frac{(\mu_i - \mathbf{o})^{\top}\Sigma_i^{-1}\mathbf{d}}{\mathbf{d}^{\top}\Sigma_i^{-1}\mathbf{d}} = -\,\frac{\mathbf{o}_g^{\top}\mathbf{d}_g}{\mathbf{d}_g^{\top}\mathbf{d}_g} \qquad\text{(3DGRT Eq. 8)} $$关键近似:不沿 ray 积分整段响应,只取响应最大点处的值作为该 Gaussian 在该 ray 上的"有效 alpha":
$$ \alpha_i^{\text{ray}} = \sigma_i \cdot g_i(\mathbf{r}(t^{*}_i)) $$这与 splatting 中"2D EWA peak"在数学上等价,但在真实 3D 空间里计算——不受相机投影模型的影响。
3.3 BVH 遍历 + per-ray k-buffer(demo)
有了 hull、有了 BVH、有了 $t^*_i$,整个流水线变成:
- 每条 ray 走 BVH,命中的所有 hull 触发
any-hit shader; - any-hit shader 计算 $t^*$ 与 $\alpha^{\text{ray}}$,插入到 ray 自己的长度 k=16 的排序缓冲;
- 缓冲满了就做一次 alpha-composite 并消费,继续 trace;
- 直到 transmittance $T \lt \epsilon$ 或 ray 走出场景。
下面这个 demo 把上面的过程在 2D 里演给你看:屏幕里散布了若干 Gaussian,左下角的 BVH(绿色矩形)在动态打包它们。拖动相机原点和方向发射一条 ray,看遍历到的 Gaussian 被按 $t^*$ 排进 k-buffer,最后合成最终颜色。打开 "per-tile sort 模式" 看光栅化方案与之的对比。
Demo C · BVH 遍历与 per-ray k-buffer
拖蓝色原点移动相机;拖紫色端点改变 ray 方向。下方面板显示沿 ray 排序的 k-buffer 与合成颜色。切换 per-ray sort(3DGRT)与 per-tile sort(3DGS)看 popping 现象。
3.4 OptiX any-hit shader 伪代码
整个核心循环差不多长这样(语义化版本,省略 OptiX 7 boilerplate):
CUDA / OptiX 7 (semantic)// Per-ray payload: 长度 k 的 sorted hit buffer + 累积颜色 + 透射率
struct Payload {
float3 color; // 已累积颜色 C
float trans; // 透射率 T
int n; // 缓冲中 hit 数
Hit buf[K]; // 按 t* 升序排好
};
// ----- any-hit: 每命中一颗 hull 三角面就被调一次 -----
extern "C" __global__ void __anyhit__particle()
{
const int gid = optixGetPrimitiveIndex() / 20; // 20 三角面/icosahedron
const Gaussian g = gaussians[gid];
const float3 o = optixGetWorldRayOrigin();
const float3 d = optixGetWorldRayDirection();
// 1) 沿 ray 的解析最大响应点 t* (Eq. 8)
const float t_star = ray_tstar(o, d, g);
// 2) 在 t* 处的 Gaussian 响应 (Eq. 9)
const float resp = gaussian_response(o + t_star*d, g);
const float alpha = g.opacity * resp;
if (alpha < 1e-3f) { optixIgnoreIntersection(); return; }
// 3) 插入 k-buffer 并保持按 t* 排序
Payload* p = getPayloadPtr();
insert_sorted(p->buf, p->n, {t_star, alpha, sh_eval(g.sh, d), gid});
// 4) 缓冲满或某种 flush 策略 → 立即消费一段
if (p->n == K) flush_buffer(p);
optixIgnoreIntersection(); // 不要终止 ray,让它继续命中下一颗
}
// ----- ray-gen: 主循环 -----
extern "C" __global__ void __raygen__() {
Payload p = {make_float3(0), 1.0f, 0, {}};
Ray r = primary_ray(launch_index);
while (p.trans > 1e-3f && !ray_exited_scene(r)) {
optixTrace(handle, r.o, r.d, t_min, t_max,
/* mask */ 0xFF, /* flags */ OPTIX_RAY_FLAG_NONE,
SBT_RAY_TYPE_PARTICLE, NUM_RAY_TYPES, 0,
/* payload */ payload_handle(&p));
flush_buffer(&p); // 消费缓冲 → 累积到 (color, trans)
r = advance_ray(r, p.last_t); // 让 ray 继续往前 trace
}
framebuffer[launch_index] = p.color + p.trans * background;
}
- "投影到屏幕" 这一步消失了:取而代之的是 BVH ray query。
- "per-tile sort" 消失了:每条 ray 自己维护一个长度 k 的小 sort buffer。
- "EWA Jacobian" 消失了:直接在 3D 空间求 $t^*$ 和响应。
- 因为 ray 已是一等公民,反射、折射、阴影只需要在 hit point 再
optixTrace一次。
训练(反向传播)怎么做?
OptiX 走两遍。Forward:trace 一次,把每条 ray 命中的 particle index 序列缓存到 global memory。Backward:用相同的 ray 重新 trace(hit 序列已被缓存),按相同顺序 replay,对 $(\mathbf{c}_i, \sigma_i, \mu_i, \Sigma_i)$ 做链式法则梯度,atomicAdd 写回共享梯度缓冲。代价:训练慢 3DGS 约 $2\times$。推理慢 $2\text{-}3\times$(见下表)。
| 数据集 | 3DGRT PSNR ↑ | 3DGS PSNR | FPS 3DGRT | FPS 3DGS | 注 |
|---|---|---|---|---|---|
| Mip-NeRF 360 | 28.88 | 28.83 | 78 | 238 | LPIPS 显著优 (0.179 vs 0.228) |
| Tanks & Temples | 23.03 | 23.35 | 190 | 319 | SSIM 更高、popping 消除 |
| Deep Blending | 29.89 | 29.43 | 119 | 267 | |
| NeRF Synth (fisheye) | 40.53 | 不支持 | — | — | splatting 无法工作 |
结论:同 PSNR、更好 LPIPS、~$3\times$ 慢——但拿回了二次光线、非针孔相机、卷帘补偿这些光栅化拿不到的能力。
§ 43DGUT:用无迹变换把畸变相机塞回光栅化
Wu, Martinez Esturo, Mirzaei, Moenne-Loccoz, Gojcic — 3DGUT: Enabling Distorted Cameras and Secondary Rays in Gaussian Splatting, CVPR 2025 Oral. 3DGRT 解决了所有问题但要付 $3\times$ 性能税。NVIDIA 接着想:能不能不上 BVH,仅把 EWA 的"一阶 Taylor"换掉,让 splatting 也能吃鱼眼、卷帘?
4.1 重新看 EWA:它本质上是个 "linearize-then-propagate"
给定 3D Gaussian $\mathcal{N}(\mu, \Sigma)$ 和相机 $\pi(\cdot)$,EWA 的做法是:
- 对 $\pi$ 在 $\mu$ 处做一阶 Taylor:$\pi(\mathbf{x}) \approx \pi(\mu) + J(\mathbf{x}-\mu)$,
- 把 Gaussian 直接套上去:$\mu' = \pi(\mu)$,$\Sigma' = J\Sigma J^{\top}$。
这就是控制论里的 "Extended Kalman Filter"(EKF)!而 EKF 在函数曲率大、协方差大的工况下会迅速积累误差——这也正是 3DGS 在鱼眼下崩坏的物理原因。
4.2 Sigma points 的精确构造(n=3,因此 $2n+1=7$ 个点)
对 3D Gaussian $\mathcal{N}(\mu, \Sigma)$:
$$ \chi_0 = \mu,\qquad \chi_i = \mu + \big(\sqrt{(n+\lambda)\,\Sigma}\big)_i,\qquad \chi_{i+n} = \mu - \big(\sqrt{(n+\lambda)\,\Sigma}\big)_i,\quad i=1,\dots,n $$其中 $\big(\sqrt{(n+\lambda)\Sigma}\big)_i$ 是 $\sqrt{(n+\lambda)\Sigma}$(一般用 Cholesky)的第 $i$ 列。尺度参数 $\lambda = \alpha^2(n+\kappa) - n$,论文取 $\alpha=1.0,\ \beta=2.0,\ \kappa=0.0$。
Weights:
$$ w_0^{m} = \tfrac{\lambda}{n+\lambda},\quad w_0^{c} = \tfrac{\lambda}{n+\lambda} + (1-\alpha^2+\beta),\quad w_i^{m} = w_i^{c} = \tfrac{1}{2(n+\lambda)},\ i=1\dots 2n $$用任意 非线性相机 $\pi$(鱼眼、卷帘、广角畸变都行)把 7 个 sigma points 各自投到 2D,再加权拟合:
$$ \mathcal{Y}_i = \pi(\chi_i),\qquad \mu' = \sum_{i=0}^{2n} w_i^{m}\,\mathcal{Y}_i,\qquad \Sigma' = \sum_{i=0}^{2n} w_i^{c}\,(\mathcal{Y}_i-\mu')(\mathcal{Y}_i-\mu')^{\top} $$下面的 demo 把这一切画给你看。左边是一颗 Gaussian + 5 个 sigma points(2D 简化版,$n=2$ 所以是 5 个,3D 真实情况是 7 个);右边是经过非线性 lens 函数 $\pi$ 后的样子。注意「真实 $\pi$ 真值」用 400 点 Monte Carlo 估计的真实投影分布的 mean+cov——这才是 UT 和 EWA 都在估计的目标。把畸变拉大,看蓝色 UT 椭圆怎么贴着绿色真值椭圆不放,而红色 EWA 椭圆则慢慢飘走。
Demo D · Sigma points 投影 vs EWA Jacobian 投影
拖动 Gaussian 在场景中移动;畸变滑块 控制 lens 非线性强度。UT(蓝)应当贴住真值(绿);EWA(红虚线)在大畸变下会偏。真值绿椭圆 = 真实投影分布的 Monte-Carlo Gaussian 拟合(400 点);绿色点线 = 真实投影 $2\sigma$ 边界本身(非高斯)。
4.3 NumPy 实现(30 行讲明 UT 投影)
Python / NumPy — 教学版import numpy as np
def unscented_project(mu, Sigma, pi,
alpha=1.0, beta=2.0, kappa=0.0):
"""
把 3D Gaussian (mu, Sigma) 通过任意非线性相机 pi: R^3 -> R^2 投影。
返回投影后的 2D 均值与协方差。
pi 可以是鱼眼、卷帘、广角畸变 —— 唯一假设是它可调用。
"""
n = mu.shape[0] # = 3
lam = alpha**2 * (n + kappa) - n
c = n + lam
# 1) Sigma points
L = np.linalg.cholesky(c * Sigma) # L L^T = (n+λ)Σ
chi = np.empty((2*n + 1, n))
chi[0] = mu
for i in range(n):
chi[1+i] = mu + L[:, i]
chi[1+n+i] = mu - L[:, i]
# 2) Weights
wm = np.full(2*n+1, 1.0 / (2*c))
wc = wm.copy()
wm[0] = lam / c
wc[0] = lam / c + (1 - alpha**2 + beta)
# 3) 让每个 sigma point 独立穿过非线性投影
Y = np.stack([pi(x) for x in chi], axis=0) # shape (7, 2)
# 4) 重新拟合
mu_p = (wm[:, None] * Y).sum(axis=0) # (2,)
dY = Y - mu_p # (7, 2)
Sigma_p = (wc[:, None, None] * (dY[:, :, None] * dY[:, None, :])).sum(0)
return mu_p, Sigma_p
# --- 用鱼眼相机模型试一下 ---
def fisheye_pi(x_cam, f=1.0):
""" Equidistant fisheye: r = f·θ, θ 是与光轴的夹角 """
x, y, z = x_cam
r3 = np.sqrt(x*x + y*y + z*z)
theta = np.arccos(z / r3)
phi = np.arctan2(y, x)
r2 = f * theta
return np.array([r2 * np.cos(phi), r2 * np.sin(phi)])
mu = np.array([0.4, 0.3, 1.0])
Sigma = np.diag([0.04, 0.04, 0.01])
mu_p, Sigma_p = unscented_project(mu, Sigma, fisheye_pi)
print("Projected mean:", mu_p)
print("Projected cov: \n", Sigma_p)
整段实现里没有任何对 $\pi$ 的导数运算。把鱼眼换成卷帘快门(带 $t$ 输入的 $\pi(x, t)$),代码一行不用动——这就是 UT 相对 EWA 的核心优势。
§ 53DGRUT:混合架构,又快又能反射
3DGRT 全部走 RT cores → 准、能做反射,但慢。
3DGUT 全部走光栅化 → 快、能吃畸变相机,但还是没二次光线。
那能不能让主光线走 UT,二次光线走 RT?这就是 NVIDIA 实际开源的 3DGRUT——不是第三篇论文,而是把 3DGUT 与 3DGRT 合并成一条混合管线的系统。
| 能力 | 原版 3DGS | 3DGRT | 3DGUT | 3DGRUT(混合) |
|---|---|---|---|---|
| 针孔相机 | ✅ | ✅ | ✅ | ✅ |
| 鱼眼 / 卷帘 | ❌ | ✅ | ✅ | ✅ |
| 反射 / 折射 / 阴影(二次光线) | ❌ | ✅ | ❌ | ✅ |
| 速度(相对 3DGS) | $1\times$ | $\sim 0.3\times$ | $\sim 0.75\times$ | $\sim 0.5\times$ 主路径快, 二次慢 |
| 需要 RT cores | ❌ | ✅ | ❌ | ✅ |
训练时混合 pipeline 的 loss 与原版同构:$\mathcal{L} = (1-\lambda_s)\mathcal{L}_2 + \lambda_s \mathcal{L}_{\text{SSIM}}$,$\lambda_s = 0.2$。带反射/折射 mask 的像素走 RT 分支,其它像素走 UT 分支,因为参数共享,梯度在两条路径上叠加回传。
§ 6衍生宇宙:基于光追思想的 3DGS 工作地图(2024.07–2026.05)
3DGRT/3DGUT 砸开了一扇大门,2025 年下半年到 2026 上半年涌出大量后续工作。下面按主题分类——每条只给一句话 takeaway 与论文链接,便于你按图索骥。
6.1 反射 / 镜面 / 玻璃
- EnvGS (CVPR 2025) — 用一组"环境高斯"代替环境贴图建模远场反射,RT 采样。arXiv 2412.15215
- RaySplats (2025) — 最干净的"全程光追 3DGS"版本,主/次光线统一。arXiv 2501.19196
- Stochastic Ray Tracing for 3DGS (2026) — 用俄罗斯轮盘随机采样,$4\text{-}8\times$ 加速。arXiv 2603.23637
- Ref-Gaussian (ICLR 2025) — mask-free 的反射解耦。arXiv 2406.05852
- Mirror-3DGS / MirrorGaussian — 专攻平面镜场景。arXiv 2404.01168
- TransparentGS (SIGGRAPH 2025) — 透明高斯 + deferred refraction。arXiv 2504.18768
- GlossyGS (TVCG 2025) — glossy 物体的 BRDF inverse rendering。project
6.2 二次光线 / Relighting / 全局光照
- IRGS (CVPR 2025) — 2D Gaussian 光追 + 完整渲染方程的可微 inter-reflection。arXiv 2412.15867
- Relightable 3D Gaussians — BRDF + 光追阴影的早期工作。arXiv 2311.16043
- RTR-GS (2025) — 统一可见性、间接光、材质回归。arXiv 2507.07733
- GeoSplatting — mesh-based 光追做 PBR inverse rendering。arXiv 2410.24204
- Approximated GI for 3DGS — 屏幕空间 Monte Carlo 一次反弹 GI。arXiv 2504.01358
- ShadowGS — 卫星图像 sun-direction shadow ray。arXiv 2601.00939
6.3 参与介质 / 雾 / 烟
- Don't Splat your Gaussians (SIGGRAPH 2025) — scattering/emissive media 用 Gaussian 体素核 + 闭式 transmittance。arXiv 2405.15425
- DehazeGS — 多视角去雾 + 重建。arXiv 2501.03659
- SmokeSeer (CMU MS thesis 2025) — 同时去烟与重建。
6.4 相机模型(鱼眼 / 卷帘 / 事件相机)
- 3DGUT — §4 主线本身。
- $200^{\circ}$ 鱼眼综述 (2025) — 实拍大畸变下 3DGUT 仍最稳。arXiv 2508.06968
- GS on the Move — 卷帘 + 运动模糊联合补偿。arXiv 2403.13327
- Event-based 3D Gaussian Ray Tracing (2025) — megapixel 事件流端到端。arXiv 2512.18640
- 4D-GRT (2025) — 4D 动态 GS + 物理光追,生成可控相机效果。arXiv 2509.10759
6.5 加速 / 工程化 / 编辑
- GRTX (2026) — ray-space transform 把各向异性高斯"球化",BVH 更紧。arXiv 2601.20429
- REdiSplats (2025) — 高斯绑 mesh proxy + 光追 = 可编辑场景。arXiv 2503.12284
- RaRa Clipper — 光栅 + 光追混合做高质量裁剪。arXiv 2506.20202
- gsplat 库集成 3DGUT — NVIDIA 已合入 nerfstudio 主线。blog
§ 7在你的机器上跑:最小路径
NVIDIA 的官方实现集中在 nv-tlabs/3dgrut。要求:Linux、CUDA 11.8-13.0、OptiX、PyTorch、带 RT cores 的 NVIDIA GPU(RTX 20 系以上)。最小 hello-world 流程:
bashgit clone --recursive https://github.com/nv-tlabs/3dgrut.git
cd 3dgrut
# 1) 装环境(CUDA 12.x 推荐)
conda create -n 3dgrut python=3.10 -y && conda activate 3dgrut
./install_env.sh # 装 PyTorch、OptiX、自家 CUDA 内核
# 2) 用 NeRF Synthetic 的 lego 数据试一发 3DGUT 训练
python train.py \
-p configs/paper/3dgut/unsorted_3dgut.yaml \
--path datasets/lego \
--out_dir ./runs/lego_3dgut
# 3) 切到 3DGRT (全程光追)
python train.py \
-p configs/paper/3dgrt/unsorted_3dgrt.yaml \
--path datasets/lego \
--out_dir ./runs/lego_3dgrt
# 4) 混合(3DGRUT):主路径 UT、反射区域走 RT
python train.py \
-p configs/paper/3dgrut.yaml \
--path datasets/lego_with_mirror \
--out_dir ./runs/lego_3dgrut
如果你只有 nerfstudio:3DGUT 已经 merged into mainline gsplat,最近的版本里 gsplat.rasterization 加 camera_model="fisheye_ut" 直接可用。
§ R参考文献与延伸阅读
- Kerbl, Kopanas, Leimkühler, Drettakis. 3D Gaussian Splatting for Real-Time Radiance Field Rendering. SIGGRAPH 2023.
- Zwicker, Pfister, van Baar, Gross. EWA Volume Splatting. IEEE Vis 2001.
- Max, N. Optical Models for Direct Volume Rendering. IEEE TVCG 1(2), 1995.
- Mildenhall, Srinivasan et al. NeRF. ECCV 2020.
- Julier & Uhlmann. A New Extension of the Kalman Filter to Nonlinear Systems. AeroSense 1997.
- Moenne-Loccoz, Mirzaei et al. 3D Gaussian Ray Tracing: Fast Tracing of Particle Scenes. SIGGRAPH Asia 2024.
- Wu, Martinez Esturo, Mirzaei, Moenne-Loccoz, Gojcic. 3DGUT: Enabling Distorted Cameras and Secondary Rays in Gaussian Splatting. CVPR 2025 Oral.
- NVIDIA Developer Blog. Revolutionizing Neural Reconstruction and Rendering in gsplat with 3DGUT. 2025.