一個光線跟蹤的簡單實例
一、光線跟蹤的基本原理(引用)
光線跟蹤(Ray-trace)是一種真實感地顯示物體的方法,該方法由Appel在1968年提出。光線跟蹤方法沿着到達視點的光線的相反方向跟蹤,經過屏幕上每一象素,找出與視線所交的物體表面點 P0,並繼續跟蹤,找出影響P0點光強的所有的光源,從而算出P0點上精確的光照強度。
如上圖所示,聯結觀察點和屏幕上的一個象素,即形成一根視線。因此,視線的數目等於象素的數目。對於每一根視線作如下處理:
計算視線V與各平面的交點。以距離最小的交點為可見交點P0。視線V在P0處產生反射和透射,所產生的反射線和透視線作為新的視線與各平面求交幾時出新的交點P1、P2,並分別產生新的反射線和透視線,……。這樣不斷遞歸,直至所產生的視線射出場景。結果是得到視線跟蹤軌跡上的一系列交點:P0、 P1、P2、…、Pn。這個過程可以表示為一棵光線跟蹤樹。
下圖所示是一棵與上圖對應的光線跟蹤樹。樹的結點代表物體表面與跟蹤線的交點。結點連線代表跟蹤線。每個結點的左兒子代表反射產生的跟蹤線(r),右兒子代表透射產生的跟蹤線(T)。空箭頭表示跟蹤絲射出場景。P0處的光強是P0、P1、P2、P3點光強的合成。計算方法是以后序周游的算法遍歷這顆光線跟蹤樹。在每一結點處,遞歸調用光照模型,算出跟蹤射線方向的光強,並按兩表面交點之間的距離進行衰減后,傳遞給父結點。如此上遞,最后得出P0點處的光強,亦即得到屏幕象素處的亮度。
二、光線跟蹤算法的優缺點(摘錄)
用光線跟蹤方法顯示真實感圖形有如下優點:
1)顯示它不僅考慮到光源的光照,而且考慮到場景中各物體之間彼此反射的影響,因此顯示效果十分逼真。
2)有消隱功能
采用光線跟蹤方法,在顯示的同時,自然完成消隱功能。而且,事先消隱的做法也不適用光線跟蹤,因為那些背面和被遮擋的面,雖然看不見,但仍榀能通過反射或透射影響着看得見的面上的光強。
3)有影子效果
光線跟蹤能完成影子的顯示,方法是從P0 處向光源發射一根陰影探測光線。如果該光線在到達光源之前與場景中任一不透明的面相交,則P0處於陰影之中,否則,P0處於陰影之外。
4)該算法具有並行性質
每條光線的處理過程相同,結果彼此獨立,因此可以大並行處理的硬件上快速實現光線跟蹤算法。
光線跟蹤算法的缺點是計算量非常大,因此,顯示速度極慢。
三、程序代碼及說明(本程序沒有考慮折射效果)

//獲取射線ray的顏色,存入colour_out,最多跟蹤traceNum次
void World::getColourForRay(const Ray& ray, Colour& colour_out,int traceNum)
{
if(traceNum != 0)
{
Object *obj;
Vec3 normal,hitpos,L,R,Ldir;
float dist,bf=0.5;//為了簡化,這里將反射系數設為常數,其本來值與dist值有關
int i;
static int ligsize = m_lights.size();
obj = closestObject(ray,dist);//返回與光線ray相交的最近的物體,並將其距離存入dist中
if(obj)//檢測光線是否與場景中的物體相交
{
Colour lightcolour,total1,total2,diffuse = Colour::black(),specular = Colour::black();
hitpos = ray.m_startPos;
hitpos.addMult(ray.m_unitDir,dist);
normal = obj->getGeometry().getNormalForPos(hitpos);
normal.normalise();
R = reflect(ray.m_unitDir,normal);//求出反射光線
for(i = 0;i < ligsize;i++)//求出每個光源對交點處光照的貢獻
{
L = m_lights.at(i)->getPos() - hitpos;//求出陰影光線
L.normalise();
if(!closestObject(Ray(hitpos,L),dist))//檢測陰影光線路徑中是否存在遮擋物
{ //不存在遮擋物則累加上該光源對交點的散射和鏡面光的貢獻
lightcolour = m_lights.at(i)->getColour();
Vec3 H = (L - ray.m_unitDir)/2;
H.normalise();
diffuse += lightcolour * (L.dot(normal)>0?L.dot(normal):0);
specular += lightcolour * pow((H.dot(normal)>0?H.dot(normal):0),obj->getMaterial().ns);
}
}
total1 = ambient_lighting * obj->getMaterial().ka
+ diffuse * obj->getMaterial().kd + specular * obj->getMaterial().ks;
getColourForRay(Ray(hitpos,R),total2,traceNum-1);//遞歸計算下個交點的光照
colour_out = total1 + total2*bf;//累加
return;
}
}
colour_out = Colour::black();//其余情況返回黑色
}