為了增加情境的表現力,在渲染中添加光照效果必不可少。首先用某種演算法對整個情境產生光照渲染效果,然後再將光照渲染效果以紋理的方式來儲存,將情境中各個多邊形所對應的光照效果存於光照貼圖中,這樣的話在即時渲染時,只需要對情境在原有紋理渲染的基礎上添加多紋理的融合即可渲染出光照效果。然而這樣得到的光照貼圖是和情境中每個單一多邊形相對應的,因此若情境中有N個多邊形就需要對應N個光照貼圖,這樣在渲染時就需要對每個多邊形進行紋理綁定操作,受限於硬體的性質,這個操作是比較耗時,因此就影響了整個渲染的效率,於是就需要將每個多邊形對應的光照貼圖進行合并得到整個情境的光照貼圖從而避免渲染時的紋理切換。於是,整個情境光照貼圖的產生過程就包括:
1.
情境帶有光照的渲染
2.
單個多邊形光照紋理貼圖的產生
3.
整個情境中多邊形光照紋理貼圖的合并
情境帶有光照的渲染
這個操作是下一步產生每個多邊形的光照紋理貼圖的關鍵。一般來說遊戲中使用的預先處理光照效果都是全域光照,包含陰影、漫射等這些常規渲染管線無法達到的效果。由於採用預先處理來產生,故而也就需要光照演算法與視點無關,這樣才可以對整個情境產生光照效果。輻射度演算法就比較符合上述特點,因此就常被用來產生情境的光照效果,而且得到的光照效果很容易與每個多邊形進行映射得到光照紋理貼圖。
多邊形光照貼圖的產生
單個多邊形光照貼圖的產生與輻射度演算法的實現方式密切相關。輻射度演算法是將情境進行網格細分,然後對每個很小的面片進行光照著色。因此對於輸入的一個原始情境,在用輻射度演算法對其進行渲染之後即成為網格細化後的另外一個情境,但是這兩個情境之間很容易進行對應。在實現中,輻射度演算法的預先處理操作是用八叉樹來對情境進行劃分,根據這種劃分原則就很容易將一個原始多邊形與網格細分後的多個子多邊形進行對應。
所示即為對一個原始多邊形進行空間分割之後得到的多個子多邊形,輻射度演算法的操作單元即是這些子多邊形。得到每個子多邊形的著色效果之後再將多個子多邊形進行合并即可得到原始多邊形的光照著色紋理。
由於在情境分割時採用八叉樹,而它的分割平面具有軸對齊的性質,利用此性質就很容易將子多形與原始多邊形進行對應。將子多邊形與原始多邊形進行對應之後就需要由這樣的對應關係來產生原始多邊形的光照貼圖。在輻射度演算法產生全域光照時,由於其演算法代價比較大,因此空間分割不會進行得太細,否則子多邊形數量太多會導致全域光照產生時間消耗太大。這樣的話在由子多邊形合并產生原始多邊形的紋理時需另外確定紋理貼圖分割粒度,一般來說可以使光照貼圖的分割精細程度大於情境分割的精細程度,這樣在產生光照貼圖時只需要額外的紋理顏色插值就可以得到很平滑的光照貼圖。
如中所示的,藍色網格為多邊形紋理的分格網格,它比輻射度演算法中的情境分割、更細。
具體的光照紋理產生操作如下:
1.
將原始多邊形和它的所有子多邊形,根據其法向量旋轉到某個軸對齊平面上(XoY , XoZ , YoZ),這樣
在判斷紋理網格處的顏色值時就變成了退化為平面的AABB求交操作,更加高效。
2.
求出旋轉後的原始多邊形和子多邊形的AABB包圍盒(包圍平面)。
3.
根據原始多邊形包圍矩形的大小及紋理的精度確定紋理網格的尺寸,並對包圍矩形進行細分。
4.
對於紋理網格中的每小網格塊,遍曆整個子多邊形列表並用此小塊的包圍矩形與子多邊形的包圍矩形
進行求交,根據相交測試的結果來確定此小塊的最終顏色值。此處可以根據插值的範圍來確定需要進
行求交斷送的子多邊形的數目。
5.
用紋理網格及各網格塊的顏色來產生最終整個原始多邊形的光照貼圖。
6.
根據投影關係確定每個頂點的紋理座標。
多邊形光照紋理貼圖的合并
在完成上述操作之後即可以得到每個多邊形的紋理及多邊形的紋理座標。但由於情境中的原始多邊形的大小不定,得到的原始多邊形的光照貼圖大小也不定,且其變化範圍可能會很大,如果直接用這樣的多個紋理進行情境渲染時效率較低,也不能夠很好地利用硬體資源,因此就需要將各個多邊形的紋理進行合并得到整個情境的光照紋理。
紋理合并問題其實可以描述為這樣一個演算法問題:給定N個大小不等的矩形,將其排放在一起,相互之間不能重疊,求一種排布方式,使其按這種方式排布後得到的總地區面積最小。這其實是一個NP問題,找到一個最優解比較困難,因而只能用近似於最優解的方法來代替最優解。採用貪心演算法可以較好地解決這類NP問題。貪心演算法的求解方法即是每次操作都使問題向著最優的方向進行,這樣最終不一定能得到最優解,但往往很近似最優解。
將貪心演算法應用到紋理合併作業中可以得到這樣的一個操作原則:對於每個紋理,選擇一個能產生最少空白地區的位置來進行放置。對於操作時使用的資料結構,結合三維情境的空間分割操作,也可以用一種樹的結構來組織。對於每個待放置的紋理,應用貪心原則,對整棵樹進行遍曆來尋找一個最佳的位置然後放置並更新整棵樹即可。
具體操作如下:
1.
統計所有的紋理面積,求出最終紋理的寬度(或高度)maxWidth
確定原則如下:maxWidth = max(所有紋理中的最大寬度 , sqrt(allArea))
2.
初始化一個空白紋理: height = 0 , width = maxWidth並產生維護它的二叉樹結構。
3.
對於所有的紋理,根據其的高度進行排序,先處理高度大的,後處理高度小的。
4.
對於每個紋理,尋找其最佳的置放位置並更新樹中相關結點的資訊及整個樹的結構。
紋理的填充
樹結點的結構
注意:由於此演算法使用貪心演算法,而且首先確定了最終紋理的最大寬度值(否則兩個自由變數的增長,演算法無法控制),因而貪心的原則就轉變為使總紋理的高度值最小,因此將所有紋理按高度值進行排序就是必要的。否則得到的排布效果就比較差
末按高度排序
按高度排序
在完成上述操作之後即可得到合并出的整個情境的光照貼圖: