smallpt: Global Illumination in 99 lines of C++講解

smallpt: Global Illumination in 99 lines of C++

光線追蹤

正向光線追蹤

正向光線追蹤符合常識:光線從發光物體出發出,“撞擊”到被觀察物體上,經過一系列光線傳輸進入人眼。
正向光線追蹤

因此正向光線追蹤的基本流程可以簡述爲:

  1. 追蹤從發光體發射的所有光線
  2. 檢測光線是否“撞擊”物體
    • 如果沒有“撞擊”物體,直接拋棄
  3. 如果撞擊到物體。看反射光線是否直接射入人眼
    -如果沒有直接射入人眼,則其可能經過一系列傳輸才射入人眼(間接射入人眼),需要進步判斷
    -如果直接射入人眼,則計算當前反射光線的顏色,作爲該撞擊點的顏色

從上述流程不難可以看出,正向光線追蹤需要追所有光線。而在這所有光線中,只有一部分光線會“撞擊”到觀察體,“撞擊”到觀察體的光線也只有部分會射入人眼。因此追蹤所有光線,計算量大且做無用功較多。

逆向光線追蹤介紹

追蹤光線的方向與正向相反:從眼睛處射出光線,追蹤光線射擊到物體後,是否能回到光源。如果能則說明該點被光源照亮,否則物體上該點可能被間接照亮,需要進一步判斷。
在這裏插入圖片描述
逆向光線追蹤爲什麼比正向光線追蹤號好呢?
因爲從圖形學的角度來看

人眼發射出的光線是有限的,而光源發射出的光線是無限的:人眼接收圖像是二維的像素組成的。每個像素記錄着該點的顏色。也即對於每個像素計算其顏色, 代表着該像素上一次光線追蹤的結果。
在這裏插入圖片描述
因此逆向光線追蹤的基本流程可以簡述爲:

FOR 每個像素點 :
	構造人眼入射光線Ray
	光線追蹤Ray :
		計算與光線Ray相交的最近的物體Obj
		IF obj == null :
			該像素點顏色爲缺省值:全局環境光AmbientColor
			Continue
		ELSE 
			看反射光Reflection是否能直接與光源相連(未被其他物體遮擋)
			IF 沒有被遮擋
				該像素點顏色爲光源顏色在該材質上的作用
			ELSE IF 被遮擋 
				該像素點顏色根據反射光的光線追蹤結果得到。

蒙特卡羅光線追蹤算法

個人理解,不對歡迎指正
蒙特卡洛思想介紹

蒙特卡羅光線追蹤對逆向光線追蹤模型進行改進,其中最大的區別在於把概率模型引入光線追蹤。

  • 逆向光線追蹤中物體的表面材質很單一。引入俄羅斯賭盤輪,可以設定漫反射、鏡面反射、甚至折射的概率。豐富表面材質的顯示
  • 每個像素點只採樣一條光線計算出的顏色,正確率不高。引入蒙特卡羅可以多次採樣求平均,優化渲染結果。
    在這裏插入圖片描述

根據上述分析光線追蹤算法中最重要的步驟可以分解成兩個:

  1. 射線與多邊形物體的求交判斷(空間劃分kdTree)
  2. 光線追蹤這個遞歸子算法(包括對各種材質的處理)

下面由於準備材料不夠,我先只主要介紹方面2:

非透明材質

漫反射材質

漫反射材質表現爲表面不規則,因此反射光線的方向無法確定判斷。它朝可能的任意方向反射。
在這裏插入圖片描述
我們假定對於漫反射材質,反射光線的方向範圍可以限定在以撞擊點爲圓心,撞擊點法相爲中心的半圓內。
在這裏插入圖片描述
假設:
入射光線爲Ray(x0, d0)。其中x0表示光線起點 d代表光線方向
入射光線與物體表面的相交點爲x
入射光線與物體表面相交點x出的法向爲n
目標:求反射光線Reflection Ray(x, d1).

1.構建以x點爲中心,n爲一個座標軸的笛卡爾直角座標系
w = n
u=((fabs(w.x)>.1?Vec(0,1,0):Vec(1,0,0)) * w).norm()
v=w * u
u/v/w即組成一個笛卡爾直角座標系

2.將反射光線方向d1分解爲u/v/w表示形式
如下圖所示
d1 = |d1| * cosα * cosθ * u + |d1| * sinα * w + |d1| * cosα * sinθ * v
其中α爲 [0, PI/2]中的隨機數;θ爲[0,2*PI]中的隨機數

