1. 程式人生 > >蒙特卡洛光線追蹤

蒙特卡洛光線追蹤

其他資料1

資料2

光線追蹤原理 

  光的基本傳遞模型 

  1 在一個要渲染的場景中,我們認為光能由預先指定的光源發出,然後我們以光線來描述光能的傳遞過程,當整個場景中的光能資訊被我們計算出來後,我們收集這些資訊轉化為頂點的亮度。 

  2 光線經過物體表面可以產生反射和漫反射,光線透過物體可以產生折射和散射。具體產生哪種出射效果,依據物體的表面屬性而定。物體的表面一般不會是理想的某種單一屬性的表面,表面可以同時存在反射,折射,漫反射等多種屬性,各種屬性按一定比例混合之後才是其表面反射模型。 

  3 一點的在某一個視線方向上的光亮度=該點在該方向的自身發光亮度+半球入射光能在該方向所產生的反射光亮度. 



  4 關於散射,高度真實的散射是一個很難模擬的物理過程,一般在渲染中都不會採用過於複雜的物理模型來表示散射,而是採用一些取巧的辦法來計算散射。 

  5 在常見的渲染中,有兩種效果很難模擬,但是它們會使人眼覺得場景更真實。 

  [1]color bleeding :入射光為漫反射,受光表面屬性為漫反射,出射光是漫反射。比如把一本藍色的紙製的書靠近白色的牆,牆上會有淺淺的藍暈。 

  [2]caustics:入射光為鏡面反射或折射,受光表面屬性為漫反射,出射光是漫反射。比如把一個裝了紅色葡萄酒的酒杯放在木桌上面,會有光透過杯中的酒在桌上形成一塊很亮的紅色區域。 

  傳統的陰影演算法: 

  遊戲中傳統的光照演算法,是利用公式法來計算特定型別光源的直接光照在物體表面所產生的反射和漫反射顏色,然後再使用陰影演算法做陰影補償。標準的陰影演算法不能計算面光源,改進以後的陰影演算法通過對面光源取樣,可以模擬出軟陰影的效果。但是這些方法計算的光照都是來自直接光源的,忽略了光的傳播過程,也就無法計算出由光的傳播所產生的效果。通過特定的修正,我們也可以計算特定的反射折射或漫反射過程,但是無法給出一種通用並且物理正確的方法。目前遊戲中大多是採用改進的陰影演算法來進行渲染,它的優點是效率比較高,結合預計算的話,還是可以產生比較生動可信的效果。 


  傳統的逆向光線追蹤: 

  正如前面描述的那樣,要想計算光能在場景中產生的顏色,最自然的考慮就是,從光源出發,正向跟蹤每一根光線在場景中的傳遞過程,然後收集資訊。然而這個想法在被提出的來的那個時代的計算機硬體上是不可能實現的,當時人們認為,正向光線追蹤計算了大量對當前螢幕顏色不產生貢獻的資訊,而且它把看不見的物體也計算在內,極大的浪費了效率。 

  於是人們想出的另一個方法是:只計算有用的,從人眼出發,逆向跟蹤光線。 

  逆向光線追蹤從視點出發,向投影螢幕發出光線,然後追蹤這個光線的傳遞過程。如果這個光線經過若干次反射折射後打到了光源上,則認為該光線是有用的,遞迴的計算顏色,否則就拋棄它。很顯然,這個過程是真實光線投射的逆過程,它同樣會產生浪費(那些被拋棄的逆向光線),而且只適用於靜態渲染。 


  逆向光線追蹤演算法中的頂點亮度主要包括三個方面: 

  1由光源直接照射而引起的光亮度 

  2來自環境中其它景物的反射折射光在表面產生的鏡面反射光亮度 

  3來自環境中其它景物的反射折射光在表面產生的規則透射光亮度 

  4預設定的頂點漫反射顏色 

  顯然,這一過程僅跟蹤景物間的鏡面反射光線和規則透射光線,忽略了至少經過一次漫反射之後光能傳遞,而且該演算法中的物體表面屬性只能是單一的,因而它僅模擬了理想表面的光能傳遞。 

  對於該演算法的具體描述: 

  1從視點出發,經過投影螢幕上的每一個畫素向場景發射一根虛擬的光線。 

  2求光線與場景最近的交點。 

  3遞迴跟蹤: 

  (1)如果當前交點所在的景物表面為理想鏡面,光線沿其鏡面反射方向繼續跟蹤。 

  (2)如果當前交點所在的景物表面為規則投射表面,光線沿其規則投射方向繼續跟蹤。 

  4遞迴異常結束: 

  (1)光線與場景中的景物沒有交點 

  (2)當前交點所在的景物表面為漫反射表面 

  (3)跟蹤層次已經超過使用者設定的最大跟蹤層數 

  (4)所跟蹤的光線對顯示畫素的光亮度的貢獻小於一預先設定的閥值 

  5遞迴正常結束: 

  (1)光線於光源相交,取得光亮度值,按遞迴層次反饋。 

  傳統的光線追蹤技術可以較好的表現出反射折射效果,也可以生成真實度比較高的陰影。但是他的光照都比較硬,無法模擬出非常細膩的柔化效果。 

  光線追蹤需要對大量的光線進行多次與場景中物體的求交計算。如何避免這些求交計算成為光線追蹤追求效率的本質。早期的光線追蹤演算法都是通過各種空間劃分技術來避免無謂的求交檢測,這些方法對於之後的理論同樣有效,常見的空間劃分方法分為兩類,一類是基於網格的平均空間劃分,一類是基於軸平行的二分空間劃分。 

  蒙特卡羅光線追蹤: 

  1對傳統的逆向光線追蹤的改進 

  傳統的逆向光線追蹤演算法有兩個突出的缺點,就是表面屬性的單一,和不考慮漫反射。我們不難通過模型的修正來緩解這兩個問題。我們首先認為一個表面的屬性可以是混合的,比如它有20%的成分是反射,30%的成分是折射,50%的成分是漫反射。這裡的百分比可以這樣理解,當一根光線打在該表面後,它有20%的概率發生反射,30%的概率發生折射,50%的概率發生漫反射。然後我們通過多次計算光線跟蹤,每次按照概率決定光線的反射屬性,這樣在就把漫反射也考慮了進去。具體的演算法如下: 

  (1)從視點出發,經過投影螢幕上的每一個畫素向場景發射一根虛擬的光線。 

  (2)當光線與景物相交時按照俄羅斯輪盤賭規則決定他的反射屬性。 

  (3)根據不同的反射屬性繼續跟蹤計算,直到正常結束或者異常結束。如果反射的屬性為漫反射,則隨機選擇一個反射方向進行跟蹤。 

  (4)重複前面的過程,把每次渲染出來的貼圖逐畫素疊加混合,直到渲染出的結果達到滿意程度。 

