跳转到内容

NeRF — 用一个 MLP 把整个场景"背"下来

是什么

NeRF(Neural Radiance Field,神经辐射场)是一种让一个小神经网络把整个 3D 场景背下来的方法。日常类比:你给它一沓某物体的照片(几十到一百张),它训练几个小时,最后变成一个”会作画的小盒子”——你随便报一个新视角,它能画出从那个角度看物体长什么样。

输入:一个 5 维坐标——空间位置 (x, y, z) + 观察方向 (θ, φ)。 输出:那个点的颜色 (R, G, B) 和”密度” σ(这点有没有东西、挡光多少)。

把光线沿着像素方向打进去,沿途采几十个点,每个点问一次小网络,再做一次加权积分,就得到这条光线对应像素的最终颜色。整张图就是几十万条光线一起算出来的。

为什么重要

不理解 NeRF,下面这些事都没法解释:

  • 为什么 2020 年之后 view synthesis(新视角合成)和 3D 重建领域几乎所有论文标题里都有”Neural”或”Radiance”
  • 为什么一个 ECCV 2020 的论文成了之后三年图形学被引用最多的工作之一
  • 为什么 3d-gaussian-splatting 整篇论文都在拿 NeRF 当参照——3DGS 的目标就是”NeRF 的质量、传统光栅化的速度”
  • 为什么”用神经网络当存储介质”会成为一种范式——场景不再是网格 + 贴图,而是一组权重

核心要点

NeRF 这条路能跑通靠 三件事

  1. MLP 当容器:8 层全连接、每层 256 个神经元,把 5D 坐标映射到 (RGB, σ)。整个场景就是这个 MLP 的权重,约 1 MB。比传统 mesh + texture 小一两个数量级。注意 MLP 内部把”位置 (x, y, z)“和”方向 (θ, φ)“分两头喂——位置先过 8 层得到密度 σ,然后把方向接到倒数第二层产生颜色。这样设计是为了让 σ 与方向无关(一个空间点的”挡光程度”不该因你从哪看而变),而颜色可以随方向变化(金属表面的反光从不同角度看不一样)。

  2. 位置编码(positional encoding):直接把 (x, y, z) 喂进 MLP,画出来全是糊的——MLP 有”低频偏好”,学不会高频细节。NeRF 用 γ(p) = (sin(2⁰πp), cos(2⁰πp), …, sin(2^L πp), cos(2^L πp)) 把每个坐标升到几十维。等于先把”细节频道”拉开,让 MLP 看得见纹理。这一步是 NeRF 之所以清晰的关键。

  3. 可微体渲染(volume rendering):沿光线 r(t) = o + td 采 N 个点,每点查一次 (c_i, σ_i),按 Beer-Lambert 公式做加权和:C(r) = Σ T_i · (1 − exp(−σ_i δ_i)) · c_i。这条公式可微,所以 loss = (渲染颜色 − 真实像素颜色)² 能反传到 MLP 权重——一切都是梯度下降。

外加一个层次采样:先用粗网络扫 64 点找物体在哪,再用细网络在物体附近密集采 128 点,省一半算力。粗网络先估出”密度沿光线的分布”,把这个分布当成概率密度做 inverse-CDF 抽样,让细网络的 128 个采样点集中落在真有东西的地方。

实践案例

案例 1:你能拿什么数据训一个 NeRF

输入是同一物体/场景的一组照片,每张照片必须知道相机位姿(在哪、朝哪)。位姿一般用 colmap-2016 这类 SfM(structure from motion)工具从照片里反推出来。

photos/ ← 60 张照片(围着物体一圈拍)
poses.txt ← 每张照片对应的相机位置 + 朝向

训练流程:

for step in range(200_000):
ray = sample_random_ray() # 从训练集随机抽一条光线
pts = sample_points_along(ray, 64) # 沿这条光线采 64 点
rgb_sigma = mlp(pos_encode(pts)) # 查 MLP
color = volume_render(rgb_sigma) # 加权积分
loss = ((color - ray.gt_color)**2).mean()
loss.backward()

