跳至主要内容
SpatialLabFollow the Journey

SPATIALLAB / NOTEBOOK

返回全部文章
SPATIAL INTELLIGENCE
文章目录

引言|从渲染三维场到学习三维场景

从渲染已知的三维场,到从多视图图像中学习一个可以被重新观察的世界。
从渲染已知的三维场,到从多视图图像中学习一个可以被重新观察的世界。
 
上一篇《从一个像素开始:从零构建可微 Volume Renderer》已经建立了完整的前向渲染与反向优化链路:从 Camera Rays 查询解析 Density/Color Field,通过 Transmittance 和 Alpha Compositing 生成 RGB Image,再利用像素误差和 Autograd 更新场参数。
但这一结果依赖一个前提:三维场的函数形式是人工给定的。前篇使用的是固定数量的高斯密度团,并在受约束的实验中更新它们的中心坐标;它尚未实现 Neural Field,也没有训练场景专属的 MLP。
所以新的问题很明确:
如果在不知道场景由怎样的解析函数组成的情况下,能否从带有已知相机参数的多视图图像中,直接学习一个共享的三维场景函数?
更重要的是,模型不仅要重建训练图像,还应该能够在训练时未出现过的相机位置生成合理的图像。这个新的交付目标就是 Novel View Synthesis(新视角合成)。

从图像观测走向场景学习

考虑一个静态的三维场景。相机从不同位置拍摄到多张 RGB 图像,每张图像都对应已知的相机参数。
这里的目标不是分别学习如何重建这些图像,而是找到同一个三维场景表示,使它能够解释所有训练视角,并在相机移动到新位置时,继续生成相应的图像。
Neural Radiance Fields(NeRF)提供了一种实现方式。
它将三维场景表示为一个连续函数:给定空间位置,函数返回该位置的 Density;给定空间位置和观察方向,函数还能够返回对应的 Color。
这个函数本身并不直接输出整张图像。
相机仍然需要为每个像素生成一条 Ray,沿 Ray 查询多个空间位置,再通过 Volume Rendering 将这些位置的贡献合成为最终的像素颜色。
因此,NeRF 并没有取代上一篇实现的可微渲染器。恰恰相反,已有的渲染器将成为整个训练过程的核心:它负责把三维场景函数转换成二维图像,也负责让图像误差沿着相反方向更新场景参数。
训练时,多张图像的 Rays 将共同约束同一个 Neural Field。
训练完成后,不再需要从某张输入图像直接预测目标图像。只要提供新的相机参数,便可以重新生成 Rays,查询已经学习到的场景函数,并渲染出对应视角。
这意味着,即将构建的 Neural Field 不再只是一个可查询的函数接口,而将真正承担由多视图图像共同约束的场景表示任务。

从 Naive NeRF 开始

前篇已提供 Camera Ray Generation、固定区间采样和可微 Volume Renderer。本篇首先增加此前不存在的表示组件:一个以空间位置和观察方向为输入、输出 Density 与 Color 的 TinyRadianceField。
得到可查询的 Neural Field 后,使用多视图训练图像建立 Ray-to-RGB 监督,先执行 Few-ray Overfitting Test,再训练共享场景函数并渲染未见视角。由此得到一个不含位置编码和重要性采样的 Naive NeRF Baseline。
后续再根据这个最小系统在复杂结构与纹理上遇到的困难,逐步引入 Positional Encoding、Stratified Sampling 和 Coarse-to-Fine Hierarchical Sampling。每项新机制都由当前实验暴露的问题驱动,并通过独立对照验证作用。

本篇目标与交付成果

本篇的目标是实现一个可以独立训练、保存和重新渲染的最小 NeRF 系统。系统接收同一个静态场景的多视图 RGB 图像与对应的相机参数,使用 PyTorch 优化共享的 Neural Radiance Field。训练完成后,给定一个未参与训练的相机位姿,系统应能够输出对应的 RGB 图像。
为了验证它是否真正具备新视角生成能力,实验将保留一部分相机视角用于测试,对比 Ground Truth 与渲染结果,并使用 PSNR 等指标评估图像重建质量。
此外,还将观察学习到的 Density、Rendering Weights 和 Depth。这些可视化用于检查渲染结果,也用于回答本篇仍需验证的问题:一个能够生成合理新视角的 Radiance Field,是否必然对应准确的三维几何?
需要注意,本篇关注的是 Novel View Synthesis,而不是直接恢复具有精确表面和拓扑的 Mesh。两者有密切联系,但并不是同一个优化目标。
当完整的 NeRF 构建完成后,三维世界将获得一项新的能力:不再只依赖手工定义的场景参数,而是能够从多视图图像中学习一个可以被重新观察的空间表示。
这也为后续研究更高效的空间表示、3D Gaussian Splatting,以及进一步的三维场景生成与交互奠定基础。
接下来,先从最基本的问题开始:一组多视图图像及其相机参数,应该如何组织成能够训练同一个三维场景的观测数据?

第 1 章|多视图如何共同约束三维场景

单个像素只确定一条射线,而多个视角开始共同约束同一个三维场景。
单个像素只确定一条射线,而多个视角开始共同约束同一个三维场景。
上一篇已经证明:给定解析 Density/Color Field,已知相机的图像可以被渲染,像素误差也能够更新场参数。但优化少量已知高斯的中心,并不意味着系统可以直接从未知场景的图像中学习场函数。
本篇要解决的是另一项任务:给定同一静态场景的多视图 RGB 与准确 Camera Poses,构建由所有视角共享的 Neural Radiance Field,使它既能解释训练数据中的观测图,也能生成未见相机位置的图像。
在定义网络之前,需要先确定哪些图像构成训练与测试数据,以及每个像素怎样通过相机参数对应到世界空间中的 Ray。
本章将建立 Novel View Synthesis 的任务定义与 Posed Multi-view Dataset,并完成 Camera Ray 的几何验证。它们将作为后续 Neural Field、采样策略与多视图训练共同使用的数据接口。

1.1 从图像拟合到新视角合成

考虑一个静态的三维物体,以及围绕它拍摄的多张 RGB 图像。每张图像都记录了物体从某一个位置观察时的外观。但这些图像本身仍然是二维的:单独查看其中一张,无法直接确定每个像素对应的真实三维位置。
如果目标只是重建已有图像,系统甚至可以为不同视角分别学习一套独立的二维函数。这样的模型可能获得很低的训练误差,却没有必要建立任何跨视角共享的三维结构。
NeRF 要解决的是另一种问题。给定同一个场景的多张图像及其相机参数,训练一个共享的三维场景函数,使来自不同视角的观测都能够由这个函数解释,也就是将相机移动到训练中没有出现过的位置,使用同一个场景函数能够生成正确的图像。
因此,需要明确区分三个目标:
  • Image Fitting:能否重建训练时已经观察过的图像。
  • Novel View Synthesis:能否在未参与训练的相机位置生成正确图像。
  • Geometry Reconstruction:能否恢复真实场景的三维表面、深度和拓扑。
这些目标彼此相关,但不等价。
尤其是一个模型能够生成高质量的新视角,并不意味着它已经唯一、准确地恢复了真实表面。NeRF 的直接训练目标仍然是 Image-space Reconstruction,Density 则是用于解释多视图图像的连续空间表示。
因此,本篇主要检验 Novel View Synthesis 能力。几何相关的 Density 与 Depth 将作为额外的诊断信息,而不是直接把 RGB 重建质量当作几何精度。
那么要建立这样的学习任务,首先需要理解一个更基础的问题:每个二维像素,究竟为三维场景提供了什么信息?

1.2 一个像素对应一条射线,而不是一个三维点

上一篇已经实现了从像素坐标生成 Camera Ray 的完整过程。
对于图像中的像素 ,已知 Camera Intrinsics 和 Camera-to-World Transform,可以得到一条世界坐标系中的射线:
其中, 是相机光心, 是单位观察方向,而 表示沿射线的距离。
这条 Ray 给出了像素对应的空间观察路径,却没有直接告诉系统该像素的颜色来自哪个深度。例如,一张图像中的红色像素,可能来自相机前方不同深度处的红色表面,也可能由一条 Ray 上多个不同密度和颜色的区域共同形成。因此,单个像素的 RGB 监督无法直接指定唯一的三维位置。
在 Volume Rendering 中,这种不确定性通过沿 Ray 查询多个位置来处理:
上一篇的解析 Density/Color Field 已经能够在这些位置返回局部属性,而 Renderer 则根据 Transmittance 和 Opacity 合成像素。这条光传输计算链已经完成,因此这里不再重复推导;本篇将进一步让 Neural Field 学习这些局部属性。
真正新增的问题是:如果来自多个相机的 Rays 都在查询同一个世界,它们是否能够共同约束空间中的 Density 与 Color?

1.3 多个视角如何约束同一个场景

想象将相机向右移动一小段距离,再次拍摄同一个场景。对比两张照片会发现:近处物体的位置变化通常比远处更加明显。这种由观察位置变化引起的图像位移称为视差(Parallax)。
如果已知相机在两次拍摄时的位置、朝向以及成像参数,就可以利用同一个空间点在两张图像中的位置差异,为它在三维空间中的深度提供额外约束。
这也是多视图几何的重要基础。
考虑两个位于不同位置的相机,它们同时观察物体上的一个特征点。
在第一个相机中,该点对应一条 Ray;在第二个相机中,它对应另一条 Ray。
NeRF 不需要在训练开始前显式找到每一个像素之间的三维对应点。它要求所有相机的 Rays 都查询同一个世界坐标系下的场景函数:
这意味着,同一个空间位置的 Density 由共享的网络参数决定,而不是随着相机编号变化。Color 则允许依赖观察方向,用于表示固定光照下的视角相关外观。
如果某个候选 Density 分布可以解释相机 A 的图像,却无法正确解释相机 B 或 C 的观测,那么这些视角的 RGB Reconstruction Loss 就会共同对场景参数产生约束。
这种约束不需要直接提供 Ground Truth Mesh、SDF 或 Depth。
不过,多视图数量增加并不自动保证准确重建。例如,如果所有相机都位于同一个位置,只发生旋转,那么不同图像仍然缺少用于三角测量的平移视差。即使相机位置不同,过小的基线、严重遮挡或者不足的视角覆盖,也可能让部分结构难以确定。
因此,学习一个三维场景至少依赖三个前提:正确的相机几何、足够有信息量的多视图观测,以及由所有视角共享的场景表示。
现在可以把训练数据写成一个明确的数学对象。设共有 张图像:
其中:
  • 是第 张 RGB 图像。
  • 是 Camera Intrinsics。
  • 是该相机从 Camera Coordinates 到 World Coordinates 的刚体变换。
给定某张图像中的一个像素 ,相机参数确定一条 Ray:
它对应的监督目标就是该像素的真实颜色:
对于所有训练视角,NeRF 最终希望求得一组共享参数 :
这里, 表示图像的像素集合, 是上一篇构建的 Differentiable Volume Renderer。
相较于上一篇只在已知解析场中恢复少量参数,这个目标引入了共享场景函数和多视图像素监督:所有训练视角都必须约束同一组场参数。但它并不意味着每次训练都必须渲染全部图像。后续会通过 Random Ray Batching 近似优化这个目标。
在实现训练之前,还需要一个能够正确保存上述对应关系的数据接口。

1.4 构建带位姿的多视图数据集

为了先验证 NeRF 本身,而不是同时解决相机估计问题,本篇采用带有已知 Camera Poses 的合成多视图场景作为主要实验对象。
这里选择 NeRF Synthetic Dataset 的 Lego 场景。这类数据通常包含一组从不同角度渲染的物体图像,以及描述相机参数的 JSON 文件。图像与相机位姿由同一个合成场景生成,因此可以避免真实场景中的许多相机标定误差。
典型的目录结构如下:
每个 transforms_*.json 文件包含该划分的相机信息。
其中,camera_angle_x 用于恢复当前图像的水平视场角和焦距;每个 Frame 则包含图像路径及对应的 transform_matrix。
首先实现一个最小的 PosedImageDataset。
这段代码主要完成三件事。
第一,读取 RGB 图像,并把像素范围归一化到 。由于合成数据可能包含 Alpha Channel,这里统一使用白色背景进行合成。后续 NeRF Renderer 必须采用相同的背景颜色,否则背景像素会产生系统性的 Reconstruction Error。
第二,根据水平 Camera Field of View 和图像宽度计算焦距:
对于这里选用的方形像素合成相机,取 ,Principal Point 设为图像中心:
这组假设适用于本章选定的标准 Synthetic 数据格式。对于具有非方形像素、非中心 Principal Point 或额外畸变参数的真实相机,需要使用数据提供的实际 Intrinsics,不能继续直接套用这个简化公式。
第三,每张图像都保留对应的 c2w。
图像像素与 Camera Pose 必须始终作为同一条观测记录的一部分。 如果 RGB 来自相机 A,却使用相机 B 的 Transform 生成 Ray,后续 Loss 仍然可能得到数值和梯度,但它们可能是错误的,因为它们对应的是错误的三维几何约束。
这里还没有对图像进行网络前向计算。Dataset 的职责只是确保:当训练系统要求某个像素的 RGB 时,同时能够找到它对应的真实 Camera Ray。

坐标约定|将 Blender 位姿适配到相机接口

当前 Lego Synthetic 数据使用 Blender/OpenGL Camera Convention:相机局部 +x 向右、+y 向上、-z 向前。而上一篇 Volume Rendering Chapter 7 使用 PyTorch3D 的 cameras_from_opencv_projection() 接口,接收 OpenCV 的 World-to-Camera 外参,其相机空间为 +x 向右、+y 向下、+z 向前。
因此,transform_matrix 不能未经适配就直接输入 PyTorch3D Camera,也不能只翻转一个 z 轴后,将得到的反射矩阵当作旋转矩阵。
先使用绕相机 x 轴旋转 180° 对应的基变换,将 OpenCV 相机坐标映射到 Blender 相机坐标:
原始数据提供 Blender Camera-to-World 。将变换扩展为齐次矩阵后:
将 的旋转、平移及数据集提供的 K 交给与上一篇相同的 PyTorch3D 接口:
这里的基变换有两个轴翻转,因此得到的 3×3 旋转部分仍属于 。它只用于将数据集的相机约定适配到已有接口;原始 Blender Pose 与世界坐标系应保持不变。
随后直接复用 Volume Rendering Chapter 7 的 get_pixels_from_image() 和 get_rays_from_pixels()。前者生成 PyTorch3D NDC 中的像素中心,后者使用相机反投影获得归一化的 World-space Rays。NDC 不参与之后的体渲染积分,所有 Field Queries 和 Segment Length 仍在世界空间定义。

1.5 本章交付:可靠的多视图射线接口

到这里,本章已经完成了 NeRF 训练所需的数据与相机接口。
现在,每条二维观测都可以与正确的 Camera Pose 绑定,并转换成共享世界坐标系中的一条 Ray。
系统也已经建立了独立的 Training/Test Split。后续训练可以使用 Training Views 的 RGB 监督共享场景函数,而 Held-out Views 将用于检验真正的新视角生成能力。
本章最重要的结论是:
学习三维场景,并不意味着分别记住多张二维图像,而是要求这些观测能够共同约束同一个空间表示。
相机几何提供了二维像素与三维射线的对应关系,多视图观测提供跨视角约束,而可微渲染器则负责将共享的三维表示重新映射成可比较的图像。
但目前系统只准备好了观测,还没有真正从这些观测中学习场景参数。
下一步,一个很自然的问题出现了:
既然可微 Volume Renderer 和多视图数据接口都已具备,能否首次构建一个可学习的 Neural Field,并使用这些观测训练出能够生成新视角的 NeRF?
下一章将先实现最小 Neural Field,再把它接入这些观测与已有 Volume Renderer,构建 Naive NeRF。

第 2 章|构建第一个 Naive NeRF

把可训练的 Neural Field 接入体渲染器,建立从图像观测到场景学习的第一条闭环。
把可训练的 Neural Field 接入体渲染器,建立从图像观测到场景学习的第一条闭环。
 