該方法是一種比較簡易的基於物理模型的渲染,其本質就是通過大量的隨機取樣來模擬半球積分。這種方法在光照細節上可以產生真實度很高的影象,但是影象質量有比較嚴重的走樣,而且效率極其低下。 

  2蒙特卡羅光線追蹤-取樣 

  蒙特卡羅光線追蹤的本質就是通過概率理論,把半球積分方程進行近似簡化,使之可以通過少量相對重要的取樣來模擬積分。蒙特卡羅光線追蹤理論中的取樣方案有很多,有時候還要混合使用這些取樣方案。 

  蒙特卡羅光線追蹤已經是一個比較完備的渲染方案,他極大的解決了光線追蹤的模型缺陷和效率問題,使得在家用圖形硬體上做基於物理的渲染成為一種可能。但是我們仍然無法實時的進行計算,而且如何解決影象走樣的問題也是蒙特卡羅光線追蹤的一大難點。 

  相對於普通光線追蹤,蒙特卡羅光線追蹤引入了更復雜的漫反射模型,從而增加了需要跟蹤的光線數量。但是他又通過取樣演算法減少了需要跟蹤光線,所以其核心效率取決於取樣模型。 

  與普通光線追蹤一樣,為了減少不必要的求交檢測,蒙特卡羅光線追蹤也需要使用空間劃分技術,最常用的是平衡kdtree。蒙特卡羅光線追蹤雖然是一種逆向光線追蹤演算法,但是其取樣的理論卻與光線追蹤的方向無關,可以用於任何一種渲染方案。 

  此外,使用蒙特卡羅光線追蹤不容易計算caustics現象。也就是說它不容易計算由鏡面反射或者規則透射引起的漫反射。(但是很容易計算由漫反射引起的鏡面反射或者規則透射;) 

  蒙特卡羅光線追蹤本身也是一種逆向光線跟蹤。逆向光線追蹤最初被設計出來是為了只計算那些會影響最終螢幕畫素的光能傳遞過程,這一思想在早期硬體並不發達,對最終影響要求也不高的年代是非常實用的。但是我認為由於對螢幕上每個畫素的跟蹤都是無關的,即每兩次跟蹤之間都不會建立通訊說哪些是計算過的,哪些是沒計算過的,所以這裡面必然會包含大量的重複計算的中間過程。當我們對影象所表現效果的真實度非常高的時候,必然會產生巨量的取樣,然後重複計算的問題就會被放大,而由逆向追蹤思想帶來的那些優勢也將蕩然無存。而且,對場景中光能貢獻越大的光源應該被越多的取樣跟蹤覆蓋到,但是逆向光線跟蹤只是對螢幕上每個畫素反覆遍歷追蹤,其結果應該趨向於取樣平均覆蓋各個光源,如果要想對高亮度光源採很多的樣本,必然也會導致對其它光源也過多的採了樣本,這會非常浪費效率。重新考慮正向光線追蹤,光由光源發出,打在場景之中,每一次光能轉化都被記錄下來,最後只要收集這些資訊就可以知道任意點上面的亮度,這個方法的描述非常的貼近真實的自然,關鍵在於如何保證速度。 

  另外,完全的逆向光線追蹤根本就不應該作為實時渲染的演算法,道理很簡單,光能的傳遞過程不變,只要視點一變,就要重新計算。 

  [輻射度演算法] 

  輻射度的演算法分為三個步驟 

  1先把場景中的面劃分為一個個小的patch,然後計算兩個patch之間的形式因子。兩個patch之間的形式因子表示了一個patch出射的光有多少比例會被另一個patch接收。對於任意一個有n個patch的場景來說,總有n*(n-1)個形式因子。 

  2通過迭代法來找到一個光能傳遞的平衡狀態 

  3把第二步所產生的亮度值作為頂點色渲染 

  輻射度演算法會非常的慢,而且如果不考慮額外的複雜度,輻射度演算法很難計算鏡面反射,改進的輻射度演算法可以緩解這一問題。很多研究者都試圖結合光線追蹤和輻射度這兩種方法,以期達到各自的優勢。 

  [photonmaping+final Gathering]: 

  前面談到,正向光線追蹤才是最自然的光能傳遞的描述,由此,在1994年,有人提出了photonmaping演算法。photonmaping是一個兩步的演算法,第一步通過正向光線跟蹤來構建光子圖,第二步通過光子圖中的資訊來渲染整個場景。它的核心思想是從光源開始追蹤光能的傳遞,把每一個傳遞中間過程都記錄下來,最後按照投影或者逆向光線追蹤來收集這些資訊,以達到渲染的目的。由於中間每一個光線和場景的相交都被記錄下來,所以他很自然的避免了逆向光線追蹤中重複計算的問題。 

  具體的,這兩步演算法又可以分為下面四步。 

  1從光源發射出N根取樣光線。光線的方向和光源的型別有關。取樣光線的數目選擇與光源自身的亮度有關,越亮的光源應該選擇越多的取樣。 

  2光子打到場景中,一步步傳遞,把光能傳遞的過程記錄下來,結果放在kdtree中 

  3用逆向光線追蹤或者反投影的方法找到可視點 

  4使用逆向光線追蹤和半球積分(比如最終聚集)方法收集光子圖中的資訊,從而計算可視點的光亮度。 

  首先要選擇一個光源,然後才能發射一個光子。對於場景中的多個光源,每次做發射一個光子取樣的時候,不能完全的隨機選擇光源,一個光源被選中的概率要正相關於他的在該場景中的能量 

  典型的,光源一般被分為: 

  (1)點光源: 

  點光源的資料結構僅僅是三維空間中的一個座標。對點光源所發出的光線進行取樣時,可以在包圍該點的單位球上任選一點,然後以球心到該點的射線作為取樣光線。也有人建議用單位立方體包圍盒取樣來代替單位球。 

  (2)方形面光源: 

  方形面光源上的每一個點都可以看做一個只能從靠近面法向量一側發射光線的點光源。 

  (3)其它光源: 

  任意空間形狀和物理特性的光源,只能具體問題具體分析。 

  一旦選好了初始要追蹤的光子向量的位置和方向,我們就可以開始一次正向追蹤。 

  一般的,光子在與場景中景物的相交的情況可以分為三類 

  (1)如果光子打到了鏡面反射表面或者規則折射表面,不用做任何記錄,繼續追蹤。 

  (2)如果光子打到了漫反射表面,則把光子所攜帶的能量和入射方向記錄下來。 

  如果入射光是折射或者反射光,則把光子記入caustics map,否則就記入全域性map; 

  其實,對於每一次相交,我們即可以記錄入射光子,也可以記錄出射光子。但是我們選擇了記錄入射光子。 

  我們記錄鏡面反射與規則折射的光子資訊是沒有意義的,因為我們不可能把所有的鏡面反射和規則折射過程都記錄下來,所以這一類亮度還是要通過逆向光線追蹤或其它方法來計算。但是我們記錄漫反射過程中的取樣資訊是有用的。因為我們可以通過某一點的部分入射取樣光子來近似的模擬該點的全部入射光子。然後我們可以計算該點任意方向上的出射光子。這也決定了我們只能記錄入射光子資訊而不是出射光子資訊。記錄入射光子還可以讓我們通過選擇不同的brdf甚至不同的簡化模型來重構每一次反射過程,這樣我們就可以隨心所欲的計算。 

