《学一辈子光线追踪》 七 直接采样光

蒙特卡洛光线追踪技术系列 见 蒙特卡洛光线追踪技术

几乎均匀地在方向上采样的问题是,光的采样率不超过其他不重要的方向的采样率。我们可以使用阴影光线并分离出直接照明。但是,作为替代方法,我只会发射更多的采样光线到光源上。我们以后可以用它来向我们想要的任何方向发射更多的光线。

很容易选择一个指向灯光的随机方向;只需在灯光上选择一个随机点并向该方向发送光线即可。但我们也需要知道pdf(方向)。那是什么?对于a区域的光,如果我们在该光上均匀采样,则该光表面上的pdf为1/a。但定义方向的单位球体区域上的pdf是多少?幸运的是,有一个简单的对应关系,如图中所示:

如果我们观察光上的一个小区域dA,采样的概率是 p_q(q)*dA 。在球面上,对球面上的小面积dw进行采样的概率为p(direction)*dw 。dw和dA之间存在几何关系:

dw = dA cos(alpha) / (distance(p,q)^2)

我们假设上面的球面是单位球面,则光源投影到球面上的面积就是其立体角

因为抽样的概率dw和dA必须相同,所以我们有:

p(direction)*cos(alpha)*dA / (distance(p,q)^2 ) = p_q(q)*dA = dA / A

这是因为,首先我们要保证我们发出光线的pdf积分为1,同时这个pdf又是作为在灯光上的pdf而存在的。

所以:

p(direction) = distance(p,q)^2 / ( cos(alpha) *A )

如果我们修改颜色函数,以一种非常硬编码的方式采样光,只是为了检查数学和获得概念,我们可以添加它(见突出显示的区域):(就是把上面的公式直接搞进程序里去)

		if (depth < 1 && rec.mat_ptr->scatter(r, rec, albedo, scattered,pdf)) {
			vec3 on_light = vec3(213 + myRandom()*(343 - 213), 554.0f, 227 + myRandom()*(332 - 227));
			vec3 to_light = on_light - rec.p;
			float distance_squared = to_light.squaredLength();
			to_light.makeUnitVector();
			if (dot(to_light, rec.normal) < 0)//如果光的方向与它夹角超过90度
				return emitted;
			float light_area = (343 - 213)*(332 - 227);
			float light_cosine = fabs(to_light.y());
			if (light_cosine < 0.000001)
				return emitted;
			pdf = distance_squared / (light_cosine*light_area);
			scattered = ray(rec.p, to_light, r.time());
			
			float mpdf = rec.mat_ptr->scattering_pdf(r, rec, scattered);
			return emitted + albedo*mpdf*color(scattered, world, depth + 1) / pdf;
		}//
		else {
			return emitted;
		}

这里的depth=50很明显是没什么用的,因为采样光线只会有两种情况,第一是碰到物体以后反射到灯光上,第二是碰到物体以后反射向灯光,但是中间被别的物体挡住了,在这种情况下,再次朝着灯光方向进行反射则会遇到第一个if的判断这种情况:

所以说反射一次的结果和设置反射50次的结果并没有什么本质区别:

反射一次。

设置反射五十次(其实也就只是反射了一次)。

这是关于我们期望的东西,只采样光源,所以这似乎正常工作了。天花板上的灯周围发出的噪音是因为灯是双面的,而且灯和天花板之间有一个很小的空间:

验证方法:保留灯的两面,并把后面的pdf屏蔽掉。

就发现没有毛边了:说明是由pdf引起的。

我们可能想只让光向下发射。我们可以让hitable的发出成员函数获取额外的信息:

	virtual vec3 emitted(const ray&r_in, const hit_record&rec,float u, float v, const vec3&p)const {
		if (dot(rec.normal, r_in.direction()) < 0.0)
			return emit->value(u, v, p);
		else
			return vec3(0, 0, 0);
	}

我们还需要翻转灯光,使其法线指向-y方向,我们得到:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章