上一章已经将多视图 RGB、Camera Intrinsics 和 Camera Poses 组织成能够查询 World-space Rays 的训练数据,但上一篇是手工定义的双高斯 Density/Color Field。它能够根据图像误差更新少量已知场参数,却不能自己决定未知场景的连续 Density 与 Color 应该如何分布。
因此,本章首先实现一个此前尚不存在的组件:用可训练 MLP 表示场查询函数。随后利用多视图数据集配合上一篇中构建的 Volume Renderer 对这个MLP共享场进行图像监督优化。
在扩大训练规模之前,先用少量固定 Rays 检查可微训练链路;然后进行多视图优化,并在未参与训练的相机位置渲染图像。最终交付不含位置编码和分层采样的 Naive NeRF Baseline,为后续针对高频表示和采样效率的改进提供对照。

2.1 从解析场到可学习的神经辐射场

上一篇使用手工高斯函数定义:
该函数已经足以验证 Volume Rendering,但它的形式和高斯数量由人工预先指定,表达能力有限。如果从图像观测中学习一个未知场景,系统需要一个表达能力足够强并且所有视角共享参数的连续场的表征函数。
这里用一个最小的 MLP 代替解析高斯。它仍然在任意三维位置返回 Density 和 Color。但是,真实场景的有效外观可能随观察方向改变。例如,同一空间位置在不同视角下可能具有不同的高光。因此,当前场函数需要接受空间位置 和单位观察方向 :
Density 只依赖位置;Color 则允许依赖位置和方向。这是静态 Neural Radiance Field 的基本接口。方向在代码中使用三个分量表示,但单位方向只有两个自由度。这里的颜色仍是固定光照下的有效外观,而不是完整的材质与光照分解。

实现|构建最小可查询 MLP

先使用共享的 Position Backbone 提取空间特征,Density Head 从位置特征预测非负密度,Color Head 将位置特征与观察方向组合后预测 RGB:
position_mlp(x) 对应由空间位置生成共享特征;softplus 保证 ,从而与上一篇 的要求一致;Sigmoid 将有效 RGB 限制在 。这些输出约束不替代 Alpha Compositing:负责衰减与遮挡的仍然是上一篇已验证的渲染器。
不需要预先建立 Voxel Grid。所有查询位置使用同一组 MLP 参数,因而来自不同相机的观测可以共同更新同一个场函数。

验证|场查询接口与结构约束

这里没有要求未经训练的 RGB 已经具有正确的视角相关外观;测试只证明网络具备约定的输入输出结构,且输出与可训练权重之间存在梯度关系。真正的 Pixel Loss 能否穿过 Volume Renderer 更新这个 Field,留到本章的 Few-ray Overfitting Test 检查。
现在,新的 TinyRadianceField 已能替换旧的解析 Field Query。但要从图像学习它,仍需将多张训练图像组织成与 World-space Rays 一一对应的 RGB 监督。

2.2 将多视图像素组织为训练射线

在上一章的数据接口中,每条观测都包含对应的 RGB、Camera Intrinsics 和 Camera Pose。
因此,可以把整个训练数据集理解成一个由 Rays 和目标颜色组成的集合:
理论上,训练目标是最小化所有训练 Rays 的 Reconstruction Error:
但直接在每个 Iteration 计算完整损失并不现实。
假设每张图像包含 个像素,每条 Ray 需要查询 个 Sample Positions,而当前数据集包含 张训练图像。
那么每次完整渲染需要大约:
次 Field Evaluation。
每一个位置又需要执行一次 MLP Forward Pass。
因此,训练不必在每一步使用全部像素。更合理的方式是随机选择 条 Rays,使用这个 Batch 估计整体 Reconstruction Loss。
这个过程与普通 Mini-batch Gradient Descent 类似,但 NeRF 的训练单位是一条 Ray,而不是一张完整图像。

实现|随机射线批次采样

对于当前小规模实验,可以先将训练图像和对应的 World-space Rays 组织成三个 Tensor:
其中 rays_o、rays_d 直接复用 Volume Rendering Chapter 7 的 get_pixels_from_image() 与 get_rays_from_pixels() 生成;Blender 位姿先使用第 1 章的 make_camera_from_blender() 适配为 PyTorch3D Camera,不另外实现一套射线生成器。
这里假设所有训练图像具有相同分辨率。先将 PosedImageDataset 转换为可复用的图像与 Ray 缓存,确保每张图像的 RGB 和 Camera Pose 始终成对使用:
缓存只保存每个像素的 Ray Origin、Direction 与 RGB,不预先存储所有深度采样位置。较大数据集也可以根据采样的相机和像素动态生成 Rays,训练接口保持一致。
下面实现最小的随机 Ray Batching:
代码中最重要的并不是 torch.randint() 本身,而是三组数据始终使用相同的索引。
view_idx 选择训练相机,row 和 col 选择该相机图像中的像素位置。
随后使用完全相同的索引提取 Ray Origin、Ray Direction 和 Ground Truth RGB。
因此,每条训练样本都保持:
输出 Tensor Shape 为:
需要注意,预先保存全部 Rays 只是当前低分辨率实验中最直观的实现方式。如果直接使用大量高分辨率图像,Ray Cache 可能占用很大内存。后续可以根据随机选择的相机与像素动态生成 Rays,而不改变训练接口。
到这里,多视图 Dataset 已经变成可以直接用于随机优化的 Ray Batches。
下一步是将它们交给 Neural Field 和 Volume Renderer。

2.3 批量渲染射线

上一篇已经完成单条 Ray 的可微体渲染。本章不需要重新推导 Alpha Compositing,也不需要改变 Transmittance 的计算方式。
对于一条 Ray:
在 Near/Far 范围内选择若干 Sample Positions:
Neural Field 对这些位置进行查询:
最后,已有 Renderer 根据 Density、Color 和 Segment Length 计算最终颜色:
这里只使用之前已经验证过的公式,不重新展开 的数学推导。

实现|从固定采样开始

为了先验证最小训练闭环,本章采用固定等距区间,并在每个区间中心查询 Field。
这保持了上一篇的 Segments 与 Boundaries 约定。
这里的 edges 对应积分区间边界,midpoints 对应区间内部的 Field Query Positions,而 deltas 是每段的实际长度。
如果 Ray Direction 已经归一化,那么这些长度可以直接解释为世界空间中的距离。
输出为:
这段代码只建立采样位置,并没有引入新的体渲染规则。

实现|连接神经辐射场与渲染器

现在可以将位置、方向、MLP 和 Renderer 连接起来。
这里的 render_rgb_batch 只是上一篇 render_ray() 的薄适配层:传入 sigma:[B,N]、rgb:[B,N,3] 和 deltas:[B,N],接收它返回的 pixel:[B,3],不另写任何 Alpha Compositing 规则。
上一篇 Chapter 6 的 render_ray() 已使用批量兼容的 T[..., :-1] 和 sum(dim=-2);Chapter 7 则在整张图像中复用了这一接口。当前 Lego Dataset 已将透明像素合成为白色,因此适配层统一使用白色背景:
这层函数只统一训练代码需要的 RGB 输出接口;中间 Alpha、Transmittance 和 Weights 仍可从原来的 render_ray() 获取,供后续分层采样与诊断使用。
再观察网络前向查询:
为什么要先 Flatten?
因为一批 Rays 包含 个空间采样点,但 MLP 只关心每次查询的三维位置和方向,不需要知道这些点原本属于哪一条 Ray。
MLP 返回预测后,再使用 reshape(B,N) 和 reshape(B,N,3) 恢复对应关系,使每条 Ray 的所有 Samples 可以独立进行 Front-to-back Compositing。
因此:
Field Evaluation 可以将所有样本视为一个大 Batch;Volume Rendering 则必须恢复每条 Ray 内部的深度顺序。
另外,Lego 数据集的透明背景已经在 Ch.1 中合成为白色,因此这里的 Renderer 也必须使用一致的白色 Background Color。
完成前向计算后,render_naive_rays() 已经能够从一批 World-space Rays 输出对应的预测 RGB。
但一个函数能够完成 Forward Pass,并不代表它真的能够被正确训练。
在进行大规模优化之前,先检查整个计算图。

2.4 少量射线能否被成功过拟合?

在 NeRF 训练中,直接从随机初始化开始拟合全部视角,可能同时受到 Camera Geometry、Sampling、Field Initialization 和 Optimization 的影响。
如果初次训练没有获得合理结果,很难判断问题究竟来自哪里。
因此,第一个实验不是重建整个 Lego 场景,而是一个更小的任务:
固定少量 Training Rays,检查同一个 Neural Field 能否逐步降低这些 Rays 的 RGB Reconstruction Error。
这可以称为 Few-ray Overfitting Test。
实验从训练集中选择一小批 Rays,固定它们的 Origins、Directions 和 Ground Truth RGB,在多次参数更新中反复使用同一批样本。
这和正式的随机 Ray Training 有一个重要区别:
正式训练每一步都会选择不同的 Rays;而这个测试故意使用不变的观测,使输入数据的随机性不会干扰对优化链路的判断。
例如:
其中 near 和 far 应使用 Ch.1 已经通过 Ray Visualization 验证的 Scene Bounds,而不是任意套用其他数据集的深度范围。
为了让这个测试更有意义,固定 Rays 最好同时包含部分前景与背景像素,避免全部样本恰好来自白色背景。
现在观察训练过程。
如果 Renderer、Field、Loss 和 Autograd 已经正确连接,那么同一批 Rays 的重建误差通常应能随着优化显著下降。
还应该额外检查:
  • loss 是否始终为有限数值。
  • MLP 参数是否具有非零且有限的梯度。
  • Predicted RGB 是否逐渐接近对应的 Ground Truth。
  • 前景与背景像素是否采用一致的颜色合成规则。
这个实验不需要证明预测的 Density 已经准确,也不需要追求完整图像质量。
它只验证一个更基础的能力:RGB Error 是否能够沿着 Ray、Volume Renderer 和 Neural Field,正确回传到共享网络参数。
如果这一实验失败,就应该先检查 Camera Convention、Near/Far、Field Output Activation、Renderer 的 Batch Shapes,以及 Background Compositing,而不是立刻修改 MLP 结构。
当这个小任务能够稳定优化时,才有理由扩大训练范围。

2.5 用多个视角训练共享场函数

Few-ray Overfit 已经隔离验证了基本计算图。
现在,将固定的少量 Rays 替换为每次迭代重新采样的 Multi-view Ray Batch。
与上一节相比,核心 Forward Pass 和 RGB Loss 完全不变。
唯一的重要变化是训练数据的采样范围:每一步都可能从不同 Training Cameras 中选择观测。
这里的 Batch Size、Learning Rate 和训练迭代数只是用于最小实验的起始配置,需要根据分辨率、硬件资源和实际 Loss 曲线调整。
为了降低第一次运行的计算成本,可以先使用较低的图像分辨率和较少的 Ray Segments。但如果缩放训练图像,Camera Intrinsics 必须同步缩放,保证像素与 Ray 的对应关系仍然正确。
这一训练循环实现了一个非常重要的性质:所有 Training Views 都在更新同一个 field 对象。来自相机 A 的 RGB Error 与来自相机 B 的 RGB Error,并不是在修改两个独立网络,而是共同改变同一个场景函数。这就是当前 Naive NeRF 建立多视图空间约束的核心机制。
此外,原始 Vanilla NeRF 是 Per-scene Optimization。训练完成后,field.state_dict() 保存的是当前 Lego 场景的专属参数。如果换成另一个全新场景,普通 Vanilla NeRF 并不能仅通过输入新图像就立即得到对应的三维表示,而需要重新优化。
训练过程中,应当周期性保存 Checkpoint,并记录 Training Loss。用于选择模型或监控泛化的 Validation Views 必须与训练 RGB 分开;最终 Test Views 则留到模型确定之后再评估。
到这里,系统已经真正开始从多个相机视角学习一个共享的 Radiance Field。但 Training Loss 的下降仍然不能说明 Novel View Synthesis 已经成功。还需要把训练完成的场景函数放到一个新的相机位置上。

2.6 渲染一个未见过的相机视角

NeRF 在推理时不需要输入训练图像。
给定一个新的相机位姿,只需要使用相同的 Camera Ray Generator 生成目标图像的所有 Rays,再使用已经训练好的 Neural Field 进行渲染。
对每一条 Ray,模型依然执行:
训练与推理的核心 Renderer 是同一个。
不同之处在于,推理阶段不再使用目标像素的 Ground Truth 进行参数更新。
对于高分辨率图像,也不需要一次将所有 Pixels 和 Samples 全部放入 GPU Memory。可以将 Rays 划分成若干 Chunks,逐批渲染,再恢复原始的图像排列。
这段代码没有引入新的渲染算法,只是将已经实现的 Ray Renderer 应用于完整图像。
每个 Chunk 独立预测 RGB,随后通过 torch.cat() 恢复原有 Pixel Order。由于场景由连续的 Neural Field 表示,不同像素之间不需要额外交换状态,所以这种分批推理不会改变相同采样配置下的渲染定义。
现在,可以使用 Held-out Camera 的 Rays 得到预测图像。对于具有 Ground Truth 的测试视角,还可以计算 Reconstruction Error 和 PSNR:
这里假设 RGB 数值已经归一化到 。
不过,评价结果时需要区分两个问题。
第一个问题是模型是否具备新视角渲染的能力:同一个训练后的 Field 能否接受新的 Camera Pose,生成具有合理空间结构的 RGB 图像?
第二个问题是渲染质量是否足够好:物体边界、局部纹理、遮挡关系与视角相关外观是否接近 Ground Truth?
第一个问题检验整个系统的功能闭环;第二个问题则反映当前 Field Representation 和 Sampling Strategy 的实际能力。
本章首先需要完成第一个里程碑,再通过对照图像与定量指标观察第二个问题。
因此,正式实验至少应展示一组训练视角与留出视角的:
Ground Truth / Predicted RGB / Error Map
并记录对应的训练配置和 Checkpoint。
实际 PSNR 和视觉结论应当从运行结果中获得,而不是提前假设 Naive NeRF 一定取得某个数值。

2.7 Naive NeRF 还缺什么?

到这里,一个最简单的 Neural Radiance Field Learning Pipeline 已经建立起来。
它从多视图数据中随机选择 Rays,根据这些 Rays 查询共享的 MLP,利用 Differentiable Volume Rendering 生成像素,再通过 RGB Reconstruction Loss 更新网络参数。
训练完成后,同一个 MLP 可以接受未参与训练的相机位置,并输出对应的 RGB 图像。
这意味着,本章首次构建的 TinyRadianceField 已经不仅能够查询局部属性,还开始承担由多个视角共同约束的三维场景表示任务。
但 Naive NeRF 仍然具有明显的简化假设。首先,网络直接接收原始三维坐标和方向,没有使用 Positional Encoding。其次,每条 Ray 只在固定位置进行采样,尚未加入 Stratified Sampling 或 Hierarchical Sampling。因此,如果实验结果在细纹理、薄结构或者锐利边界上出现模糊,目前还不能直接判断问题究竟来自 MLP 的表达与优化特性,还是有限采样带来的误差。
为了继续改进系统,需要把这些因素逐步分离。下一章首先保持 Camera、Renderer、训练数据和 Sampling Strategy 不变,只研究 Neural Field 本身:
为什么一个理论上能够表示复杂连续函数的 MLP,在直接输入三维坐标时,仍然可能难以学习快速变化的空间细节?
这个问题将引出 Spectral Bias,以及 NeRF 中最重要的输入表示设计之一:Positional Encoding。

第 3 章|位置编码:让 NeRF 学习高频细节

从原始坐标到多频率特征,让 Neural Field 更容易表达精细的空间变化。
从原始坐标到多频率特征,让 Neural Field 更容易表达精细的空间变化。
 
