1、概述
上一篇博客,3D游戲常用技巧Normal Mapping (法線貼圖)原理解析——基礎篇,講了法線貼圖的基本概念和使用方法。而法線貼圖和一般的紋理貼圖一樣,都需要進行壓縮,也需要生成mipmap。但是由於法線貼圖存儲的是法線信息,壓縮和生成mipmap的方法自然會有所變化。
現在已經許多用於法線貼圖壓縮和生成mipmap的工具,大部分商業游戲引擎也集成了相關方法,只需要點幾下鼠標就可以完成。本文僅針對法線貼圖的紋理壓縮和mipmap的方法進行原理性的說明,至於在具體的工具中如何操作,可以參看相關工具的說明文檔。
法線貼圖壓縮的中文資料還是比較多的,也不太復雜;但是生成mipmap的方法中文資料不多,《Real-Time Rendering 3rd》講解的比較詳細,本文的這部分內容主要來源於這本書,如果想詳細了解的,可以看原書,網上有電子版。
2、法線貼圖的壓縮
傳統的jpg等壓縮方式解壓時間太長,且壓縮比不固定,所以在實時渲染中一般采用DXTC及其改進方法,簡單來說,就是把4*4的像素當做一個Block,對其進行簡化表示,詳情參見百度百科http://baike.baidu.com/view/736449.htm。由於法線貼圖存儲的數據並不是RGB信息而是法線方向,所以需要在一般紋理壓縮的方法的基礎上進行一定的改變。
壓縮的第一步很簡單,由於歸一化的法線長度為1,且在切線空間下,法線的z分量不可能為負數,所以只需要存儲x和y值即可。本文的壓縮方法在這一步壓縮的基礎上,利用現有的紋理壓縮方法,進行進一步壓縮。
在支持DirectX10的顯卡上,可以使用BC5格式進行壓縮。BC5的壓縮方法內存情況如圖1所示,該格式有兩個顏色通道(R和G),每個通道使用兩個1Byte的值來表示,每個像素使用3Bit在這兩個顏色值之間進行插值。將法線貼圖中每個法線的x和y值利用BC5格式進行壓縮,如圖2所示。對每個Block(16個像素)存儲x的最大、小值和y的最大、小值,然后每個像素利用3Bit進行插值,相當於在圖2右圖所示的8*8區域內取樣(為了簡化表示,圖2只畫了4*4點)。
圖1 BC5壓縮方法
圖2 法線貼圖壓縮示意圖,右圖框內應該是8*8個點,為了畫圖方便簡單表示為4*4
對於不支持DirectX10的顯卡,可以使用DXT5格式進行壓縮(DXT5為DirectX9.0的紋理壓縮格式,如果連DirectX9.0都不支持,建議直接送博物館),將法線的x和y值存儲到紋理的alpha和Green通道即可。之所以是存儲到這兩個通道,而非其他通道,是因為每個DXT5中每個Block選擇的兩個參考像素alpha通道有8Bit,RGB通道分別為5、6、5Bit,所以使用alpha和Green通道可以獲得較高精度。
3、法線貼圖的mipmap
使用一般紋理mipmap方法生成的法線貼圖對於漫反射表面基本沒問題,但是在鏡面表面會導致嚴重的視覺問題。對於漫反射表面來說,光照的計算公式為l·n,l為光線方向的相反方向,n為法線,l·n1 + l·n2 + l·n3 + l·n4 = l·(n1 + n2 + n3 + n4) / 4,而mipmap則是事先計算(n1 + n2 + n3 + n4) / 4,所以對於漫反射表面,對法線貼圖使用傳統方式的mipmap基本沒問題。為什么是基本沒問題而不是完全沒問題呢?因為這里存在一個近似,若l·n < 0,則光照值為0(光照不能為負),若將這個因素考慮進去,漫反射表面也會有問題,不過在實際當中這種情況表現不明顯,所以可以認為基本沒問題。
對於鏡面表面來說,當視線偏離反射光線方向的時候,光照強度會急劇下降,反映在公式中是因為其含有cosm(h·n)項(具體公式可以Google),而漫反射光照是線性變化,所以對於鏡面表面,不能使用傳統方法生成法線貼圖的mipmap。法線貼圖對於鏡面反射的mipmap如圖3所示,第一幅圖中有4個像素,每個像素有法線和鏡面反射波瓣(紅色的是法線,周圍一圈是鏡面反射波瓣,鏡面反射波瓣用於表示不同方向的反射強度)。圖2中間部分,表示正確的mipmap情況,分別從4個像素合並為2個像素,從兩個像素合並為1個像素。而現有的方法中,沒有方法可以做到這樣的mipmap,所以只能用其他方法進行近似。
圖3 法線貼圖的mipmap示意圖
圖2的底部左圖,是使用一般紋理的mipmap方法對法線進行平均,可以看到這種方法產生出的鏡面反射波瓣和正確的鏡面反射波瓣差距很大,其根本原因是使用線性方法對非線性的參數進行計算。圖2底部圖右圖,每次在平均法線的同時,改變表面的光澤度(即改變鏡面光公式中的m),雖然最終結果與正確的mipmap有一些差距,但是比一般紋理的mipmap的方法要好很多。
所以,對法線貼圖的mipmap方法之一,就是在使用一般紋理的mipmap方法對法線進行平均的同時,每張mipmap都必須附帶一張光澤貼圖(gloss map),記錄每個像素點的光澤度(即m),m的計算原則就是讓最后的鏡面反射波瓣與正確的鏡面反射波瓣最接近。當然,還有其他很多方法能得到不錯的結果,具體可以參看《Real-Time Rendering 3rd》,或去搜索相關論文。
參考資料
[1]Akenine-Möller T, Haines E, Hoffman N. Real-time rendering 3 [M].