V100 单卡跑一天,得到一个能对那个场景从任意角度作画的 MLP。

案例 2:NeRF 慢在哪

渲染一张 800×800 的图 = 64 万条光线 × 每条 192 个采样点 ≈ 1.2 亿次 MLP 前传。一张 V100 渲一帧大概 30 秒。这就是为什么 3d-gaussian-splatting 在 2023 年能凭”实时渲染”反超——3DGS 用光栅化绕过了这每点一次 MLP 的代价。

案例 3:positional encoding 为什么有效

不加位置编码,输入只有 (x, y, z) 三维,MLP 学到的函数会非常”光滑”,画出来像被高斯模糊过。加了 sin/cos 高频基,等于告诉 MLP:“你可以在 x 方向每 1/2^L 米发生一次变化”,于是细节回来了。Tancik 等人 2020 年的另一篇论文 Fourier Features Let Networks Learn High Frequency Functions 把这个观察推广成了一个独立结论——MLP 拟合任何高频信号都需要这种编码。

NeRF 论文里位置坐标用 L=10、方向用 L=4,输入维度从 (3+2)=5 升到 (3·2·10 + 2·2·4)=76。这一招后来在很多”用 MLP 拟合连续函数”的工作里都被照抄——SDF(signed distance field)/ neural BRDF / DeepSDF / Occupancy Network 都标配 positional encoding。

案例 4:体渲染公式怎么从光学方程来

光线穿过半透明介质时,每经过一小段 δ_i,剩下的光强按 exp(−σ_i · δ_i) 衰减——这就是 Beer-Lambert 定律。NeRF 把光线切成 N 段,每段贡献的颜色 = 这段被点亮的概率 × 这段的颜色 = (1 − exp(−σ_i δ_i)) · c_i,前面再乘累积透明度 T_i = ∏_{j<i} exp(−σ_j δ_j)。把所有段加起来就是最终像素颜色。这个公式 1984 年 Max 就在 SIGGRAPH 写过(Optical Models for Direct Volume Rendering),NeRF 的贡献不在公式本身,而在让公式里的 (c, σ) 来自一个可微 MLP。

踩过的坑

  1. NeRF 是”每个场景训一个网络”:它不是预训练好就能渲染任何场景的通用模型,每来一个新场景都要重新训几小时。这是它和”通用 3D 生成模型”的本质区别。

  2. 相机位姿不准 → 渲出来全糊:COLMAP 误差稍大,NeRF 就把误差当真实信息学进去了。后续 BARF / NeRF— 等工作专门解决”位姿和场景一起优化”。

  3. 背景和远景:原版 NeRF 假设场景是有界盒子,远景 / 天空容易”溢出”。NeRF++ / Mip-NeRF 360 用球壳坐标和反向参数化把这块补上。

  4. 混淆抗锯齿:单点采样在远处会出现锯齿和闪烁。Mip-NeRF(Barron 2021)改成”用一段圆锥体而不是一条线”,每点变成”一团高斯”,本质就是把多分辨率纹理过滤的思想搬进辐射场。

  5. 训练慢、推理慢:原版 12 小时 / 场景。Instant-NGP(Müller 2022)用 hash grid 把训练压到秒级;Plenoxels 直接去掉 MLP 用稀疏体素;Tensorial / TensoRF 用张量分解。一年内训练时间从 12 小时压到 5 秒。

  6. 照片必须曝光一致:NeRF 假设同一个 3D 点从不同照片看颜色一致(差异都来自观察方向)。手机自动白平衡、不同时段拍的、手动调过滤镜——这些会让”同一点不同照片像素”对不上,损失下降不下去。NeRF in the Wild(Martin-Brualla 2021)专门加了”每张图一个 latent code”来吸收这种外观差异。

适用 vs 不适用场景

适用

  • 静态场景的高质量新视角合成(博物馆文物 / 房屋扫描 / 电影置景重建)
  • 需要存储一个连续可微的 3D 表示(机器人仿真、可微渲染管线下游)
  • 数据是”一组带位姿的照片”——不是点云、不是 mesh