上一章已经完成了一个最简单的 Neural Radiance Field Learning Pipeline。
多视图 RGB 图像通过 Camera Ray Generation 转换成共享世界坐标中的 Rays;TinyRadianceField 在每条 Ray 的采样位置预测 Density 和 Color,再由已有的 Volume Renderer 合成像素,并通过 Reconstruction Loss 更新网络参数。
训练完成后,同一个 Neural Field 已经可以接受新的相机位置,生成对应的 RGB 图像。
这证明了最基本的 NeRF 训练闭环能够成立。
但构建完整的三维场景表示,还需要回答一个新的问题:当场景包含细小纹理、锐利边界和快速变化的空间结构时,普通 Coordinate MLP 是否能够高效地学习这些细节?
此前的 TinyRadianceField 直接使用原始三维位置与观察方向作为输入,没有引入额外的坐标编码。
对于连续、平滑的场景函数,这种方式可能已经足够。但复杂场景中的颜色和 Density 往往会在很短的空间距离内快速变化。
本章将研究这种变化为什么可能难以被普通 MLP 学习,并通过一个更简单的二维坐标回归实验,隔离网络本身的高频学习问题。
随后,从零实现 Fourier Positional Encoding,将它接入现有的 Neural Radiance Field,并通过受控的 NeRF Ablation 检查编码前后的差异。
本章只修改 Field 的输入表示。Camera、固定 Ray Sampling、Volume Renderer 和 RGB Loss 都保持不变,以便观察新增机制本身的作用。

3.1 当 NeRF 无法恢复细节

先回到上一章中的 Naive NeRF。它接收一个三维位置 和一个观察方向 ,预测对应的 Density 与 Color:
这里的网络只看到原始坐标。例如,两个非常接近的空间位置:
仅在第一个坐标上相差 。但这两个位置对应的真实场景属性,未必只存在很小的变化。如果它们分别位于物体边界的两侧,一个位置可能几乎没有 Density,另一个位置却需要具有较高的 Density。同样,物体表面一条很细的高对比纹理,也可能让相邻空间位置的 RGB 出现明显差异。
这意味着,连续场景函数并不一定是一个缓慢变化的函数。它可能包含大量高频结构。
一个足够大的 MLP 在理论上能够表示非常复杂的连续函数,但函数能够被表示,不等于梯度下降能够在有限训练预算内轻松找到对应参数。
如果网络训练时更容易先拟合缓慢变化的部分,那么它可能已经重建了物体的大致形状与颜色,却仍然没有学清局部细节。
这种现象与普通神经网络的 Spectral Bias 有关。不过,仅凭第二章的 RGB 图像还不能断定所有模糊都来自 MLP。有限 Ray Samples、训练时间、网络容量和相机误差同样可能影响结果。因此,先暂时移除 Camera、Density 和 Volume Rendering,构建一个只保留 Coordinate MLP 的小实验。

3.2 用二维坐标拟合实验观察高频表达

假设目标不是学习一个三维场景,而是让一个 MLP 记住一张二维 RGB 图像。
对于图像中的每个像素,使用其归一化二维坐标作为输入:
网络预测对应的颜色:
训练目标是:
与 NeRF 相比,这个任务简单得多。
它没有多视图关系,没有深度歧义,也没有 Transmittance 或 Ray Integration。
每个二维坐标直接对应一个已知的 RGB Target。
如果直接输入坐标的 MLP 在这里仍然难以学习高频变化,那么问题就不可能完全归因于三维渲染系统。

构造|用于对照的二维信号

为了避免真实图像的噪声和复杂内容干扰观察,可以先构建一个具有已知频率的合成 RGB 图像。
这张图像的三个颜色通道包含不同频率的空间变化。
红色通道主要沿水平方向变化,绿色通道主要沿竖直方向变化,蓝色通道则包含斜向的周期纹理。
所有颜色都位于 范围内。
更重要的是,这个实验的 Ground Truth 是解析定义的,因此可以准确知道目标信号包含哪些频率,不需要依靠主观判断来描述细节。
接下来,使用一个普通 ReLU MLP 进行坐标回归:
在最初的 Raw Coordinate 实验中,input_dim=2。
训练仍然使用普通的 RGB MSE:
运行后,可以将预测颜色重新排列为 [H,W,3],与解析 Ground Truth 对照,并绘制 Training Loss。
这里重点观察两个方面:
第一,网络是否首先拟合较平滑的整体变化,而较高频率的纹理收敛较慢。
第二,在相同训练预算下,不同空间频率的重建误差是否存在明显差异。
如果普通 MLP 对较高频率的分量学习得更慢,就为后续的输入表示设计提供了一个可以独立检验的动机。

3.3 为什么 MLP 更容易学习平滑函数?

上述现象对应神经网络优化中的 Spectral Bias(频谱偏置)。
直观来说,使用普通坐标输入训练的 MLP,往往更容易先拟合低频、缓慢变化的函数分量,而高频、快速振荡的分量通常需要更多优化步骤。
这里需要区分两个概念。
Representation Capacity 关注一个网络是否存在某组参数,可以近似表示目标函数。
Learnability 则关注优化过程能否在有限的数据、计算预算和训练时间内找到这些参数。
两者不是一回事。例如,考虑一个一维函数:
虽然输入 只有一个标量,但目标函数需要随着 快速振荡。
直接输入 时,MLP 需要通过多层非线性变换学习这种振荡结构。
但如果输入特征中已经包含:
那么网络就不必完全依靠自身的隐藏层从原始坐标中构造这一频率响应。
这引出了一个新的设计问题:
能否在输入网络之前,先把低维坐标转换成具有多种空间频率的特征?
这种输入映射就是 NeRF 中的 Positional Encoding。

3.4 从原始坐标到傅里叶特征

先考虑一个标量坐标 。
如果希望网络能够观察不同频率下的变化,可以构建一组正弦与余弦特征:
并继续增加频率。
更一般地,使用 个频带:
原来的一个标量 ,现在被映射为 个特征。这些特征具有不同的空间变化尺度。
低频分量随坐标变化较慢,适合表达较平滑的变化;高频分量变化较快,使网络更容易利用输入特征表示细小结构。
从局部变化率也可以看到这种差异。
对于:
有:
当 增大时,输入特征可能对更小的坐标变化产生更明显的响应。
因此,Positional Encoding 改变了 MLP 观察空间坐标的方式。
不过,这并不意味着高频编码能够自动生成真实细节。高频特征只提供一种更适合表示快速空间变化的输入基础,具体的 Density 和 Color 仍然需要通过训练数据学习。

从二维扩展到三维位置编码

对于三维位置:
可以分别编码三个坐标,再将结果拼接:
每个坐标产生 维,因此总维度为:
原始 NeRF 通常对 Position 使用十个频带:
对于 View Direction,则使用四个频带:
Direction 在几何上只有两个自由度,但代码通常使用三维 Unit Vector 表示,因此按照三个输入分量进行编码。
Position 和 Direction 使用不同数量的频带,也体现了不同的建模假设:空间结构可能包含非常快速的变化,而视角相关外观通常可以先使用相对较低频的方向特征来描述。
这里采用纯 Fourier Features,不额外拼接 Raw Input。因此位置输入为 60D,方向输入为 24D。
如果代码选择同时保留原始三维输入,就需要重新计算输入维度,并在实验配置中明确记录。

3.5 从零实现位置编码

现在将上面的数学定义转换为 PyTorch 代码。
编码器只负责特征变换,不需要训练参数。
这段代码可以分成三个步骤理解。
首先,torch.arange() 生成从 到 的频带索引,再计算:
随后,x.unsqueeze(-2) 在原始坐标之前增加一个 Frequency Dimension,使每个坐标都能与不同频率相乘:
最后,对所有 Phase 分别计算 Sin 和 Cos,并把频率、坐标及正余弦通道展开成一维特征向量。
例如,如果输入为:
使用十个频带后,中间 Tensor Shape 为:
其中 flatten(start_dim=-3) 只展开最后三个特征维度,保留前面的 Batch Dimensions。因此同一个 Encoder 也可以处理 [B,N,3] 的 Ray Sample Positions。

验证|位置编码器

在接入 NeRF 之前,先用简单输入验证数值与输出维度。
当输入坐标为零时,所有 Sin 分量都应为零,而所有 Cos 分量都应为一。
这可以直接验证编码公式与最后的 Flatten 顺序是否一致。
此外,也可以使用随机输入检查:
  • 输出是否全部为有限数值。
  • 输出维度是否等于 。
  • 对输入坐标是否能够计算梯度。
  • 不同 Batch Shapes 是否产生一致的编码结果。
编码器本身不需要拥有可训练参数,但 Sin/Cos 仍然是可微函数,因此不会破坏已有的 Differentiable Rendering Pipeline。

实验|重新检查二维拟合结果

现在使用相同的二维坐标回归任务,对比两个模型:
Raw Coordinate MLP:
Fourier-encoded MLP:
第二个模型的输入维度为:
因此可以使用相同的 MLP 结构,但将第一层输入维度从 2 改成 24。
使用相同的 Target Image、训练步数、Optimizer 类型与主要 Hidden Dimensions 训练两个模型,并绘制重建结果和 Loss Curves。
这里需要注意:输入维度发生变化,也会改变第一层的参数数量。因此,这是一组检验 Positional Encoding 实际效果的主要消融,并不是严格保持所有参数数量相同的理论实验。
如果需要进一步隔离参数规模的影响,可以补充参数量接近的 Raw MLP 作为对照。
最终关注的是:多频输入特征是否使高频颜色变化更容易在有限训练预算内被重建。
这个结果将为下一步修改三维 Radiance Field 提供依据。

3.6 将位置编码接入 NeRF

二维实验研究的是普通 Coordinate MLP 的高频学习行为。
现在回到 Ch.2 的 Naive NeRF。
此前的场景函数直接接收:
加入 Positional Encoding 后,输入变成:
网络最终仍然预测相同的属性:
因此,Positional Encoding 不会改变 Radiance Field 与 Renderer 之间的接口。
Renderer 仍然只关心每个位置的 Density、Color 和对应 Segment Length,不需要知道这些值来自 Raw Coordinate MLP 还是 Encoded MLP。

实现|修改现有场函数

为了保留 Ch.2 的网络结构,只修改 Input Encoding 与第一层输入维度即可。
下面给出一个简化的可配置实现:
这个实现保留了三个重要的结构性约束。
第一,Position Encoding 只改变位置的输入表示,不改变空间坐标本身。
第二,Density Branch 只依赖 Position Features,不接收 Direction Features:
第三,Color Branch 在 Position Features 的基础上额外接收 Direction Encoding:
因此,同一个位置从不同方向观察时,网络可以预测不同外观,但 Density 不会因为 View Direction 改变而改变。
这是 Vanilla NeRF 用于保持几何一致性的结构性先验之一。
需要强调,这里采用的是 Ch.2 的小型教学 MLP,并不是已经完整复刻原论文 8 层、256 Channels 的网络。完整的原始架构将在最终系统组装阶段统一说明。
现在只改变输入特征,能够让本章实验更清楚地聚焦于 Positional Encoding。

保持体渲染器不变

第二章中已经通过:
查询 Neural Field,加入 Positional Encoding 后,训练循环不需要改变。
只需要把模型初始化配置改为:
仍然使用第二章中的 Random Ray Batching、Fixed Sampling、Volume Rendering、RGB MSE 和 Optimizer。这也是把 Scene Representation 与 Rendering Algorithm 解耦的直接价值。编码方式虽然发生改变,但渲染系统并不需要重新设计。

3.7 用对照实验检验位置编码

完成代码修改后,最后的问题是:加入 Positional Encoding 是否真的改善了当前场景的学习结果?
实验需要同时回答两个问题。
第一,网络能否更快或更准确地拟合训练视角中的细节?
第二,这种改善能否延续到训练中未出现过的相机位置?
因此,仅绘制 Training Loss 还不够。

实验 A|原始坐标与位置编码的对比

首先固定 Camera、Dataset Split、Renderer、Sampling Strategy、Training Budget 和主要 MLP 结构。
比较两种配置:
Setting
Position Input
Direction Input
A:Raw NeRF
Raw XYZ
Raw Direction
B:Position PE
10-frequency Encoding
Raw Direction
这组实验主要用于观察高频空间输入表示的作用。
对应配置为:
训练时,应使用相同的数据预处理、Near/Far、固定 Samples、训练步数和主要优化配置。
模型需要独立初始化,并记录随机种子。
建议同时保存训练视角与留出视角的渲染结果,以及局部 Crop、Training Loss 和 PSNR。
如果多频位置编码确实改善了当前任务的高频学习,那么与 Raw Coordinates 相比,实验可能观察到更快的细节拟合、更清晰的局部结构,或更好的 Held-out View Reconstruction。
但这些都必须根据真实训练结果确认。

实验 B|加入方向编码的影响

接下来,再加入 Direction Encoding:
这时,位置输入为 60D,方向输入为 24D。
由于方向相关外观与空间几何并不是同一个学习问题,Experiment B 应重点观察具有视角相关高光或反射变化的区域。
如果场景的大部分表面近似漫反射,那么 Direction Encoding 的收益未必明显。
这个实验主要用于说明:空间函数的高频变化与方向相关外观可以分别建模,不能把所有改进都归因于 Position Encoding。

如何解读实验结果?

建议最终展示同一相机视角下的 Ground Truth、Raw NeRF、Position-encoded NeRF 和 Full PE NeRF。
随后选取几个局部区域,放大比较物体边缘、细杆、纹理和高对比结构。
还应结合 Loss 和 PSNR,判断可视化变化是否与定量结果一致。
这里尤其要避免一个结论:
“Position Encoding 提高了 PSNR,因此已经恢复了正确几何。”
RGB Reconstruction Quality 与 Geometry Accuracy 并不等价。
即使加入 PE 后新视角结果明显改善,学习到的 Density 仍然可能具有错误厚度、空间漂浮结构或其他不受 RGB Loss 唯一约束的分布。
此外,过多高频特征也未必总是有利。
更高的 Frequency Bandwidth 可能让网络更容易拟合复杂细节,但也可能增加对相机误差、数据噪声和采样不足的敏感性。
因此,Positional Encoding 的价值不在于“频率越高越好”,而在于为当前场景提供适当的多尺度输入表示,使网络更容易学习所需的变化。

3.8 下一步:如何沿射线更有效地采样?

到这里,Neural Field 已经从直接接受原始坐标的 MLP,升级为使用 Multi-frequency Fourier Features 的连续函数。与第二章中的Naive NeRF相比,Camera、Ray Generator、Volume Renderer 和训练损失都没有改变。改变的是 Scene Representation 的输入参数化方式。
实验验证了 Positional Encoding 的收益,这说明:在固定采样和训练条件下,改善输入特征可以让 MLP 更容易学习部分高频空间信号。
但这里仍然存在一个尚未解决的问题。无论 Neural Field 的表达能力有多强,Volume Renderer 都不可能沿一条 Ray 查询无穷多个空间位置。第二章中的Naive NeRF采用固定深度采样。假设某个非常薄的高 Density 区域刚好位于两个采样点之间,即使 Field 已经能够准确预测这个结构,Renderer 也可能因为没有在正确位置查询而错过它。
这说明两个问题需要分别解决:一个是场景函数能否表达并学到细节;另一个是有限的 Ray Samples 能否捕获这些细节。Positional Encoding 主要处理前一个问题。
下一章将保持已训练场景函数的查询接口不变,重新研究沿 Ray 的采样策略:如果连续三维场能够表示复杂结构,应该怎样选择有限的采样位置,才能更可靠地把它们渲染出来?
这将引出 Stratified Sampling,以及后续利用 Rendering Weights 分配计算预算的 Hierarchical Sampling。

第 4 章|从固定采样到区间分层随机采样

连续的三维场无法被无限查询;采样位置决定哪些空间细节有机会被渲染出来。
连续的三维场无法被无限查询;采样位置决定哪些空间细节有机会被渲染出来。
 
上一章已经通过 Positional Encoding 改善了 Neural Field 的输入表示,使 Coordinate MLP 更容易学习快速变化的空间信号。
但一个能够表达复杂细节的连续函数,并不意味着这些细节一定能够出现在最终渲染图像中。
原因在于,NeRF 的场景表示虽然定义在连续的三维空间中,Renderer 却不能查询无穷多个空间位置。
对于每条 Camera Ray,系统只能在 Near/Far 区间内选取有限数量的 Sample Positions,查询对应的 Density 与 Color,再通过离散 Volume Rendering 近似最终的像素颜色。
Ch.2 和 Ch.3 一直使用固定位置采样。这种方式简单、可复现,也便于检查已有 Renderer,但它留下了一个新的问题:
如果一个很薄的高 Density 区域刚好位于两个固定 Sample Points 之间,即使 Neural Field 已经正确表示这个结构,Renderer 是否仍然可能完全错过它?
本章将从一个已知解析解的 Density Field 开始,单独研究采样位置对渲染结果的影响。
随后,在保持采样数量不变的条件下,将 Fixed Sampling 替换为 Stratified Sampling:把整条 Ray 划分成多个连续区间,每个区间都保留一个查询点,但允许它在训练过程中随机改变位置。
最后,将新的 Sampler 接入上一章的 PE-NeRF,并与 Fixed Sampling Baseline 进行对照。
本章只修改采样机制,不改变 Field Representation、Volume Renderer 或 RGB Loss。

