LightMap 生成

爲了增加場景的表現力,在渲染中添加光照效果必不可少。首先用某種算法對整個場景生成光照渲染效果,然後再將光照渲染效果以紋理的方式來存儲,將場景中各個多邊形所對應的光照效果存於光照貼圖中,這樣的話在實時渲染時,只需要對場景在原有紋理渲染的基礎上添加多紋理的融合即可渲染出光照效果。然而這樣得到的光照貼圖是和場景中每個單一多邊形相對應的,因此若場景中有N個多邊形就需要對應N個光照貼圖,這樣在渲染時就需要對每個多邊形進行紋理綁定操作,受限於硬件的性質,這個操作是比較耗時,因此就影響了整個渲染的效率,於是就需要將每個多邊形對應的光照貼圖進行合併得到整個場景的光照貼圖從而避免渲染時的紋理切換。於是,整個場景光照貼圖的生成過程就包括:

     1.       場景帶有光照的渲染

     2.       單個多邊形光照紋理貼圖的生成

     3.       整個場景中多邊形光照紋理貼圖的合併

 

場景帶有光照的渲染

這個操作是下一步生成每個多邊形的光照紋理貼圖的關鍵。一般來說遊戲中使用的預處理光照效果都是全局光照,包含陰影、漫射等這些常規渲染管線無法達到的效果。由於採用預處理來生成,故而也就需要光照算法與視點無關,這樣纔可以對整個場景生成光照效果。輻射度算法就比較符合上述特點,因此就常被用來生成場景的光照效果,而且得到的光照效果很容易與每個多邊形進行映射得到光照紋理貼圖。

 

多邊形光照貼圖的生成

單個多邊形光照貼圖的生成與輻射度算法的實現方式密切相關。輻射度算法是將場景進行網格細分,然後對每個很小的面片進行光照着色。因此對於輸入的一個原始場景,在用輻射度算法對其進行渲染之後即成爲網格細化後的另外一個場景,但是這兩個場景之間很容易進行對應。在實現中,輻射度算法的預處理操作是用八叉樹來對場景進行劃分,根據這種劃分原則就很容易將一個原始多邊形與網格細分後的多個子多邊形進行對應。

RadiosityGrid       RadiosityTexture

上圖所示即爲對一個原始多邊形進行空間分割之後得到的多個子多邊形,輻射度算法的操作單元即是這些子多邊形。得到每個子多邊形的着色效果之後再將多個子多邊形進行合併即可得到原始多邊形的光照着色紋理。

由於在場景分割時採用八叉樹,而它的分割平面具有軸對齊的性質,利用此性質就很容易將子多形與原始多邊形進行對應。將子多邊形與原始多邊形進行對應之後就需要由這樣的對應關係來生成原始多邊形的光照貼圖。在輻射度算法生成全局光照時,由於其算法代價比較大,因此空間分割不會進行得太細,否則子多邊形數量太多會導致全局光照生成時間消耗太大。這樣的話在由子多邊形合併生成原始多邊形的紋理時需另外確定紋理貼圖分割粒度,一般來說可以使光照貼圖的分割精細程度大於場景分割的精細程度,這樣在生成光照貼圖時只需要額外的紋理顏色插值就可以得到很平滑的光照貼圖。

 

SkinGrid

如上圖中所示的,藍色網格爲多邊形紋理的分格網格,它比輻射度算法中的場景分割、更細。

具體的光照紋理生成操作如下:

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.       對於每個紋理,尋找其最佳的放置位置並更新樹中相關結點的信息及整個樹的結構。

 

EmptySkin                       PSTreeNode

紋理的填充                                                   樹結點的結構

 

注意:由於此算法使用貪心算法,而且首先確定了最終紋理的最大寬度值(否則兩個自由變量的增長,算法無法控制),因而貪心的原則就轉變爲使總紋理的高度值最小,因此將所有紋理按高度值進行排序就是必要的。否則得到的排布效果就比較差

 

 

NoHeightSorted                 HeightSorted

    末按高度排序                                                                         按高度排序

 

在完成上述操作之後即可得到合併出的整個場景的光照貼圖:

 

PackedSkin

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章