那麼我們後面如何通過一點的入射光子來計算該點的出射光子呢,我們選擇一個包圍該點的範圍很小的球空間,把這個空間裡所有的入射光子按照半球積分模型計算,就可以算出該點的出射光子。 

  (3)如果光子打到的表面既有一定的鏡面反射屬性,又有一定的漫反射屬性,則依據兩種屬性各自所佔的百分比,使用俄羅斯輪盤賭原則來決定該次的反射屬性。 

  (4)光子再決定了反射屬性之後,還要依據反射屬性再隨機一次,以判定其是被表面吸收還是發射出去。

  構造好光子貼圖之後,我們就可以在第二步收集這些資訊來計算頂點亮度。 

  我們首先來看一個對光能半球積分簡化過了的公式: 

  L*f = ( L(l)+L(c)+L(d) )*( f(s)+f(d) ) 

  這個公式中,L表示入射光的集合,f表示該點的表面反射屬性集合。 

  L(l)表示直接光照,L(c)表示純粹的反射折射光,L(d)表示至少經歷了一次漫反射的入射光 

  f(s)表示鏡面反射或者規則透射brdf,f(d)表示漫反射brdf。 

  L*f的結果就是出射光的亮度,我們要做的就是如何快速的計算L*f。 

  我們把上面的等式分化一下: 

  L*f = L(l)*( f(s)+f(d) ) 

  + f(s)*L(c) + f(s)*L(d) 

  + f(d)*L(c) 

  + f(d)*L(d) 

  如果直接採用半球積分方程進行計算,需要大量的取樣,我們這種分化把半球積分分化為四部分,對不同的部分採用不同的辦法計算,這樣每一種都不會產生大量的取樣,合起來的計算複雜度遠遠低於原來不分開計算的。 

  (1)直接光源照射,反射屬性為所有。 

  (2)入射光源為鏡面反射或者規則透射或者漫反射,反射屬性為鏡面反射或者規則透射。 

  (3)入射光源為純反射或透射,反射屬性為漫反射。 

  (4)入射光源為至少經過一次漫反射的,反射屬性為漫反射。 

  對於(1),我們採用shadow ray的方法計算直接光照。 

  對於(2),我們採用經典Monte Carlo光線追蹤來計算。 

  對於(3),我們收集來自caustics map中的光子資訊。 

  對於(4),我們收集來自全域性map中的光子資訊。 

  這樣,一次典型的正向光線追蹤的計算就完成了。即使是photon map演算法,對於普通硬體,暫時也只能用於靜態渲染。但是我們依然可以把它用在遊戲中,比如在地圖編輯器中對靜態光源和大型靜態場景進行預渲染,如果光源是變化的,那麼對光源變化的過程取樣,渲染後在通過插值計算來模擬光源變化。通過基於光線追蹤計算出的影象,具有很高的光真實感,可以令使用者產生賞心悅目的感受。 

  [photonmap實時渲染方案的想法] 

  1 區別於靜態渲染,不是一次發射所有必須的光子,而是隻產生少量的光子,把相關資訊儲存在光子圖中,然後每楨逐步遞加光子,過了一定時間以後,就拋棄舊的光子資訊。 

  2 構造類似於windows髒矩形思想的髒光線演算法。 

  光線跟蹤是一種真實地顯示物體的方法,該方法由Appel在1968年提出。光線跟蹤方法沿著到達視點的光線的反方向跟蹤,經過螢幕上每一個象素,找出與視線相交的物體表麵點P0,並繼續跟蹤,找出影響P0點光強的所有光源,從而算出P0點上精確的光線強度,在材質編輯中經常用來表現鏡面效果。 

  光線跟蹤或稱光跡追蹤是計算機圖形學的核心演算法之一。在演算法中,光線從光源被拋射出來,當他們經過物體表面的時候,對他們應用種種符合物理光學定律的變換。最終,光線進入虛擬的攝像機底片中,圖片被生成出來。由於該演算法是成像系統的完全模擬,所以可以模擬生成十分複雜的圖片