3D Generation
NeRF、Instant-NGP、3DGS、SDS、DreamFusion、Trellis 与 3D 生成面试题。
原文来自 ARIS 导入版本,尚未做本地逐段复审。建议阅读时优先核对最新论文、模型报告和库 API。
§0 TL;DR Cheat Sheet
💡 9 句话搞定 3D Generation — Embodied AI / AR / VR 面试核心要点(详见后文 §1–§11 推导)。
-
三大表示:NeRF(隐式神经场 + 体渲染)、3DGS(显式 Gaussian 点云 + 光栅化)、Mesh / SDF(显式表面 / 隐式距离场)。重建质量与速度的 sweet spot:3DGS(Kerbl 2023 SIGGRAPH Best Paper)。
-
NeRF 核心公式:,其中 是 transmittance。离散化得到 -compositing:。
-
Instant-NGP (Müller 2022 SIGGRAPH):多分辨率 hash 网格 + tiny MLP,5+ OOM 加速;hash collision 由 MLP 在含碰撞表项上自动学习消歧(被 loss + 多尺度冗余共同压制)。
-
3DGS 核心:场景表示为一组 3D Gaussian ,可微光栅化通过将 3D 协方差用 Jacobian 投影到 2D:,按深度排序后做 front-to-back alpha-blending。
-
DreamFusion SDS (Poole et al. 2022 arXiv → ICLR 2023 Outstanding):用 pretrained 2D diffusion 监督 3D 表示:,故意去掉 U-Net 对 求导的 Jacobian 项,使得训练 simulation-free。代价:mode-seeking → over-saturation / Janus。
-
VSD (Wang 2023 NeurIPS, ProlificDreamer):把 3D 参数 视为 random variable ,最小化的是渲染加噪图像分布之间的 KL:。梯度形式为 relative score ,其中 是 LoRA 微调的辅助 score。CFG 可从 100 降至 7.5。
-
Single-image / Few-view 3D:Zero-1-to-3 (Liu 2023 ICCV) 用 viewpoint conditioned diffusion;SyncDreamer / MVDream 学多视图联合一致性;TripoSR / InstantMesh / Stable Fast 3D 把 image-to-mesh 推到秒级(TripoSR ~0.5 秒、InstantMesh ~10 秒)。
-
3D Foundation Models (2024-25 开源):Trellis (Microsoft 2024) 用 structured latent + flow matching;Hunyuan3D-2 (Tencent 2025) shape→texture 两阶段;CLAY (Zhang 2024 SIGGRAPH, arXiv:2406.13897) 大尺度 latent diffusion = 多分辨率 VAE + latent DiT。
-
Embodied AI 关键应用:Sim2Real 资产生成、NeRF/3DGS 作为可微 simulator、language-conditioned 3D affordance。面试常见交叉:NeRF SLAM、Gaussian-Splat scene editing、3D 物理一致性。
§1 三大表示的直觉对比
3D 生成的第一选择题是 representation——选错了下游全废。
| NeRF (Implicit Field) | 3DGS (Explicit Point) | Mesh / SDF | |
|---|---|---|---|
| 存储 | MLP 权重 | 一堆 3D Gaussian | 三角网格 / signed distance |
| 渲染 | Ray marching + 体积分(GPU 数百 ms/帧) | Differentiable rasterization(GPU 数 ms/帧) | Rasterization(实时) |
| 训练 | 数百视图,数小时 (vanilla) | 数十视图,10-30 分钟 | 需要 mesh + texture optim |
| 质量 | 视图合成 SOTA | 与 NeRF 持平甚至更好(PSNR 高) | 受多边形分辨率制约 |
| 编辑 | 困难(神经场不可解释) | 容易(点可移动、删除、合并) | 容易(标准 DCC 流程) |
| 导出 mesh | 难(需 NeuS / Poisson) | 中等(2DGS / GSDF / SuGaR) | 自身就是 mesh |
| 下游适配 | 物理仿真不友好 | 容易接 PBR、IsaacSim、URDF | 标准 robot / AR/VR pipeline |
💡 面试直觉 — Embodied AI 倾向 mesh / 3DGS(仿真器友好);AR/VR 看场景规模(前景小物 mesh,大场景 3DGS);视觉重建 SOTA 用 3DGS。NeRF 现在偏 research baseline,工业落地以 3DGS 为主。
§2 NeRF:体渲染原理推导(必考)
2.1 连续体渲染公式
NeRF (Mildenhall 2020 ECCV Best Paper Honorable Mention) 把场景表示为 5D 神经场 :
- 输入:3D 位置 + 视角方向
- 输出:体密度 (与方向无关)+ 颜色 (依赖方向,捕捉镜面反射)
对相机射线 ,沿 体积分得到像素颜色:
其中 transmittance(光线从 到 没被遮挡的概率):
2.2 为什么是这个形式?— 从物理推导
考虑光线穿过参与介质(participating medium)。在 段内:
- 被吸收 / 散射出射线的概率:
- 介质在该点发射的颜色贡献:
令 为光线从 到 仍存活的概率。从 ,存活概率变化:
这是一个一阶 ODE,初值 ,解出:
每个深度 处贡献到像素的颜色 = 存活概率 × 该处吸收概率 × 该处颜色:
积分即得 。
2.3 离散化:-compositing(必考推导)
实际无法做连续积分,把 切成 段,每段间距 ,假设段内 为常数 。
段内 transmittance 衰减:在 上 满足 ,所以
由此段间 transmittance:
段内颜色贡献(积分而非简单矩形):
记 (段不透明度),合并得 NeRF 离散公式:
这正是图形学 front-to-back alpha-compositing 公式。关键: 而非 ;当 小时近似相等(一阶 Taylor),但大时差异显著(饱和到 1 vs 线性发散)。
✅ 物理一致性 — 与 保证颜色合成 永远在 [0, 1] 内;且无论 ray 怎么穿,(剩余 给背景)。
2.4 位置编码 :表示高频细节
MLP 默认是低频偏置(NTK 分析),直接拟合 会糊。NeRF 用 positional encoding 提频:
对 用 (60 维),对 用 (24 维)。后续 Tancik et al. 2020 "Fourier Features" 给了 NTK 解释:高频 让 kernel 衰减更慢,使 MLP 可学高频。
2.5 Hierarchical Sampling:粗 → 细
- Coarse 网络:均匀采样 点,渲染得到 weights
- Fine 网络:归一化 为 PDF,按重要性采 新点(重要性采样:表面附近权重大,应密集采样)
- 用 coarse + fine 总共 点合成最终颜色
- Loss:(两个网络都监督,coarse 提供 sampler 的 well-defined 梯度)
2.6 NeRF 训练代码(核心 30 行)
import torch import torch.nn as nn import torch.nn.functional as F def positional_encoding(x: torch.Tensor, L: int) -> torch.Tensor: """ x: [..., D]; 返回 [..., D*2*L],NeRF γ(p) 不含原始 x """ freqs = 2.0 ** torch.arange(L, device=x.device, dtype=x.dtype) * torch.pi args = x.unsqueeze(-1) * freqs # [..., D, L] pe = torch.stack([torch.sin(args), torch.cos(args)], dim=-1) # [..., D, L, 2] return pe.flatten(-3) # [..., D*2*L] def volume_render(sigma: torch.Tensor, color: torch.Tensor, t_vals: torch.Tensor, ray_d: torch.Tensor): """ NeRF 离散 α-compositing sigma: [B, N] 体密度 (>= 0; 通常已经过 softplus / ReLU) color: [B, N, 3] 颜色 t_vals: [B, N] ray 上采样点的 t 值(单调递增) ray_d: [B, 3] ray 方向(用于把 Δt → 真实距离) 返回 C: [B, 3], weights: [B, N], depth: [B] """ # δ_i = t_{i+1} - t_i ;最后一段补 1e10(吸收掉远端到无穷的剩余) deltas = t_vals[..., 1:] - t_vals[..., :-1] delta_far = torch.full_like(deltas[..., :1], 1e10) deltas = torch.cat([deltas, delta_far], dim=-1) # [B, N] deltas = deltas * torch.norm(ray_d[:, None, :], dim=-1) # 把 t-间距换成真实欧氏距离 alpha = 1.0 - torch.exp(-sigma * deltas) # [B, N] # T_i = ∏_{j<i} (1 - α_j) —— 用 cumprod,shift 一位让 T_1 = 1 T = torch.cumprod(torch.cat([torch.ones_like(alpha[..., :1]), 1.0 - alpha + 1e-10], dim=-1), dim=-1)[..., :-1] weights = T * alpha # [B, N] C = (weights[..., None] * color).sum(dim=-2) # [B, 3] depth = (weights * t_vals).sum(dim=-1) # [B] return C, weights, depth
⚠️ 数值陷阱 —
1 - alpha在 alpha 接近 1 时会下溢到 0,连乘后 T 整体被 zeroed;加+ 1e-10防止 backward 时log(0)。最后一段δ → 1e10强行把背景的 transmittance 推到 0,否则不可见区域的 ray 颜色会受未采样段污染。
2.7 Mip-NeRF / Mip-NeRF 360(抗锯齿)
vanilla NeRF 在低分辨率 / 缩放下走样严重(同一像素对应不同尺度的 cone,但 NeRF 当作 ray 处理)。
- Mip-NeRF (Barron 2021 ICCV):把 ray 视为 cone(视锥),用 Integrated Positional Encoding (IPE)——对 cone 段内的 PE 做闭式 Gaussian 期望 。对频率向量 而言,(标量情形即 );高频系数被 cone 协方差 通过 自动衰减,自然实现 multi-scale。
- Mip-NeRF 360 (Barron 2022 CVPR):unbounded scene 用 contraction for ,把无穷远压缩到 ball;加 distortion / proposal MLP 损失。
2.8 NeuS / VolSDF:体渲染 + SDF(导出 mesh 的关键)
NeRF 是 density-based,提取 mesh 需选 阈值(不稳)。NeuS (Wang 2021 NeurIPS) 用 SDF 替换 density:
其中 是 sigmoid, 是可学习"sharpness"。性质:表面处()权重峰值;可直接 Marching Cubes 提 mesh(mesh 是 )。
VolSDF (Yariv 2021 NeurIPS) 用 Laplace CDF ,思想类似。
§3 Instant-NGP:5+ OOM 加速(必考)
NeRF (vanilla) 训练一个场景要 1-2 天。Instant-NGP (Müller 2022 SIGGRAPH Best Paper) 5 秒就能拟合一个简单场景。
3.1 核心 idea:多分辨率 hash 网格
把"密集网格 vs 大 MLP"换成"稀疏 hash 网格 + tiny MLP"。
- 层分辨率(如 ),第 层格点数 N_\ell = \lfloor N_\min \cdot b^\ell \rfloor,几何级数(;论文典型取值 N_\min = 16,N_\max \in [512, 2048] 视场景大小而定)
- 每层用 hash function 把格点坐标映到固定大小的特征表(–,典型 )
- 查询点 :对每层做 8 角点三线性插值 → 拼成 维特征()
- 喂给 tiny MLP ( 层, hidden 64) 输出
3.2 Hash function
是互质的大常数(论文取 ——注意 并非质数,是论文有意设的,使第一维直接映射不打散)。 是 XOR。这是一种 spatial hash:常用于物理仿真的 BVH。
3.3 Hash collision 怎么消歧?(L3 高频追问)
当 (fine level 必然发生),多个格点映到同一表项 → 冲突。Why does it still work?
- Multi-resolution 冗余:粗 level 的特征 unique(),细 level 提供补充。MLP 可从粗特征恢复结构,细 level 只负责 detail。
- 稀疏性优先:大部分空间是空(NeRF 场景多数 voxel 是 background),有意义的 query 集中在表面附近,冲突的"有效格点对"很少。
- 梯度自动消歧:训练时只有表面附近格点会有非零梯度(被 ray weights 加权)。空白区的"冲突 entry"得不到梯度信号,不污染表面 entry。
- MLP 后处理:tiny MLP 在 拼接特征上学一个分类/回归,遇到冲突的表面点可用 其他 level 不冲突的特征 disambiguate。
💡 面试高分回答 — "Hash collision 看似破坏 unique 性,但实际生效区域是稀疏的(场景的 thin surface 仅占 voxel 总数极小比例),冲突区域大概率是无监督信号的 background;即便表面也有冲突,多分辨率层的非冲突特征 + tiny MLP 也能学到一致输出。这是个 'lazy collision resolution':与其代价昂贵地搞 perfect hash,不如用冗余 + 数据驱动消歧。"
3.4 Instant-NGP 训练公式
参数:hash table + MLP weights 。Loss 仍是 photometric MSE,但训练 5 秒 vs NeRF 1 天的差别来自:
- Tiny MLP:参数少 100×,前向快 ~50×
- Hash 表:稀疏激活,cache friendly
- CUDA kernel fuse:tiny-cuda-nn 把 forward + backward 融合
- Occupancy grid:粗占用网格 skip 空白区采样,避免无效 query
3.5 Plenoxels / TensoRF(同期的 explicit 派)
Plenoxels (Fridovich-Keil 2022 CVPR):纯 voxel 网格 + 球谐 SH 系数 + density,完全没 MLP,直接梯度下降到 voxel;速度类似 Instant-NGP 但显存大。TensoRF (Chen 2022 ECCV):把 3D grid field 用 VM / CP 分解 压缩,参数量从 降到 CP 的 或 VM 的 。
§4 3D Gaussian Splatting:显式可微光栅化(当前主力)
3DGS (Kerbl 2023 SIGGRAPH Best Paper) 解决了 NeRF 的两大痛:渲染慢、editing 难。
4.1 场景表示
场景 = 一组 3D Gaussian ,每个:
- 均值 (位置)
- 协方差 (形状),分解为 (rotation + diag scaling )
- 不透明度
- 颜色 用 SH 系数(,每色 16 系数,共 48 参数)
为什么用 而不直接学 ?— 要保证 正定。直接学 矩阵在梯度下会跑出半正定锥;分解后只需保证 正交(用四元数 参数化)+ 正(用 参数化),自然满足。
4.2 3D → 2D 投影 Jacobian(L3 必考推导)
把 3D Gaussian splat 到屏幕上做光栅化,需要把 3D 协方差 投影到 2D 协方差 。
Step 1:World → Camera:刚体变换 。(这里 取旋转部分;平移不影响协方差)。
Step 2:Camera → Screen:透视投影非线性:
非线性映射的协方差近似用一阶 Taylor。在均值 处求 Jacobian:
Step 3:2D 协方差(核心公式):
推导:若 ,则一阶近似 ,所以 。这就是 EWA splatting (Zwicker 2001) 的经典推论。
4.3 可微光栅化:tile-based front-to-back alpha-blending
像素 的颜色:
其中 是 2D Gaussian 在像素的值, 是覆盖 的 Gaussian 按深度排序。
关键工程:
- Tile 划分:屏幕分成 tile,每 tile 内 Gaussian 按深度排序,并行渲染
- GPU sort:用 radix sort,按
(tile_id, depth)复合键 - Front-to-back early stop:当累计 时退出
- CUDA kernel:作者公开
diff-gaussian-rasterization,前向 + 反向都是 manual derivative
4.4 3DGS 前向(PyTorch reference 实现)
def quat_to_rot(q: torch.Tensor) -> torch.Tensor: """ q: [N, 4] (w, x, y, z) already normalized; 返回 R: [N, 3, 3] """ w, x, y, z = q.unbind(-1) R = torch.stack([ 1 - 2*(y*y + z*z), 2*(x*y - w*z), 2*(x*z + w*y), 2*(x*y + w*z), 1 - 2*(x*x + z*z), 2*(y*z - w*x), 2*(x*z - w*y), 2*(y*z + w*x), 1 - 2*(x*x + y*y), ], dim=-1).reshape(-1, 3, 3) return R def gaussian_splat_forward( means3D: torch.Tensor, # [N, 3] Gaussian 中心 (world) scales: torch.Tensor, # [N, 3] log-scale (取 exp 得真实 scale) quats: torch.Tensor, # [N, 4] 四元数 (会归一化) opacities: torch.Tensor, # [N, 1] σ(logit) → α colors: torch.Tensor, # [N, 3] (这里简化为 RGB,不展 SH) viewmat: torch.Tensor, # [4, 4] world→camera K: torch.Tensor, # [3, 3] 内参 (fx, fy, cx, cy) H: int, W: int, ): """ 教学版前向:不做 tile sort / CUDA,仅展示数学。 实际生产用 gsplat / diff-gaussian-rasterization。 """ N = means3D.shape[0] device = means3D.device # --- 1. World → Camera --- homo = torch.cat([means3D, torch.ones(N, 1, device=device)], dim=-1) mu_cam = (homo @ viewmat.T)[:, :3] # [N, 3] z = mu_cam[:, 2].clamp(min=1e-4) # 防除零 # --- 2. 协方差 (3D) --- q = quats / quats.norm(dim=-1, keepdim=True) R = quat_to_rot(q) # [N, 3, 3] S = torch.diag_embed(torch.exp(scales)) # [N, 3, 3] cov3D = R @ S @ S.transpose(-1, -2) @ R.transpose(-1, -2) # [N, 3, 3] # World→Cam 旋转部分 W_rot (3x3) 应用到协方差 W_rot = viewmat[:3, :3] cov_cam = W_rot @ cov3D @ W_rot.T # [N, 3, 3] # --- 3. 投影 Jacobian J (2x3) --- fx, fy = K[0, 0], K[1, 1] x_c, y_c, z_c = mu_cam[:, 0], mu_cam[:, 1], z J = torch.zeros(N, 2, 3, device=device) J[:, 0, 0] = fx / z_c J[:, 0, 2] = -fx * x_c / z_c**2 J[:, 1, 1] = fy / z_c J[:, 1, 2] = -fy * y_c / z_c**2 # --- 4. 2D 协方差 Σ' = J W Σ W^T J^T --- cov2D = J @ cov_cam @ J.transpose(-1, -2) # [N, 2, 2] cov2D = cov2D + 0.3 * torch.eye(2, device=device) # low-pass filter (anti-aliasing) # --- 5. 2D 中心 (像素坐标) --- cx, cy = K[0, 2], K[1, 2] mu2D = torch.stack([fx * x_c / z_c + cx, fy * y_c / z_c + cy], dim=-1) # [N, 2] # --- 6. 深度排序 (front to back) --- depth = z_c order = depth.argsort() # ascending z mu2D, cov2D = mu2D[order], cov2D[order] colors_o = colors[order] alphas = torch.sigmoid(opacities[order]).squeeze(-1) # [N] # --- 7. 像素遍历 (教学版用全图 loop;真实实现 tile + CUDA) --- yy, xx = torch.meshgrid(torch.arange(H, device=device), torch.arange(W, device=device), indexing='ij') pix = torch.stack([xx, yy], dim=-1).float() # [H, W, 2] img = torch.zeros(H, W, 3, device=device) T_acc = torch.ones(H, W, device=device) inv_cov = torch.linalg.inv(cov2D) # [N, 2, 2] for i in range(mu2D.shape[0]): diff = pix - mu2D[i] # [H, W, 2] # 2D Gaussian: exp(-0.5 (diff)^T Σ^-1 (diff)) G = torch.exp(-0.5 * (diff @ inv_cov[i] * diff).sum(-1)) # [H, W] contrib = alphas[i] * G # [H, W] contrib = contrib.clamp(max=0.99) # 数值 img = img + (T_acc * contrib).unsqueeze(-1) * colors_o[i] T_acc = T_acc * (1 - contrib) if (T_acc < 1e-4).all(): # early stop break return img
⚠️ 教学版 vs 生产版差距 — 上面是 ,30k Gaussian + 800×800 图就要好几秒。真实
gsplat是 (a) tile-based: 每 tile 只处理"接触此 tile"的 Gaussian;(b) GPU radix sort 复合键;(c) 整个 forward / backward 全 manual CUDA,1080p ≤ 10ms。
4.5 自适应密度控制(面试常问)
3DGS 初始用 SfM (COLMAP) 稀疏点云,但训练过程要"撒"出更多 Gaussian。
| 触发条件 | 操作 | 直觉 |
|---|---|---|
| 梯度大 + scale 小 | clone(复制一份,沿梯度方向偏移) | "under-reconstruction"——这块区域缺细节 |
| 梯度大 + scale 大 | split(拆成 2 个小 Gaussian,scale ÷ 1.6) | "over-reconstruction"——一个大 Gaussian 覆盖了不该覆盖的区域 |
| opacity 接近 0 | prune(删除) | 该 Gaussian 没贡献,浪费显存 |
| 每 3k iter | reset opacities to 0.005 | 让 model 重新学透明度,防止 floater |
启发式条件:gradient norm > τ_pos(如 ),scale > τ_scale(场景尺度 1%)。
def densify_and_prune(gaussians, grad_thresh=2e-4, scale_thresh=0.01, max_screen_size=None): """ 教学版 densify 决策(简化;真实 gsplat 还有 screen-size 触发)。 假设 gaussians 暴露以下 1D 形状的字段(N = 当前 Gaussian 数量): xyz_grad_accum: [N] ‖累积 xyz 梯度范数‖ denom: [N] 累积次数(防 /0) scales: [N, 3] log-scale opacities: [N] sigmoid 后 ∈ (0, 1) screen_size: [N] 最近一次渲染的屏幕投影大小(可选) grad_dir: [N, 3] 最近一次梯度方向(用于 clone offset) """ grad_norm = gaussians.xyz_grad_accum / gaussians.denom.clamp(min=1) # [N] mean_scale = gaussians.scales.exp().max(dim=-1).values # [N] # ⚠️ 先在原始 N 个 Gaussian 上把 clone / split 两个 mask 都算出来, # 再做任何 append/删除——否则 clone_at append 之后数组变长,原 N 长度的 # split_mask 会与新数组错位(mask-length mismatch / 作用到错误对象)。 clone_mask = (grad_norm > grad_thresh) & (mean_scale <= scale_thresh) # [N] split_mask = (grad_norm > grad_thresh) & (mean_scale > scale_thresh) # [N] # CLONE:高梯度 + 小 scale —— 复制一份并沿梯度方向偏移;原始保留(append 到末尾) gaussians.clone_at(clone_mask, offset=gaussians.grad_dir[clone_mask]) # SPLIT:高梯度 + 大 scale —— 拆成 2 个子高斯(scale ÷ 1.6),并在末尾删除原始 # split_at 显式只作用于原始 N 个:传 original_n,不依赖 append 后的数组长度隐式对齐 gaussians.split_at(split_mask, n=2, scale_div=1.6, original_n=split_mask.shape[0]) # PRUNE:低 opacity / 屏幕过大 / 已被 split 标记 # ⚠️ 注意:clone 增加的新 Gaussian 已 append 到末尾,长度变了;这里的 mask 仅作用于原 N 个 prune_mask = (gaussians.opacities[:split_mask.shape[0]] < 0.005) | split_mask if max_screen_size is not None: prune_mask = prune_mask | (gaussians.screen_size[:split_mask.shape[0]] > max_screen_size) gaussians.remove_original(prune_mask) # 只删除原始 N 个里被标记的 gaussians.reset_grad_accum() return gaussians
💡 典型超参数 — Kerbl 2023 paper: densify every 100 iters;最大 Gaussian 数 5e6;总训练 30k iter;约 30 分钟到几小时。
4.6 2DGS / Surfels(surface-aligned)
3DGS 的 ellipsoid 不是 surface-aware;提 mesh 需 SuGaR / GSDF 后处理。2DGS (Huang 2024 SIGGRAPH) 把 3D ellipsoid 退化成 2D disk(一个 axis = 0),直接对齐表面,更适合 normal / depth 监督,提 mesh 时质量明显更好。
4.7 动态 4DGS
Dynamic 3DGS (Luiten 2024 3DV, arXiv:2308.09713) 用一组持续存在的 Gaussian:颜色 / 不透明度 / 大小跨帧保持不变,只让位置 与旋转随时间运动,并用 local-rigidity 正则约束邻域刚性;4DGS (Wu 2024 CVPR / Yang 2024 ICLR) 把 写成时间函数(MLP 或 spline);SC-GS (Huang 2024) 用 sparse control points 驱动密集 Gaussian(类似 LBS)。
§5 Mesh 提取:Marching Cubes / DMTet
NeRF / 3DGS 重建后,下游(仿真器、AR、3D 打印)经常需要 mesh。
5.1 Marching Cubes(经典必考)
输入:3D 标量场 (density / SDF)+ 阈值 。输出:水平集 的三角网格。
算法骨架:
- 把空间 voxel 化(每 voxel 8 角点)
- 对每个 voxel,8 角点二值化( 为 1, 否则 0)→ 256 种可能配置
- 查 lookup table:每个配置预定义了几条等值面三角片 + edge 上的顶点位置
- 线性插值 找精确顶点:在 edge 两端 之间,插值 ,顶点
- 合并所有 voxel 三角片 → 完整 mesh
def marching_cubes_sketch(density: torch.Tensor, threshold: float): """ 真实实现用 mcubes / scikit-image / pytorch3d;这里写思路 """ from skimage.measure import marching_cubes # density: [Nx, Ny, Nz];detach() 切断 autograd 图,cpu().numpy() 转 host verts, faces, normals, _ = marching_cubes( density.detach().cpu().numpy(), level=threshold, spacing=(1.0, 1.0, 1.0), gradient_direction='descent', # 法线方向;descent = surface 朝低密度 ) return verts, faces, normals
⚠️ NeRF 提 mesh 坑 — vanilla NeRF 没有"表面"概念,提 mesh 时阈值 难选,且 floater 会被一起提出来。用 NeuS / VolSDF 提 mesh 才稳(SDF 0 等值面定义良好)。
5.2 Differentiable: DMTet / FlexiCubes(端到端学 mesh)
Marching Cubes 不可微(lookup table 离散)。
- DMTet (Shen 2021 NeurIPS / Munkberg 2022 CVPR):用 deformable tetrahedral grid,每四面体 4 顶点 SDF + 位置 offset 可微。Marching Tetrahedra 替代 MC,topology 由 SDF sign 决定,几何由顶点位置决定,全程可微。
- FlexiCubes (Shen 2023 SIGGRAPH):泛化 dual marching cubes,引入额外可学参数(dual vertex offset / interpolation weight)解决 quality artifacts。
典型用法:DreamFusion 之后的 Magic3D、Fantasia3D 用 DMTet 在 SDS 监督下学 mesh + texture。
§6 SDS Loss:用 2D Diffusion 监督 3D(DreamFusion 系列)
6.1 问题设置
我们想生成 3D 资产但 没有 3D 训练数据——3D 数据稀缺(ShapeNet ~5万件,Objaverse-XL 1000万件但质量参差)。Pretrained 2D diffusion(Stable Diffusion, Imagen)海量。能否用 2D diffusion 当老师 supervise 3D?
DreamFusion (Poole et al. 2022 arXiv → ICLR 2023 Outstanding Paper) 提出 Score Distillation Sampling (SDS)。
6.2 Setup
- 3D 表示 (NeRF 参数 / DMTet vertices / 3DGS 点云)
- 可微渲染器 , 是图像( 是相机视角)
- Pretrained 2D diffusion ( 是文本 prompt)
目标:让 看起来像" 的 photo",即 落在 diffusion 学到的数据流形上。
6.3 SDS gradient 推导(L3 必考)
直觉:用 diffusion training loss 反传到 。Naive 想法:把渲染图 当训练样本,最小化
对 求梯度(链式法则):
问题:U-Net Jacobian 计算昂贵且数值差(diffusion 模型大且未训练 second-order 稳定)。
SDS trick:直接扔掉 U-Net Jacobian,得到:
(原 DreamFusion 论文写成 形式; 与常数 2 被吸收到 里。)
6.4 为什么扔掉 Jacobian 反而 work?
第一种解释(DreamFusion 原版,score 视角):(score)。 SDS gradient = (predicted score - noise) × renderer Jacobian,相当于把渲染图朝高概率方向推。
第二种解释(mode-seeking):SDS 等价最小化 的某种 mode-seeking 形式:往 的高概率区跑。
6.5 SDS 副作用:over-saturation / mode collapse / Janus
- Over-saturation:颜色饱和,对比度过高("plastic-y look")
- Over-smoothing:细节糊
- Mode collapse:物体趋于"canonical" 单一形式
- Janus problem:3D 物体在不同视角都长出一张"前脸"(人脸出现在头后;动物两边都是头)
根因:SDS 等价 mode-seeking KL,加上大 CFG 系数(DreamFusion 默认 100)才能逃出 mean-mode。CFG=100 把分布锐化到极端 → over-saturation。
⚠️ 面试高分点 — SDS 公式形式上"丢掉 Jacobian"省了计算,但代价是隐式变成 mode-seeking KL,需要超大 CFG 来缓解 mean-seeking blur;超大 CFG 又导致 over-saturation。这是个信息论 trade-off:simulation-free + computationally cheap = mode-seeking artifact。
6.6 SDS 代码(核心 30 行)
def sds_loss( renderer, # θ → x (B, 3, H, W) theta, # 3D 参数 (NeRF / 3DGS / DMTet) prompt_emb, # 文本 embedding (cond) [B, L, D] uncond_emb, # 文本 embedding (uncond / null) [B, L, D] unet, # frozen 2D diffusion U-Net (e.g. SD); 返回 noise pred Tensor alpha_cumprod: torch.Tensor, # [T_max] precomputed bar-alpha schedule cfg_scale: float = 100.0, t_range: tuple = (0.02, 0.98), ): """ Score Distillation Sampling loss (DreamFusion). 约定: unet(x_t, t, encoder_hidden_states=emb) -> [B, 3, H, W] noise pred. 如果用 diffusers 的 UNet2DConditionModel, 包一层取 .sample 即可。 返回的是 grad surrogate, 直接 backward 即可。 """ x = renderer(theta) # [B, 3, H, W] B = x.shape[0] device, dtype = x.device, x.dtype T_max = alpha_cumprod.shape[0] # 通常 1000 # 1. 采样 t 和 noise,forward 加噪 t = torch.randint(int(t_range[0] * T_max), int(t_range[1] * T_max), (B,), device=device) noise = torch.randn_like(x) abar = alpha_cumprod.to(device=device, dtype=dtype)[t].view(B, 1, 1, 1) x_t = abar.sqrt() * x + (1 - abar).sqrt() * noise # 2. U-Net 预测 noise (cond / uncond),CFG 组合;关键:不对 diffusion 求导 with torch.no_grad(): eps_uncond = unet(x_t, t, encoder_hidden_states=uncond_emb) eps_cond = unet(x_t, t, encoder_hidden_states=prompt_emb) eps_pred = eps_uncond + cfg_scale * (eps_cond - eps_uncond) # 3. SDS gradient: w(t)(ε_pred - ε) · ∂x/∂θ; w(t) = σ_t² 是常见选择 grad = ((1 - abar) * (eps_pred - noise)).detach() # backward of (grad · x) 给出 grad · ∂x/∂θ return (grad * x).sum() / B
💡 训练 loop — 每 iter 随机采视角 ,渲染 ,算 SDS loss,反传到 。NeRF 表示要训 10k-100k 步(GPU 几小时);3DGS 表示(GaussianDreamer / DreamGaussian)几分钟到一小时。
6.7 VSD:变分 SDS(ProlificDreamer, NeurIPS 2023 Spotlight)
VSD (Wang 2023 NeurIPS) 把 SDS 视为"对 single 做点估计"的特殊情况,泛化为对 分布 做变分推断。
Setup
- 把 3D 参数 当 latent random variable, 是其分布
- 目标:让 rendered image distribution 与 diffusion 学到的 prior 分布对齐(不是 mode 对齐)
Objective and gradient
ProlificDreamer 把目标写成 KL:
其中 是 "从 渲染 + 加噪到 " 诱导的分布。变分梯度(Wang et al. 2023, Theorem 2 略写)给出关于 的更新方向为 relative score:
对比 SDS:把 raw noise 替换成辅助 score 。 是 LoRA 微调 的 score network,在线最小化 score-matching loss 来跟踪当前 的 score;它扮演的是 "variance-reduction baseline" 的角色(与 RL actor-critic 的 value baseline 同构)。注意:上面是 gradient form,不是平方-loss form;论文没有"先写一个 标量 loss 再求导"的可实现形式—— 依赖 ,那样会丢掉 KL 的关键项。
直觉为什么缓解 over-saturation
- SDS:把 rendered 推向 prior 的 mode(mean-mode → 需 CFG=100 → over-saturation)
- VSD:用辅助 score 学当前 rendered 分布的"自己的 mode",更新方向 = 从"我现在在哪"指向"prior 在哪"(relative gradient),不需大 CFG 拉满。CFG 可降到 7.5(常规 diffusion 默认值),avoid extreme sharpening
- 实验上:VSD 颜色更自然,几何更复杂,可同时维护多个 mode(ProlificDreamer 给出 50k 步训练得 photorealistic Buddha 等)
✅ VSD vs SDS 的关键认知 — SDS 是 "single-point + mode-seeking";VSD 是 "particle / variational + relative score"。后者本质是给 SDS 加了个可学习 baseline()减方差,思想上类比 RL actor-critic 的 value baseline。
6.8 SDS 衍生家族:mesh / 3DGS + SDS
| 方法 | 表示 / Stages | 关键点 |
|---|---|---|
| DreamFusion | NeRF + SDS @ low-res | 原版,提 mesh 难 |
| Magic3D (Lin 2023 CVPR) | Instant-NGP @ 64px → DMTet + SDS @ 512px | 两阶段:粗结构 → 高分辨率端到端 mesh |
| Fantasia3D (Chen 2023 ICCV) | DMTet 几何 + PBR material | normal-as-input + 物理材质 BRDF |
| DreamGaussian (Tang 2024 ICLR) | 3DGS + SDS, ~2 分钟 / 物体 | GPU 速度优势;mesh export + UV-Net texturing |
| GaussianDreamer (Yi 2024 CVPR) | Point-E / Shap-E init → 3DGS + SDS | 缓解 from-scratch 几何混乱 |
§7 Single-Image / Few-View 3D 生成
更实用的设定:给一张图,生成 3D。
7.1 Zero-1-to-3 范式(novel view via diffusion)
Zero-1-to-3 (Liu 2023 ICCV):用 Objaverse 上 finetune Stable Diffusion,让它接收 (input view, target camera) → output novel view。
- Input:单图 + 相对相机位姿
- Diffusion conditioning:image embedding (CLIP) + camera embedding (sinusoidal)
- Output:在 视角下的图
用法:给一个 input view,sample 16-32 个 novel view,再用 NeRF / 3DGS 重建。
衍生:
- Zero-1-to-3++ (Shi 2023):固定生成 6 个 anchor view(北极视角 + 4 平视角 + 俯视角),减少 randomness
- SyncDreamer (Liu 2024 ICLR):在 latent 上联合预测多视图(cross-attention 让 views 看到彼此),保证 3D 一致
- MVDream (Shi 2024 ICLR):text-to-multi-view,4 视图同时生成;后接 SDS 精化
7.2 One-2-3-45 / InstantMesh / TripoSR / Stable Fast 3D
| 方法 | 输入 | 输出 | 速度 | 关键 |
|---|---|---|---|---|
| One-2-3-45 (Liu 2023 NeurIPS) | 单图 | mesh | 45 秒 | Zero-1-to-3 → SparseNeuS |
| One-2-3-45++ (Liu 2024) | 单图 | mesh | 60 秒 | 多视图 + SDF |
| TripoSR (Tochilkin 2024, Stability+Tripo) | 单图 | NeRF/mesh | 0.5-2 秒 | LRM (Large Reconstruction Model) 风格 transformer |
| InstantMesh (Xu 2024, arXiv:2404.07191) | 单图 | mesh | ~10 秒 | Zero-1-to-3++ 多视图 → sparse-view recon transformer |
| Stable Fast 3D (SF3D, Stability 2024) | 单图 | textured mesh | ~0.5 秒 | TripoSR 后继;加 illumination disentangle + UV unwrap |
LRM (Hong et al. 2023 arXiv → ICLR 2024) 设定:把图当 token + Plucker ray embedding,transformer 输出 NeRF triplane。这是 TripoSR / InstantMesh 的母模型。
7.3 LRM Triplane 表示(面试高频)
- Triplane (Chan 2022 EG3D):3 个轴对齐 2D 平面(XY, YZ, XZ),共 维
- 查询 3D 点 :在每个平面双线性插值 → 三个平面特征逐元素相加(EG3D 是 sum,不是 concat)→ 小 MLP →
- 优点:比 voxel grid 显存少( vs ),比 hash grid 更 dense 适合 transformer 输出
- LRM / TripoSR / InstantMesh 都让 transformer 直接 regress triplane tokens
§8 3D Foundation Models(2024 开源浪潮)
8.1 Trellis (Microsoft 2024, 开源)
Trellis (Xiang 2024 arXiv) 是首个尝试做"3D 的 Stable Diffusion"开源工作。
- Structured Latent (SLAT):把 3D 资产编码到 voxel 上的稀疏 latent grid——既保留空间结构(适合 sparse conv / sparse attention),又紧凑(仅 active voxel 存 latent)
- 3D VAE:把 mesh + texture (signed distance field 派生) → SLAT
- Flow matching prior:在 SLAT 上跑 rectified flow,conditioned on text/image
- 多 decoder:从 SLAT decode 出 NeRF / 3DGS / mesh 三种表示(同一 latent,可选输出格式)
- 训练数据:Objaverse-XL 子集 + 内部高质量集
- 效果:text-to-3D / image-to-3D,几秒到几十秒,质量超过 SDS 系列
8.2 Hunyuan3D-1 / -2 (Tencent 2024-25, 开源)
Hunyuan3D 走 shape-then-texture 两阶段路线。
- Hunyuan3D-1 (Yang 2024 arXiv):
- Stage 1: text/image → multi-view image (Zero-1-to-3 系)
- Stage 2: multi-view → 3D mesh (LRM-like reconstructor)
- 几秒到几十秒输出 textured mesh
- Hunyuan3D-2 (Tencent 2025, arXiv 2501.12202):
- Hunyuan3D-DiT:geometry-only DiT 在 SDF latent 上生成 mesh
- Hunyuan3D-Paint:multi-view PBR texture diffusion,UV space refinement
- 高质量 PBR texture(实战可用于游戏 / VR 资产)
- 开源:HuggingFace 上完整权重 + 推理代码
8.3 CLAY (Zhang 2024 SIGGRAPH, arXiv:2406.13897)
- 多分辨率 VAE + latent DiT:3D shape VAE 把 mesh 编码到 neural field latent,再在 latent 上跑 DiT diffusion(区别于 3DShape2VecSet,Zhang et al. 2023, arXiv:2301.11445,那是一个单独的 vector-set 表示工作)
- 大规模训练(Objaverse-XL + 内部清洗集)
- 输出 SDF → marching cubes → mesh
- 加 PBR texture stage(类似 Hunyuan3D-2)
Rodin (Microsoft 2023, 商业):早期 text-to-3D-avatar 产品级系统,diffusion on triplane,主打 character / avatar。
8.4 对比表
| 方法 | 表示 | Prior | 训练规模 | 开源 |
|---|---|---|---|---|
| Trellis | Structured Latent (SLAT) + 多 decoder | Rectified Flow | Objaverse-XL 子集 | ✅ |
| Hunyuan3D-2 | SDF latent (Shape DiT) + UV texture diff | Diffusion | 内部大规模集 | ✅ |
| CLAY | 多分辨率 VAE latent (latent DiT) | Diffusion | Objaverse-XL + 内部 | 部分 |
| Rodin | Triplane | Diffusion | 商业内部 | ❌ |
| TripoSR / SF3D | NeRF/mesh feedforward | 无 prior,纯 regression | Objaverse 类 | ✅ |
💡 架构选择直觉 — 大 scene / general object 用 Trellis 风格 SLAT(保留空间结构);高质量 single mesh 用 CLAY 风格 latent DiT(多分辨率 VAE latent,global attention);快速推理用 LRM/TripoSR feedforward(不做 diffusion,直接 regress)。
§9 复杂度 / 资源对比
| 方法 | 训练 | 推理 (一帧) | 显存 (训练) | 显存 (模型) |
|---|---|---|---|---|
| NeRF vanilla | 1-2 天 | 数秒 | 8 GB | <10 MB MLP |
| Instant-NGP | 5 秒 - 5 分钟 | 30 fps+ | 4-12 GB | 100-500 MB hash |
| 3DGS | 10-30 分钟 | 100 fps+ | 6-24 GB | 100 MB - 1 GB Gaussian |
| 2DGS | 与 3DGS 接近 | 与 3DGS 接近 | 类似 | 类似 |
| DreamFusion (NeRF+SDS) | 2 hr / 物体 | — | 12 GB | NeRF 本身 |
| DreamGaussian (3DGS+SDS) | 2 分钟 / 物体 | — | 8-16 GB | — |
| ProlificDreamer (VSD) | 3-6 hr / 物体 | — | 24 GB | — |
| TripoSR feedforward | 训练 50 GPU 天 | 0.5 秒 (A100) | inference 6 GB | 1.5 GB |
| Trellis | 训练 100+ GPU 天 | 数秒 | inference 16 GB | 数 GB |
| Hunyuan3D-2 | 训练大集群 | 数十秒 | inference 24+ GB | 多模型组合 |
§10 与相关方法对比 & Embodied AI 应用
10.1 3D-vs-2D 生成关键区别
| 维度 | 2D 生成 (Stable Diffusion) | 3D 生成 |
|---|---|---|
| 数据量 | LAION-5B 50亿图 | Objaverse-XL 1000万件(小 500×) |
| 数据格式 | 图像(统一 RGB) | mesh / SDF / point cloud / NeRF / 3DGS(碎片化) |
| 训练 prior | 直接 train diffusion | 用 2D diffusion 蒸馏 (SDS / Zero-1-to-3) 或 用 3D-native diffusion (Trellis / CLAY) |
| 评测 | FID, CLIP score | Chamfer / IoU / PSNR (recon) + perceptual + user study |
| 下游 | 直接出图 | 出资产 → 渲染 / 仿真 / 编辑 |
10.2 Embodied AI / AR / VR 实战路线
| 任务 | 推荐表示 | 关键工具链 / 约束 |
|---|---|---|
| Sim2Real 资产 | mesh (PBR) | Trellis / Hunyuan3D-2 → IsaacSim / MuJoCo |
| 室内大场景 | 3DGS | COLMAP → 3DGS(chunk-wise 用 VastGS / CityGS) |
| NeRF/3DGS as simulator | NeRF / 3DGS + physics | DreamGaussian-Sim / Splatting Physics |
| 3D affordance / manipulation | point cloud / 3DGS feature | OpenScene / LERF / RVT / 3D Diffuser Actor |
| AR 物体扫描 | 3DGS(光照真实 + 实时) | mobile 算力(PostShot / Luma),剪枝 / 量化 |
| VR 大场景 | 3DGS (large-scale) | 60 fps stereo + 6DoF |
| Avatar | mesh + LBS 或 3DGS avatar | 实时表情 / 头发 |
| Object insertion | mesh + PBR | 环境光照一致(IBL) |
⚠️ Embodied AI 面试追问示例 — "做 NeRF 物理仿真器最大挑战?" 要点:NeRF 是 radiance,没 mass / friction → 需手动叠物理 prior;mesh 提取有 floater → 碰撞检测难;可微但 backward 慢;业界更多用 3DGS / mesh 而非 vanilla NeRF。
§11 工程实战 & 易踩坑
11.1 COLMAP / SfM 前处理(重建必经)
输入多视图 → 输出内参 + 外参 + 稀疏点云;标准流程 SIFT → matching → incremental SfM → bundle adjustment。常见坑:texture-less / 镜面物体 SfM 失败;动态物体污染外参。
11.2 数值稳定(NeRF/3DGS 通用)
| 问题 | 症状 | 修复 |
|---|---|---|
| Sigma 爆炸 | floater 充斥空间 | 用 softplus 或 truncated;occupancy grid skip |
| Alpha 饱和 | 1-α 下溢 → T 全 0 | (1-α).clamp(min=1e-10) 或 log-space cumprod |
| Gaussian 退化 | 极小 scale / 极大 anisotropy | clamp scale lower bound;regularize anisotropy |
| Densify 爆炸 | Gaussian 数量飙到内存上限 | 加 max gaussian 数;周期 prune;reset opacity |
| SDS Janus | 多视角脸 / 头 | 加 view-conditioning("front view" / "back view");MVDream |
| SDS over-sat | 颜色饱和 | CFG 降低;改用 VSD;或 negative prompt |
11.3 多机分布式 & 评测指标
分布式:NeRF / Instant-NGP / 3DGS 单 GPU 标准;大场景 3DGS 用 chunk-wise (VastGaussian, CityGaussian);SDS/VSD 每 iter 跑 2 次 SD forward,8×A100 可显著提速;Trellis / Hunyuan3D 训练是大规模 multi-node DDP。
| 评测指标 | 用途 | 算法 |
|---|---|---|
| PSNR / SSIM / LPIPS | 视图合成(重建) | 与真实视图对比 |
| Chamfer Distance | mesh 几何 | 两点云最近邻距离平均 |
| F-Score (3D) | mesh / point | precision + recall under threshold |
| CLIP Score / CLIP-R-Prec | text-to-3D 对齐 | render → CLIP 相似度 / 区分干扰 prompt |
| User study | 最终质量 | MTurk / lab-internal |
§12 25 高频面试题
按难度分 3 档(L1 必会 / L2 进阶 / L3 顶级 lab)。每题点开看答案要点 + 易踩坑。
L1 必会题(任何 3D / vision 岗都会问)
Q1.NeRF 体渲染公式?
-
-
是透射率
-
离散化 → -compositing:,
只写 漏 ;或把 写成 (一阶近似但严格错)。
Q2.为什么 NeRF 要 positional encoding?
-
MLP 默认低频偏置(NTK 分析)
-
提供高频 basis
-
直接学 出来的图糊;加 PE 后高频细节恢复
误以为 PE 是给 MLP 加位置(其实是给空间频率谱),或弄反 vs 的频率级数( vs )。
Q3.NeRF 的 hierarchical sampling 是什么?
-
两个网络:coarse + fine
-
coarse 均匀采 64 点,渲染得 weights
-
把 归一化为 PDF,按重要性采 128 个 fine 点(密集采在表面)
-
Loss 同时监督两网络
说"只采一次更密集"——错过了 importance sampling 的核心。
Q4.Instant-NGP 为什么比 NeRF 快 5+ OOM?
-
Hash 网格替代密集 grid:固定 大小哈希表,cache-friendly
-
Tiny MLP (2 层 hidden 64) 替代大 MLP(NeRF 8 层 256)
-
多分辨率级联 + occupancy grid skip 空白区采样
-
CUDA fused kernel(tiny-cuda-nn)
只说"用了哈希"——漏了 multi-resolution + tiny-MLP + occupancy skip 的组合贡献。
Q5.3DGS 的"高斯"是怎么定义的?
-
每个 Gaussian
-
位置, 协方差
-
分解( 用四元数, 用对角 + ),保证半正定
-
用球谐 SH 系数(,48 参数)
只说"高斯分布"——漏了协方差参数化技巧 + SH color。
Q6.3DGS 渲染怎么做?
-
把 3D Gaussian 投影到 2D()
-
按深度排序
-
Front-to-back alpha-blending(与 NeRF -compositing 同源)
-
实际是 tile-based + CUDA radix sort
只说"光栅化",不提投影 Jacobian / 排序 / alpha-blend。
Q7.3DGS 的 densification 怎么做?
-
高梯度 + 小 scale → clone(under-reconstruction)
-
高梯度 + 大 scale → split(over-reconstruction)
-
低 opacity 或过大 screen-size → prune
-
周期性 reset opacity 防 floater
把 clone 和 split 弄反;忘了 reset 这步。
Q8.NeRF vs 3DGS 对比?
-
NeRF:隐式 (MLP),渲染慢(ray march),editing 难
-
3DGS:显式(点云),渲染快(rasterize),editing 易
-
质量:3DGS PSNR 通常 ≥ NeRF;NeRF 在体积效应(烟雾 / 半透明)更好
-
业界趋势:3DGS 主流,NeRF research-only
把两者当不可比较的不同事物——其实都是 volumetric scene rep,3DGS 是 explicit version of NeRF。
Q9.Marching Cubes 是什么?
-
输入 3D 标量场 + 阈值,输出三角网格
-
每 voxel 8 角点二值化(高/低于阈值)→ 256 种 lookup table
-
Edge 上线性插值定顶点位置
-
不可微(lookup 离散)
说"找等高线"——MC 是 3D,等高线是 2D Marching Squares 的事。
Q10.SDS 大致是什么?
-
用 pretrained 2D diffusion (Stable Diffusion) 监督 3D 表示
-
渲染 ,加噪 ,问 diffusion "这是 的图吗"
-
gradient
-
DreamFusion (Poole et al. 2022 arXiv → ICLR 2023 Outstanding Paper) 提出
只说"用 SD 训 NeRF",漏了 SDS gradient 的特殊形式(去掉 U-Net Jacobian)。
L2 进阶题(research-oriented 岗位)
Q11.推导 NeRF 连续积分 → 离散 $\alpha$-compositing。
-
满足 ,段内 常数 →
-
段内颜色贡献
-
记 ,则 ,
把 写成 而非 ;或省了 ODE 求解过程。
Q12.推导 3DGS 的 3D→2D 投影 Jacobian。
-
透视投影 非线性
-
一阶 Taylor:
-
-
( 是 world→cam 旋转)
直接套 "covariance projection" 公式不推;或忘了 这步(World→Cam 旋转)。
Q13.Instant-NGP 的 hash collision 如何消歧?
-
Multi-resolution 冗余:粗 level 不冲突,细 level 才冲突;MLP 可从粗-fine 共同推
-
稀疏激活:有效 supervision 集中在 surface 附近;空白区冲突 entry 无梯度
-
MLP 后处理:在 拼接特征上学非线性融合,可 disambiguate
-
没有 explicit collision resolution;靠"lazy resolution by sparsity + redundancy"
以为有 hash chaining 之类的传统消歧——实际是数据驱动 implicit 消歧。
Q14.SDS gradient 漏了哪项 Jacobian?为什么?
-
Naive diffusion training grad:
-
SDS 把 U-Net Jacobian 扔掉
-
直觉:(1) 计算昂贵;(2) U-Net 没训练 second-order 稳定 → Jacobian 噪声大
-
代价:SDS 变成 mode-seeking KL,需要大 CFG (100) 才能逃 mean-mode → over-saturation
只说"为了简化"不说后果。或不知道 mode-seeking 是 KL 方向决定的。
Q15.VSD 如何缓解 SDS 的 over-saturation?
-
SDS:拉向 prior 的 mode;需 CFG=100 强化 → over-saturation
-
VSD:把 3D 参数 视为 random variable ,最小化 KL(rendered dist || prior)
-
引入辅助 score (LoRA 微调 SD)跟踪当前 的 score
-
gradient = —— relative score,不需大 CFG
-
类似 RL actor-critic 用 value baseline 减方差
说 VSD 用 "variational" 但讲不清 替代 raw noise 的角色。
Q16.Zero-1-to-3 / SyncDreamer / MVDream 区别?
-
Zero-1-to-3 (Liu 2023 ICCV):input view + 相机 → single novel view;每次独立 sample
-
Zero-1-to-3++ (Shi 2023):固定 6 个 anchor view,一次出多张(减 randomness)
-
SyncDreamer (Liu 2024 ICLR):在 latent 上联合预测多视图,cross-attention 让 views 互看 → 一致性更好
-
MVDream (Shi 2024 ICLR):text-to-multi-view(不需要 input image),4 视图同生成 + SDS 精化
只说"都是 novel view"——漏了独立 vs 联合 vs text-only 这条主线。
Q17.Mip-NeRF 怎么抗锯齿?
-
Vanilla NeRF 把像素当 ray;不同分辨率下同像素对应不同尺度 → aliasing
-
Mip-NeRF 把像素当 cone(视锥),cone 段近似 anisotropic Gaussian
-
IPE (Integrated Positional Encoding): 有闭式解
-
高频系数被 衰减 → multi-scale 自动平滑
只说"用 cone",不讲 IPE 的高频衰减作用。
Q18.NeuS vs vanilla NeRF 提 mesh 的差别?
-
vanilla NeRF:density 没明确 surface,提 mesh 要选 阈值(不稳)
-
NeuS (Wang 2021 NeurIPS):用 SDF 替换 density,定义 via sigmoid 导数
-
表面 = ,良好定义
-
Marching Cubes 直接对 SDF 跑,质量明显更好
直接说"用 SDF",但不讲 NeuS 怎么把 SDF 接到 NeRF 体渲染里。
Q19.LRM 系列(TripoSR / InstantMesh)核心?
-
Triplane 表示:3 个轴对齐 2D 平面, 显存
-
Transformer 把图像 token + Plucker ray embedding → regress triplane tokens
-
推理 feedforward(无 SDS / 无 iterative 优化),秒级出 3D(TripoSR ~0.5 秒、InstantMesh ~10 秒)
-
TripoSR (Stability+Tripo 2024) / InstantMesh (Xu 2024) / SF3D (2024) 都属此族
把它们当成 SDS 系列——错,LRM 完全 feedforward;不算 distillation。
Q20.3DGS 怎么提 mesh?
-
vanilla 3DGS 不友好(ellipsoid 不是 surface)
-
SuGaR (Guédon 2024 CVPR):surface alignment loss + Poisson reconstruction
-
2DGS (Huang 2024 SIGGRAPH):把 ellipsoid 退化为 2D disk,对齐表面 → MC 提 mesh 更稳
-
GSDF (Yu 2024):joint train SDF head 与 3DGS
说"直接 MC"——3DGS 没有 density 场,直接 MC 不 work;必须先 surface-align。
L3 顶级 lab 题(顶会 / industry 研究岗)
Q21.手推 NeRF 离散 $\alpha$-compositing。
-
ODE ,初值 →
-
段内 上 常数 ,所以
-
段间累积 ,其中
-
段内颜色贡献
-
合成
-
关键: 严格 vs 一阶近似(在 时一致)
省略 ODE 推导直接套结论;或在 大时用 替 出错。
Q22.Instant-NGP hash collision 如何被 MLP 自动消歧?
-
冲突发生场景:fine level 的格点数 (hash 表大小),多个格点映到同一 entry
-
稀疏激活:场景大部分 voxel 是 background,仅 surface 附近 voxel 有非零监督梯度——冲突的"两个 background entry"得不到信号,互不污染
-
多分辨率冗余:粗 level 保证 unique;细 level 提供 detail。即使细 level 冲突,粗 level 的非冲突特征已唯一识别该点
-
MLP 后处理:tiny MLP 在 拼接特征上学非线性融合,遇到冲突 entry 时可以用其他 level 的非冲突特征 disambiguate
-
梯度自动调节:训练时高梯度自然集中在 surface entry;冲突 entry 若同时在表面(罕见)会被 loss 推到妥协位置(取多次采样平均)
-
物理直觉:与其代价昂贵地搞 perfect hash,不如允许冲突 + 用数据驱动 implicit 消歧("lazy collision resolution")
说"哈希冲突由 MLP 解决"但讲不清是哪些 mechanism(稀疏性 + 多尺度 + MLP 非线性)共同作用。
Q23.推导 3DGS 3D→2D 协方差投影 Jacobian。
-
World → Camera:刚体变换 ;协方差只受旋转影响,
-
Camera → Screen:透视投影 非线性
-
在均值 处一阶 Taylor:,
-
-
-
这是 EWA splatting (Zwicker 2001) 的经典推论;3DGS 直接沿用
-
实际实现还加 low-pass filter(anti-aliasing)
不会做一阶 Taylor 把非线性投影线性化;或漏了 World→Cam 那步。
Q24.SDS gradient 丢掉哪项 Jacobian?为什么"反而 work"?
-
Naive diffusion training gradient:
-
SDS 扔掉 U-Net Jacobian :
-
为什么扔掉 reasonable:
- U-Net Jacobian 计算昂贵(H×W×3 输入 → H×W×3 输出的 second-order)
- U-Net 没训练 second-order 稳定,Jacobian 数值差
- 本身就是 score 的代理(),扔掉 Jacobian 相当于用 first-order score 信号
-
代价:SDS 数学上等价 mode-seeking KL(往 prior 的 mode 跑),加大 CFG (100) 才能逃出 mean-blur
-
症状:over-saturation(颜色饱和)+ Janus(多视角同一面孔)+ over-smoothing(细节糊)
只说"为简化扔了 Jacobian",不解释 mode-seeking 后果 + 为什么需要大 CFG。
Q25.VSD 为什么能在小 CFG 下避免 over-saturation?
-
SDS 视角:把 当点估计;gradient 拉向 mode;大 CFG 让 mode 更尖 → over-saturation
-
VSD 视角:把 当 random variable ;最小化的是渲染加噪图像分布之间的 KL:(不是直接在 域上对一个 3D prior 求 KL)
-
引入辅助 score (用 LoRA 微调 Stable Diffusion)跟踪当前 rendered 分布的 score
-
VSD gradient:
即 relative score(target prior score current rendered score)
-
几何直觉:从"我现在在哪"指向"prior 在哪"——是局部"梯度方向"而非全局 mode;不需大 CFG 锐化
-
类比 RL:actor-critic 用 value baseline 减方差;VSD 用 作为 baseline 减 SDS 噪声
-
效果(ProlificDreamer):CFG 可降至 7.5,颜色自然;几何更细;可维护多 mode(多样性)
只说"VSD 引入变分推断"不讲 角色 + relative gradient 视角。
§A 附录:代码完整骨架 + 参考文献
A.1 完整 from-scratch 代码包含
volume_render() (NeRF α-compositing 含数值稳定) · positional_encoding() (γ(p) Fourier features) · gaussian_splat_forward() (3DGS 教学版前向 + 投影 Jacobian) · densify_and_prune() (3DGS densification 启发式) · sds_loss() (SDS gradient surrogate) · marching_cubes_sketch() (mesh 提取接口,用 scikit-image)。
A.2 关键论文 reading list
- NeRF 系:Mildenhall 2020 ECCV (HM); Müller Instant-NGP SIGGRAPH 2022 Best; Barron Mip-NeRF / 360 ICCV 2021 / CVPR 2022; Wang NeuS + Yariv VolSDF NeurIPS 2021; Fridovich-Keil Plenoxels CVPR 2022; Chen TensoRF ECCV 2022.
- 3DGS 系:Kerbl 3D Gaussian Splatting SIGGRAPH 2023 Best; Huang 2D Gaussian Splatting SIGGRAPH 2024; Luiten Dynamic 3DGS 3DV 2024; Wu 4DGS CVPR 2024; Guédon SuGaR CVPR 2024.
- Mesh / SDF:Shen DMTet NeurIPS 2021 / FlexiCubes SIGGRAPH 2023.
- SDS 系:Poole DreamFusion arXiv 2022.09 → ICLR 2023 Outstanding; Wang ProlificDreamer (VSD) NeurIPS 2023 Spotlight; Lin Magic3D CVPR 2023; Chen Fantasia3D ICCV 2023; Tang DreamGaussian ICLR 2024; Yi GaussianDreamer CVPR 2024.
- Single-image 3D:Liu Zero-1-to-3 ICCV 2023 / One-2-3-45 NeurIPS 2023 / SyncDreamer ICLR 2024; Shi Zero-1-to-3++ arXiv 2023 / MVDream ICLR 2024; Hong LRM arXiv 2023.11 → ICLR 2024; Tochilkin TripoSR arXiv 2024; Xu InstantMesh arXiv 2024; Boss Stable Fast 3D arXiv 2024.
- 3D Foundation Models:Xiang Trellis arXiv 2024 (Microsoft); Tencent Hunyuan3D-2 arXiv 2501.12202 (2025); Zhang CLAY SIGGRAPH 2024.
A.3 Embodied AI / AR / VR 常见追问
3DGS 接物理引擎 → 先 2DGS / SuGaR 提 mesh → IsaacSim / MuJoCo;NeRF 动态化 → 4DGS / D-NeRF / K-Planes;AR 实时 3DGS → mobile-friendly (PostShot, Luma) + 剪枝 / 量化;3D 数据不足 → Objaverse-XL (Trellis) 或 2D 蒸馏 (DreamFusion 系) 或 multi-view 启发式 (MVDream)。
3D Generation Quick Reference · 主要参考:Mildenhall 2020 (NeRF), Müller 2022 (Instant-NGP), Kerbl 2023 (3DGS), Poole 2022/ICLR 2023 (DreamFusion), Wang 2023 (VSD), Xiang 2024 (Trellis), Tencent 2025 (Hunyuan3D-2). 涵盖:NeRF 体渲染推导、Instant-NGP hash 网格、3DGS 投影 Jacobian、SDS / VSD 梯度推导、single-image 3D、3D foundation models。Embodied AI / AR / VR 必备。