体散射·自由路径采样求散射点的方法

本篇文章主要介绍体散射自由路径采样,求散射点的方法,并对应给出ExposureRender绘制部分的相关代码。

要解决的问题:参与介质(体数据)的自由路径怎样通过采样的方法得到,如何计算参与介质内的交点

方法              :woodcock追踪,可集成到蒙特卡罗算法中

适用情况       :参与介质,包括最大衰减系数远大于大部分粒子的衰减系数的不均匀介质。

 

下面具体来看:

    1. 自由路径的采样

  • 光线衰减项

参与介质由于吸收或向外散射会产生衰减。参与介质沿光线p(s)方向衰减的辐射率为:

体散射·自由路径采样求散射点的方法_第1张图片

是衰减系数(extinction coefficient),定义了光子到达p点时光子与粒子碰撞的概率密度

  • 光学深度和CDF

对衰减系数积分得到光学深度τ。含义:度量介质吸收辐射的能力

其中P(s’)是发生碰撞的位置,s’是自由路径,也就是我们要求的东西。

根据反演方法算出累积概率分布(mulative probability distribution,CDF)。

沿着光线p(s)方向的自由路径长度的CDF为

这里没看懂不影响文章主要思路的理解,只用知道这里给出了自由路径长度和光学深度的关系就行了。

均匀随机数r是以下等式的解:

如果介质是不均匀的,那它的衰减系数就不用只用一个常数来表示,但可以用当前位置对应的周围网格上的有限值来表示。这样的话,就引出了本篇文章的重点之一,就是怎么找到交点位置,先给出这个式子:

通常根据ray marching的路数,就是沿着光线方向,每次走一小步△s,看看累计和是否大于−log(1−r),如果大于了,则这两步中间就包含着交点。

 

  • Woodcock tracking

Woodcock用以下算法对自由路径进行采样:

(1)用最大衰减系数 产生一个路径长度s的试验值

(2)以概率 接受击中点

(3)击中点被拒绝,则不改变粒子方向,从刚才的试验点接着进行下一轮迭代

这个方法在下面这种情概况下会变得低效:最大衰减系数远大于区域内的衰减系数。这样的话分母变得比分子大很多,接受的概率就低,迭代次数就多。

本篇论文中,将woodcock tracking中的衰减系数拓展为任意上限函数。具体方法见下。

 

    2. 本篇论文的算法

  • 处理不均匀介质

处理方法:添加虚拟粒子。这个虚拟粒子是不影响计算的,因为它不改变散射方向和能量。通过添加虚拟粒子,可以将衰减系数简化为一个上限函数.这个衰减系数 等于虚拟粒子的衰减系数 加上介质本身的衰减系数 。这样,通过比率 就能知道击中虚拟粒子或者真实粒子的概率

 

  • 虚拟粒子的自由路径采样

(1)用上限衰减函数产生路径长度s

(2)得到一个潜在散射点p,用概率估计是否击中的p点是虚拟点。如果是,则不改方向进行下次迭代。

看完处理的思路后再针对具体形式的上限函数来分析。

 

  • 分段多项式形式的上限函数

还记得刚才讨论的式子吗:

得到它的解,就求出了自由路径的长度s

将这个式子变换一下得到

同样滴,就是要求这个式子里的s。论文假设上限函数是定义在一个低分辨率网格上的分段多项式。这是什么意思呢?看下figure 3.

体散射·自由路径采样求散射点的方法_第2张图片

意思就是说,正常的体素分辨率高些,比如左下角的红格子,而上限函数没有那么多值,他分布在大一些的黑色格子的角点上,如果要求格子中间的值,可以通过多项式插值得到。

  通过执行3D DDA算法,能找到包含解的超像素。沿着光线,一个个地遍历超体素,每次都检查是否包含解,通过下面这个不等式来检查区间中是否有散射点:

体散射·自由路径采样求散射点的方法_第3张图片

这个式子是第i个超体素相交的射线段的光学深度,用来求出上边界的消光系数。

过程如图所示:

体散射·自由路径采样求散射点的方法_第4张图片

与Marchinng最大的不同,就是这里的步长 并不是一个常数。而是光线与超体素相交部分的长度,采样点Si在超体素的边界上。当在第n步时,不等式首次成立,表示找到了包含散射点的超体素,如fig4中的红点所示。通过下式,可以计算出散射点的自由路径长度s

体散射·自由路径采样求散射点的方法_第5张图片

右边通过计算均匀的采样r(一个随机数)和以前访问过的超级体素的光学深度得到。

给出单个超体素的示意图如fig5.

体散射·自由路径采样求散射点的方法_第6张图片

前方左下角的坐标全0,后方右上角的坐标全1.8个顶点上对应8个衰减系数,衰减系数的下标对应xyz的坐标值。光线从入口点e进入超体素,从出口点o射出。超体素内的点p满足下式:

其中 衰减系数是笛卡尔坐标的三变量多项式,是参数t的线性函数,可以表示为:

