一開始讀入 Shader 時,需要編譯兩份 Shader,分別是要運用在產生虛擬點光 源和繪製場景,兩份都具有自己的 Vertex Shader 和 Fragment Shader;再來是讀入 模型,由於實驗時是使用 Sponza 場景,場景中包含多個顏色區塊,並且有類似騎 樓的陰暗區適合觀察全域照明的效果,因此使用 Assimp(讀取模型資料的函式庫) 加上 DevIL(讀取圖片資料的函式庫)來完成讀入模型的工作。然而接下來的四大 步驟其實都是在操作光容積資料,下一段會詳細說明。
4.2 光容積的資料結構與使用
首先在 CPU 端初始化儲存光容積的資料結構,本系統利用 Vector 串接三維 光容積的資料,例如 vector<SH> RedColorSHs,其中儲存的資料就是球諧函數的 四個係數,可以使用 struct 包起來,上一章有提到有三種顏色,所以需要三組 Vector 容器去裝光容積資料,這裡使用 Vector 的好處是可以方便接受不同大小的光容積,
像是32 × 32 × 32、10 × 10 × 10等等。
下一步產生虛擬點光源時,使用 GPU 和 OpenGL 的繪圖管線來運算,依照 Reflective Shadow Map(RSM)的演算法,把光源當眼睛並且使用單一 FrameBuffer 輸出多張貼圖,每張貼圖都儲存不同的資訊,深度(Depth)、位置(Position)、光源 方向(Light)、法向量(Normal)和顏色(Color),最後再使用 glGetTexImage 把資料從 貼圖中抓出來;如下表 4.2.1,此為一個光源在 Sponza 的正上方時,利用 RSM 所 產生的資料並輸出的多張貼圖。
深度(Depth) 位置(Position) 光源方向(Light)
法向量(Normal) 顏色(Color)
表 4.2.1:RSM 產生的資料
接下來兩個步驟,虛擬點光源注入光容積和光能量傳遞都是直接使用 CPU 端運算。注入光容積時,用一個迴圈對所有虛擬點光源做取樣,判斷該點光源是 屬於哪一格光容積後,就依照上一章分析的公式壓縮進去,如下圖 4.2.1,白點即 為虛擬點光源,白線為光容積網格。
圖 4.2.1:注入光容積
光能量傳遞時,必須先設定一個傳遞次數值,它關係到間接照明的範圍,同 樣的依照傳遞公式越遠的會被遞減,用一個迴圈重複執行光能量傳遞,對所有光 容積做檢查並傳遞出去,算是一次傳遞,執行完傳遞次數即可。如下圖 4.2.2,左 圖傳遞次數為 0、中圖傳遞次數為 1、右圖傳遞次數為 2。
圖 4.2.2:光能量傳遞次數示意圖
最後一個步驟繪製場景,同樣使用 GPU 和 OpenGL 的繪圖管線來運算,除 了最基本的場景資料要傳送給 Shader,也要傳遞光容積的資料,正好在 Shader 裡可以運用 sampler3D 這資料型態,這個指的是說三維貼圖(不同於 sampler2D),
每個像素可以儲存四個浮點數(RGBA),可以放入球諧函數的四個係數,並且三維 的特性與光容積非常相容,此時要傳遞三個 sampler3D 參數,一樣是代表紅色、
綠色、藍色,最後要運用該資料還原光輻射時,使用 texture3D(Texture, index).xyzw
即可抓取到資料。如下圖 4.2.3,需要還原三組光容積。