【MIT6.837】A1:Ray Casting

A1:Ray Casting(光線投射)


學術名詞

camera obscura:針孔照相機
Perspective:透視投影
Orthographic:正交投影
Parallel projection:平行投影
Ray-Plane Intersection:射線與平面相交
Ray-Sphere Intersection:射線與球面相交
orthonormal basis:標準正交基
Ray Casting:光線投射
Ray tracing:光線追蹤
Geometric:幾何學
Algebraic:代數學
Object-Oriented Design:面向對象設計

實驗概述

本實驗的目標是要完成一個光線投射算法。光線投射算法的原理如下圖(文字部分爲個人見解):
對於每一個像素
 構建一條從視點處開始的射線
  對場景中的每一個對象
  判斷當前射線是否和當前對象有交點(此處要用到求交點函數)
  如果有且該交點距離視點更近,則更新最近交點值

在這裏插入圖片描述

下面說明一下本實驗完成過程的重點。

射線和球模型的計算交點方法的實現

此處採用的是Algebraic方法,該方法的實現不是很難理解,根據PPT的公式及實驗講解即可完成該部分。Geometric方法使用較少因此沒有實現。

在這裏插入圖片描述
在這裏插入圖片描述

bool Sphere::intersect(const Ray& r, Hit& h, float tmin) {
	bool flag = false;//有交點返回true
	Vec3f ro = r.getOrigin() - center;//ro:射線到球中心的向量
	Vec3f rd = r.getDirection();//射線方向
	float a = rd.Dot3(rd);//rd與rd點積
	float b = 2 * ro.Dot3(rd);//ro與rd點積,可能爲負
	float c = ro.Dot3(ro) - radius * radius;
	float delta = b * b - 4 * a * c;
	
	//float t;//當前距離,此處可以不用
	//獲取最近的交點
	if (delta >= 0) {
		float d = sqrt(delta);//一定爲正
		float t1 = (-b - d) / (2 * a);//通常t1更小
		float t2 = (-b + d) / (2 * a);
		// For an orthographic camera, rays always start at infinity, so tmin will be a large negative value
		//由於本節作業是正交相機,正交相機的tmin爲無窮大,因此下面這塊代碼可以不用
		//if (t1 >= tmin) {//在origin之前
		//	t = t1;
		//}
		//else if (t2 >= tmin) {
		//	t = t2;
		//}
		if (t1 < h.getT()) {//closer當前交點,更新
			h.set(t1, material, r);
			flag = true;
		}
	}
	return flag;
}

正交相機的實現

直接參見PPT上給出的原理進行實現
在這裏插入圖片描述

Ray OrthographicCamera::generateRay(Vec2f point) {
	Vec3f ro = center + (point.x() - 0.5) * horizontal * size + (point.y() - 0.5) * up * size;//射線起始點到對象中心
	return Ray(ro, direction);
}

光線投射算法的實現

該函數在主函數中進行了實現,代碼如下

    /*輸入——初始化場景、相機、對象組及兩幅圖像*/
	SceneParser scene(input_file);
	Camera* camera = scene.getCamera();
	Object3D* group = scene.getGroup();
	Image image(width, height);//設置圖像大小
	image.SetAllPixels(scene.getBackgroundColor());//圖像背景顏色設置爲場景背景顏色
	Image depthImage(width, height);//設置深度圖像
	depthImage.SetAllPixels(Vec3f(0.0, 0.0, 0.0));//設置成黑色

    //光線投射
	/*循環遍歷圖像平面中的像素,使用OrthographicCamera類生成射線,將其與Group中的各個對象求交點,並將最近點保存到hit中*/
	for (int i=0;i<width;++i){
		for (int j = 0; j < height; ++j) {
			float x = float(i) / float(width);
			float y = float(j) / float_t(height);
			Ray ray = camera->generateRay(Vec2f(x, y));//產生射線
			Hit hit(INFINITY, nullptr);//hit存儲最近的交點,此處初始t設置爲了無窮大
			bool flag = group->intersect(ray, hit, camera->getTMin());//使該條射線和每一個基本圖元求交點
			if (flag) {//如果存在交點
				image.SetPixel(i, j, hit.getMaterial()->getDiffuseColor());//設置該點像素的顏色
				float t = hit.getT();
				//可視化深度t
				//超過則設置爲邊界值
				if (t > depth_max) t = depth_max;//此處max=1、min=0
				if (t < depth_min) t = depth_min;
				t = (depth_max - t) / (depth_max - depth_min);//t越小越近,越近的顏色越深
				depthImage.SetPixel(i, j, Vec3f(t, t, t));//設置顏色值
			}
		}
	}

實驗結果

在這裏插入圖片描述 在這裏插入圖片描述

實驗總結

在實驗的過程中,閱讀完實驗講義和PPT,不是很明白實驗需要完成那些內容。因此我直接參考了別人已完成的代碼,並對需要實現的部分一點點對着閱讀和理解。邊看代碼邊看PPT和實驗講義,對每個文件每個函數充分理解,實驗也逐漸完成,本實驗也是之後實驗的重點,需要充分弄明白。
自己完成實驗的步驟:
1.通看PPT
2.通看實驗講義
3.執行別人的代碼
4.在不懂的情況下閱讀實驗講義、PPT並閱讀別人的代碼

  • 記錄自己要編寫那些文件,完成那些函數
  • 記錄這些函數的實驗原理,進一步理解這些算法的思想
  • 記錄下參考過的資料
  • 重複該過程,直到弄懂

5.整理實驗,並寫成報告

參考資料

  • https://blog.csdn.net/weixin_40552524/article/details/104436544
  • https://www.cnblogs.com/fengyuheliu/archive/2011/08/28/2155627.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章