1. 程式人生 > >置換貼圖,法線貼圖和凹凸貼圖詳解

置換貼圖,法線貼圖和凹凸貼圖詳解

Normal Mapping在遊戲領域中的實踐是一個非常值得記住的時期--Geforce3上市,GPU概念出現,硬體可程式設計流水線的出現(Shaders),Normal Mapping是一種凹凸貼圖技術,它的另外一個名字叫做Dot3 bump mapping。

用於實現它的控制紋理是一張叫做Normal Map的紋理,也是目前大家在討論如何製作的那種。我們先說說這張叫做Normal Map的圖。這張圖中儲存的東西是每個原始表面法線的迭代,說起來有點複雜,但是不難理解。舉例說我們的說面,一般在遊戲的3D模型上,表面法線就像是一根站立於桌面的鋼筆,垂直向上。而Normal Map中儲存的東西就是我們這支表示表面法線方向的鋼筆所“應該”指向的方向--比如說朝左邊傾斜15度。

Normal Map有兩種主要形式,一種叫做世界空間的Normal Map,一種叫做切空間的Normal Map。第一種在遊戲中沒有實用價值,我們說第二種,也就是大家最常見的一種。

那麼,為什麼我們看到的Normal Map會有這麼奇怪的顏色呢?其實Normal Map和Bump Map一樣,即它顯示出來的顏色和它所起的作用是沒有直接聯絡的。大家一定對空間座標的概念非常熟悉了。在Normal Map的定義中,有一個事先的約定,這個約定就是--原本表面的垂直方向,我們稱為Z軸;而表面的UV座標兩個方向,分別對應X軸和Y軸。(確切的說,應該是稱作切線和負法線,但是這兩個東西和大家熟悉的UV座標剛好重疊,所以就用大家更習慣的說法了)然後我們知道如果我們在XYZ軸上各取一個點,這個點的取值位置在-1到1之間,那麼我們就可以得到一個指向任何方向的法線方向(不用多解釋,大家知道法線是一個向量,向量有方向和長度兩個概念,但是對法線來說,長度是不需要的)。但是,請大家注意,我們在描述色彩的時候,RGB三個通道的取值範圍都是從零開始的。可是當我們嘗試把一個任意的法線儲存在一張紋理中的時候,會面臨取負值的問題。因此我們要把法線做壓縮。方法很簡單,把XYZ每個軸上的法線投影長度進行N+1/2的運算。這樣就把所有的法線壓縮到了0和1的範圍裡。然後我們把XYZ的方向分別儲存在RGB三個通道中。似乎我們還沒有說到關於為什麼Normal Map會是藍兮兮的原因是吧。那麼現在就是公佈結果的時候了!首先,我們知道如果在一個物體表面,法線垂直向上,那麼它的XYZ座標是多少?是0,0,1對不對?然後我們把這個數字按照我們前面所說的壓縮方法進行壓縮,每個數字加1然後再除以2,那麼我們得到的是0.5,0.5,1對不對?好我們把它代入到RGB中,那麼我們會得到128,128,255對不對?好了,試試看在調色盤裡的顏色吧!

P.S.現在FXCarl和你猜個謎,看看FXCarl說的對不對。現在我們在Normal Map上看見一個顏色,這個顏色是219,128,219。那麼這個表面的法線方向是垂直向右偏45度。大家用MAX做一個Normal Map看看FXCarl說的對不對?

如果你還沒有理解Normal Map的意思,或者說你有興趣再深入瞭解一些,那麼FXCarl再和你說的深入一些。不知道大家對於切空間的理解是什麼?我們來個實驗,找三支筆。然後其中兩隻筆在桌面放成互相成90度,筆尾接筆尾。最後我們把第三支筆,筆尖向上,筆尾和那兩隻桌面上的筆的筆尾疊在一個點上。注意看我們的三支筆!這三支筆就是這張桌面上這個點的切空間座標了!大家一定想到了原來我們的Normal Map中儲存的表面法線方向原來就是一個切空間向量啊,恩沒錯,就是切空間向量。但是似乎看起來切空間沒什麼作用是不是?呵呵,我們不妨把桌面換成一個籃球。記住,保持三支筆的互相關係,然後用三支筆並在一起的筆尾去接觸籃球的表面。呵呵,發現了沒有?切空間的優勢在於,在任意表面上,切空間中的座標都是有效的!也就是說始用切空間中的資料就可以做到和3D模型的複雜度無關!你可以用在任意的表面,甚至這個表面一直在動也不會影響到Normal Map發揮作用,你說這個切空間是不是很有用呢?

讓我們回到開頭,大家就會發現,如果使用世界空間的Normal Map會有什麼樣的結果呢?嘿嘿那樣會造成一個很尷尬的結果,比如說我們做了一個人物身上的Normal Map,可是我們的場景中有兩個一樣的人物,但是他們的姿勢和麵對的角度都不一樣。那麼……My God ~肯定有一個人物的Normal Map是沒法適用的!而用切空間的Normal Map就沒有問題了。恩,不過這個大家可以放心,MAX或者Maya做出來的Normal Map都是切空間的Normal Map,證明的方法很簡單……看看這張貼圖是不是主要由藍色構成的……