4.1 固定采样为什么会错过场景?

先暂时移除 Neural Field 的训练过程。
考虑一条最简单的 Ray,其深度范围为:
假设沿这条 Ray 的 Density 不是由 MLP 预测,而是由一个已知的解析函数给出:
这里, 决定 Density Peak 的位置, 决定其宽度,而 控制整条高斯密度曲线的总积分。
当 很小时,Density 只集中在一个狭窄的深度范围内。
这样的函数虽然仍然是连续的,却可以模拟非常薄的体密度结构。
现在沿 使用 16 个等宽 Segments,并在每个 Segment 的中心查询 Density。
第 个 Segment 的宽度为:
对应的固定 Sample Depth 为:
假设 Density Peak 位于:
宽度取:
这个峰值恰好位于两个相邻区间的边界附近,而最近的固定采样点位于 和 。
由于 Density 的变化范围很窄,这两个采样点都可能只观察到接近零的 Density。
于是出现了一个值得注意的现象:
Field 本身完全正确,但 Renderer 可能认为这条 Ray 几乎没有穿过任何介质。
为了验证这一点,可以先比较真实 Optical Depth 与离散估计。
连续 Optical Depth 为:
对于当前高斯函数,它具有解析解:
而固定采样的离散估计为:
这两个值并不一定接近。
即使某个位置的 Density Peak 非常高,如果采样点没有覆盖它,离散计算仍然会低估整体 Optical Depth。
由于 Transmittance 满足:
Optical Depth 的估计误差会进一步改变最终的背景透射率和像素颜色。
这与 Positional Encoding 解决的问题完全不同。
上一章改善的是 Neural Field 学习高频空间函数的能力。
当前的问题则发生在 Field Evaluation 之后、Volume Integration 之前:连续函数中的重要变化,没有被有限个 Sample Positions 正确观察。
因此,这是一种 Sampling / Numerical Integration Error,而不是 Field Representation Error。

4.2 为什么不直接增加采样点?

最直接的解决方式,是增加 Ray Samples 的数量。
如果把区间从 16 段细分成 32 段、64 段甚至更多,采样间距会逐渐缩小。对于当前已知的高斯 Density,足够密集的采样通常可以更准确地近似积分。
但在 NeRF 中,每个 Sample Position 都需要查询 Neural Field。
对于 条 Training Rays,每条 Ray 使用 个 Samples,一次 Forward Pass 至少需要处理:
个 Field Queries。
当 MLP 较深、图像分辨率较高时,这些查询会构成显著的计算成本。
因此,增加 Sample Count 并不是没有代价。
而且,对于固定采样位置,还有另一个值得考虑的问题:
即使在训练过程中反复优化同一组 Rays,如果每条 Ray 的采样深度始终不变,那么 Renderer 就会一直查询相同的位置。
那些始终落在采样点之间的狭窄变化,可能长期得不到充分的观察与监督。
相比单纯增加采样数量,是否存在另一种更小的修改?
保持每条 Ray 的样本数量不变,但让训练时的查询位置发生变化。
这里还需要满足一个条件:所有 Samples 不能在整个 Near/Far 区间内完全独立地随机选择。
因为完全随机的 Sample Points 可能在某些区域集中,而在另一些区域留下很大的空缺。
因此,新的采样策略应该同时具备两个性质:
  1. 每个深度区间至少有一个 Sample,保证整条 Ray 的分段覆盖。
  1. Sample 在对应区间内部随机变化,使不同训练迭代能够查询不同位置。
这就是 Stratified Sampling 的核心思想。

4.3 从固定采样到区间分层随机采样

Stratified Sampling 的第一步,与固定分段采样完全相同。
把 Near/Far 区间:
划分成 个不重叠的 Segments。
定义边界:
因此第 个 Segment 为:
Fixed Midpoint Sampling 始终在区间中心查询:
Stratified Sampling 则在每个区间内部引入一个随机变量:
并计算:
当 时,它退化为固定中点采样。
而当 在 中随机变化时,Sample Depth 也会在对应的 Segment 内改变。
需要强调,Stratified Sampling 并不是每次都生成一套完全任意的深度位置。
第 个 Sample 始终位于第 个 Segment 中。
这意味着:
  • 每个 Segment 都获得一次 Field Query。
  • 所有 Samples 保持由近到远的顺序。
  • 相邻区间不会出现由完全随机采样造成的大范围无覆盖区域。
  • 同一个 Segment 在不同训练迭代中,可以被查询到不同的位置。
这恰好满足前面提出的两个条件。

分层随机采样如何改变积分区间长度?

这里需要保持与上一篇 Volume Rendering 一致的接口。
前面的 Renderer 使用 个 Segments、 个 Boundaries,每个 Segment 对应一个 Density、Color 和 Length:
在本章的实现中,虽然 Segment 内部的 Sample Position 发生变化,但 Segment 本身仍然固定。
因此:
不会因为随机样本在区间内部移动而改变。
在 Sample Position 查询得到的 和 ,用于近似对应整个 Segment 的局部属性。
已有 Volume Renderer 仍然使用:
这里没有更改 Alpha、Transmittance 或 Compositing 公式。
改变的只有一个因素:用于代表每个 Segment 的 Field Query Position。
这种写法与上一篇已经采用的 Piecewise-constant Segment 模型直接兼容。
它保留了原始 NeRF Stratified Sampling 的核心随机覆盖机制,但不完全等同于一些原始 NeRF 实现中根据相邻随机采样深度计算积分区间的约定。后续引入 Hierarchical Sampling 时,需要明确统一两种离散化接口,而不是直接混用。

4.4 从零实现分层随机采样器

现在将采样公式转化为 PyTorch 代码。
对于一批 Rays,输入仍然是:
采样器需要返回每条 Ray 的 Depths、3D Sample Positions 和 Segment Lengths。
这段代码与上一章的 Fixed Sampler 非常接近。
真正新增的核心是:
它为每条 Ray 的每个 Segment 生成一个独立的随机数。
随后:
把 上的随机数线性映射到对应深度区间。
这里的 left、right 定义积分区间,而 depths 只是区间内部的 Field Query Positions。
最后,根据已有 Camera Rays 计算三维位置:
代码中的:
正是这一关系的批量实现。
输出 Tensor Shape 为:
这里继续假设同一批 Rays 共享标量 Near/Far。如果后续希望对每条 Ray 使用不同的 Scene Bounds,可以把 edges 扩展为 [B,N+1],但这不是当前最小实现的必要条件。

验证|分层随机采样器

采样器虽然简单,仍然需要独立测试。
首先检查,每个 Sample 是否始终位于对应区间内:
这里最后一项检查的并不是采样点覆盖了多少距离,而是所有 Segment Lengths 的总和是否仍然等于 Near/Far 区间长度。
由于每个 Segment 的边界没有变化,它们的长度总和必须满足:
还应检查 perturb=False 是否严格退化成固定中点:
这个测试会成为后续训练和推理切换采样模式时的重要保障。
在训练时,perturb=True 使网络能够在不同迭代中探索不同位置。
在评估时,perturb=False 可以恢复确定性的中点采样,避免重复渲染同一 Camera Pose 时,由采样随机性引入额外的图像波动。

4.5 随机采样能否改善积分估计?

Stratified Sampling 已经实现。
但仅凭 Sample Positions 的可视化,还不能证明它真的改善了数值积分。
因此,重新回到 4.1 中的解析 Density Field。
由于它具有已知真值,可以直接比较两种方法对 Optical Depth 的估计。
定义:
对于固定中点采样, 不随实验重复而改变,因此每次得到同一个 。
对于 Stratified Sampling, 每次重新随机生成,因此得到一组不同的估计值。

实验 A|固定采样与分层随机采样

首先使用同一条 Ray、相同的 Near/Far 和相同的 Sample Count。
固定 、 和 Density 的幅度,重复运行多次 Stratified Sampling。
然后比较:
  • 真实 Optical Depth。
  • Fixed Midpoint 的估计值。
  • Stratified Sampling 多次估计的平均值。
  • Stratified Sampling 的标准差。
  • 不同 Sample Count 下的误差变化。
这里可以使用前面定义的解析函数:
这里 optical_depth=0.8 表示高斯密度在整条实数轴上的总积分,当前 Near/Far 区间足够覆盖该高斯的主要质量。
运行不同采样方法后,可以得到两类误差。
Fixed Sampling 可能长期遗漏狭窄峰值,因此对当前解析场产生明显的系统性低估。
Stratified Sampling 则有机会在不同 Iterations 中查询到峰值附近的位置,使多次 Optical Depth 估计能够围绕连续积分的真实值变化。
但这不意味着每一次随机估计都更加准确。
当 Density Peak 非常狭窄时,单次随机采样仍然可能完全错过峰值;少数恰好落在高密度区域的 Samples 也可能产生很大的估计值。
因此需要同时观察 Bias 与 Variance。

为什么随机采样不一定更准确?

对于确定的函数 ,定义:
由于每个区间内是均匀采样:
代入:
因此:
这说明 Stratified Sampling 对普通积分具有无偏估计的性质。
但 NeRF 的像素颜色不是一个简单的 Density 积分。
即使在颜色不随深度变化的特殊情况下,Transmittance 仍然包含:
由于指数是非线性函数,一般有:
因此,即使 对真实 Optical Depth 无偏,也不能直接推导出最终渲染 RGB 无偏。
这也是为什么本章不能仅凭“采样位置随机化”就声称 Stratified Sampling 必然优于固定中点。
它的核心价值是:在保持分段覆盖的同时,让训练过程能够观察不同的区间内部位置,从而减轻固定采样位置造成的持续性遗漏。
对于某些平滑函数,固定中点仍可能具有更小的单次积分误差。

实验 B|比较渲染误差

Optical Depth 的测试验证了采样机制,但最终目标仍然是正确的 RGB Rendering。
因此,可以进一步固定整个场的 Color:
并选择白色背景:
此时连续渲染结果有一个非常简单的解析形式:
而离散估计只需要将 替换为 。
这样可以在没有 MLP、没有训练过程的条件下,比较采样误差如何进一步影响像素颜色。
正式配图建议同时展示 Density Curve、固定采样位置、随机采样位置,以及两种方法的 RGB Error Distribution。
需要特别注意:这是一种受控的数值测试,不是完整 NeRF 质量提升的证明。
最终是否改善 Learned NeRF 的训练和新视角渲染,还需要回到真实的多视图任务中验证。

4.6 将分层随机采样接入 NeRF

现在,Stratified Sampler 已经通过基本的几何与数值测试。
接下来将它接入 Ch.3 的 PE-NeRF。
原来的训练流程为:
现在变为:
整个系统只有一个模块发生变化。
Field 仍然使用 Positional Encoding,Density 与 Color 的输出定义保持不变,Volume Renderer 也继续使用已经验证的 Alpha Compositing 规则。
对于训练阶段:
然后对 points 查询现有的 PE-NeRF,并把得到的 sigma、rgb 和 deltas 交给 Ch.2 已经实现的 Batched Volume Renderer。
训练循环中的 RGB Loss 和 Optimizer 不需要修改。
对于评估阶段,可以使用:
保持推理输出的确定性。
这里的训练与评估采样方式不同,但仍然使用同一套连续 Field Parameters。
这与普通机器学习中的训练随机化类似:训练期间不断变化的采样位置可以让 Field 在更多空间位置受到图像监督,而最终评估采用固定查询方式,以便进行稳定、可复现的比较。

验证|比较替换采样器前后的结果

为了判断 Stratified Sampling 是否对当前 NeRF 有实际帮助,需要重新训练两个配置:
Baseline A: PE-NeRF + Fixed Midpoint Sampling。
Experiment B: PE-NeRF + Stratified Sampling。
二者应当使用相同的训练图像、Camera Poses、Model Architecture、Near/Far、Segment Count、主要优化设置和训练预算。
只有采样位置的生成方式发生变化。
实验需要记录 Training Loss、Held-out PSNR 和相同测试视角的 RGB 对照。
如果 Stratified Sampling 能够改善场景的细结构学习,实验可能观察到更稳定的训练行为或更好的局部渲染质量。
但它也可能在训练早期带来更多随机波动,甚至在某些设置下没有明显收益。
这些结果都应该如实展示。
此外,若训练与评估采用不同采样策略,最终结论还需要考虑 Evaluation Sampler 的影响。必要时,可以让两组训练出的模型都使用同一个确定性采样器进行评估,确保比较主要反映训练采样方式的不同。

4.7 下一步:把更多样本放到关键区域

到这里,PE-NeRF 的采样系统已经从固定中点升级为 Stratified Sampling。
与上一章相比,Neural Field 的输入表示没有改变。与更早的 Volume Rendering 相比,Alpha、Transmittance 和 RGB Compositing 也没有改变。
改变的是:每个 Segment 不再总是在同一个位置查询 Field。
这使训练过程能够在保持全区间覆盖的同时,对不同的深度位置进行探索。
但 Stratified Sampling 仍然有一个明显的局限。
它只知道如何划分空间,却不知道场景中哪些区域真正重要。
例如,一条 Ray 可能穿过很长一段空空间,随后在物体表面附近出现较高 Density。
即使空空间中的大部分 Samples 几乎不影响最终像素,Stratified Sampling 仍然会为这些区间分配查询预算。
对于物体后方已经被完全遮挡的区域,情况也类似。
事实上,现有 Volume Renderer 已经计算了一个很有价值的量:
这些 Rendering Weights 同时反映了局部 Opacity 和前方 Transmittance,因此能够提供每个 Segment 对当前像素的可见贡献信息。
目前,这些权重只被用于 RGB Compositing。
但它们是否还能承担另一项任务?
如果某些 Segments 的 Rendering Weights 明显更高,能否让下一轮采样在这些区域放置更多 Samples,而不是继续平均覆盖整条 Ray?
这个问题将引出下一章的 Coarse-to-Fine Hierarchical Sampling。
它不仅改变 Sample Positions,还将开始利用 Neural Field 自己的预测结果,决定下一次应该在哪里投入更多计算。

第 5 章|重要性采样:把计算用在关键位置

从均匀覆盖走向重点采样,让 Coarse Weights 引导计算投入更重要的深度区域。
从均匀覆盖走向重点采样,让 Coarse Weights 引导计算投入更重要的深度区域。
上一章已经将固定深度采样升级为 Stratified Sampling。
对于每条 Camera Ray,Near/Far 区间被划分成多个 Segments。训练时,每个 Segment 都会随机选择一个位置查询 Neural Field,从而在保持全区间覆盖的同时,让不同 Iterations 能够观察不同的深度位置。
这解决了固定采样点可能长期遗漏某些空间变化的问题。
但 Stratified Sampling 仍然存在一个明显的限制。
它能够决定怎样覆盖空间,却不知道哪些空间区域更值得投入计算。
考虑一条穿过不透明物体的 Ray。
从 Camera Origin 到物体表面之间,可能存在很长一段空空间;物体表面附近的 Density 开始增加;继续向后,虽然还可能存在其他结构,但这些结构已经被前方物体遮挡。
对于最终像素而言,这三个区域的重要性可能截然不同。
然而,Stratified Sampling 仍然在每个预先划分的 Segment 内分配一个 Query。
在 NeRF 中,每个 Query 都意味着一次 MLP Forward Evaluation。如果大部分 Samples 的有效贡献很小,计算预算就没有被充分利用。
因此,一个新的问题出现了:
既然 Volume Renderer 已经计算了每个 Segment 对最终像素的贡献,能否使用这些信息决定下一轮应该在哪里采样?
本章将利用上一章已经熟悉的 Rendering Weights,构建一个 Coarse-to-Fine Hierarchical Sampling Pipeline。
首先使用 Stratified Sampling 对整条 Ray 进行粗采样,再将 Coarse Rendering Weights 转换成概率分布,通过 Inverse-CDF Sampling 在重要区域增加 Fine Samples。
最后,使用独立的 Fine Network 在合并后的全部位置重新查询 Radiance Field,并通过 Coarse/Fine 两个 RGB Loss 共同训练系统。
完成本章后,NeRF 将不再只使用预先确定的空间采样分布,而是能够利用当前学到的场景信息,指导下一阶段的计算。

