HubPart I · Primer
Part I · Primer · 基础

从"表示"开始把地基打牢

这一部讲六件事:3D 生成到底在生成什么、3D 有哪些"表示"、怎么从一个连续场抠出一张网格、 当今主线(原生 3D latent 扩散)的流水线长什么样、驱动它的两台引擎(SDS 与 rectified flow)有何不同, 以及把全书串起来的 4 个痛点。后面每一部都建立在这六节之上。

§1.13D 生成在生成什么?

你已经熟悉 NeRF 和 SDF。请先把一句话刻进脑子里: NeRF、SDF、网格、点云——这些都是「表示 (representation)」,每一个都是一个已经拟合好的物体。 你训练一个 NeRF,是把一个场景的几百万个权重优化到位;它回答的是"这个形状怎么存?"

3D 生成问的是完全不同的问题:学一个关于形状的分布,从里面采样出的形状。 关键的认知转弯是:你几乎从不直接生成 NeRF/SDF 的那几百万个权重;而是先训一个 autoencoder,把每个形状压成一个小小的 latent(可能是一组向量 vecset、一个 triplane、或一个稀疏体素格),它的 decoder 能把 latent 还原成一个 occupancy/SDF 场;然后在这些 latent 上学分布(用扩散 / flow 模型)。 NeRF/SDF 退化成了 decoder 的目标,生成发生在更高一层——latent 空间里。

关键想法 · 一句话

3D 生成 = 先学会把一个 3D 形状压成一个 latent,再学会在这个 latent 空间里采样。 全站所有流派,本质上都是在回答"latent 取什么、分布怎么学、监督从哪来"这三个子问题。

§1.2表示 zoo · 你能把 3D 存成什么

生成的第一步是选一种表示。下表是这个领域的"动物园"。看两件事:哪些是显式 (explicit)(直接存坐标)、 哪些是隐式 (implicit)(存一个要去查询的函数);以及哪些通常用作生成 latent、哪些用作输出格式

