基本概念

相关链接

论文地址

基础版NeRF实现(PyTorch)

基础版NeRF实现(TensonFlow)

广义理解

NeRF通过神经网络(MLP)来隐式的存储3D信息。

  • 显式的3D信息:有明确的x,y,z值。
  • 隐式的3D信息:无明确的x,y,z的值,只能输出指定角度的2D图片。

也因此,模型并不具备泛化能力,一个模型只能存储一个3D信息。

模型结构

论文中模型的输入是一个5D的向量(x,y,z,theta,phi),分别是坐标值、方位角、仰角。输出是4D向量,分别是密度和颜色(RGB)。模型的结构是一个8层的MLP

NeRF的模型结构比较简单,重点在于是前处理和后处理。对于输入,需要有一个图片转5D的前处理;对于输出,需要有一个4D转图片的后处理。

实际上,输入的5D向量是粒子的空间位姿,输出的4D向量是粒子对应的颜色以及密度。

真实场景及相机模型

真实场景

现实生活中有多个光源,同时会有物体的折射和反射。

相机模型

相机模型连接了3D世界与2D图片,主要分为四个坐标系:

  • 世界坐标系:现实世界的三维坐标系,相当于地球的经纬度。
  • 相机坐标系:以相机镜头中心为原点的三维坐标系,通过相机的位置+朝向将世界坐标转换过来。
  • 归一化相机坐标系:把相机前方的空间压缩成一个固定边长的立方体(一般为2),统一不同距离的物体缩放比例。
  • 像素坐标系:最终照片上的像素网格位置

体渲染

定义

  • 属于渲染技术的分支
  • 目的是解决云/烟/果冻等非刚性物体的渲染建模
  • 将物质抽象成一团飘忽不定的粒子群
  • 光线在穿过时,是光子在跟粒子发生碰撞的过程

光子与粒子发生作用的过程

  • 吸收:光子被粒子吸收
  • 放射:粒子本身发光
  • 外射光:光子在冲击后,被弹射
  • 内射光:其他方向弹射来的粒子。

NeRF假设

  • 物体是一团自发光的粒子
  • 粒子有密度和颜色
  • 外射光和内射光抵消
  • 多个粒子被渲染成指定角度的图片

模型的输入输出

模型的输入:将物体进行稀疏表示的单个粒子位姿

模型的输出:该粒子密度和颜色

粒子的采集——光线原理

射线推导像素点

对于空间中的一个发光粒子:

  • 空间坐标(x, y, z)
  • 发射的光线通过相机模型
  • 成为图片上的像素坐标(u, v)
  • 粒子颜色即为像素颜色

其中(u, v)(x, y, z)的公式如下:

转换公式

其中R为旋转矩阵(3x3)T为平移向量(3x1)

反之,对于图片上的某一个像素(u, v)的颜色。可以看作是沿着某一条射线上的无数个发光点的“和”,利用相机模型,反推射线,那么这个涉嫌表示为:
$$
r(t)=o+td
$$
其中o为射线原点,d为方向,t为距离,使用极坐标的方法表示。理论上来讲,t的取值范围为$(0,+\infty)$。

对于一张大小为(H, W)的图片而言,其射线数量为$H\times W$。

像素点推导射线

粒子的采集——光线原理

由像素点$P(u, v)$反推射线基本过程如下。

像素平面$\rightarrow$物理成像平面:
$$
(x_n,y_n)=(-(u-\frac{w}{2}),v-\frac{h}{2})=(\frac{w}{2}-u,v-\frac{h}{2})
$$
物理成像平面$\rightarrow$相机坐标系:
$$
(x_c,y_c,z_c)=(x_n,y_n,-f)
$$
其中$f$为相机焦距。

归一化:
$$
(x_c,y_c,z_c)=(\frac{x_c}{f},\frac{y_c}{f},-1)
$$
相机坐标系$\rightarrow$世界坐标系:
$$
(x_w,y_w,z_w)=c2w\times(x_c,y_c,z_c)
$$

代码

1
2
3
4
5
6
7
8
9
10
11
# Ray helpers
def get_rays(H, W, K, c2w):
i, j = torch.meshgrid(torch.linspace(0, W-1, W), torch.linspace(0, H-1, H)) # pytorch's meshgrid has indexing='ij'
i = i.t()
j = j.t()
dirs = torch.stack([(i-K[0][2])/K[0][0], -(j-K[1][2])/K[1][1], -torch.ones_like(i)], -1)
# Rotate ray directions from camera frame to the world frame
rays_d = torch.sum(dirs[..., np.newaxis, :] * c2w[:3,:3], -1) # dot product, equals to: [c2w.dot(dir) for dir in dirs]
# Translate camera frame's origin to the world frame. It is the origin of all rays.
rays_o = c2w[:3,-1].expand(rays_d.shape)
return rays_o, rays_d

这个方法一共有四个参数,HW表示图像的大小,K表示相机的内参,c2w表示旋转矩阵和平移向量。