5.1 均匀采样把计算浪费在哪里?

先观察一条已经经过 Coarse Rendering 的 Ray。
假设它包含 个 Stratified Samples,每个 Sample 都对应一个积分区间。
经过 Neural Field 和 Volume Renderer 后,可以获得:
这些量已经足够计算最终像素颜色:
但并非所有 Samples 的 都同样重要。
如果某个 Segment 位于空空间,那么它的 Density 可能接近零,对应的 Opacity 与 Rendering Weight 也很小。
如果某个 Segment 位于前景物体表面附近,则可能产生较高的 Rendering Weight。
而对于已经被前景遮挡的后方区域,即使 Density 不低,最终 Rendering Weight 也可能很小。
因此,可以为某条 Ray 同时绘制三组信息:
这里的 Density 与 Weight 分布是概念示意,正式实验应使用当前 Coarse Field 的真实输出。
这张图会揭示一个问题:
Coarse Samples 的位置分布,与这些 Samples 最终对像素的贡献分布,并不一致。
Stratified Sampling 主要负责空间覆盖。但在已经获得一次 Coarse Estimate 之后,继续使用同样的空间采样分布,未必是最有效的计算策略。
一个自然的改进是:
先保留 Coarse Pass 对整条 Ray 的覆盖,再根据当前预测的 Rendering Weights,为更重要的区间追加 Samples。
不过,为什么使用 Rendering Weights,而不是更直观的 Density?

5.2 为什么渲染权重可以指导采样?

上一章已经证明,离散 Volume Rendering 中的每个 Segment 对应:
以及:
最终 Rendering Weight 为:
这里不再重新推导 Beer–Lambert Law 或 Alpha Compositing,而是重新观察两个因子的含义。
表示:如果 Ray 已经到达第 个 Segment,在这个 Segment 中发生终止的条件概率。
表示:Ray 到达第 个 Segment 之前,尚未被前方介质终止的概率。
因此, 同时包含了两个条件:
当前位置有足够的局部 Opacity,并且这个位置能够从当前相机方向被观察到。
这使它比单独使用 Density 更适合作为 Importance Signal。

为什么不能只根据密度采样?

考虑一条 Ray 依次穿过两个高密度区域。
假设前方 Segment 的 Opacity 为:
后方 Segment 的 Opacity 为:
虽然第二个 Segment 的局部 Opacity 也很高,但光线到达它之前,已经有 的终止概率质量分配给了前方 Segment。
因此:
对应的 Rendering Weights 为:
如果只根据 Density 或局部 Opacity 分配 Samples,两个位置看起来都很重要。
但根据 Rendering Weights,前方 Segment 的可见贡献概率质量远高于后方。
这是因为 Rendering Weight 不只描述局部是否存在介质,还编码了前方累计遮挡。
因此,Hierarchical Sampling 不应该简单解释成“哪里 Density 高,就在哪里多采样”。
更准确地说:
Coarse Rendering Weights 为 Fine Sampling 提供了沿 Ray 的可见终止概率分布。
不过,这个分布并不是严格的最优采样策略。
较低的 Rendering Weight 不一定意味着该区域没有学习价值。如果 Coarse Field 本身遗漏了真实的薄结构,对应 Weight 也可能错误地接近零。
因此,Hierarchical Sampling 仍然需要保留覆盖整条 Ray 的 Coarse Pass,而不能从训练一开始就完全依赖 Fine Sampling。
现在,重要区域的判断依据已经明确。
接下来需要把一组离散 Weights 转换成能够实际采样的概率分布。

5.3 从渲染权重构建采样分布

假设 Coarse Pass 将 Near/Far 区间划分成 个 Segments。
每个 Segment 都有对应的边界:
以及从 Coarse Volume Rendering 得到的非负权重 。
目标是构造一个新的采样分布,使得:
  • Rendering Weight 较大的 Segment,获得更高的采样概率。
  • Rendering Weight 较小的 Segment,获得较低的采样概率。
  • 整个 Near/Far 范围仍然具有明确的概率定义。

步骤 1|归一化渲染权重

最直接的方法是归一化:
其中 是一个很小的正数,用于避免所有 Weights 都为零时出现除零。
这样可以得到:
需要注意,这里的 是第 个 Segment 被选中的概率质量,而不是单位深度上的概率密度。
对于宽度为:
的 Segment,如果区间内部采用均匀采样,那么连续的 Piecewise-constant PDF 为:
这一步在后面处理非均匀深度区间时尤其重要:概率质量与单位长度概率密度不能混为一谈。
同样需要强调, 只用于决定 Fine Samples 的位置。
它不应该替换 Volume Renderer 中原有的 。
原有 Rendering Weights 的总和可以小于 1,剩余质量对应 Background Transmittance;Importance Sampling 的概率分布则必须归一化为 1。
两者有不同用途。

步骤 2|构建累积分布函数

有了每个 Segment 的概率质量,就可以计算 Cumulative Distribution Function。
定义:
于是:
CDF 的作用是将每个深度区间映射到 上的一个概率区间。
概率质量较大的 Segment,在 CDF 上占据更宽的范围。
因此,如果从 均匀选择一个随机数,它更容易落入高概率质量 Segment 所对应的范围。

步骤 3|逆 CDF 采样

现在从均匀分布中采样:
找到满足:
的 Segment。
如果 落在第 个概率区间内,就可以通过线性插值,将它映射回真实的 Depth:
这个公式有非常明确的几何意义。
首先, 决定采样落在哪个 Segment。
随后:
计算随机数在该概率区间内部的相对位置。
最后,将这个相对位置线性映射到真实的深度区间 。
因此,原本均匀分布在概率空间中的随机数,经过 Inverse-CDF 变换后,会在实际深度空间中形成非均匀分布。

示例|用最小数值例子理解逆 CDF

考虑一条 Ray,深度范围为 ,划分成四个等宽 Segments。
边界为:
为方便手算,假设归一化后的权重是:
那么 CDF 为:
现在选择一个随机数:
它位于:
因此,这个随机数对应第三个 Segment,也就是 Depth 区间:
在这个概率区间内部的相对位置为:
映射回 Depth:
因此,一个位于概率空间中的随机数,最终变成了 Ray 上的实际 Sample Depth。
这个例子说明了整个 Inverse-CDF Sampling 的本质:
不是直接在深度空间均匀选择位置,而是先在概率空间均匀采样,再利用 Coarse Weights 决定这些概率质量对应的空间位置。

5.4 从零实现逆 CDF 采样

现在将上述公式转换成 PyTorch 代码。
本篇继续沿用 Ch.4 的 Segment 约定。
每条 Ray 拥有 个 Coarse Boundaries,以及 个 Coarse Rendering Weights。
因此输入 Tensor Shape 为:
输出为:
实现如下:
这段代码包含五个步骤,每一步都能对应到前面推导的数学定义。
第一,safe_weights 将 Coarse Rendering Weights 转换成稳定的非负概率质量。加入 可以避免所有 Weights 为零时无法归一化;对于当前等宽 Coarse Bins,这种退化情况会得到近似均匀的 Bin Sampling。
第二,torch.cumsum() 构造累计概率,再在最前方补上:
第三,torch.rand() 在 中生成均匀随机数。deterministic=True 则使用固定的分位点,使相同权重分布能够得到可复现的采样结果。
第四,torch.searchsorted() 查找每个随机数落入 CDF 的哪一个区间。
第五,使用 CDF 区间与真实 Depth 区间的对应关系,对深度进行线性插值。
其中最关键的三行是:
它们实际上就是在寻找:
最后的线性插值:
则对应前面的 Inverse-CDF 公式。
需要注意,当前函数使用了:
这是一个有意的设计。
由于采样区间选择包含离散的 searchsorted() 操作,这个最小实现不尝试沿采样分布到 Coarse Network 进行反向传播。
Coarse Network 将由自己的 RGB Reconstruction Loss 直接训练,而 Fine Network 则根据新位置上的 Volume Rendering Loss 更新。
这与原始 Vanilla NeRF 中将 Hierarchical Sampling 作为不求导的 Proposal Step 的实践一致。

验证|检查采样分布

首先不接入 Neural Field,直接使用手工构造的概率分布检查采样器。
例如,给定四个等宽区间:
第三个区间的权重占全部权重的 。
如果随机生成足够多的 Fine Samples,那么落在 的比例应该接近 。
这个测试检查的是最终 Sample Depth Distribution,而不只是 PDF 或 CDF 的数值。
此外,还需要验证全零 Weights、确定性采样、Near/Far 边界、输出 Shape 和数值有限性。
特别是当所有 Coarse Weights 都很小时,采样器不能产生 NaN,也不能让 Fine Samples 跑到 Scene Bounds 之外。
对于真实 NeRF,某些背景 Rays 可能几乎没有 Foreground Density,因此这一退化情况并不罕见。

5.5 从粗采样到精细渲染

目前,sample_pdf() 已经能够根据 Coarse Weights 生成新的 Fine Depths。
但生成这些深度,并不等于完成 Fine Rendering。
因为 Volume Rendering 的 Transmittance 具有明确的前后顺序。
如果直接将新增 Fine Samples 拼到 Coarse Samples 后面,再按照原有顺序渲染,那么距离相机更近的 Fine Sample 可能被错误地放在更远的 Coarse Sample 之后。
这会破坏 Front-to-back Compositing 的物理与数值意义。
因此,新的采样位置必须先合并、再排序。

实现|合并粗采样与精采样

设:
是 Coarse Samples,
是新增的 Fine Samples。
完整 Fine Pass 使用:
对应的代码非常简单:
注意,Fine Network 并不是只查询新增的 个位置。
在原始 NeRF 的两网络结构中,Fine Network 会在合并后的全部 个位置重新预测 Density 和 Color。
这是因为最终 Fine Rendering 仍然需要完整的前后深度顺序,以及每个区间对应的局部场景属性。

实现|重建积分区间

合并后的采样位置一般不再等距。
因此,Ch.4 中直接使用固定均匀 Segment Length 的做法,不能不加修改地继续使用。
这里采用与前面博客一致的 Segment-based Renderer 接口。
设合并排序后的 Samples 为:
为了让每个 Query Position 对应一个积分区间,可以利用相邻 Samples 的中点定义内部边界:
再添加首尾边界:
于是,每个 Segment 的长度为:
这是一种用于本系列 Renderer 接口适配的中点分段近似:每个 Sample 被视作其对应区间内的代表值。它与原始 NeRF 代码按相邻采样深度构造 delta 的方式并非逐项相同,但保留了有序的非均匀积分区间。
对应实现为:
随后:
输出 Shape 为:
现在,每个 Fine Query Position 都具有对应的 Segment Length。
整个区间仍然覆盖原来的 Near/Far 范围:
注意这里没有重新推导或改变 Alpha Compositing。
已有 Renderer 仍然使用:
以及:
真正改变的是输入给 Renderer 的 Sample Positions 和 Segment Lengths。
另外,原始 NeRF 的 Sample-based Interval Convention 与这里的 Segment-centered Convention 存在数值差异。若要求与原论文实现逐项对齐,需要在后续系统整理时明确选择一种约定,并保证 Coarse/Fine 两阶段始终一致。
对于本系列当前代码,保留已经验证的 Segment-based Renderer 能够减少不必要的重复实现。

5.6 训练 Coarse/Fine 双辐射场

到这里,Hierarchical Sampling 已经能够完成以下过程:
但还有一个问题:
Coarse Weights 从哪里来,又由什么保证它们逐步变得可靠?
原始 Vanilla NeRF 使用两个独立的 Radiance Field Networks。
第一个是 Coarse Field,负责在较少的 Samples 上预测初步的场景分布,并产生用于下一阶段的 Rendering Weights。
第二个是 Fine Field,负责在合并后的全部采样位置上重新预测 Density 与 Color,生成最终的 Fine RGB。
它们可以使用相同的网络结构,但拥有不同的可训练参数:
因此,Coarse Field 不仅用于产生 Sampling Proposal,本身也需要正确拟合训练图像。
否则,如果它在训练初期预测的 Weights 没有得到可靠监督,那么 Fine Sampling 就可能长期集中到错误的位置。
这就是为什么需要两个 RGB Loss。

粗细两阶段的重建损失

对于同一批 Training Rays,Coarse Network 产生:
Fine Network 产生:
二者都与同一个 Ground Truth RGB 比较。
定义:
最终损失为:
这两个 Loss 的作用不同。
Coarse Loss 直接训练 Coarse Field,使它逐步形成能够解释训练图像的 Density/Color Distribution,并为 Fine Sampling 提供更有意义的 Weights。
Fine Loss 则训练 Fine Field,使最终的 Rendered RGB 接近 Ground Truth。
两者共同构成原始 Vanilla NeRF 的 Coarse-to-Fine 训练机制。

实现|完整前向计算

接下来将所有模块拼接起来。
这里沿用 Ch.4 的:
并继续使用 Ch.3 的 PE-NeRF Field,以及上一篇的 Batched Volume Renderer。
其中 render_field_on_samples 表示已有 Field Query 和 Volume Renderer 的组合接口,要求返回预测 RGB 和未归一化的 Foreground Rendering Weights。
这段代码是本章新增采样机制与已有训练模块的组合示意。render_field_on_samples 需要按照前面已经实现的 Field/Renderer 接口进行封装,而不是重新实现体渲染数学。
从概念上看,整个训练过程分成两次场景查询。
第一次查询负责获得对整个 Ray 的粗略认识。
Coarse Field 的输出不仅用于计算一个 RGB Prediction,还提供后续采样分布。
第二次查询利用新的采样位置,对 Coarse Field 认为重要的区域进行更密集的观察,并由 Fine Field 生成最终颜色。
虽然两个阶段使用不同的 MLP,但它们都在相同的世界坐标系中查询同一个静态场景,并受到相同多视图 RGB 数据的监督。
最终参数更新时,Coarse Loss 更新 Coarse Network,Fine Loss 更新 Fine Network。
由于 Inverse-CDF Sampling 使用了不求导的采样步骤,这个最小实现不依赖 Fine Loss 穿过采样过程去更新 Coarse Network。
这种分工也让 Coarse Sampling Proposal 与 Fine Rendering 的职责更加清晰。

5.7 用对照实验验证分层重要性采样

完整的 Coarse-to-Fine Pipeline 已经建立。
但 Samples 在高权重区域集中,并不自动代表最终质量一定更好。
因为 Hierarchical Sampling 的实际收益还取决于 Coarse Field 是否正确识别了重要区域。
如果 Coarse Field 对真实表面的 Density 估计错误,那么 Fine Samples 也可能集中到错误位置。
因此,实验需要同时验证两个层次:
第一,采样器是否按照 Coarse Weights 正确分配 Samples?
第二,这种分配是否真正改善了训练和新视角渲染?

实验 A|可视化采样分布

首先选择一条来自当前 Lego 场景的训练 Ray。
对它执行 Coarse Pass,保存:
  • Coarse Sample Depths;
  • Coarse Density;
  • Coarse Rendering Weights;
  • Fine Sample Depths。
然后把这些信息绘制在同一深度坐标轴上。
这张图应该直接展示:
Coarse Samples 如何覆盖整个 Near/Far 区间,而 Fine Samples 如何根据 Coarse Weight Distribution 改变位置分布。
这里需要检查采样器是否按照概率工作,而不是仅凭视觉效果判断。
例如,如果某些 Segments 占据了大部分概率质量,生成大量 Fine Samples 后,对应区间应获得相近比例的样本数量。
同时,也应保留低权重区间的少量探索概率,避免采样分布完全退化。

实验 B|均匀增采样与分层重要性采样

接下来进行真正的 NeRF Ablation。
为了排除网络容量和计算预算的干扰,两个模型都使用:
  • 相同的 Training/Test Views。
  • 相同的 Coarse/Fine Networks。
  • 相同的 Position/Direction Encoding。
  • 相同的 Coarse Stratified Sampling。
  • 相同的 Coarse/Fine Sample Counts。
  • 相同的 Renderer、RGB Loss 和主要训练配置。
