🗒️Neural Radiance Fields
type
status
date
slug
summary
tags
category
icon
password
NeRF
论文地址:
NeRF(Neural Radiance Fields)是一种用于3D场景重建和渲染的深度学习技术。它通过神经网络来学习场景的连续体积表示,能够从稀疏的图片集合中重建出高质量的3D场景,并从新的视角生成逼真的图像。
NeRF的核心思想是利用一个小型的神经网络来建模场景中每个点的颜色和密度。网络接受一个3D位置和一个视角方向作为输入,并输出该位置的颜色和体积密度。通过这种方式,NeRF能够学习到一个连续的、高度详细的场景表示,包括复杂的光照效果和半透明材质。
在渲染过程中,NeRF使用体积渲染技术来累积沿着从摄像机出发穿过场景的光线的贡献。这个过程涉及到对每条光线上多个点的神经网络评估,然后根据每个点的颜色和密度,使用数值积分来估计最终像素的颜色。
NeRF的创新之处在于其能够产生极其细致和逼真的渲染效果,即使是从非常有限的视角信息出发。然而,这种方法的主要缺点是计算成本高,尤其是在渲染时,因为需要对每一帧进行大量的网络评估和积分计算。尽管如此,NeRF及其变体在计算机视觉和图形学领域引起了极大的关注,为3D重建和虚拟现实等应用开辟了新的可能性。
三维重建
以三维重建作为例子:
如图上所示是图形学渲染的大致流程,存储的Buffers经过Render合成最终的图像。而三维重建就是逆向渲染,反向得到原来的模型。因此三维重建内的工作就是Inverse Rendering。
Ray marching
因为NeRF大致用了体积雾的渲染方式,所以有必要学习下体积雾。常用的一种体积雾渲染方式也是通过Ray marching算法来实现的。
以相机为起始点,向每个像素发射射线,设定雾的起始距离还有步进次数,在步进的过程中进行颜色混合,最终就能达到体积雾的效果。
GLSL代码如下:
至于高度和噪声相关的,后面有时间再写,重点不是这个。
原理
刚刚可以看到,重点是Ray marching算法。因为采用的是这个算法,所以生成出来的模型是有些雾蒙蒙的感觉的。
废话不说,一个大致的流程是这样的:
可以理解为:
我们使用一个NeRF神经网络采取体积雾的渲染方式,通过已知视角的图片进行训练,然后输入其他相机视角的参数,从而预测出未出现视角的图片。
此时三维模型的信息就存储在了NeRF神经网络中。这是一种“隐式”的表示方法,而不是点云、体素、网格这种“显式”的表示方式。
因此就有了以下大致的流程。
- 用存体素信息:
- 然后用体素渲染方程获得生成视角图片:光线采样+积分
- 最后与原视角图片计算损失更新网络
数据收集
需要拍摄一组场景的照片,这些照片提供了场景从不同角度看的样子。
5D Input
NeRF的输入是一个5D向量,其中是相机的位置,是相机方向的球坐标表示(方位角和俯仰角,其实就是欧拉角)。
注意的是:
- 是点在平面上的投影与原点的连线和轴正方向所称的夹角,其实就是极坐标的,因此取值范围是
- 是点与原点所称连线和轴正半轴所称的夹角,所以取值范围
- 实际上在网络上会将,以表示射线方向
4D Output
NeRF的输出是一个4D向量,其中是自发光颜色,是不透明度(不用或者表示的主要原因是符号问题,因为的计算是一种函数)。
射线方向上对采样渲染点进行积分,在第一次出现波峰对该像素点的着色影响最大。
位置编码(Position Encoding)
NeRF中直接采用频率变换来做位置编码。为何要构造位置编码?为了避免空间相邻采样点在MLP表示中的过平滑问题。比如位置(237, 332, 198)和位置(237,332,199)这两个点作为MLP的输入,MLP可能对个位不够敏感,导致输出过平滑的问题。例如:
没有进行位置编码会导致纹理相近区域的细节会丢失。
位置编码公式为::
比如两个位置相近,分别是,经过编码后,。差距就一目了然了,这里512是频率,如同公示的。
另外可以看看如下图看看差距:
网络
输入:
- 输入到这个网络的是一个包含位置的三维坐标和方向的二维视角信息。
- 其中,位置信息用于确定体积元素(voxel)在空间中的位置,而方向信息用于模拟相机视角。
输出:
- 网络的输出是颜色值RGB和体积密度(sigma)。颜色值用于渲染图片,而密度值则用于确定体积元素的不透明度。
网络结构:
- 网络由多个全连接层(或称为密集层)组成,通常每一层都有256个神经元。
- 在网络的最后,有一个输出层,负责输出颜色值和密度值。
中间激活函数:
- 黑色箭头采用ReLU激活函数,因为它可以帮助网络学习非线性映射,同时避免梯度消失的问题。橙色箭头不采用激活函数。虚线采用Sigmoid激活函数。
特殊处理:
- 在这个网络中,位置和方向信息首先被转换到一个合适的维度,然后被合并(concatenate)起来作为网络的输入。
- 位置信息和视角信息在进入网络之前可能会经过一些预处理,比如位置编码或视角编码,来帮助网络更好地捕捉细节。
体素渲染
通过网络得到最终的4D向量后,需要用它进行最后的体素渲染,使用如下的体素渲染方程:
这个公式通过积分计算了从光线进入场景到离开时经历的所有颜色的加权累积,其中权重是由场景的透明度和体积密度决定的。
具体符号的定义:
- 是沿着光线累积的颜色,可以理解为最终相机捕捉到的光线颜色。
- 和是光线路径上的起始点和终止点
- 是表示光线起点到点之间的透明度累积,也叫做透射率(transmittance)。它决定了到达之前有多少光被场景的其他点吸收。
- 表示光线路径上的体积密度,它决定了该点吸收和散射光线的强度
- 即为
- 计算了从起点到终点的累积密度,确定透射率
如图所示是光线方程的形象图:
注意:
- 代表颜色值与采样位置和观测角度有关
- 代表不透明度仅和采样位置有关
因为计算机只能计算离散数据,所以做如下近似。
令,则根据黎曼积分的求和公式可以得出:
其中叫做采样点间距,有:
多层级体素采样(Hierarchical volume sampling)
接下来就是采样问题,因为实际上NeRF的渲染过程计算量很大,每条射线都要采样很多点。但是想一下其实射线的绝大部分区域都是空区域或者被遮挡的区域,这对最终的颜色的贡献是很小的。因此论文采用了Coarse-to-Fine的形式,同时优化Coarse网络和Fine网络。
对于Coarse网络,可以采样较为稀疏的个点,并将前面所述的离散求和重新表述:
接着对归一化:
这里的其实就是沿着射线的PDF,如图所示,可以很清楚的看到射线在物体上的分布情况:
接下来我们通过得到的PDF来采样个点,并用这个个点和个点一起计算Fine网络的渲染结果:
L2损失函数
我们的损失函数为coarse网络损失函数和fine网络的损失函数平方和:
其中是每batch的射线集合,和是真实值,coarse网络预测值和fine网络预测值。因此最终的渲染结果RGB来自。