這兩個步驟對應smallpt: Global Illumination in 99 lines of C++中代碼片段56-60

 if (obj.refl == DIFF){                  // Ideal DIFFUSE reflection 
     double r1=2*M_PI*erand48(Xi), r2=erand48(Xi), r2s=sqrt(r2); 
     Vec w=nl, u=((fabs(w.x)>.1?Vec(0,1):Vec(1))%w).norm(), v=w%u; 
     Vec d = (u*cos(r1)*r2s + v*sin(r1)*r2s + w*sqrt(1-r2)).norm(); 
     return obj.e + f.mult(radiance(Ray(x,d),depth,Xi)); 
   }

在這裏插入圖片描述

鏡面反射材質

鏡面反射比較簡單,反射光線可假定嚴格按照反射定律來求(入射角定於反射角)
在這裏插入圖片描述
假設:
入射光線爲Ray(x0, d0)。其中x0表示光線起點 d代表光線方向
入射光線與物體表面的相交點爲x
入射光線與物體表面相交點x出的法向爲n
目標:求反射光線Reflection Ray(x, d1).
具體計算步驟如下圖所示
d1 = d0 - n * 2 * (n * d0)
在這裏插入圖片描述
這兩個步驟對應smallpt: Global Illumination in 99 lines of C++中代碼片段61-62

else if (obj.refl == SPEC)            // Ideal SPECULAR reflection 
     return obj.e + f.mult(radiance(Ray(x,r.d-n*2*n.dot(r.d)),depth,Xi)); 

透明材質

反射與折射

非透明材質在光線追蹤的過程中,要不是漫反射,要不是鏡面反射。與非透明材質不同,透明材質在光線作用下,反射和折射是同時存在的(全反射除外)。因此在光線追蹤透明材質時,需要同時考慮這兩種發射情況。

折射光線方向的計算:斯涅爾定律

簡單介紹一下斯涅爾定律,它主要是描述清楚了入射角與折射角之間的關係:
n1sinθ1 = n2sinθ2
其中n1/n2是兩個介質的折射率;θ12分別是入射角/折射角

折射光線方向計算:
假設:
入射光線爲Ray(x0, d0)。其中x0表示光線起點 d代表光線方向
入射光線與物體表面的相交點爲x
入射光線與物體表面相交點x出的法向爲n
兩種介質的折射率分別爲n1、n2
目標:求折射光線Reflection Ray(x, d1).

在這裏插入圖片描述
這個步驟對應smallpt: Global Illumination in 99 lines of C++中代碼片段68

Vec tdir = (r.d*nnt - n*((into?1:-1)*(ddn*nnt+sqrt(cos2t)))).norm(); 

考慮全反射:入射角大於某一臨界角θc(光線遠離法線)時,折射光線將會消失,所有的入射光線將被反射。
當折射角等於90°時,入射角即達到臨界角。
這部分步驟對應smallpt: Global Illumination in 99 lines of C++中代碼片段63-67

Ray reflRay(x, r.d-n*2*n.dot(r.d));     // Ideal dielectric REFRACTION 
bool into = n.dot(nl)>0;                // Ray from outside going in? 
double nc=1, nt=1.5, nnt=into?nc/nt:nt/nc, ddn=r.d.dot(nl), cos2t; 
if ((cos2t=1-nnt*nnt*(1-ddn*ddn))<0)    // Total internal reflection   全反射
     return obj.e + f.mult(radiance(reflRay,depth,Xi)); 

折射光線與反射光線混合:菲涅耳公式

在這裏插入圖片描述
這部分步驟對應smallpt: Global Illumination in 99 lines of C++中代碼片段69-73

double a=nt-nc, b=nt+nc, R0=a*a/(b*b), c = 1-(into?-ddn:tdir.dot(n)); 
double Re=R0+(1-R0)*c*c*c*c*c,Tr=1-Re,P=.25+.5*Re,RP=Re/P,TP=Tr/(1-P); 
return obj.e + f.mult(depth>2 ? (erand48(Xi)<P ?   // Russian roulette 俄羅斯賭盤
     radiance(reflRay,depth,Xi)*RP:radiance(Ray(x,tdir),depth,Xi)*TP) : 
     radiance(reflRay,depth,Xi)*Re+radiance(Ray(x,tdir),depth,Xi)*Tr); 

待更新


參考:
1.smallpt: Global Illumination in 99 lines of C++
2.scratchapixel: Introduction to Ray Tracing: a Simple Method for Creating 3D Images
3.scratchapixel:Rendering an Image of a 3D Scene: A Light Simulator
4.百度百科:斯涅爾定律、菲涅耳公式
5.知乎:如何用 C++ 實現光線跟蹤軟渲染器?

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