Per Pixel Lighting [Part 4]

来源:互联网 发布:格式化硬盘还能恢复数据吗 编辑:程序博客网 时间:2024/05/17 11:32


上一頁中,已經把理論的部分說明得差不多了。這裡則會開始討論實作的細節部分。

首先,先來看看我們的例子:光源是 directional light,物體則是一個「膠囊」,並有貼圖。如下圖所示:

A capsule on vertex lighting

這個物體的三角面數目並不多。基本上,它的橫切面是八邊形,而兩端的球形部分則各切成四段。所以,整個算起來有 128 個三角面。這個圖並不大,但是已經可以看到討厭的 Mach band effect。另外,還有一些 color bleeding 的現象,也就是在應該是背光的地方,卻因為內插的關係,而顯得過亮了。相反的,也有一些地方應該是面對著光源,卻顯得過暗。

現在把前一頁所討論的方法,用在同樣的東西上面。得到的結果變成:

A capsule on pixel lighting

看起來好多了。不過,雖然在討論理論的部分,看起來並不會太複雜,但是實際上的 3D 硬體是有限制的,所以並沒有這麼容易。

無論如何,要做到這樣的 per pixel lighting,最重要的是產生 "diffuse 函數",也就是用來查出 diffuse 亮度的 cubic environment map。為了簡單起見,可以把光源的方向定為 <0, 0, 1>,在 Direct3D 的座標系中,是朝向螢幕的方向的光源。

再來是產生 cubic environment map。產生的方法很簡單:對 cubic environment map 的六張貼圖,對其每個 texel 算出相對應的法向量後,把這個法向量 normalize,再把它和 <0, 0, 1> 求內積(就是取其 Z 的部分)。若結果小於 0,則設為 0。最後,把該 texel 的 RGB 三個 components 都設成計算的結果就可以了。

不過,實際上是會有很多麻煩的。首先,是貼圖的格式。各種顯示晶片支援的貼圖格式可能都會不太一樣。以 DirectX 8 所支援的格式來看,就有 32 bits、24 bits、16 bits、8 bits 等等許多不同的格式,而且像是 32 bits 就有兩種不同的格式,而 16 bits 有更多不同的組合方式。所以要處理這些東西是很麻煩的。為了避免這種問題,一個比較簡單的方法,是把 cubic environment map 分成六個不同的 BMP 檔案,再用 DirectX 8 SDK 的 Texture Tool 把它們組合成一個單一的貼圖檔(DDS 檔)。這樣一來,就可以在程式中用 D3DXCreateCubeTextureFromFile這個函式來讀取這個貼圖檔。這個函式會自動處理各種不同的貼圖格式,所以相當方便。

下圖是一個產生好的 "diffuse 函數" 的例子:

 Positive Y  Negative XPositive ZPositive XNegative Z Negative Y  

如果不想自己寫程式來產生這個 cubic environment map 的話,也可以從這裡下載。這是光源方向為 <0, 0, 1> 大小為 128x128 的 cubic environment map。

把 "diffuse 函數" 弄好之後,再來就是計算打光效果的部分了。不過,物體的 3D 模型還需要一些準備。基本上,因為 Direct3D 的自動產生貼圖座標的功能中,並沒有「複製法向量」的功能,所以只好自己指定一組三維的貼圖座標,並把它們設定成和各個頂點的法向量相同。當物體在世界座標系中有移動時,把移動的矩陣設定成貼圖座標轉換的矩陣就可以了。如果光源有轉動,則把光源轉動的矩陣做轉置,再乘到貼圖座標的轉換矩陣上。這樣 3D 模型的部分就算是準備好了。

接下來就是繪製結果了。首先是 ambient 部分,這部分很簡單,把光源的 ambient 顏色和物體的 ambient 顏色相乘就可以了。把算好的顏色放到貼圖常數中,和物體的貼圖相乘,就完成 ambient 的部分了。這部分得到的結果是這樣:

Ambient of the capsule

再來是 diffuse 部分。先把光源的 diffuse 顏色放到貼圖常數中,把貼圖座標轉換的功能開啟,Direct3D 就會把貼圖座標乘上之前設定好的轉換矩陣。因為之前已經把貼圖座標設定成和法向量一樣,所以當物體在轉動時,貼圖座標也要跟著「轉」。指定貼圖為之前產生好的 cubic environment map,再和物體的貼圖相乘,再乘上貼圖常數。最後,用 alpha blending 的方式,把 D3DRS_SRCBLEND 和 D3DRS_DESTBLEND 都設為 D3DBLEND_ONE,就可以和之前的 ambient 結果加起來,得到最後的結果。整體來說,整個動作是:

STAGE 1: TFACTOR * COLOR_TEXTURE 
STAGE 2: CUBIC_TEXTURE * COLOR_TEXTURE * TFACTOR

這裡使用的是 two pass 的方式。當然,它其實只用到兩個貼圖,所以,如果 3D 硬體支援適當的功能的話,這兩個動作是可以合在一起的,在 one pass 就可以完成了。