遞資料方法 uniform 將場景資料傳到 shader 上;第四節主要使用 texture memory 上資料傳遞的方法,改進 uniform 上的一些限制;第五節針對光線追蹤的演算法 入 vertex shader 去做設定,接著將設定好的像素位置資訊傳入 fragment shader 中,
如此就可以從定義的眼睛位置去對每個像素去打光線,然後執行遍歷、找交點以 及最後的渲染步驟,對每個像素去做填色。
整個光線追蹤的演算法主要都是在 fragment shader 中完成,最初步的定義球 場景也是在裡面直接宣告完成,這算是光線追蹤的一項優勢,給定球心的座標及 半徑後就可以畫出球的模型,其幾何圖形的片數僅計算為一片,相較於傳統光柵 化的內建球方式,省下不少處理場景幾何圖形的時間,有了球的模型我們就可以
在上面做打光、反射或折射等等的效果,而這邊我用的是 Phong Lighting 的打光 效果,搭配陰影去呈現整個畫面。
圖 3.1.1: GLSL 光線追蹤流程圖
第二節 傳統光柵化之相關設定
在前面第二章第二節有介紹傳統光柵化的基本概念,他是計算機圖學領域中 最基本的繪圖方式之一,而我使用的傳統光柵化程式主要是為了與 GLSL 光線追 蹤去做效能的比較,其撰寫的版本只有一個,而為了達到比較公平的測試條件,
其場景的設定與光線追蹤的調整成幾乎一模一樣,包含眼睛(相機)的座標位置、
場景中模型的座標位置、光線的座標位置以及模型的顏色等等,但畢竟是兩種不 同的渲染方式,兩者畫面上有一些細小的差異,但我認為對整體效能的比較影響 不大。
第三節 讀入場景之資料傳輸
由於光線追蹤的場景如果只有定義球會顯得過於單調,為了增加場景的複雜 度,讀入 obj 是必須要做的事情,但是讀入檔案只能在 Host 端實作,所以如何 把資料傳入 shader 中是一大重點。GLSL 資料傳輸的種類有三種:uniform、attribute 和 varying,uniform 是用於 Host 端與 fragment shader 之間的資料傳遞,attribute 則是 Host 端與 vertex shader 之間的資料傳遞,而 varying 是 vertex shader 與 fragment shader 之間的資料傳遞,由於光線追蹤是實作在 fragment shader 上,所 以資料傳遞自然就用 uniform 的方式,將 obj 檔案中的點座標及面的資訊傳進 shader 中再去作組合,最後就直接用三角片的交點判斷、Phong lighting,渲染出 欲呈現的場景畫面。
雖然最直覺的資料傳遞方法就是 uniform,但是他存在記憶體大小限制的問 題,uniform 其實可以想像為快取記憶體(cache memory),通常在使用的時候是傳 遞一些參數,像是動作參數或是數學運算的參數,不太會用來傳遞數量較多的值,
因此他可暫存的大小並不多,例如我程式中傳遞一個方塊的模型需要用到大小為 36 的陣列、cornel box 需要大小為 96 的陣列,這些都還存的進 uniform 的記憶體 中,但是如果要存入一個簡單的球模型,其需要 360 的陣列大小,此時程式就會 出現陣列過大的錯誤。這方面可以利用 texture memory 來解決,這將會在下一節 做詳細的介紹。
第四節 Texture memory 之使用
除了本章第三節所提及的 uniform 方法,還有一個方法可以傳遞資料到 fragment shader,那就是使用 texture memory。Texture memory 是一塊專門用來做 繪圖相關的記憶體,例如以 OpenGL 來說,他可以將讀入的材質貼圖以 RGB 的 形式存在該記憶體中,然後 fragment shader 中就可以利用像是 texture()或
texelFetch()等等的指令去把他讀取進來做處理。一般來說,texture memory 是用 來使用像是模型做貼圖,或是對整個場景背景做貼圖,但是針對光線追蹤,我把 texture memory 拿來當作一般的記憶體空間,將資料以陣列的方式傳入 shader 中 而非以材質的模式,而如何把資料存入並且做連結,這邊會使用一種資料結構來 處理。
在 fragment shader 中抓取 texture 的資料結構如圖 3.4.1 所示,根據 obj 檔案 內的格式,一個面有三個點的編號資訊,一個點有 x、y、z 三個點的座標,圖中 的三角片清單材質(Triangle List Texture)是將面裡所有資訊存成一個材質,三角片 點材質(Triangle Vertex Texture)是將所有點的座標資訊存成一個材質,在 fragment shader 中首先先使用 texelFetch()抓取面中的三角片編號資訊,因為剛好是三個參 數,所以我們可以把 RGB 三個通道都打開做使用,分別存這三個三角片的編號,
接著將這些編號分別當作參數再使用一次 texelFetch()去抓取該三角片的三個頂 點座標,如此就可以得到我們所需的三角片,進行光線追蹤的後續步驟。雖然 texture memory 也有限制,但是可以跑到一定數量的三角片,就我測試的資料可 以跑到 8000 片三角片,比 uniform 的多了好幾倍。此外,我所使用的材質類型 為一維,也就是一個寬為 1 的一維陣列,雖然最大可以使用二維材質,但是使用
二維材質會使整個效能降低,所以為了維持效能而選擇使用較小的二維材質。
圖 3.4.1: 資料結構示意圖
第五節 光線追蹤演算法之優化
光線追蹤實作在 shader 上是一種間接使用 GPU 的幫助來加速的方法,屬於 硬體層面,其影響效能的部分佔比較大的比例,但是從軟體的角度來切入,也有 許多演算法可以使效能提升,最有名的加速結構包括 kd-tree、BVH 和 grid 的方 式,但是實作在 shader 上面各有利弊,例如 kd-tree 必須傳入大量的樹節點資訊 及邊界資訊,這對 texture memory 也是種負擔,而且當程式為互動式的時候效能 就沒有想像中好,因此,我使用的是最基本的 bounding box 的測試來優化。
由於光線追蹤是對每個像素逐一做填色的動作,所以當畫面像素一多的時候,
打的光線數也越多,效能會越差,然而真的有必要每個像素都去做處理呢?其實 不然,我們只需要對有場景覆蓋的像素做處理就好,因此這裡使用 bounding box 的方式,如圖 3.5.1 所示,先找出整個場景的最大值座標和最小值座標,根據這 個座標我們可以把場景像是關在一個盒子裡面,判斷當我的光線有打在這個盒子
裡面的時候才做後續的步驟,否則不做任何事或是直接畫出背景的顏色,這個方 法可以省略不必要的像素,減少交點判斷和陰影判斷等比較花時間的指令,略為 提升整體的效能,不過其缺點是當場景為封閉式或是幾乎填滿整個畫面,比如說 很多人都有使用過的 cornel box 場景,bounding box 測試效果就會比較差,因為 幾乎所有像素都要做處理。
圖 3.5.1: bounding box 示意圖,在邊界外的像素就填成紅色,整個場景像是被關 在一個盒子裡面,減少不必要的像素處理。