不适用

  • 实时应用(VR、游戏、闭环仿真)→ 用 3d-gaussian-splatting
  • 动态场景(人在动、车在跑)→ 用 D-NeRF / HyperNeRF / 4DGS
  • 没有相机位姿的随手拍照片 → 先用 COLMAP,或用 BARF 联合优化
  • 想”训一次跑所有场景” → NeRF 不行,需要 PixelNeRF / IBRNet 这类 generalizable NeRF

历史小故事(可跳过)

  • 1984 年:Max 在 SIGGRAPH 写下”Optical Models for Direct Volume Rendering”,给出了 NeRF 用的体渲染积分公式。论文当时只用于医学 CT 可视化
  • 1986 年kajiya-1986-rendering-equation —— Kajiya 写下”渲染方程”,把所有渲染问题统一成一个积分式
  • 2019 年:Sitzmann 的 SRN(Scene Representation Networks)用 MLP 表示场景,但渲染靠 LSTM 步进、效果一般。Lombardi 的 NeuralVolumes 用一个 3D 体素网格 + MLP 解码,速度快但分辨率低
  • 2020 年 3 月:UC Berkeley + Google + UCSD 合作的这篇 NeRF 上传 arXiv,效果直接把同期 SRN / NeuralVolumes 拉开两个数量级
  • 2020 年 8 月:ECCV 2020 收录,拿了 best paper honorable mention
  • 2021 年:CVPR 一年内 50+ 篇 NeRF 衍生工作。Mip-NeRF / NeRF-W / NeRF in the Wild / BlockNeRF / GIRAFFE 全在这一年
  • 2022 年:Instant-NGP 把训练压到 5 秒,NVIDIA 写了官方 demo。Google 出 Block-NeRF 把整个旧金山街景渲染出来
  • 2023 年3d-gaussian-splatting 出现,质量更好、训练快 10 倍、渲染快 1000 倍。NeRF 在工业界基本被 3DGS 替代,但理论遗产(可微体渲染 + 神经场)仍是后续所有工作的基石

学到什么

  1. 神经网络可以当存储介质:把场景”压”进 MLP 的权重里,是 NeRF 之前几乎没人这么干的事。这条路打开了”神经场”(neural fields)整个子领域
  2. 可微 + 大量数据 + 简单网络 = 涌现质量:NeRF 没用什么花哨架构,就一个 8 层 MLP,但它选对了”可微体渲染”这条监督信号
  3. 位置编码是个独立可迁移的洞见:MLP 拟合高频信号必须先升频,这一观察远超 3D 重建本身
  4. 慢不是它的失败而是它的代价:每点一次 MLP 是它清晰的来源,也是它被 3DGS 替代的原因。设计永远在权衡
  5. “5D 输入 + 体渲染监督”是个能复用的范式:把它换成”4D(加时间)+ 体渲染”就是动态 NeRF;换成”5D + 表面渲染”就是 NeuS / VolSDF;换成”6D(加光照方向)+ 体渲染”就是 NeRV / NeRF in dark。同一个骨架,撑起一整个研究方向
  6. 从论文到工业落地只用了三年:2020 投稿、2021 学界爆炸、2022 Instant-NGP 让训练秒级、2023 起 Polycam / Luma AI 把它装进手机 App。学术工作能”几年内变成普通人能点的按钮”是少见的——NeRF 是其中一个

延伸阅读

关联

  • 3d-gaussian-splatting —— 3DGS 把 NeRF 的”每点查 MLP”换成”几百万光斑直接光栅化”,速度快 1000 倍
  • kajiya-1986-rendering-equation —— NeRF 的 volume rendering 是 Kajiya 1986 渲染方程在体介质下的离散积分
  • attention —— positional encoding 的”高频升维”思想和 Transformer 里的 sin/cos 位置编码同源(甚至公式形式几乎一样)
  • pytorch —— 原版 NeRF 用 PyTorch 实现,社区版本绝大多数也基于 PyTorch
  • flash-attention —— 与 NeRF 同样属于”内存访问决定速度上限”的工作,体渲染采样的 cache 局部性问题在 Instant-NGP 里被重新设计

反向链接