表示显/隐存什么内存量级对生成的利弊
Voxel / occupancy 格显式规则 3D 网格上的占据/密度$O(N^3)$+ 规则网格好上 3D CNN;− 立方内存,分辨率卡在 ~$256^3$
Point cloud显式无序 $(x,y,z)$ 集$O(N)$+ 便宜灵活;− 没有面 / 拓扑,得再 meshing
Polygon mesh显式顶点 + 面(连通性)$O(V{+}F)$+ 美术 / 引擎要的最终格式;− 不规则、变长,极难直接生成
隐式 SDF隐式有符号距离 $f(x)$,面在 $f{=}0$一个 MLP+ 光滑、水密、任意分辨率;− 必须取面
隐式 occupancy 场隐式$o(x)\in[0,1]$,面在 $0.5$一个 MLP同 SDF;是分类器口味
NeRF / 辐射场隐式密度 $\sigma$ + 视角相关颜色 $c$MLP / hash 格+ 从图像出真实感;− 是外观不是几何,无干净面
3D Gaussian Splatting显式一堆 3D 高斯(均值、协方差、不透明度、SH 色)$O(N)$+ 实时渲染、锐利;− 不是面,meshing 是额外活
Triplane混合3 张轴对齐 2D 特征平面 + 小 MLP$O(3R^2C)$+ 紧凑、对 2D CNN 友好的 latent;− 轴对齐偏置、容量有限
Vecset隐式 latent无序的 $N$ 个 latent 向量($N{\times}C$)$O(NC)$+ 置换不变、无网格偏置,transformer 的理想 latent;− 需要学好的 decoder
DMTet混合可变形四面体格 + 逐顶点 SDF → marching tets$O(\#\text{tet 顶点})$+ 可微网格、能学拓扑;− 分辨率受 tet 格限制
FlexiCubes混合cube 格上的标量场 + 额外逐 cell 权重 → dual MC$O(N^3)$+ 比 DMTet 更锐、质量更高的可微网格;− 仍受格分辨率限制

记住这条分工:生成 latent 通常是 triplane / vecset / 稀疏体素结构化 latent输出几乎总是一张 polygon mesh(从隐式场取出来的),偶尔附带 3DGS/NeRF 做外观。 SDF/occupancy 是 latent 解码出来的那个中间场。下面这个 demo 让你亲手感受同一个形状的五种存法和它们的"参数账单"。

Demo 1 · 表示 zoo · 同一个形状的五种存法
先点 隐式 SDF:整张平面被一个连续函数染色,零等值线就是表面——一个 MLP 就能存任意分辨率。 再切 Voxel 格并拉分辨率,感受 $O(N^3)$ 内存(这里是 2D 的 $O(N^2)$,3D 里更狠)。 点云没有面、网格不规则、高斯只管渲染不管表面。这就是为什么"latent 用隐式场、输出用网格"成了标准分工。

§1.3从场到面 · 取面与"可微取面"

隐式场(SDF/occupancy)要变成能用的网格,得做等值面抽取 (iso-surface extraction)。经典算法是 Marching Cubes(Lorensen & Cline, 1987):把场采样到规则格上,看每个立方体 8 个角的符号; $2^8$ 种符号组合按对称性归约成 15 个 case 查表,每个 case 给一组固定的局部三角化;顶点位置在立方体的边上由场值线性插值到等值 $\tau$ 决定。 快、稳,但对拓扑不可微(离散查表挡住了梯度),还会带格对齐的伪影。

现代生成里我们常常想让渲染出的图像上的 loss 一路反传到底层的场。于是出现了可微取面: 离散的拓扑决定每步固定,但顶点位置是场值的连续函数,所以 $\partial(\text{顶点})/\partial(\text{SDF})$ 存在。

DMTet(Shen et al., NeurIPS 2021)在一个(可变形的)四面体格的每个顶点 $v_i$ 上存一个 SDF 值 $s_i$。 Marching Tetrahedra 按每个 tet 四个 SDF 值的符号三角化;边 $(v_a,v_b)$ 上的过零点为

$$ v = \frac{s_b\, v_a - s_a\, v_b}{\,s_b - s_a\,}, $$

它对 $s$ 和 $v$ 都可微。格顶点本身也是可学的偏移,所以模型能变形格子去贴细节FlexiCubes(Shen et al., SIGGRAPH 2023)在 Dual Marching Cubes 上再加一组可学的逐 cell 权重 $\alpha,\beta$(把对偶顶点挪到 cell 内任意位置)和切分权重 $\gamma$,给优化器足够的自由度去抓锐利的边和角、产出水密流形网格——这是 DMTet/MC 做不到的。 你在 GET3D、Fantasia3D、InstantMesh、MeshLRM 里反复会见到它们。

Demo 2 · Marching Squares · 取面的 2D 玩具
Marching Cubes 的 2D 表亲。拖动两个青色圆心改变 SDF;拉分辨率看实线(抽取的折线)逼近虚线(真实等值线)。 每个格 cell 看 4 角符号、在变号的边上线性插值出一个顶点——分辨率越高越平滑,但 3D 里就是 $O(N^3)$ 的内存代价。 这正是高分辨率原生生成(Direct3D-S2 把它推到 $1024^3$)必须用稀疏体素的原因。

§1.4当今主线 · 原生 3D latent 扩散流水线

把 2023–2025 的主流配方画成一条流水线(3DShape2VecSet → CLAY → TripoSG / Step1X-3D / Hunyuan3D)。它和老的 SDS 完全不同:

阶段做什么
(a) 训一个 3D VAEEncoder 把采样的表面点压成 latent(vecset $N{\times}C$,或稀疏体素结构化 latent);decoder 把 latent + 查询点解码成 occupancy/SDF。监督 = 重建 + KL。
(b) 在 latent 空间训 DiT在那个 latent 空间里训一个 扩散 / flow Transformer,条件是图像 embedding(DINOv2/CLIP)或文本(CLIP/T5),靠 cross-attention 注入。这才是真正的生成模型。
(c) 推理从 DiT 采一个 latent → decoder 在稠密查询格上解出场 → Marching Cubes → mesh → (单独一步)贴材质

整个领域里最该背下来的一条公式是 3DShape2VecSet(Zhang et al., SIGGRAPH 2023)的 encode/decode 结构: 用 farthest-point sampling 选出的查询种子 $Q$ 去 cross-attention 输入点特征,得到 latent 集 $Z$; 任意查询点 $x$ 的 occupancy 是 $x$ 对 $Z$ 的一次 cross-attention 读出:

$$ Z = \mathrm{SelfAttn}\!\big(\mathrm{CrossAttn}(Q,\ \mathrm{PE}(P))\big),\qquad O(x)=\mathrm{FC}\!\big(\mathrm{CrossAttn}(\mathrm{PE}(x),\ Z)\big) $$

其中 $P=\{p_i\}$ 是采样的表面点、$\mathrm{PE}$ 是位置编码、$Q=\mathrm{PE}(\mathrm{FPS}(P))$。 vecset 是无序的、置换不变的、没有网格偏置——天生就是 transformer 的 latent。 (精确的 normalization / 头部细节请对照原文,这里给的是正确的结构骨架。)

为什么"几何先行、材质后补"成了几乎所有系统的两段式标配?三点: (1) 几何的分布和 RGB 差一个量级,几何用 3D-native 的 occupancy/SDF 目标学最好,而材质复用预训练的 2D / 多视图图像扩散最省; (2) 先有一张干净 mesh,就有了精确表面去 condition 材质(渲 depth/normal/position → 多视图 PBR → 烤进 UV),保证几何-材质对齐; (3) 模块化——VAE/DiT 和 paint 模型能各自独立替换升级。CLAY、Hunyuan3D、TripoSG、Step1X-3D 全这么干。

把推理写成 20 行教学伪代码(rectified-flow 版,下一节解释为什么是直线流):

# 原生 3D latent 扩散:从噪声到一张 mesh(教学版,省略 batch / 数值细节)
import torch
from skimage.measure import marching_cubes

@torch.no_grad()
def generate_mesh(dit, vae_decoder, image_embed, N=2048, C=64,
                  steps=25, grid_res=256):
    # 1. 从 VAE latent 空间的纯高斯噪声起步(vecset 形状 N x C)
    z = torch.randn(1, N, C, device="cuda")

    # 2. rectified-flow ODE:把速度场从 t=0(噪声) 积分到 t=1(数据)
    #    直线流 => 朴素 Euler、很少的步数就够
    ts = torch.linspace(0, 1, steps + 1, device="cuda")
    for i in range(steps):
        t  = ts[i].expand(1)
        v  = dit(z, t, cond=image_embed)      # 预测的速度 v_theta(z_t, t | image)
        z  = z + (ts[i+1] - ts[i]) * v        # 朝数据流形走一个 Euler step

    # 3. 在单位立方体里铺一个稠密查询格
    lin = torch.linspace(-1, 1, grid_res, device="cuda")
    gx, gy, gz = torch.meshgrid(lin, lin, lin, indexing="ij")
    queries = torch.stack([gx, gy, gz], -1).reshape(1, -1, 3)

    # 4. decoder 把 latent 解码成每个查询点的 occupancy/SDF
    #    (每个查询点对 latent 集 z 做一次 cross-attention)
    field = vae_decoder(queries, z).reshape(grid_res, grid_res, grid_res)

    # 5. 抠等值面(occupancy=0.5 或 SDF=0)-> 三角网格;贴材质是下游单独一步
    verts, faces, normals, _ = marching_cubes(field.cpu().numpy(), level=0.5)
    return verts, faces

Demo 7(在 Part VI)把第 1、4、5 步可视化:采表面点 → 压成 $N$ 个 latent → 查询解码 → marching squares 重建。 现在你已经能看懂那张图了。

§1.5两台生成引擎 · SDS vs Rectified Flow

全站的生成动力,归根结底来自同一类模型:扩散模型 (diffusion model)。两台"引擎"都建在它上面—— 引擎 A 把一个冻结的扩散模型借来当评委(SDS),引擎 B 则是扩散模型的一个更快变体(rectified flow)。 所以先花一节,假设你没接触过扩散,从零把它讲清楚。

先修 · 扩散模型 30 秒讲清

扩散模型只干一件事:学会把噪声一点点"擦"回数据。分两半看。

① 前向(加噪,不用学):拿一份干净数据 $x_0$(一张图、或一个 latent),逐步掺高斯噪声,掺到最后变成一团纯噪声。任意一步都有闭式:

$$ x_t = \sqrt{\bar\alpha_t}\,x_0 + \sqrt{1-\bar\alpha_t}\,\varepsilon,\qquad \varepsilon\sim\mathcal N(0,I) $$

时间 $t$ 从 $0$(干净)走到 $T$(纯噪声),保留系数 $\bar\alpha_t$ 从 $\approx 1$ 单调降到 $\approx 0$。这一步没有参数,就是照公式掺噪声。

② 反向(去噪,要学):训一个网络 $\varepsilon_\theta(x_t,t)$——看着一张加了噪的 $x_t$,把当初掺进去的那团噪声 $\varepsilon$ 猜出来。目标就是一个均方误差:

$$ \mathcal L_{\text{DDPM}} = \mathbb E_{x_0,\,t,\,\varepsilon}\big\lVert \varepsilon - \varepsilon_\theta(x_t,t)\big\rVert_2^2 $$

怎么生成:从一团纯噪声 $x_T\sim\mathcal N(0,I)$ 出发,反复"猜噪声、减掉一点点",$x_T\to x_{T-1}\to\cdots\to x_0$, 最后落到数据分布上——就采出一个样本。想生成"对应某张图 / 某段文字"的东西,就把条件 $c$(图像 / 文本 embedding)一起喂进去 $\varepsilon_\theta(x_t,t,c)$; 想让它更听话,用 classifier-free guidance (CFG) 把条件方向放大:$\hat\varepsilon=\varepsilon_\theta(\varnothing)+s\,(\varepsilon_\theta(c)-\varepsilon_\theta(\varnothing))$,这里的 $s$ 就是常说的"CFG 强度"。

两个要记住的点

(1) 网络学的是"这一步该减掉多少噪声"——预测噪声 $\varepsilon_\theta$ 在数学上正比于分数 (score) $\nabla_x\log p_t(x)$。所以后面 "Score Distillation" 里的 score,指的就是它。 (2) 标准 DDPM 采样要走几十到上千步小步去噪才够好——。这正是引擎 B 的 rectified flow 要解决的痛点。

引擎 A · Score Distillation Sampling(SDS)

DreamFusion(Poole et al., 2022)的招数:优化一个 3D 表示(参数 $\theta$,比如一个 NeRF),让它的渲染图 $x=g(\theta)$ 被一个冻结的文生图扩散模型 $\hat\epsilon_\phi$(就是上一节那个噪声预测器 $\varepsilon_\theta$,权重锁死不动)打分:

$$ \nabla_\theta \mathcal{L}_{\text{SDS}} = \mathbb{E}_{t,\epsilon}\!\left[ w(t)\,\big(\hat\epsilon_\phi(x_t; y, t) - \epsilon\big)\,\frac{\partial x}{\partial \theta} \right] $$

$x_t=\alpha_t x+\sigma_t\epsilon$ 是加噪的渲染图,$y$ 是文本 embedding。白话:给渲染图加点噪声,问冻结的 2D 模型 "往哪改更像这段文字?",再把 3D 参数往那个方向推;全程不需要任何 3D 训练数据。 它过饱和 / Janus 的原因也写在公式里:要拿到可用信号得用很大的 guidance(mean-seeking → 过平滑过饱和); 而每个视角被一个没有 3D 一致性约束的 2D 模型独立打分,于是每个视角都想长成"正脸"——多面 Janus 由此而来。

引擎 B · DDPM vs Rectified Flow

刚讲的就是标准扩散 DDPM(Ho et al., 2020):逆转那条多步加噪链、每步预测噪声 $\epsilon$,采样要走几十~几百步,而且轨迹是弯曲的。 Rectified flow / flow matching(Liu et al., 2022;Lipman et al., 2022)换个目标:不学"逐步去噪",而是直接学一个速度场 $v_\theta(x_t,t)$, 把噪声 $x_0$ 沿直线匀速送到数据 $x_1$:

$$ x_t = (1-t)\,x_0 + t\,x_1,\qquad \mathcal{L} = \mathbb{E}_{t\sim U[0,1],\,x_0,x_1}\big\lVert v_\theta(x_t,t) - (x_1 - x_0)\big\rVert_2^2 $$

目标速度就是那条直线的常数方向 $x_1-x_0$。直线路径 → 近乎线性的 ODE → 采样步数少得多、训练更稳。 这就是 2025 那一波(TripoSG、Step1X-3D、TRELLIS)都改用 rectified-flow 的原因:3D 的一次前向(解码 + marching cubes)很贵, 砍步数比在 2D 里更值钱

Demo 3 · 直线流 vs 弯曲扩散 · 为什么 RF 能少走几步
八个噪声样本(灰点)要走到数据环(青点)。把步数 K 调到很小(如 4),切换路径类型: 直线流哪怕 2 步也几乎精确落点;弯曲扩散步数一少就overshoot、落不到环上。 每一步在 3D 里都意味着一次大 DiT 前向,所以"直线 → 少步"是 2025 主线模型的共同选择。

§1.64 个痛点 · 把全书串起来的轴

在读任何一篇论文之前,先把这四个痛点记住。后面每一张 paper 卡都会映射回其中至少一个。 不同流派的根本区别,就是它们优先攻哪个痛点。

痛点 (a):数据稀缺

2D 图像模型吃的是几十亿张图(LAION-5B);而最大的开源 3D 库 Objaverse 才 ~80 万、Objaverse-XL ~1000 万但很脏。 差几个数量级。→ 这逼出了"借 2D 先验"(SDS、多视图扩散),也让"原生 3D 预训练"格外珍贵。

痛点 (b):没有"画布"——表示与拓扑

2D 图永远是规则的 $H{\times}W$ 像素阵,CNN/ViT 直接吃。可网格是不规则的(顶点/面数可变、连通性任意), 没有固定张量形状——这逼出了上面的 latent 迂回,还要额外解决取面(DMTet/FlexiCubes/MC)和拓扑(亏格、洞、分件)。

痛点 (c):多视图一致性 / Janus

一个物体必须从每个角度看都对、且自洽。2D 先验方法最典型的翻车就是 Janus(多面)。→ 这逼出了一致多视图扩散、和直接学 3D 分布的原生方法。

痛点 (d):又快、又好、又能用

逐资产优化(SDS,分钟~小时)vs 前馈(亚秒)vs 采样质量,是一条贯穿始终的张力。 而"能用"还要求干净拓扑 + 可重光照 PBR 材质 + 可绑定动画。→ 这逼出了 LRM、rectified flow、蒸馏、mesh-AR、PBR、可动资产生成。

这一部学完,你应该能回答
  • "为什么我们生成的是一个 latent,而不是直接生成 NeRF 的几百万权重?"
  • "marching cubes 为什么不可微,DMTet / FlexiCubes 是怎么把它变可微的?"
  • "原生 3D 扩散的三段式(VAE → DiT → 取面+贴材质)每一段在干嘛?"
  • "为什么 2025 的主线模型几乎都从 DDPM 换成了 rectified flow?"
  • "4 个痛点分别是什么,哪个流派攻哪个?"

评估两句话备用:几何看 Chamfer Distance(两点集最近邻距离均值,越小越好) $d_{CD}(X,Y)=\frac{1}{|X|}\sum_{x}\min_{y}\lVert x-y\rVert^2+\frac{1}{|Y|}\sum_{y}\min_{x}\lVert y-x\rVert^2$ 与 F-score(阈值内精确率/召回率的调和平均,更抗离群点); 文本/图像对齐看 CLIP-score / CLIP-R-Precision 与三模态的 ULIP。术语都收进了附录名词表