唯一需要改变的是 Fine Samples 的采样分布。
Baseline A:Uniform Extra Sampling
Coarse Pass 完成后,在整个 Near/Far 范围内非自适应地追加 个 Samples。
Experiment B:Hierarchical Extra Sampling
Coarse Pass 完成后,利用 Coarse Weights 构造 PDF/CDF,按 Importance Distribution 追加相同数量的 Samples。
这样,两组实验使用相同数量的 Field Evaluations。
例如,在经典设置下:
每条 Ray 都需要:
次 Field Evaluations。
这意味着,实验比较的不是“花四倍计算获得更清晰图像”,而是:
在相同查询预算下,依据已有场景信息重新分配采样位置,是否提高了计算的有效性?
实验建议展示 Held-out RGB、PSNR、局部细节 Crop、Sampling Distribution 与 Wall-clock Training Cost。
还需要明确区分 Sample Count 与实际运行速度。
Hierarchical Sampling 额外包含 PDF/CDF 构建、searchsorted() 和 Sample Sorting。即使它提高了单位计算预算下的渲染质量,也不能直接说明整个 Pipeline 运行更快。

粗阶段预测错误会发生什么?

最后,还应观察一个可能失败的情况。
在训练早期,Coarse Field 的预测可能尚不准确。它可能将较高 Weight 分配给错误的区域,或者完全遗漏某个薄结构。
此时,Fine Sampling 会继承这项错误。
因此,Hierarchical Sampling 并不是在训练开始前就知道真实表面在哪里,而是在不断利用当前场景估计生成新的查询位置。
这种机制的效果依赖于 Coarse Proposal 的质量。
保留全范围 Coarse Stratified Sampling,并使用 Coarse RGB Loss 进行监督,就是为了让这个采样分布在训练过程中逐步变得有意义。
如果实验仍然出现 Thin Structures Missing、Floaters 或 Depth Distribution 异常,就需要检查 Coarse Coverage、Density Initialization、采样预算与网络训练情况,而不是简单增加 Fine Samples。

5.8 从改进采样到完整 NeRF

到这里,NeRF 的采样系统已经完成了从固定空间查询到自适应两阶段查询的演化。
最初的 Naive NeRF 使用固定 Sample Positions。
随后,Stratified Sampling 让每个 Segment 在训练过程中能够探索不同的深度位置。
现在,Hierarchical Sampling 进一步利用 Coarse Rendering Weights,让 Fine Samples 更多地集中在当前场景估计中具有较大可见贡献的区域。
整个过程只改变了空间查询策略,并没有改变最终 RGB 的体渲染规则。
Renderer 依然使用:
以及 Front-to-back Compositing。
但这些 Rendering Weights 不再只用于颜色合成。它们开始影响下一阶段的计算位置。
这正是本章最重要的能力升级:
Neural Field 不仅提供用于渲染的 Density 和 Color,还能够通过 Renderer 的中间结果,参与决定下一次在哪里进行更细致的空间查询。
至此,原始 Vanilla NeRF 的核心机制已经基本具备:
Positional Encoding 让 MLP 更容易学习高频变化;Stratified Sampling 提供全区间覆盖;Hierarchical Sampling 根据可见贡献追加查询;Coarse/Fine Networks 和两个 RGB Loss 则将这些机制连接成完整的训练目标。
但目前这些模块仍然分别在不同章节中实现和验证。
接下来,需要将它们整理为一个统一、可复现的完整系统:确定网络配置、建立正式的训练与推理入口、管理 Checkpoints,并在同一套 Held-out Views 上评估最终结果。
这就是下一章 组装完整的最小 NeRF 系统 的目标。

第 6 章|组装完整的最小 NeRF 系统

让分散的表示、采样和渲染模块,组成一个可训练、可保存、可复现的 NeRF 系统。
让分散的表示、采样和渲染模块,组成一个可训练、可保存、可复现的 NeRF 系统。
到目前为止,构建 NeRF 所需的主要组件已经全部完成。
Ch.1 将多视图 RGB 图像转换成共享世界坐标系中的 Camera Rays;Ch.2 建立了能够通过图像监督训练的 Naive NeRF;Ch.3 使用 Positional Encoding 改善高频空间函数的学习;Ch.4 将固定深度采样扩展为 Stratified Sampling;Ch.5 则进一步利用 Coarse Rendering Weights,构建了 Coarse-to-Fine Hierarchical Sampling。
这些模块分别解决了不同的问题,也已经通过各自的实验进行了验证。
但一个新的问题随之出现。
每个模块能够独立运行,并不代表将它们连接起来后,整个 NeRF 系统就一定正确。
例如,Coarse Sampler 和 Fine Sampler 都可能生成合理的深度分布,但如果 Fine Rendering 使用了错误的 Segment Length,Transmittance 仍然会被错误计算。
同样,Coarse/Fine 两个网络都能够执行 Forward Pass,也不代表它们都能从最终 Loss 中获得正确的梯度。
因此,本章不再引入新的场景表示或采样算法,而是将此前独立构建的组件整理成一个完整、可训练、可保存和可恢复的 Minimal NeRF。
最终系统应当能够从已知相机参数的多视图图像中学习场景参数,保存训练状态,并在重新加载后,根据新的 Camera Pose 生成完整 RGB 图像。
为了保证这项能力不是由偶然的训练状态或隐藏的代码依赖产生,本章还将建立必要的 Integration Tests,验证网络梯度、采样顺序、Checkpoint 恢复和分块推理的一致性。

6.1 完整 NeRF 需要哪些模块接口?

先观察当前系统已经具备的计算流程。
对于一个 Training Ray Batch,Coarse Field 首先在 Stratified Samples 上预测 Density 与 Color,并通过 Volume Renderer 计算第一幅 RGB Prediction。
Coarse Rendering Weights 随后被用于构造 Importance Sampling Distribution。
新增 Fine Samples 与已有 Coarse Samples 合并后,Fine Field 在全部位置上重新预测 Density 和 Color,再经过 Volume Rendering 生成最终 RGB。
两次渲染都受到同一个 Ground Truth RGB 的监督。
整个 Forward Pass 可以写成:
这张图没有引入新的数学机制。
它的作用是把前面所有模块放回同一个系统中,并明确它们的数据依赖关系。
尤其需要注意,Coarse Field 和 Fine Field 并不是串联的两个普通神经网络层。
Coarse Field 的重要输出之一是 Sampling Proposal:通过 Rendering Weights,影响 Fine Field 将在哪些空间位置被查询。
与此同时,Coarse 和 Fine Networks 各自拥有独立参数,也各自接受 RGB Reconstruction Loss。
因此,最终的系统既包含普通的可微神经网络计算,也包含不对采样索引求导的离散采样步骤。
要让整个系统正确运行,首先需要统一所有模块的输入和输出约定。

模块间的接口约定

所有 Rays 都处于同一个 World Coordinate System 中,方向向量满足:
所有 Sample Depths 都位于经过验证的 Near/Far 范围内。
每个积分区间都使用自己的实际长度:
Renderer 则保留上一篇 Volume Rendering 已经建立的 Front-to-back Alpha Compositing 和 Background Transmittance 规则。
其中,有三项约定尤其重要。
第一,Sample Depths 必须按从近到远排序,因为 Transmittance 依赖前方所有 Segments 的累计 Density。
第二,Fine Pass 合并了不均匀分布的 Samples,因此不能继续假设所有 Segments 都具有相同长度。
第三,Rendering Weights 在用于计算像素时不应被任意归一化。只有构建 Fine Sampling 的 PDF 时,才需要将 Coarse Weights 转换成总概率质量为 1 的分布。
这些要求都已经在前面的章节中出现,但现在它们成为整个系统必须共同遵守的接口契约。

6.2 定义最终模型与训练配置

此前的实验分别使用不同的采样数量、训练设置和网络规模,以便隔离验证每项机制。
但进入完整系统后,如果这些设置继续散落在各个函数内部,就很难确认一次实验究竟使用了什么配置。
因此,首先将影响训练和渲染的主要参数集中管理。
这里的 Config 不只是为了减少函数参数的数量。
它让一次训练所使用的空间范围、采样预算、输入编码、网络规模和优化设置成为一个明确的数据对象。
需要注意,near 与 far 不应该直接从其他实验复制,而应使用 Ch.1 已经检查过的 Scene Bounds。
n_coarse=64 和 n_fine=128 是原始 Vanilla NeRF 常见的采样配置,但当前较小的 Ray Batch 和 Hidden Width 是为了构建一个更容易运行的教学系统,并不等于完整复现原论文的网络规模和训练预算。

实现|构建 Coarse/Fine 双辐射场

接下来,为 Coarse 和 Fine 两个阶段创建独立的 Neural Field。
由于 Ch.3 已经实现了带 Positional Encoding 的 Field,本章首先复用它,而不是重新定义位置编码器和 Density/Color Branch。
这里使用一个 field_factory 创建两个结构相同、但参数相互独立的网络。
例如,最小运行配置可以使用:
这里的 Near/Far 只是示例值。实际配置应根据当前数据的 Scene Bounds 检查结果确定。
这种结构使 Coarse 与 Fine 两个网络都满足相同的 Field Interface:
但由于它们由两次独立的 field_factory() 调用创建,因此拥有不同的可训练参数。
后续可以将 Field 实现替换为更接近原始论文的 8 层、256 Channels Position Trunk,并加入中间的 Position Encoding Skip Connection 及 128 Channels Color Branch。
这种改变属于 Field Architecture Configuration,不需要重新设计 Sampler、Renderer 或 Training Loop。
因此,系统可以先使用较小的模型完成 Smoke Test,再在正式训练中切换到 Vanilla NeRF 参考架构。
需要注意,改变网络深度和宽度后,训练结果就不能再被直接归因于单一的 Sampling Strategy。若需要做严格消融,模型结构必须保持一致。

6.3 一次前向计算完成粗细两次渲染

现在开始组装最重要的部分:完整的 Coarse-to-Fine Forward Pass。
与 Ch.5 相比,这里不需要新的数学推导,但需要消除重复的 Field Query 与 Rendering 代码。
先定义一个统一的 Field Rendering Adapter。

实现|统一场渲染接口

对于任意一批 Rays,给定已排序的 Sample Depths 和与之对应的 Segment Boundaries,系统都应该能够得到 RGB Prediction 和 Rendering Weights。
这里的 volume_render_batch() 是对已有 Volume Renderer 的薄封装,约定返回:
背景颜色使用当前 Config 已确定的约定。
如果之前的 Renderer 只返回 RGB,那么这里应扩展它,使已有的 Rendering Weights 也能作为辅助输出返回。
不需要重新实现另一个 Alpha Compositing 算法。
现在观察 query_and_render() 的职责。
它并不知道当前输入来自 Coarse Sampling 还是 Fine Sampling。
它只要求:
  • Rays 处于正确的世界坐标系。
  • Sample Depths 已排序。
  • Edges 与 Samples 一一对应。
  • Field 能够预测 Density 和 Color。
  • Renderer 能够处理对应的非均匀 Segment Lengths。
因此,同一个函数可以同时服务于 Coarse 和 Fine Rendering。

实现|组合粗细两次渲染

接下来将 Stratified Sampling、Importance Sampling 和两次 Field Rendering 连接起来。
这段代码就是整个 Minimal NeRF 的核心 Forward Pipeline。
它没有引入新的空间表示、损失函数或者光传输模型,而是按照前面推导的依赖关系,将所有独立模块连接起来。
有三个细节值得特别检查。
第一,Fine Samples 必须由 Coarse Weights 产生。
如果 Sampling PDF 读取了错误的 Weights,或者误把已经归一化的 Sampling Probability 当成 Rendering Weights,系统的自适应采样行为就会发生改变。
第二,Fine Network 查询的是合并后的全部 Sample Positions。
若 Coarse Samples 为 个,新增 Fine Samples 为 个,则 Fine Field 需要查询 个位置。
因此,两级网络每条 Ray 的 Field Evaluation 数量为:
对于 64 个 Coarse Samples 和 128 个新增 Fine Samples,总共是 256 次 Field Evaluation,而不是 192 次。
第三,Sampling Proposal 不通过 Inverse-CDF Step 进行反向传播。
with torch.no_grad() 只包住 Fine Sampling,不包含 Fine Field 或 Fine Volume Rendering。
这意味着 Coarse Weights 决定 Fine Query Positions,但 Fine Loss 仍然能够正常更新 Fine Field 参数。
Coarse Network 则通过它自己的 RGB Loss 接受监督。
到这里,完整的 Forward Pass 已经具备。
但能够输出两幅 RGB Prediction,还不代表两个网络都能正确学习。
因此,下一步先验证 Backward。

6.4 构建可验证的训练循环

训练目标与 Ch.5 相同:
两项损失分别计算 Coarse/Fine Predictions 与 Ground Truth RGB 的误差。
对于随机选择的 条 Rays:
以下代码使用 F.mse_loss,它对 Batch 和 RGB Channels 同时求平均,因此与上面按 RGB 向量平方范数定义的损失相差固定比例。正式实现只需保持训练和报告采用同一约定。
首先构建 Optimizer:
由于 NeRFSystem 已经将 Coarse 和 Fine Fields 注册为子模块,因此 model.parameters() 包含两个网络的全部可训练参数。

测试|单步梯度检查

开始正式训练之前,先从训练集中选择一个 Ray Batch,执行一次 Forward 和 Backward。
此时还不急着执行 optimizer.step()。
先检查两个网络是否都得到了有限且非零的梯度。
这里验证的不是两个网络必须得到相同梯度。
恰恰相反,由于 Coarse 和 Fine Fields 查询不同的样本分布,并分别生成 RGB Predictions,它们通常会得到不同的更新信号。
测试真正关心的是:两套参数是否都参与了优化,以及完整计算图中是否出现了意外的梯度截断或数值异常。
通过检查后,才执行:

实现|从单步更新到多视图训练

完整训练循环继续使用 Ch.2 的 Random Ray Batching。
与 Ch.2 的 Naive NeRF 相比,训练循环几乎没有发生结构性变化。
输入仍然是一组 Training Rays 和 Ground Truth RGB;输出仍然是 RGB Loss;Optimizer 仍然使用 Autograd 计算出的梯度更新网络参数。
真正的变化发生在 render_nerf_batch() 内部。
这说明一个非常重要的系统设计原则:
只要 Field 和 Renderer 保持清晰、稳定的输入输出接口,更复杂的空间采样策略就可以在不重写外层训练系统的条件下逐步加入。
在进入完整训练之前,还应该重新执行 Ch.2 的 Few-ray Overfit Test。
如果加入 Hierarchical Sampling 后,系统反而无法拟合固定的少量 Rays,就需要优先检查 Coarse/Fine Tensor Shapes、Sampling Gradients、Depth Sorting 和 Renderer Integration,而不是直接增加训练步数。
只有通过这种局部验收后,才进行完整 Multi-view Training。

6.5 保存状态并保证实验可复现

现在,完整 NeRF 已经能够接受多视图训练并更新参数。
但这里还缺少一项独立的工程能力。
假设训练运行到某个 Step 后被中断。如果只保存当前模型的网络参数,虽然可以重新进行推理,却不一定能够继续原来的优化过程。
这是因为训练状态不仅包含 Field Parameters,还包含 Optimizer 内部的统计量、训练步数、随机采样状态以及实验配置。
因此,需要把一次训练明确地保存为一个可恢复的 Checkpoint。

实现|保存训练状态

首先将模型、Optimizer、Config 和 Step 保存到同一个文件。
这里最重要的是:
它保存 Coarse 与 Fine Networks 的全部参数。
由于两个 Field 都是 NeRFSystem 的子模块,一个 state_dict() 就能够覆盖整个场景表示。
而:
保存 Adam 等 Optimizer 的内部状态,使训练恢复时不必丢失历史优化信息。
step 用于确定恢复后的训练进度,config 则记录本次实验的网络与采样设置。
最后,PyTorch 和 CUDA Random States 用于尽可能恢复随机采样序列。
这一实现假设 Ray Sampling 主要使用 PyTorch 随机数。如果 Dataset Shuffle 或其他逻辑还依赖 Python、NumPy 或独立的 DataLoader Generator,则需要额外保存对应状态。
这些信息能够提高实验的可复现性,但不应将它理解为跨不同硬件和软件版本保证完全逐位相同。