OK,下面是重頭戲,告訴大家Normal Map是如何發生作用的。

使用Normal Map的先決條件--逐畫素著色。先來說一下傳統著色,傳統遊戲使用的是一個Phong光照模型的簡化版,甚至有遊戲使用Ground模型。這兩種演算法的方式都是隻對物體3D模型的頂點計算光照,而3D表面上的大面積區域則使用差值填充。逐畫素著色是到了Shaders出現之後才有的,因此Normal Mapping也是一個Shaders必須的演算法。計算一個物體表面漫反射光照的公式是很簡單的NdotL--什麼是NdotL,就是物體表面的法線和光照方向的點積。點積是一個線性代數的問題,美術朋友們可以不用深究,寫成程式也很容易:Diffuse = saturate(Mul(Normal,Light));。想要簡單的理解就是--光線的方向向量在法線向量上的投影,然後這個投影的結果變成黑白中間的一個值。我們同樣舉個簡單的例子,用兩支筆放在桌面上,然後一支筆不動,令一支筆筆尾和第一支筆的筆尾相連,不動,然後以共同的筆尾做為圓心,移動筆。這時如果我們從一支筆尖往另外一支筆的筆桿上垂直拉一條線(一條垂線)就會看到這時移動後的一支筆在原本的筆桿所投影的長度(就是一支筆的筆尖連垂線到另一支筆的筆桿上的位置,這個位置沿著筆桿到共同筆尾的長度)會越來越短,當兩支筆垂直的時候,投影的結果就是零--沒有光照貢獻了。這個容易理解,當光線的方向和一個表面絕對平行的時候,這個表面就會再也接受不到光線了。現在我們引入NormalMap。這時我們的光照計算和以往有點不同,我們把表面的法線用NormalMap中儲存的法線來替代。這樣當我們在計算表面光照情況的時候,就會因為法線不斷的變化而產生比原來豐富的多的明暗變化。

至於為什麼會感覺出凹凸來這個就是人的眼睛自己騙自己了……其實那裡本沒有凹凸的,但是我們人眼睛太多管閒事了。就像Windows的按鈕哪個純平面的東西我們還以為是凸出來的呢。

Normal Map看來可以增加細節,但是它的缺點也很明顯。不過在說缺點之前,要提前說一句--Normal Map帶來的優勢是遠遠大於它的缺點的。因此仍然是個極好的東西,不要對它有偏見,特別是在我們後面介紹的更牛的技術前面,千萬不要。最大的也是最明顯的缺點應該就是它的視角問題。因為Normal Map只是改變的表面上的光照結果,並沒有改變表面上的形狀。因此,表面上看來,似乎只要是不接近水平,NormalMap就不會有視角問題。其實不然,NormalMap因為不能實現自身內部的遮擋,因此不能表現平面上凹凸起伏比較大的場合。比如說我們一個桌面上突出一塊,然後在突出的這塊東西邊上放一支牙籤。如果用Normal Map表現,會發現。根據經驗,這個凸起會很輕易的擋住我們的視線,讓我們看不見那支牙籤。可是Normal Map卻不會這麼做。因此我們一直能看見障礙物背後的東西,這一點是個問題--也就是說只有在垂直於平面的時候NormalMap才會發揮最好的作用。這樣一來,Normal Map只能用在大家對遮擋關係不敏感的場合,比如場景等,不是不能用於人物,而是用Normal Map的人物不太經得起特寫,放大了,角度刁鑽了都容易穿幫。

雖然Normal Map有個不能平視的巨大問題,但是依然是好處遠大於小障礙,因此還是非常值得推廣的。後面的幾種新興演算法其實都是由Normal Mapping發展起來的,因此做為基礎的東西,也還是最有理解價值的。

P.S.關於Normal Map的一點祕籍。注意理解……Normal Map其實並不是從低模的表面凸出高模的細節的,而是把高模中比最高點的位置低的地方凹進去的!因此低模要比高模大一點點才會很準。大家可以想像成我們是用一個比高模稍微大一點點尺寸的低模石膏模型來把高模雕刻出來的。
P.S.2.關於Normal Map的做法,其實早期發明Normal Map的時候還沒有MAX這種這麼方便生成Normal Map的方法,Normal Map都是從Bump Map計算得到的,因此其實通過很簡單的演算法就可以從Bump Map算出Normal Map的,甚至可以On The Fly(就是讓遊戲引擎直接讀BumpMap然後轉換成NormalMap)。因此對於一些建起模來效率很低,但是又能明顯增加表面細節的東西,例如水泥表面的顆粒,用畫Bump的方式來做是個更好的主意,然後交給技術美工去搞定好了--當然你會用Z-Bursh那就當我什麼都沒說了,呵呵。說來FXCarl估計MAX生成法線圖的方式也是比較高低模上每個點的高度偏移,然後生成每個UV圖素上的高度差來得到一個BumpMap,然後再從BumpMap變成NormalMap。

3.Parallax mapping 視差貼圖