体散射·自由路径采样求散射点的方法_第7张图片

光学深度可以表示为:

体散射·自由路径采样求散射点的方法_第8张图片

通过带入末点 ,在超体素i中从si到si+1的光学深度可以表示为

体散射·自由路径采样求散射点的方法_第9张图片

这里有点看不懂不影响,只是给出了光学深度的计算表达式,重点看下面的算法伪码。

 

  • 算法细节

给出算法伪码:

体散射·自由路径采样求散射点的方法_第10张图片

分析:可以用嵌套循环实现上述过程。

(1)外层循环

决定当前击中点对应的是否是虚拟粒子。

参数准备:对光学深度(衰减系数的积分)进行采样,这个采样即通过rnd()函数产生一个均匀分布的随机数,再基于它计算。

判断:当内层循环因找到击中点而退出,需要判断这个击中点是否是虚拟粒子。根据之前提到的概率 来判断,如果产生的随机数大于Preal,即是真实散射点的概率小了,所以认为击中的是虚拟粒子,继续迭代。

(2)内层循环

3D DDA 在超体素网格遍历,判断是否有击中点。

访问超体素。首先初始化相关参数,初始位置即光线原点。

初始化:

出射点o设置为入射点的位置和参数,迭代中会将入点参数更新为出射点参数。出射点的光学深度就是从入射点开始在超体素中的累计和。

迭代:

如果超出了体的范围都没有找到交点,则返回“无散射点”

否则,计算下一个出射点o,拟合多项式,求和得到这一段的光学深度 .如果它小于 ,即没有找到击中点,继续迭代。当它第一次大于 ,则找到击中点

由于看该篇论文的目的,主要是为了看懂ExposureRender软件的绘制代码,只仔细研究了上述内容。原论文[1]还有其他形式的光学深度的计算方法等内容,感兴趣的可以去看,这里就不展开了。

 

  • 代码实现

以下代码摘自ExposureRender软件

DEV inline bool FreePathRM(CRay& R, CRNG& RNG)
{
	const int TID = threadIdx.y * blockDim.x + threadIdx.x;

	__shared__ float MinT[KRNL_SS_BLOCK_SIZE];
	__shared__ float MaxT[KRNL_SS_BLOCK_SIZE];
	__shared__ Vec3f Ps[KRNL_SS_BLOCK_SIZE];

	if (!IntersectBox(R, &MinT[TID], &MaxT[TID]))
		return false;

	MinT[TID] = max(MinT[TID], R.m_MinT);
	MaxT[TID] = min(MaxT[TID], R.m_MaxT);

	const float S	= -log(RNG.Get1()) / gDensityScale;
	float Sum		= 0.0f;
	float SigmaT	= 0.0f;

	MinT[TID] += RNG.Get1() * gStepSizeShadow;

	while (Sum < S)
	{
		Ps[TID] = R.m_O + MinT[TID] * R.m_D;

		if (MinT[TID] > MaxT[TID])
			return false;
		
		SigmaT	= gDensityScale * GetOpacity(GetNormalizedIntensity(Ps[TID]));

		Sum			+= SigmaT * gStepSizeShadow;
		MinT[TID]	+= gStepSizeShadow;
	}

	return true;
}

代码分析:

(1)给出当前位置

这份代码是用cuda写的,方便做并行加速。不了解不影响。由于要遍历各个位置,发射光线,找交点,这里第一步就是给出当前遍历到的位置,即TID。

(2)判断光线是否在体内(intersectBox)

注意刚才算法伪码的内循环中有判断是否超出体,如果超出体范围还没有找到交点,就是当前光线上没有散射点。详细算法见《AABB包围盒与光线相交的检测算法》

(3)准备参数

计算采样值,即 ,通过RNG产生一个均匀分布的随机数得到。min[TID]为光线上的位置,即在位置TID处发射一根光线,这根光线(o_start + t*direction)的参数t就是min[TID].

(4)迭代寻找击中点

注意这里有一个简化:体是一个均匀体,可用各向同性的相位函数表示,所以并不需要像论文中那样添加虚拟粒子,也就不需要外循环来判断击中点是否是虚拟粒子。

 

这里判断循环终止的条件即当前位置段是否包含击中点。sum就是光学深度的求和,当它第一次大于S时,即认为找到击中点。

Ps是当前的光线位置。如果光线位置超出范围为则跳出循环。否则对sum进行累计求和,并累加一个步长,走到下一个位置。

注意这里的光学深度实际上就是当前位置上的不透明度,而这个不透明度是该位置的强度值归一化后,通过不透明度传递函数计算出来的。这个传递函数可以理解为把强度值,转换成不透明度值的一个映射关系。

至此,如果找到击中点,则ps就是击中点位置

 

参考:

[1] Free Path Sampling in High Resolution Inhomogeneous Participating Media

你可能感兴趣的:(体绘制,3d渲染)