一、理論基礎
1、三維場景中創建圖像
第一步:透視投影。這是一個將三維物體的形狀投影到圖像表面上的幾何過程,這一步只需要連接從對象特征到眼睛之間的線,然后在畫布上繪制這些投影線與圖像平面相交的輪廓。
第二步:添加顏色。圖像輪廓繪制好之后,給它的骨架添加顏色,這樣就完成了三維場景中的圖像創建過程。
2、物體的顏色和亮度
? 主要是光線與物體材質相互作用的結果。
? 光由光子(電磁粒子)組成,光子由各種光源發射。當一組光子撞擊一個物體時,可能發生三種情況:被吸收,反射或透射。發生這三種情況的光子百分比因材料而異,通常決定了物體在場景中的顯現方式。然而,所有材料都有一個共性:入射光子總數總是與反射光子、吸收光子、透射光子的總和相同。
? 白光由“紅”、“藍”、“綠”三種顏色光子組成。當白光照亮紅色物體時,光子吸收過程會過濾掉“綠色”和“藍色”光子。因為物體不吸收“紅色”光子,所以它們將被反射,這就是物體呈現紅色的原因。
? 我們之所以能夠看到物體,是因為物體反射的一些光子向我們傳播并擊中了我們的眼睛。我們的眼睛由光感受器組成,可以將光信號轉換為神經信號,然后我們的大腦能夠使用這些信號來辨別不同的陰影和色調。
3、光與物體的關系
? 沒有光線,我們都看不到周圍的物體。
? 周圍環境中沒有物體,我們看不到光。
1、Forward Tracing
在用計算機生成的圖像中模擬光與物體相互作用過程之前,我們需要了解一個物理現象。一束光線照射在物體上時,反射的光子中只有少數會到達我們眼睛的表面。想象一下,假設有一個每次只發射一個光子的光源,光子從光源發出并沿著直線路徑行進,直至撞擊到物體表面,忽略光子的吸收,該光子會以隨機的方向反射。如果光子撞擊到我們的眼睛表面,則我們會看到光子被反射的點。具體過程如下圖所示。
現在從計算機圖形的角度來看待這種情況。首先,我們用像素組成的平面代替我們的眼睛。在這種情況下,發射的光子將撞擊圖形平面上許多像素的一個,并將該點的亮度增加到大于零的值。重復多次直到所有的像素被調整,創建一個計算機生成的圖像。這種技術稱為前向光線追蹤(Forward Tracing),因為我們是沿著光子從光源向觀察者的前進的路徑。
但是,這種技術在計算機中模擬光子與物體相互作用是不太現實的,因為在實際中反射的光子擊中眼睛表面的可能性是非常非常低的,我們必須投射大量的光子才能找到一個能夠引起眼睛注意的。此外,我們也不能保證物體的表面被光子完全覆蓋,這是這項技術的主要缺點。
換句話說,我們可能不得不讓程序一直運行,直到足夠的光子噴射到物體的表面上獲得精確的顯示。這意味著我們要監視正在呈現的圖像以決定何時停止應用程序。這在實際生產環境中是不可能的。另外,正如我們將看到的,射線追蹤器中最昂貴的任務是找到射線幾何交點。從光源產生大量光子不是問題,但是在場景內找到所有的交點將會是非常昂貴的。
2、Backward Tracing
這項技術為前向光線追蹤技術的缺陷提供了一個方便的解決方案。由于我們的模擬不能像自然一樣快速完美,所以我們必須妥協,并追蹤從眼睛進入到場景中的光線。
光線照到一個物體時,我們可以通過將另一條光線(稱為光線或陰影光線)從擊中點投射到場景的光線,得到它所接受到的光子數量。這個“光線”有的時候會被另一個物體阻擋,這意味著我們原來的撞擊點在陰影中,沒有獲得任何照明。
三、算法實現
1、基本原理
? 光線追蹤算法采用由像素組成的圖像。對于圖像中的每個像素,它將主光線投射到場景中。該主光線的方向是通過追蹤從眼睛到像素中心線獲得的。一旦我們確定了主射線的方向,我們就開始檢查場景中的每個對象,看它是否與其中的任何一個相交。當發生主射線與多個對象相交的情況時,我們選擇交點離眼睛最近的物體。
? 然后,我們從交叉點向光線投射陰影射線。如果這條特定的光線在通往光源的路上不與某個物體相交,那么這個點就被照亮了。
? 如果它與另一個物體相交,則該物體在其上投下陰影。
? 最后,如果我們對每個像素重復這一操作,就可以獲得三維場景的二維表示。
2、偽代碼
光線追蹤算法實現的偽代碼如下所示:
for (int j = 0; j < imageHeight; ++j) { ? ?for (int i = 0; i < imageWidth; ++i) { ? ? ? ?// compute primary ray direction ? ? ? ?Ray primRay; ? ? ? ?computePrimRay(i, j, &primRay); ? ? ? ?// shoot prim ray in the scene and search for intersection ? ? ? ?Normal nHit; ? ? ? ?float minDist = INFINITY; ? ? ? ?Object object = NULL; ? ? ? ?for (int k = 0; k < objects.size(); ++k) { ? ? ? ? ? ?if (Intersect(objects[k], primRay, &pHit, &nHit)) { ? ? ? ? ? ? ? ?float distance = Distance(eyePosition, pHit); ? ? ? ? ? ? ? ?if (distance < minDistance) { ? ? ? ? ? ? ? ? ? ?object = objects[k]; ? ? ? ? ? ? ? ? ? ?minDistance = distance; // update min distance ? ? ? ? ? ? ? ?} ? ? ? ? ? ?} ? ? ? ?} ? ? ? ?if (object != NULL) { ? ? ? ? ? ?// compute illumination ? ? ? ? ? ?Ray shadowRay; ? ? ? ? ? ?shadowRay.direction = lightPosition - pHit; ? ? ? ? ? ?bool isShadow = false; ? ? ? ? ? ?for (int k = 0; k < objects.size(); ++k) { ? ? ? ? ? ? ? ?if (Intersect(objects[k], shadowRay)) { ? ? ? ? ? ? ? ? ? ?isInShadow = true; ? ? ? ? ? ? ? ? ? ?break; ? ? ? ? ? ? ? ?} ? ? ? ? ? ?} ? ? ? ?} ? ? ? ?if (!isInShadow) ? ? ? ? ? ?pixels[i][j] = object->color * light.brightness; else pixels[i][j] = 0; } }
四、加入反射和折射
1、基本原理
在光學中,反射和折射是總所周知的現象。反射和折射分向都是基于相交點處的法線和入射光線(主光線)的方向。為了計算折射方向,我們還需指定材料的折射率。
同樣,我們也必須意識到像玻璃球這樣的物體同時具有反射性和折射性的事實。我們需要為表面上的給定點計算兩者的混合值。反射和折射具體值的混合取決于主光線(或觀察方向)和物體的法線和折射率之間的夾角。有一個方程式精確地計算了每個應該如何混合,這個方程被稱為菲涅耳方程。
加入反射折射后,進行以下三個步驟:
? 計算反射
為此,我們需要兩個項:交點處的法線和主光線的方向。一旦我們獲得了反射方向,我們就朝這個方向發射新的光線。我們假設反射光線撞擊了紅色球體,通過向光線投射陰影射線來找出到達紅色球體上的那個點的光線多少。這會得到一種顏色(如果是陰影,則為黑色),然后乘以光強并返回到玻璃球的表面。
? 計算折射
注意,因為光線穿過玻璃球,所以它被認為是透射光線(光線從球體的一側傳播到另一側)。為了計算透射方向,我們需要在知道擊中點的法線,主射線方向和材料的折射率。
當光線進入并離開玻璃物體時,光線的方向會改變。每當介質發生變化時都會發生折射,而且兩種介質具有不同的折射率。折射對光線有輕微彎曲的作用。這個過程就是讓物體在透視時或在不同折射率的物體上出現偏移的原因。
現在讓我們想象一下,當折射的光線離開玻璃球時,它會碰到一個綠色的球體。在那里,我們再次計算綠色球體和折射射線之間交點處的局部照明(通過拍攝陰影射線)。然后,將顏色(如果被遮擋,則為黑色)乘以光強并返回到玻璃球的表面。
? 應用菲涅爾方程
我們需要玻璃球的折射率,主光線的角度,以及擊中點的法線。使用點積,菲涅耳方程返回兩個混合值。
這種算法的美妙之處在于它是遞歸的。迄今為止,在我們研究過的情況下,反射光線照射到一個紅色的、不透明的球體上,而折射光線照射到一個綠色的、不透明的和漫射的球體上。但是,我們會想象紅色和綠色的球體也是玻璃球。為了找到由反射和折射光線返回的顏色,我們必須按照與原始玻璃球一起使用的紅色和綠色球體的相同過程。
這是光線追蹤算法的一個嚴重缺陷。想象一下,我們的相機是在一個只有反射面的盒子里。從理論上講,光線被困住了,并且會持續不斷地從箱子的墻壁反彈(或者直到你停止模擬)。出于這個原因,我們必須設置一個任意的限制值,從而防止光線相互作用導致的無限遞歸。每當光線反射或折射時,其深度都會增加。當光線深度大于最大遞歸深度時,我們就停止遞歸過程。
2、偽代碼
偽代碼如下所示:
// compute reflection color color reflectionCol = computeReflectionColor(); // compute refraction color color refractionCol = computeRefractionColor(); float Kr; // reflection mix value float Kt; // refraction mix value fresnel(refractiveIndex, normalHit, primaryRayDirection, &Kr, &Kt); // mix the two color glassBallColorAtHit = Kr * reflectionColor + (1-Kr) * refractionColor;
-
算法
+關注
關注
23文章
4620瀏覽量
93046 -
圖像
+關注
關注
2文章
1087瀏覽量
40499 -
光線追蹤
+關注
關注
0文章
183瀏覽量
21495
原文標題:計算機圖形學——光線追蹤(RayTracing)算法
文章出處:【微信號:Imgtec,微信公眾號:Imagination Tech】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論