《一週學完光線追蹤》學習 四 顯示一個球

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

讓我們在光線跟蹤器中添加一個對象。人們經常在光線跟蹤器中使用球體,因爲計算光線是否擊中球體非常簡單。回想一下,以半徑R原點爲中心的球體的方程是x*x+y*y+z*z=R*R。讀取該方程的方法是“對於任何(x,y,z),如果x*x+y*y+z*z=R*R,則(x,y,z)在球體上,否則它不在”。如果球體中心位於(cx,cy,cz),則會變得更醜:

在圖形中,你幾乎總是希望你的公式是矢量形式的,所以所有的x/y/z都在vec3類中。您可能會注意到,從中心C=(C x,C y,C z)到點p=(x,y,z)的向量是(p-C)。點((p-C),(p-C))=(y-cy)*(y-cy)+(z-cz)*(z-cz)。所以矢量形式的球方程是:

我們可以把它理解爲“滿足這個方程的任何點p都在球面上”。我們想知道我們的射線p(t)=A+t*B是否曾撞擊過球體的任何地方。如果它真的碰到了球,有一些t,p(t)滿足球方程。所以我們在尋找任何一個t,如果這是真的:

或展開射線p(t)的完整形式:

向量代數的規則就是我們想要的,如果我們把方程展開,把所有的項移到左邊,我們得到:

方程中的向量和R都是常數和已知的。未知的是t,方程是二次方程,就像你在高中數學課上看到的。你可以解t,有一個平方根部分,它要麼是正的(意味着兩個實解),要麼是負的(意味着沒有實解),要麼是零(意味着一個實解)。在圖形學中,代數幾乎總是與幾何學直接相關。我們所擁有的是:

如果我們把這個數學公式和硬編碼輸入到程序中,我們可以通過在z軸上的-1處的小球上塗上紅色來測試它:

bool hit_sphere(const Vector3& center, float radius, const Ray&r) {
	Vector3 oc = r.origin() - center;
	float a = dot(r.direction(), r.direction());
	float b = 2.0*dot(oc, r.direction());
	float c = dot(oc, oc) - radius*radius;
	float discriminant = b*b - 4 * a*c;
	return (discriminant > 0);
}
Vector3 color(const Ray&r) {
	if (hit_sphere(Vector3(0, 0, -1), 0.5, r))return Vector3(1, 0, 0);
	Vector3 unit_direction = unitVector(r.direction());
	float t = 0.5*(unit_direction.y() + 1.0);
	return (1.0 - t)*Vector3(1.0, 1.0, 1.0) + t*Vector3(0.5, 0.7, 1.0);
}


........ main函數 ........ 

	for (int j = HEIGHT-1;j >= 0;j--) {
		for (int i = 0;i < WIDTH;i++) {
			int offset = (WIDTH*j + i) * 4;
			float u = float(i) / float(WIDTH);
			float v = float(j) / float(HEIGHT);
			Ray r(origin, lower_left_corner+u*horizontal+v*vertical);
			Vector3 col = color(r);
			Pixels[offset + 0] = (unsigned char)255.99*col[0];
			Pixels[offset + 1] = (unsigned char)255.99*col[1];
			Pixels[offset + 2] = (unsigned char)255.99*col[2];
			Pixels[offset + 3] = 255;
		}
	}

測試結果:

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