实现|从 Checkpoint 恢复训练

恢复时,首先根据相同的模型配置构建 Coarse/Fine Networks 和 Optimizer,然后加载对应状态。
这里假设模型和 Optimizer 已按目标设备正确初始化;实际恢复 GPU 训练时,还应检查 Optimizer State Tensors 的设备位置。
另外,weights_only=True 是用于读取包含 Tensor 与基础 Python 容器的 Checkpoint 的较安全方式,仍应只加载可信来源的文件。
完成加载后,应该验证恢复出的模型在相同 Rays 和确定性采样下,能够重现保存前的输出。
这一步比简单打印“Checkpoint loaded”更有意义。
因为它直接检验:保存和恢复操作是否保持了场景函数的实际渲染行为。

6.6 分块渲染完整的新视角图像

训练后的 Radiance Field 已经保存在网络参数中。
现在,给定一个新的 Camera Pose,系统应当能够生成该视角的完整 RGB 图像。
这里不需要改变任何 NeRF 算法。
Ch.1 已经实现的 Camera Ray Generator 根据目标相机生成完整的 Rays:
这些 Rays 再交给训练完成的 render_nerf_batch()。
但直接将一整幅高分辨率图像中的所有 Rays 和 Samples 同时送入 MLP,可能占用大量显存。
因此,推理阶段引入一个简单的工程策略:
把完整图像的 Rays 划分成多个 Chunks,逐批渲染,再恢复原始像素排列。
这个操作不会改变 Field 本身,也不会引入新的采样数学。

实现|分块渲染

这里出现了两个新的工程约定。
第一:
将网络切换到 Evaluation Mode。
对于当前不包含 Dropout 或 Batch Normalization 的 NeRF MLP,eval() 本身不一定改变网络输出,但它仍然明确表达当前正在执行推理,而不是训练。
第二:
关闭训练阶段的随机扰动。
这样 Coarse Samples 使用确定性位置,Fine Samples 也使用确定性的 PDF Quantiles。
因此,同一个 Camera Pose 在相同模型与配置下,不会因为每次重新生成随机采样位置而产生不同 RGB。
随后,所有 Rays 按照图像原来的 Flatten Order 分批渲染,最后重新恢复成 [H,W,3]。
这意味着 chunk_size 只应控制一次处理多少 Rays,而不应该改变这些 Rays 对应的空间位置、Sample Distribution 或最终颜色定义。
完成推理后,系统已经能够输出一幅完整的 Novel-view RGB Image。
不过,一个工程上重要的问题仍然存在:
如果改变 Chunk Size,最终生成的图像是否保持一致?
这个问题将在下一节通过回归测试回答。

6.7 验证完整系统

现在,整个 NeRF Pipeline 已经具备训练、保存、恢复和推理能力。
但每个模块原先的单元测试,只能证明它们在独立运行时符合预期。
完整系统还需要通过 Integration Tests,验证模块组合后的行为。

测试 1|Tensor 形状与深度顺序

首先验证:
同时检查全部 Samples 均位于 Near/Far 区间内,合并后的 Fine Depths 单调递增,并且每条 Ray 的积分区间长度总和满足:
这可以及时发现 Sampler 和 Renderer 在深度顺序、区间边界方面的接口错误。

测试 2|渲染权重守恒

对于任意一条 Ray,Foreground Rendering Weights 与剩余 Background Transmittance 应满足:
这项测试已经在 Volume Rendering 中完成过,但现在必须重新检查非均匀 Fine Segments 与完整 Batched Renderer。
尤其要确认,Fine Sampling 的 PDF Normalization 没有错误地改变用于 RGB Compositing 的原始 Rendering Weights。

测试 3|粗细网络的梯度

通过一次 Forward/Backward,检查两个网络都能获得有限且非零的梯度。
如果只有 Fine Network 的参数更新,而 Coarse Network 没有获得梯度,就应该优先检查 Coarse Loss 是否真正包含在 Total Loss 中。
如果 Fine Network 没有梯度,则需要检查是否误把整个 Fine Pass 都放进了 torch.no_grad()。

测试 4|不同 Chunk Size 的结果一致性

使用同一个 Checkpoint、相同 Camera Rays 和确定性采样,分别采用不同 Chunk Sizes 进行推理。
理想情况下,两幅图像应在数值容差内一致。
如果改变 Chunk Size 后出现明显不同的颜色,通常意味着某个计算错误地依赖了 Batch Composition、随机采样状态或 Tensor Reshape Order。
因此,这项测试能够检查 Chunked Rendering 是否仅仅改变内存调度,而没有改变渲染结果。

测试 5|Checkpoint 保存与恢复的一致性

最后,保存模型后重新初始化同结构的 NeRFSystem,加载 Checkpoint,再用相同 Rays 渲染。
保存前与恢复后的预测应当在数值容差内一致。
如果两者差异明显,就需要检查 Coarse/Fine Parameters、模型配置、Device、Evaluation Mode,以及推理时是否仍然启用了随机采样。
这里需要特别区分:
Checkpoint Round-trip 检验的是场景函数的保存与恢复是否正确,不是检验三维重建本身是否正确。

测试|最终多视图冒烟测试

完成上述测试后,再运行一次完整的 Multi-view Training。
建议先使用较小的训练配置,确认 Loss、Gradient、Checkpoint 和 Validation Rendering 全部正常,然后再切换到正式的网络规模与训练预算。
Validation Views 只用于训练期间的诊断和模型选择;最终 Test Views 应保留到完整评估阶段,避免反复根据测试图像调整超参数。
完成后,系统至少应该能够稳定生成:
  • Coarse/Fine Training Loss Curves。
  • 可恢复的 Model Checkpoint。
  • 指定 Validation Camera 的 RGB Rendering。
  • 对应的 Config 与 Training Metadata。
这些输出共同证明完整 NeRF 工程已经具备基本可运行性和可复现性。

6.8 本章交付:可训练、可恢复的 NeRF

到这里,前面所有章节的关键机制已经组成一个完整的 Neural Radiance Field Learning System。
从多视图图像开始,训练系统能够随机选择 Camera Rays,通过 Stratified Sampling 获得 Coarse Sample Positions,使用 Positional-encoded MLP 预测 Density 与 Color,再利用 Rendering Weights 指导 Fine Sampling。
Coarse 和 Fine Networks 分别生成 RGB Predictions,并通过同一个 Differentiable Volume Renderer 接受图像重建监督。
训练完成后,整个场景表示可以保存为 Checkpoint。
恢复模型后,给定一个新的 Camera Pose,系统能够生成完整的 World-space Rays,并通过分块推理输出对应的 RGB 图像。
因此,这个连续三维场景已经具备了一套完整的输入、学习、保存和查询接口。
但还需要区分两件事:
系统正确运行,证明计算链路与工程接口已经完成。
系统真正学到了怎样的三维结构,则需要通过独立评估来判断。
训练 Loss 的下降不等于 Novel View Synthesis 的成功;某个 Validation View 看起来合理,也不能证明整个可观察空间都具有一致几何。
更重要的是,NeRF 的直接优化目标仍然是 RGB Reconstruction,而不是精确 Surface Reconstruction。
因此,最终需要重新回到 Prologue 提出的问题:
如果把相机移动到训练中没有出现过的位置,这个共享的 Neural Field 是否仍能生成正确图像?
此外,本篇从 Naive NeRF 到最终模型的训练仍留下另一个问题:即使 RGB 能够被正确拟合,预测的 Density Profile 也可能与真实空间结构不同。
现在,完整 NeRF 已经具备进一步研究这两个问题所需的全部工具。
下一章将使用 Held-out Cameras、RGB Error Maps、Depth、Opacity 和 Density Distributions,检验这个系统究竟学到了什么。
这就是 第 7 章「评估新视角合成与几何重建的局限」 要回答的问题。

第 7 章|评估新视角合成与几何重建的局限

从未见视角的 RGB 出发,再观察 Depth 与 Density,区分渲染质量和几何准确性。
从未见视角的 RGB 出发,再观察 Depth 与 Density,区分渲染质量和几何准确性。
 
上一章已经将所有关键模块组合成了一个完整的 Minimal NeRF。
从已知 Camera Poses 的多视图 RGB 图像开始,系统能够使用 Stratified Sampling 查询 Coarse Field,根据 Rendering Weights 追加 Fine Samples,再通过 Differentiable Volume Rendering 和 RGB Reconstruction Loss 优化两个 Neural Fields。
训练完成后,场景参数可以被保存为 Checkpoint。重新加载模型,只需要提供新的 Camera Pose,就能够生成对应的 RGB Image。
至此,整个训练与推理闭环已经成立。
但这还没有回答 Prologue 提出的核心问题:
一个能够拟合训练图像、并从新相机位置输出 RGB 的 Neural Field,是否真的学到了一个三维场景?
回答这个问题,需要区分两种能力。
第一种是重建已经观察过的图像。网络可能通过记忆训练观测获得较低的 Reconstruction Loss。
第二种是解释训练中没有出现过的相机位置。此时模型不能重新优化参数,只能依靠同一个 Radiance Field 生成新的视角。
后者才是 Novel View Synthesis 的直接目标。
而在新视角生成之外,还存在第三个问题:即使输出图像足够准确,学习到的 Density 是否真的对应场景的三维几何?
本章将从这三个不同层次评价完整的 NeRF。
首先,通过 Held-out Camera Views 验证 Novel View Synthesis 能力;随后检查连续相机运动中的视觉一致性;最后利用 Rendering Weights 观察 Depth、Opacity 和 Density,并通过一个解析实验解释为什么好的 RGB Reconstruction 不一定意味着准确的 Geometry Reconstruction。
完成本章后,将得到一套可复现的 NeRF Evaluation Pipeline,以及对这个场景表示实际能力和局限的明确认识。

7.1 训练图像不能证明新视角合成能力

先考虑一个已经训练完成的 NeRF。
如果把 Training Camera 的 Rays 重新输入模型,生成对应 RGB,并发现它们与训练图像非常接近,这说明网络能够解释训练时见过的观测。
但这样的结果不能直接证明三维场景已经被正确恢复。
原因在于,所有 Training Views 的 RGB 都参与过参数优化。
网络可能学习了一个足以解释这些观测的 Radiance Field,却仍然无法解释训练相机之间的其他位置。
因此,评价需要使用 Ch.1 已经建立的独立数据划分:
  • Training Views:用于优化场景参数。
  • Validation Views:用于训练过程中的模型选择与配置调整。
  • Test Views:用于最终确定模型后的独立评估。
这里最重要的约束不是文件名不同,而是 Test RGB 不能参与训练和模型选择。
如果反复观察 Test PSNR 并根据结果调整 Learning Rate、Sampling Count 或训练步数,那么 Test Set 实际上已经参与了开发决策,不能继续被当作完全独立的最终评估。
对于本篇选用的 Lego Synthetic Scene,测试视角由已知 Camera Poses 定义,因此可以直接生成对应 Rays。
值得注意的是,测试相机位置没有出现在训练中,并不意味着它位于任意未知的三维空间。
对于围绕同一物体拍摄的数据集,位于训练相机轨迹附近的 Held-out Views 通常拥有较充分的几何约束;而远离训练视角、需要观察大量未覆盖区域的相机位置,则属于更困难的 Extrapolation。
因此,本章首先使用标准 Held-out Views 评价 Novel View Synthesis,再单独讨论超出训练观察范围时的局限。

7.2 在未见视角评估 RGB 重建质量

现在使用 Ch.6 已经保存的 Checkpoint,加载最终 Fine Network,并将 Test Camera Poses 输入完整 Renderer。
整个过程不需要任何参数更新。
对于一个测试相机,首先生成完整的 World-space Rays:
随后使用训练后的 Field 预测对应图像:
其中 是测试相机参数, 则是训练完成后固定的场景参数。
因此,与训练阶段不同,Test RGB 只在渲染结束后才用于计算评价指标。

评估|量化 RGB 重建质量

最直接的评价指标是 Mean Squared Error:
对于 RGB 归一化到 的图像,可以计算:
PSNR 越高,说明预测图像与 Ground Truth 的像素平方误差越小。
但 PSNR 并不直接衡量几何准确性,也不能完整反映人类对纹理、轮廓和视觉质量的感知。
因此,本篇同时保留 RGB Error Map 进行定性分析。
这里的 error_map 对每个像素的三个颜色通道计算平均绝对误差:
因此它的 Shape 为 [H,W]。
与单个 PSNR 数字相比,Error Map 能够显示误差集中在哪些位置。
例如,物体边缘误差较大,可能与几何位置、遮挡变化或采样分辨率有关;高频纹理区域的误差,则可能与表示能力或训练预算有关。
但 Error Map 本身也不能直接判断唯一原因。它应作为后续定位问题的观察证据,而不是自动诊断结果。
正式评估时,每张 Test Image 独立计算 PSNR,最后报告整个 Test Split 的平均值。同时保存每个测试相机的单独指标,避免一个平均数掩盖不同观察方向上的显著差异。
所有图像必须保持与训练相同的颜色空间、背景合成与分辨率约定。对于本篇采用白色背景合成的 Lego 数据,不能在评估时改成黑色背景。
最终至少保存三种结果:
Ground Truth / Rendered RGB / Absolute Error Map
这将形成完整 NeRF 的第一组真正任务评估结果。

7.3 相机移动时,场景是否保持一致?

Held-out View Evaluation 已经能够检验多个未参与训练的相机位置。
但单独查看几张静态图像,仍然可能遗漏一个重要现象:
当相机连续移动时,场景的几何、遮挡和外观是否保持稳定?
NeRF 定义的是一个连续 Radiance Field。
给定不同相机位置,所有 Rays 都在查询同一套场景参数。因此,理想情况下,物体的位置、轮廓和局部结构应该随相机运动形成一致的视差变化。
为了观察这种行为,可以沿着训练相机覆盖的区域构建一条连续 Camera Trajectory。
相机平移使用连续插值,旋转则使用合适的旋转插值方式,避免直接对 Rotation Matrix 的元素进行线性插值后产生非法旋转。
对轨迹上的每个 Camera Pose,执行相同的确定性渲染:
随后将所有帧组合成一个短视频。
这个实验最值得观察的不是某一帧是否足够清晰,而是相机运动过程中是否出现:
  • 物体轮廓跳动或不合理变形。
  • 局部纹理随着观察方向发生异常漂移。
  • 前后遮挡关系出现不稳定变化。
  • 细结构突然消失或重新出现。
  • 高光与视角变化表现出明显异常。
这些现象可能与 Geometry、View-dependent Appearance、相机误差或未覆盖的观察区域有关。
需要注意,连续视频看起来平滑,仍然不能单独证明几何准确。方向相关 Color Function 可能在一定视角范围内掩盖部分空间结构错误。
此外,如果生成的 Camera Trajectory 没有对应 Ground Truth,就只能进行定性检查,不能为这些帧凭空计算 PSNR。
因此,本章采用两种互补方式:
Held-out Views 提供具有 GT 的定量证据。
Camera Fly-through 提供连续视角变化下的定性诊断。
到这里,NeRF 的 Novel View Synthesis 能力已经可以得到相对完整的评价。
但这些实验观察到的仍然主要是二维渲染结果。
为了理解同一个 Radiance Field 内部究竟形成了什么空间结构,需要进一步观察 Density 和 Rendering Weights。

7.4 观察密度、不透明度与深度

NeRF 与直接的 Image-to-Image Prediction 不同。
它并不是直接生成整张目标图像,而是在三维空间中定义一个能够查询 Density 与 Direction-dependent Color 的函数:
每条 Ray 的最终颜色由多个 Sample Positions 共同贡献。
因此,训练完成后,除了 RGB Image,还可以从 Volume Renderer 的中间结果中获得关于空间结构的信息。
这也是此前保留 Rendering Weights 输出的重要原因。

从渲染权重计算累计不透明度

