本文轉自http://www.unitymanual.com/forum.php?mod=viewthread&tid=5895
首先我想說,對于凹凸貼圖在計算機圖形領域中的研究,最早開始于70年代末,至今已經有接近30年曆史了。NormalMap隻是一種目前很流行的凹凸貼圖技術,而這裡将會介紹一些目前遊戲和在XBOX360和PlayStation3這種新世代主機上将會運用的凹凸貼圖技術。
BumpMapping 凹凸貼圖
做過CG的朋友一定比 FXCarl還要更早的認識BumpMap。這種貼圖是一種灰階圖,用表面上灰階的變化來描述目标表面的凹凸,是以這種貼圖是黑白的,如果節省空間的畫,甚至可以把貼圖的Alpha通道征用來用作Bump。值得注意的是,這種貼圖表面上存儲的東西是高度域--即每個點和原始表面的高度差,記住,每個點的顔色不是色彩,是高度,一個數值!是以,對這個貼圖做任何的操作都會影響到這個物體3d的外觀質感。不能憑感覺用事。
在遊戲中,所使用的算法确切的說應該叫做fake bump mapping ,假凹凸貼圖。因為在遊戲中BumpMap并沒有改變物體的表面而隻是影響光照的結果,欺騙眼睛而已。最簡單的做法是,直接把BumpMap疊加在已經渲染好的表面上,造成亮度上的擾動,進而讓人以為是凹凸的--這個很容易了解,把一面白色的牆面有技巧的部分劃成灰色就會變成蝕痕,這些諸位會比小的更擅長。而計算複雜度是基本加減法。這個所謂的 FakeBumpMapping 從Geforce2就開始硬體支援,但是從來沒有大範圍的應用過。
不過有趣的是,BumpMap這個東西卻從未過時,在後來的渲染算法中,其儲存表面高度域的特性仍然發揮着巨大的作用。我們後文再提
NormalMapping 法線貼圖
NormalMapping在遊戲領域中的實踐是一個非常值得記住的時期--Geforce3上市,GPU概念出現,硬體可程式設計流水線的出現(Shaders),NormalMapping是一種凹凸貼圖技術,它的另外一個名字叫做Dot3 bump mapping。
用于實作它的控制紋理是一張叫做NormalMap的紋理,也是目前大家在讨論如何之作的那種。我們先說說這張叫做NormalMap的圖。這張圖中存儲的東西是每個原始表面法線的疊代,說起來有點複雜,但是不難了解。舉例說我們的說面,一般在遊戲的3D模型上,表面法線就像是一根站立于桌面的鋼筆,垂直向上。而NormalMap中存儲的東西就是我們這支表示表面法線方向的鋼筆所“應該”指向的方向--比如說朝左邊傾斜15度。
NormalMap有兩種主要形式,一種叫做世界空間的NormalMap,一種叫做切空間的NormalMap。第一種在遊戲中沒有實用價值,我們說第二種,也就是大家最常見的一種。
那麼,為什麼我們看到的NormalMap會有這麼奇怪的顔色呢?其實NormalMap和BumpMap一樣,即它顯示出來的顔色和它所起的作用是沒有直接聯系的。大家一定對空間坐标的概念非常熟悉了。在NormalMap的定義中,有一個事先的約定,這個約定就是--原本表面的垂直方向,我們稱為Z軸;而表面的UV坐标兩個方向,分别對應X軸和Y軸。(确切的說,應該是稱作切線和負法線,但是這兩個東西和大家熟悉的UV坐标剛好重疊,是以就用大家更習慣的說法了)然後我們知道如果我們在XYZ軸上各取一個點,這個點的取值位置在-1到1之間,那麼我們就可以得到一個指向任何方向的法線方向(不用多解釋,大家知道法線是一個向量,向量有方向和長度兩個概念,但是對法線來說,長度是不需要的)。但是,請大家注意,我們在描述色彩的時候,RGB三個通道的取值範圍都是從零開始的。可是當我們嘗試把一個任意的法線儲存在一張紋理中的時候,會面臨取負值的問題。是以我們要把法線做壓縮。方法很簡單,把XYZ每個軸上的法線投影長度進行N+1/2的運算。這樣就把所有的法線壓縮到了0和1的範圍裡。然後我們把XYZ的方向分别存儲在RGB三個通道中。似乎我們還沒有說到關于為什麼NormalMap會是藍兮兮的原因是吧。那麼現在就是公布結果的時候了!首先,我們知道如果在一個物體表面,法線垂直向上,那麼它的 XYZ坐标是多少?是0,0,1對不對?然後我們把這個數字按照我們前面所說的壓縮方法進行壓縮,每個數字加1然後再除以2,那麼我們得到的是 0.5,0.5,1對不對?好我們把它代入到RGB中,那麼我們會得到128,128,255對不對?好了,試試看在調色闆裡的顔色吧!
P.S.現在FXCarl和你猜個謎,看看FXCarl說的對不對。現在我們在NormalMap上看見一個顔色,這個顔色是219,128,219。那麼這個表面的法線方向是垂直向右偏45度。大家用MAX做一個NormalMap看看FXCarl說的對不對?
如果你還沒有了解NormalMap的意思,或者說你有興趣再深入了解一些,那麼FXCarl再和你說的深入一些。不知道大家對于切空間的了解是什麼?我們來個實驗,找三支筆。然後其中兩隻筆在桌面放成互相成90度,筆尾接筆尾。最後我們把第三支筆,筆尖向上,筆尾和那兩隻桌面上的筆的筆尾疊在一個點上。注意看我們的三支筆!這三支筆就是這張桌面上這個點的切空間坐标了!大家一定想到了原來我們的NormalMap中存儲的表面法線方向原來就是一個切空間向量啊,恩沒錯,就是切空間向量。但是似乎看起來切空間沒什麼作用是不是?呵呵,我們不妨把桌面換成一個籃球。記住,保持三支筆的互相關系,然後用三支筆并在一起的筆尾去接觸籃球的表面。呵呵,發現了沒有?切空間的優勢在于,在任意表面上,切空間中的坐标都是有效的!也就是說始用切空間中的資料就可以做到和 3D模型的複雜度無關!你可以用在任意的表面,甚至這個表面一直在動也不會影響到NormalMap發揮作用,你說這個切空間是不是很有用呢?
讓我們回到開頭,大家就會發現,如果使用世界空間的NormalMap會有什麼樣的結果呢?嘿嘿那樣會造成一個很尴尬的結果,比如說我們做了一個人物身上的 NormalMap,可是我們的場景中有兩個一樣的人物,但是他們的姿勢和面對的角度都不一樣。那麼……My God ~肯定有一個人物的NormalMap是沒法适用的!而用切空間的NormalMap就沒有問題了。恩,不過這個大家可以放心,MAX或者Maya做出來的NormalMap都是切空間的NormalMap,證明的方法很簡單……看看這張貼圖是不是主要由藍色構成的……
OK,下面是重頭戲,告訴大家NormalMap是如何發生作用的。
使用NormalMap的先決條件--逐像素着色。先來說一下傳統着色,傳統遊戲使用的是一個Phong光照模型的簡化版,甚至有遊戲使用Ground模型。這兩種算法的方式都是隻對物體3D模型的頂點計算光照,而3D表面上的大面積區域則使用內插補點填充。逐像素着色是到了Shaders出現之後才有的,是以NormalMapping也是一個Shaders必須的算法。計算一個物體表面漫反射光照的公式是很簡單的NdotL--什麼是NdotL,就是物體表面的法線和光照方向的點積。點積是一個線性代數的問題,美術朋友們可以不用深究,寫成程式也很容易:Diffuse = saturate(Mul(Normal,Light));。想要簡單的了解就是--光線的方向矢量在法線矢量上的投影,然後這個投影的結果變成黑白中間的一個值。我們同樣舉個簡單的例子,用兩支筆放在桌面上,然後一支筆不動,令一支筆筆尾和第一支筆的筆尾相連,不動,然後以共同的筆尾做為圓心,移動筆。這時如果我們從一支筆尖往另外一支筆的筆杆上垂直拉一條線(一條垂線)就會看到這時移動後的一支筆在原本的筆杆所投影的長度(就是一支筆的筆尖連垂線到另一支筆的筆杆上的位置,這個位置沿着筆杆到共同筆尾的長度)會越來越短,當兩支筆垂直的時候,投影的結果就是零--沒有光照貢獻了。這個容易了解,當光線的方向和一個表面絕對平行的時候,這個表面就會再也接受不到光線了。現在我們引入NormalMap。這時我們的光照計算和以往有點不同,我們把表面的法線用NormalMap中存儲的法線來替代。這樣當我們在計算表面光照情況的時候,就會因為法線不斷的變化而産生比原來豐富的多的明暗變化。
至于為什麼會感覺出凹凸來這個就是人的眼睛自己騙自己了……其實那裡本沒有凹凸的,但是我們人眼睛太多管閑事了。就像Windows的按鈕哪個純平面的東西我們還以為是凸出來的呢。