《學一輩子光線追蹤》 七 直接採樣光

蒙特卡洛光線追蹤技術系列 見 蒙特卡洛光線追蹤技術

幾乎均勻地在方向上採樣的問題是,光的採樣率不超過其他不重要的方向的採樣率。我們可以使用陰影光線並分離出直接照明。但是,作爲替代方法,我只會發射更多的採樣光線到光源上。我們以後可以用它來向我們想要的任何方向發射更多的光線。

很容易選擇一個指向燈光的隨機方向;只需在燈光上選擇一個隨機點並向該方向發送光線即可。但我們也需要知道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方向,我們得到:

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