首先观察:
这些权重表示 Ray 在对应 Segment 中发生终止的概率质量。
因此,沿整条 Ray 的 Foreground Weights 求和,可以得到:
根据上一篇已经证明的权重守恒关系:
这个量称为 Accumulated Opacity。
当 接近零时,Ray 的 Foreground Termination Mass 很小,最终图像可能主要由背景贡献。
当 接近一时,Ray 的前景终止质量较高。
对于具有明确前景背景的 Synthetic Object Scene,可以把 Opacity Map 作为模型空间占据结构的一种诊断。
但 Opacity 不等于真实的 Binary Occupancy,也不直接给出物体表面的位置。
为此,还需要观察 Depth。

从渲染权重计算期望深度

由于每个 Sample 都具有对应的 Ray Depth ,可以通过 Rendering Weights 计算深度统计量:
然而,Foreground Weights 的总和通常不保证等于 1。
对于几乎完全命中背景的 Ray:
这时, 也可能接近零,但这并不意味着场景表面位于距离 Camera 非常近的位置。
因此,如果希望观察前景条件下的深度分布,可以在 Foreground Opacity 足够大时使用:
它表示:在 Ray 于当前积分范围内发生前景终止这一条件下,终止深度的加权期望。
对于 Background Rays,则应直接标记为无效,而不是将其错误解释为零深度。

实现|深度与不透明度诊断

这里不需要重新实现 Renderer,只需要让 Ch.6 的 Fine Pass 额外提供:
然后添加一个独立诊断函数:
这段代码所做的事情非常有限。
opacity 对应 Foreground Rendering Weights 的总和;weighted_depth 对应权重与 Sample Depth 的加权和;两者相除则得到前景条件下的 Expected Depth。
min_opacity 只是用于可视化的有效性阈值,不是 NeRF 的物理常数,也不是预测出来的真实 Surface Threshold。正式实验应记录这个阈值。
输出的 Depth 仍然沿着单位 Camera Ray 的方向测量距离,不一定等于 Camera-space Z-depth。
这样,模型就能够为指定视角同时输出:
Rendered RGB / Accumulated Opacity / Conditional Depth
不过,Expected Depth 只是一个统计量。
它并不保证对应某个真实表面。

分析|单条射线的渲染贡献

为了进一步理解这个区别,可以选择一条穿过物体前景的 Ray,并把其 Rendering Weights 按 Depth 绘制出来。
如果大部分 Weight 都集中在一个狭窄的深度附近,那么 Expected Depth 通常接近该分布的主要峰值。
但如果 Weight Distribution 很宽,说明当前 Ray 的有效贡献分散在较大的深度范围内。
如果同时存在两个明显的高权重峰,Expected Depth 甚至可能落在两个峰之间,而那个位置本身并没有显著 Density。
因此,不能仅凭一张颜色平滑的 Depth Map,就推断整个场景存在准确、清晰的三维表面。
此外,还可以在选定的二维世界空间切片上查询:
得到 Density Slice。
这种可视化能够显示高密度区域的大致空间分布,但只有在具备可靠几何参考时,才能进一步判断它是否真的对应物体表面。
对于本篇 Lego RGB 数据,如果没有额外准备 Ground Truth Depth 或 Mesh,就不能将模型自身产生的 Density Slice 当作真实几何进行定量评价。
这些可视化至此揭示了一个重要事实:
NeRF 不只是学习颜色,它确实形成了一个具有空间结构的 Density Field;但这个 Field 的几何语义仍然需要被谨慎解释。
接下来,通过一个完全可控的实验,进一步理解其中的原因。

7.5 不同的几何能否渲染相同图像?

回到本篇从 Naive NeRF 到完整 Coarse/Fine NeRF 的训练过程。
共享 Neural Field 已经能够利用多视图 RGB Loss 更新参数,并对未见相机位置进行渲染。但这些实验首先衡量的是图像误差,并没有直接监督每个空间位置的真实 Density。
Positional Encoding、更多观察视角和 Hierarchical Sampling 改善了外观拟合与空间查询,却没有改变这一监督目标的本质。
但这些机制仍然没有改变最基本的监督目标:
优化器最终比较的是渲染后的 RGB,并没有直接要求每个空间位置的 Density 必须等于真实值。
这是否意味着,不同的 Density Distributions 可能产生同一个像素颜色?
可以通过一个简单的解析实验回答。

实验|构造几何歧义对照

考虑一条 Ray,沿线的前景 Color 为常数:
背景颜色固定为:
记整条 Ray 的 Optical Depth 为:
在这一特殊情况下,连续 Volume Rendering Equation 可以简化为:
这意味着,最终 RGB 只取决于前景颜色、背景颜色和总 Optical Depth。
Density 在 Ray 上的具体位置,并没有出现在最终表达式中。
因此,可以构造两个 Density Fields,使它们具有相同的 Optical Depth,却把高密度区域放在不同位置。
例如,将 划分为四个宽度为 的 Segments:
这两个场的总 Optical Depth 都为:
但 Field A 的高 Density 位于较近的 Segment,而 Field B 的高 Density 位于更远的 Segment。
下面使用上一篇已经实现的 Alpha Compositing 公式进行数值验证。
这里的 alpha、transmittance 和 weights 完全沿用 Volume Rendering 已有的数学定义,只是为了构造一个不依赖 MLP 训练的可控测试。
两个 Fields 的 Rendered RGB 应完全一致,Opacity 也相同。
但它们的 Conditional Expected Depth 不同。
因为按照 Segment Midpoint 进行深度统计,Field A 的主要 Weight 位于 ,Field B 的主要 Weight 位于 。
这说明:同一个像素颜色可以对应不同的 Density 位置。
并且,这不是网络训练失败或 Optimizer 不充分造成的现象,而是当前受控问题本身存在不同的有效解释。
需要强调,这个实验只针对单条 Ray 和常颜色的特殊情况。
多视图相机几何会为同一个 Density Field 提供额外约束,使许多单视角下合理的分布无法同时解释全部图像。
但它并不能保证在有限视角、有限分辨率和复杂材质条件下,真实三维结构一定具有唯一解。
因此:
RGB Reconstruction Consistency 是重要的三维学习信号,但并不是 Geometry Uniqueness 的证明。
这也解释了为什么一个 NeRF 可能生成视觉上合理的新视角,却仍然具有较厚的 Density 分布、漂浮结构或不准确的可提取表面。

7.6 NeRF 真正学到了什么?

现在可以重新评价前面得到的全部结果。
Training Views 验证了共享 Neural Field 对已观测图像的拟合能力。
Held-out Views 检验了它在未参与训练的相机位置上,是否能够从同一个场景表示生成正确图像。
Camera Fly-through 则补充观察了相机连续运动时的纹理、遮挡与外观稳定性。
与此同时,Depth、Opacity 和 Rendering Weights 让模型内部的部分空间结构变得可观察。
因此,完整 Minimal NeRF 的能力可以表述为:
给定一个静态场景的多视图 RGB 图像和准确的相机参数,系统可以通过可微体渲染优化一个共享的连续 Radiance Field,并利用它生成训练中未直接观察过的新相机视角。
但这项能力有明确的适用边界。
首先,Vanilla NeRF 是 Per-scene Optimization。训练完成的 MLP Parameters 表示当前场景,而不是一个能够直接泛化到任意新场景的通用图像重建网络。
其次,场景被假定为静态,并在相对稳定的采集光照条件下进行学习。Direction-dependent Color 能够拟合部分视角相关外观,但它没有显式恢复完整的材质 BRDF 或可独立控制的光照。
再次,Novel View Synthesis 的可靠性依赖训练相机覆盖、Camera Pose Accuracy 和图像质量。对于大面积未观测的区域,模型没有足够的观测信息来保证真实重建。
最后,Density 是用于 Volume Rendering 的连续空间表示,而不是具有明确 Surface Distance Semantics 的几何函数。
因此,准确的 RGB Rendering 不意味着可以直接从任意 Density Threshold 中提取准确 Mesh。
如果要进一步证明 Geometry Accuracy,需要额外引入 Ground Truth Depth、Mesh、Surface Normals 或其他独立几何参照,并设计对应指标。
这些要求并不意味着 NeRF 没有学到几何。
恰恰相反,如果没有空间相关的 Density 与可见性分布,模型通常难以通过同一个 Field 解释多视图之间的视差和遮挡变化。
更准确的结论是:
NeRF 通过渲染监督学习了能够支持新视角合成的三维结构,但原始训练目标并不保证这一结构等价于真实场景的精确表面。

7.7 回顾从可微渲染到场景学习

回顾整个构建过程,最初的问题来自 Volume Rendering 的最后一章。
当时,一个小型 Neural Field 已经能够根据 RGB Loss 调整 Density 和 Color,却没有证明它能够在更多相机视角上形成一致的场景表示。
本篇首先将这一问题转化为 Novel View Synthesis。
随后,多视图 Camera Rays 为共享 Field 提供了跨视角监督;Positional Encoding 改善高频函数的学习;Stratified Sampling 让训练过程能够探索不同的深度位置;Hierarchical Sampling 则利用 Rendering Weights,把额外查询分配到当前场景估计中更重要的区域。
最终,Coarse/Fine Networks、Differentiable Volume Rendering 与 RGB Loss 被组装成一套可以训练、保存和重新加载的完整系统。
到这里,三维场景已经不再只能依赖手工定义的解析函数。
一个连续 Radiance Field 可以从多视图图像中被学习,并从新的相机位置被重新观察。
但在回答了“能否学习一个可渲染的三维场景”之后,仍然留下了两个重要问题。
第一个问题关于几何:如果训练目标主要是图像一致性,怎样让场景表示具有更清晰、更可靠的三维表面语义?
第二个问题关于效率:如果每个像素都需要执行大量 MLP Queries,怎样让这个世界变得足够高效,可以支持更大范围的场景和更流畅的观察?
这两个问题,将成为后续空间表示演化的起点。

结语|从 NeRF 迈向更高效的空间表示

已构建的系统与能力

从上一篇 Volume Rendering 开始,整个系统经历了两次重要的能力变化。
第一次是 Forward Rendering。
给定一个三维 Density/Color Field,Camera Rays 和 Volume Rendering Equation 能够将空间信息转换成二维图像。
随后,Differentiable Rendering 使这个过程具有反向优化能力:图像误差不再停留在二维像素上,而可以通过 Transmittance、Opacity 和 Rendering Weights,更新场景参数。
第二次是 Learning a Radiance Field。
场景不再由手工设置的 Gaussian Parameters 定义,而是由一个共享的 Coordinate MLP 表示。
多视图图像为这个函数提供监督;Position Encoding 和 Hierarchical Sampling 改善它的学习与空间查询;最终训练得到的网络参数,可以用于生成未观测视角的图像。
整个过程可以概括为:
这是一个重要的里程碑。
机器不再只能渲染一个已经给定的三维场景,而是能够从图像观测中学习一个可查询的连续空间表示。
但这个表示仍然不是整个 Spatial Intelligence 的终点。

当图像质量不足以衡量几何

Ch.7 已经展示,好的 RGB Reconstruction 并不唯一确定真实三维 Geometry。
NeRF 的 Density 控制体渲染中的局部终止强度,而不是直接表示到最近表面的距离。
因此,即使学习到的 Density 在某些物体边界附近形成高密度结构,也不能直接假设某个固定 Density Isosurface 就是真实表面。
如果目标进一步从 Novel View Synthesis 转向 Accurate Surface Reconstruction,就需要一个具有更明确几何语义的场景表示。
Signed Distance Field(SDF)提供了这样的选择。
它不再让网络自由预测用于渲染的 Density,而是学习一个连续标量函数:
物体表面由零等值集合定义:
如果进一步满足距离场性质,函数值还能描述空间位置到最近表面的带符号距离,其梯度也与局部 Surface Normal 相关。
这使 Geometry 不再只是由体渲染权重间接解释,而具有更明确的 Surface Representation。
但引入 SDF 又会带来新的问题:怎样求出 Camera Ray 与隐式曲面的交点?怎样对这个交点进行可微优化?如果使用硬 First-hit Surface Rendering,Miss Rays 和 Visibility Discontinuities 又该如何处理?
VolSDF 和 NeuS 等工作,正是在这样的背景下研究如何将具有几何语义的 SDF 与可微 Volume Rendering 结合起来。
这构成后续研究的一条重要几何路线。

当连续场的渲染成本过高

另一个问题来自计算效率。
NeRF 用 MLP 的共享参数表示整个连续场景,避免了高分辨率 Dense Voxel Grid 随空间分辨率立方增长的存储成本。
但代价是每个空间查询都需要执行 Neural Network Forward Evaluation。
在当前 Coarse/Fine Pipeline 中,每条 Ray 通常需要大量 Field Queries。
如果 Coarse Pass 使用 64 个 Samples,Fine Pass 在合并后的 192 个位置重新查询,那么每个像素对应约 256 次 Field Evaluations。
对于一张包含大量像素的图像,这意味着重复执行大量 MLP 计算。
即使经过 Hierarchical Sampling,系统仍然需要持续进行 Coarse Queries,并反复评估 Fine Network。
因此,下一个问题不只是怎样调整 Sampling Distribution,而是:
空间信息是否必须全部存储在一个需要反复执行的深层 MLP 中?
如果一个场景的细节主要分布在局部区域,是否可以把更多信息直接存储在空间数据结构里,让查询变得更局部、更高效?
这引出了多种表示路线。
Mip-NeRF 重新考虑 Pixel Footprint,通过具有空间范围的查询改善尺度相关的 Aliasing。
Plenoxels 则使用 Sparse Voxel Grid 和 Spherical Harmonics 表示 Density 与 View-dependent Appearance,说明 Radiance Field 不一定需要由大型 Neural Network 参数化。
Instant-NGP 进一步使用 Multi-resolution Hash Encoding,让可训练的局部空间特征与轻量 MLP 共同表示场景。
这些方法虽然采用不同的参数化策略,但都在重新思考同一个问题:
场景信息应该存储在哪里,怎样以更少的计算获得足够准确的空间查询?
沿着这条路线继续发展,显式空间基元提供了另一种选择。
3D Gaussian Splatting 不再要求为每个像素沿 Ray 密集查询一个 Coordinate MLP,而是将场景表示为一组三维 Gaussian Primitives。
每个 Gaussian 可以显式存储位置、形状、Opacity 和 Appearance 信息,并通过投影到屏幕空间的 Splatting 与 Alpha Compositing 生成图像。
这意味着改变的不只是 Scene Representation,还包括主要的 Rendering Algorithm。
从 NeRF 到 3DGS,新的问题将不再只是如何拟合一个连续空间函数,而是:
如何选择适合高效存储、优化和实时渲染的三维基元,让一个学到的世界更容易被观察?

迈向空间智能

完成 Minimal NeRF,并不意味着空间智能系统已经构建完成。
当前模型能够学习静态场景的 Radiance Field,并通过已知 Camera Pose 生成新视角图像。
但它还没有直接获得对物体类别、空间关系或交互行为的理解。
它也不能仅凭这个静态 Field 预测物体被推动后如何运动,或者在接收到语言指令后,自动修改场景并保持合理的动态行为。
这些能力需要新的表示、监督信号和推理机制。
在本系列的后续阶段,三维世界将继续沿着空间表示与学习能力两个方向演化。
一方面,更高效的表示和渲染方法,将使场景能够覆盖更复杂的空间范围,并逐步接近可交互的运行要求。
另一方面,随着大规模数据、语义信息、语言和动态控制机制的引入,场景表示将不再只回答“从这个位置看过去是什么颜色”,还需要回答“空间中有什么”“这些对象具有怎样的关系”,以及“某个动作发生后,场景会如何变化”。
但这些问题必须建立在可靠的空间表示与渲染基础之上。
本篇完成的 Minimal NeRF,正是这个基础中的一个重要部分。
从最初的一条 Camera Ray,到可以优化的 Volume Renderer,再到能够通过多视图图像监督学习的 Neural Radiance Field,整个系统已经完成了从 Forward Rendering 到 Inverse Scene Learning 的跨越。
三维世界已经能够从观测中被学习,并从新的位置被重新观察。
接下来的问题,是如何让这个世界变得更高效、更准确,并最终从一个可以被观察的场景,逐步发展为能够被理解、生成和交互的空间表示。
 
全部文章搜索Powered by NotionNext 4.11.0阅读配色:深色