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