在上一頁的討論中,我們知道在 local lighting 的 model 中,打光的效果基本上是由觀察者的位置、物體相對於光源的位置、和物體的表面法向量這三者所決定的。基本上這有點複雜。為了簡化問題,我們可以先只考慮 ambient 和 diffuse。因為在一般的光源下,大部分物體的顏色有很大部分是由這兩者決定的。而 ambient 和 diffuse 又和觀察者的位置無關,所以處理起來就容易得多。
再來,我們還要把問題更加簡化:使用 directional light(即平行光源)。什麼是 directional light?基本上,directional light 的一個重要特性就是,無論物體的位置在哪裡,光源的方向都是一致的,也就是平行的,所以才會稱為平行光源。當然,真實世界中並沒有所謂的平行光源,所有的光源都應該是來自某個點,或是某個區域。不過,如果光源的位置非常的遠,它的表現就會很類似平行光。最簡單的例子就是太陽光。所以,使用平行光的模式來處理太陽光,是很適合的。
現在來看看在 directional light 下,ambient 和 diffuse 會有什麼性質。首先,ambient 和光源的位置是無關的,所以在 directional light 下也是一樣。而 diffuse 就比較有趣了。由於 diffuse 是由「光源方向」和「法向量」的內積求得,而在 directional light 下,光源方向又是常數(對所有的 pixel 來說),所以 diffuse 就等於是物體表面的法向量的函數,姑且就稱之為 "diffuse 函數" 吧!
現在要做的是 per pixel lighting。所以,簡單的說,就是要對每個 pixel,以它的法向量求出 "diffuse 函數" 的值。在 3D 繪圖中,函數很容易解決:用貼圖就可以了。因為,貼圖等於是一個「內插+查表」的動作,所以可以想成:在頂點指定貼圖座標為法向量,則三角形內部的顏色就是三個頂點的法向量的內插,再查表的結果。不過,現在大部分的顯示晶片都只支援平面的貼圖,所以只是二維向量的函數。可是,法向量卻是三維向量,那要怎麼辦呢?難道一定要有三維貼圖嗎?答案當然是否定的。因為在 diffuse 的公式中,有一點非常重要:法向量需是正規化的。所以,這表示法向量的長度並不會影響 "diffuse 函數" 的值,也就是說,原來有三個自由度的法向量,就變成只有兩個自由度了。
眼尖的讀者可能已經看出來,這個問題就和地圖的投影是同樣的。因為單位長度的法向量會形成一個球,所以 "diffuse" 函數的空間是在一個球上。若想要用平面的貼圖來表示這個函數,就需要一個投影的方法,把球面投影到平面上。不幸的是,不管是什麼投影方法,都會引進某種形式的奇異點(singularity),而且解析度也會在不同的地方,產生不同的扭曲現象。
在 3D 繪圖中,有一種專門的方法,用來處理這種和法向量有關的貼圖,就是 environment mapping(環境貼圖)。環境貼圖常常用來表現出物體反射周圍環境的現象。在這裡,我們遇到是同樣的問題,所以可以直接應用 environment mapping。不過,傳統上經常使用的 spherical environment mapping(球形環境貼圖),就是試圖把一個球面投影到平面上,因此會產生 singularity。在一般情形下,這個 singularity 並不是個問題,因為它總是背對者觀察者。但是,如果觀察者的位置改變,那這個 singularity 就會跑出來了。另外,spherical environment mapping 也和前面所提到的一樣,會有解析度扭曲的問題。
所以,基本上,spherical environment mapping 是不能用的。當然,如果有興趣的話,也可以試試使用其它的投影方式,例如 Gall's stereographic cylindrical projection。不過,這些投影方法通常都會有些問題,像是:觀察者不能動、有 singularity、嚴重的解析度扭曲情形、或是計算過於複雜。
另一個方法,是不要把它投影到一個平面,而是把它投影到六個平面。也就是說,用一個立方體包住球體。這樣一來,球體的每個面都能兼顧,不會有 singularity 的問題,解析度扭曲的情形也不會太嚴重。更重要的是,它可以轉,所以觀察者的位置可以改變。這樣的做法,就稱為 cubic environment mapping。不幸的是,目前有支援這個功能的顯示晶片並不多。
使用 cubic environment mapping 就可以做到 "diffuse 函數" 的功能。不過,現在還有一個問題:當物體移動位置時,光源相對物體的方向就會改變,"diffuse 函數" 當然也就改變了。可是總不能在光源或物體改變方向或位置時,就重新產生一個貼圖吧!這樣的工作量會非常大,而失去意義。不過,注意 diffuse 的計算方式:
diffuse 函數 = max(N.L, 0)
若光源相對物體改變了方向,例如 L 變成 RL(R 是一個 3x3 矩陣),那就會變成:
diffuse 函數 = max(N.RL, 0) = max(NTRL, 0) = max(L.RTN, 0)
也就是說,這樣就可以固定住光源,只要轉動法向量就可以了。所以,只要選定一個光源的方向,算出相對的 cubic environment map,如果光源的方向改變,就不需要重新計算這個 cubic environment map,只需要改變物體的法向量就可以了。
說了這麼多,理論上的東西已經差不多了。接下來就以一個簡單的例子,來說明這個