从相机出发的光线,是一条射线,可以表示成:
在nerf中,需要随机选取一部分从相机发出并且穿过像素的光线进行渲染。
因此,利用相机的内参、外参,获取采样光线的流程为:
利用内外参矩阵,首先计算像片上每个像素点对应的世界坐标Pi
利用外参矩阵,计算相机中心的世界坐标Pcam
,即光线起点center
求像素世界点到相机世界点的向量Pi-Pcam
,经过归一化得到该相机穿过该像素点的光线朝向ray_unit
在像片上的H*W
个像素中,随机抽取rays_num
个,穿过这些像素的光线即这张像片的采样光线
至此就确定了光线的起点和方向。下面看光线上采样点的位置,也就是t该如何确定。
首先确定采样范围,即确定near和far平面。
一般将场景放在一个球体内。球体的球心即场景的中心,也就是世界坐标系的原点。球体的半径即场景的半径。
注意,一般默认这个球体的半径为1,也就是说要把主要场景缩放到球体之内。需要更改json文件中的场景包围盒、相机的世界坐标(中心化、归一化)。
将相机发出的光线和这个球体求交,从而获得近点、远点两个交点,在这之间的部分按照object采样,之外的部分按照background采样。
遵循的是nerf++的策略,前景和背景分别用一个MLP,从而把无限远处的采样点反球面参数化到有限空间。如果是提取SDF,前景和背景分别采用表面渲染和体积渲染;如果不需要,则均进行体积渲染。
然后由不同采样方式获取采样点的t。
之后计算采样点世界坐标,后续就将其编码,再输入MLP。
alpha:当前粒子的透明度
sdf
计算density
计算visibility: 当前粒子的可见性,就是光线穿过之前的粒子到达该粒子的概率(透过率)
weight: 当前粒子的权重,就是当前粒子的颜色能够多大程度地决定最终像素的颜色
opacity: 最终像素的不透明度,就是采样光线上所有粒子的权重之和
nerf只能对某一个场景进行过拟合,针对不同场景只能分别训练。一般把全部数据作为训练集,从中挑选出一部分像片作为所谓的验证集,在训练一定轮次后验证渲染效果。
训练过程,对象是采样光线穿过的像素。
首先根据1中流程,获取采样光线的起点center
和相机朝向ray_unit
。
采样光线和中心球求交,获取采样点的范围near,far
利用前景和背景MLP,分别计算前景object
和背景background
所有采样点的色彩rgbs
和透明度alphas
利用体渲染公式,综合前景和背景,求出采样光线穿过的像素最终的颜色rgb
输出:(加s是指单个采样点,不加s指的是整条采样光线)
用来计算渲染loss、sdf一阶梯度loss、sdf二阶梯度loss:
rgbs, gradients, hessians
用来进行从粗到细的优化,指导下一次采样:
dists , weights
验证过程,对象是验证集每一张像片上的所有像素。
center
和相机朝向ray_unit
。rgbs, gradients, hessians, dists, weights
。rgb, gradient, dist, depth, opacity
。