1. 程式人生 > 其它 >【讀書筆記】計算機圖形學基礎(虎書)第11章 - 紋理對映(Texture Mapping)

【讀書筆記】計算機圖形學基礎(虎書)第11章 - 紋理對映(Texture Mapping)

除了物體的顏色以外,我們希望能夠模擬真實物體表面的各類細節(磨損、凹凸等)。這些細節會改變物體的質感,但是不會改變物體的整體形狀。我們把這類細節的總和叫做材質/紋理。為了讓相同的物體可以快速套用不同的材質,我們選擇的做法是將紋理製作成紋理圖(Texture Map/Image),再將物體表面的每一個點與這個紋理圖上的某一個點一一對應,使得物體表面獲得紋理圖的效果(一個比方是將一塊布包在球體上)。這個一一對應的過程叫做紋理對映(Texture Mapping)。這種操作方法除了形容物體表面以外,其後又發展出了很多應用,例如儲存每個點法向量的Normal Map,儲存光線方向的Light Map等,都稱為紋理。書中章節介紹了紋理對映在表面細節、陰影和反射上的應用。
需要注意的是

  • 紋理對映本身是一個取樣再重構的過程,所以會存在走樣的問題。
  • 我們要確保對映時模型和紋理圖的對應方式與對映方式較為接近。哪怕沒有走樣如果對映的方法錯誤也會讓最後呈現出來的效果不好(比如臉扭曲在一起,只長了眼睛其他都沒有等)。
  • 動畫和紋理對映結合時會有更嚴重的走樣問題,需要運用一些反走樣技術來降噪。

1. 查詢紋理顏色值

紋理對映是為了給頂點查詢出顏色或者其他屬性資訊,因此查詢紋理值的過程就是給定頂點,尋找其在紋理圖中的對應位置,再在該位置上提取值。其中第二步屬於簡單的提取畫素,因此更為重要的是第一步尋找對應座標。我們把第一步的對映稱為Texture Coodinate Function(紋理座標方程),它是一個三維表面S投影到二維紋理T的過程。我們通常把二維紋理用\((u,v)\in[0,1]^2\)

來表示,我們也把紋理座標叫做UV座標。

我們通常關注一下兩個問題

  • 紋理座標方程是如何定義的,是否合理。書中的例子是一個傾斜的木板紋路,我們應該用斜面一一對映還是直接使用xz平面的投影來一一對映。另外一個常見的例子是世界地圖,不同地區的大小會因為經緯度有所改變。
  • 提取出的紋理值會不會帶來走樣,見下圖,遠處出現了走樣問題。

2. 紋理座標方程

我們通常將紋理座標方程看做

  • 將三維表面慢慢捋順成一個平面的過程,例如地球地圖
  • 將二維UV圖通過形變覆蓋到三維表面的過程
    我們通常在以下幾點中進行權衡:
  • 雙射(Bijective),通俗來說就是一一對應。如果多個表面點對應同一個UV紋理點,則會放大UV走樣的問題。
  • 尺寸變形(Size distortion),紋理對映的單元間距最好保持同樣的比率,類似於線性變換的網格一樣,而不是有的密有的稀。
  • 形狀變形(Shape distortion),紋理圖中的某一個形狀在覆蓋到三維表面後也應該維持類似的形狀。
  • 連續性(Continuity),紋理圖中連續的點在三維表面中也是連續的。
    紋理座標方程對於平面而言較為簡單,直接利用引數方程(p+au+bv)中稍作修改即可。對於複雜的隱式表面而言,我們通常選擇利用幾何座標系進行計算。對於多個三角形或者巨集觀意義上的多面體表面,我們利用頂點的紋理座標插值進行處理。

2.1 幾何座標系計算

幾何座標系適用於簡單的形狀,或者手工除錯的起點。以下介紹了幾種方法

平面投影 Plannar Projection

平面投射基本和相機座標到標準空間座標的投影一樣,將表面簡單投影到二維UV平面上,這樣顯然存在維度的丟失,法向量方向的三維表面(類似於線性代數裡的null space)會擁有極其類似或者一樣的UV座標,造成下圖的結果。但是當表面是一個平面,厚度可忽略時就會有較好的結果。

球投影 Spherical Projection

球投影指的是如同地球地圖一樣,按照經緯度方式進行投影。我們採用球體座標(半徑,橫向角度。縱向角度)的話使用後兩者可以直接得出。注意這種方法球的兩極會存在聚集和失真現象,在中間又會因為運動過快產生縮小的現象。為了方便計算有以下公式。這個方法在兩個極上不連續。

圓柱投影 Cylindrical Projection

圓柱投影對球投影進行了一定的修改,改為一個軸+圓周運動的模擬,公式如下。上下公式對比為y值的差別(圓周到軸)。這個方法使得UV圖中兩個對邊聚集在兩個點上,形成了一些不連續性(可以想象一個地圖作為卷軸再把上下兩條邊粘起來)。這個也是標準地球地圖的製作方法,也被稱為墨卡託投影。

立方體投影 Cube Map

從球到圓柱,我們能減少尺寸和形狀變形,卻增加了不連續性。我們進一步在這個方向上行走便來到了立方體投影。立方體投影利用了六個不同的紋理拼接而成,具體可以參考我的世界。這個方法在所有邊上都是不連續的,但是在面上擁有極好的尺寸和形狀不變形。需要注意的是紋理投影函式和正方體的紋理設定必須一一匹配,否則可能會有錯誤的擺放情況(比如我的世界臉放反了)。這個方法在之後說的環境對映中也會用到。一個慣例是對於\((x,y,z)\)而言,它會被投影到絕對值最大的那個對應平面,例如\((-1,-0.5,-.5)\)會被投影到\(x=-1\)平面。其次\((u,v)\)從立方形中間往外看應該總在右下角。OpenGL版本(第二個圖)則是左上角。可以用手卡著一個手勢然後隨著視角移動來對照。

2.2 紋理座標插值

這個方法就是直接基於頂點上的紋理座標用重心座標來進行插值。這個方法非常的簡便好用,但是也會出現一些問題。如下圖所示,頂點插值的結果其實和將左邊的圖拼接黏貼是一樣的,那麼很顯然左圖最外圍輪廓均為不連續的,拼接起來會有問題。

如果三維座標和紋路座標不具有類似的形狀則會出現較大的形狀變形。

如果三維座標和紋路座標三角形面積的比率沒有被維持,那麼可能會出現較大的面積變形。

2.3 瓦片,捲回模式和紋理變換(Tile, Wrapping Modes and Texture Transformation)

因為一些計算問題或者設計模式,我們可能需要計算超出邊界的紋理值。我們有幾種不同的處理思路。

  • 當紋理只希望覆蓋一部分的表面,但整個表面與標準\([0,1]^2\)紋理空間一一對應,我們可以選擇製作一個帶有空白背景色而部分含有原紋理圖的新圖直接蓋到空間上,也可以選擇將紋理空間的尺寸放大對應至整個表面再最後用單位格代表那一小部分。
  • 對紋理超出界限的情況,我們可以選擇設定一個背景色,選擇最接近的值直接裁剪採集(clamp)或者利用迴圈/求餘的方式加以尋找(即最右邊超出一點和最左邊多一點是一樣的,稱為瓦片Tiling)。這兩種都被稱為處理越界紋理座標值的捲回模式。我們可以通過矩陣或函式的方式處理超出邊界的紋理座標。

2.4 透視視角下的紋理

從下圖我們可以看到,如果從螢幕空間進行線性插值我們會得到不正確的透視結果。左圖是正確透視而右邊是錯誤的。這個問題產生的主要原因是因為如果在螢幕空間進行紋理的線性插值,我們只知道四個頂點的UV值,插值在螢幕空間下將會是線性的,產生右圖的均勻結果。

我們實際上應該做的是在世界座標下進行紋理對映,再在螢幕空間中找到正確的點。就GAME101所說我們可以利用轉換矩陣的逆矩陣來在fragment shader處反向求值,也可以有如下的方法。推導公式可以見此連結

與其做對比的更簡單的方法是在透視轉換時便計算UV座標,即將UV座標與正常的向量[x,y,z,w]一起轉換,具體如下圖。

2.5 連續性與接縫 (Continuity and Seam)

之前提到過在閉合三維體(球體、柱、立方體)上進行紋理對映時,一個較為簡單的對映方法是將紋理圖的邊緣拼接在一起,形成一條接縫,這樣可以保證除了接縫外的點都擁有雙射(bijective)和連續的特徵。通常而言,我們複製接縫的點,並賦予他們截然不同的UV值(例如0和1代表紋理圖的左右兩側),這個方法與立方體8個頂點實際利用24個頂點來實現相同頂點不同面不同法向量的方法類似。在此方法下我們可以保證跨越接縫的兩點之間也擁有正常的插值,即類似 [0.8->1]->[0->0.2] 的分兩段插值。

3 紋理查詢中的反走樣

因為紋理對映涉及到紋理細節的投影以及基於有限畫素的重建,紋理對映也會涉及到常規取樣流程中各類困難,例如各類不同的走樣問題。因為紋理對映本身容易含有高度細節化的資訊,因為細節對應的高頻訊號所引起的走樣問題會更加明顯。與之前章節類似,解決走樣問題的方法包含但不限於超取樣(super sampling)、取樣與重建過濾器等。

3.1 Footprint of Pixel (以下均主要採取英文,簡單而言是 “螢幕空間內畫素在紋理圖所佔區域”)

在下圖我們可以看到,同樣是螢幕空間的畫素,他們在紋理圖中所佔據的區域有所不同。螢幕空間遠處的點因為透視佔據紋理圖更大的面積,導致遠處畫素擁有噪聲更高,隨機性更大的紋理值。即使紋理對映時需要一些不規則邊框的投影(例如圓形或者曲形),我們也通常利用線性近似對紋理值加以計算。即假設一個螢幕空間畫素內XY移動量為\(\Delta X, \Delta Y \in [-1,1]^2\),那麼其對應的紋理圖UV改變數可以由投影函式對應XY的偏微分計算出,即\(\Delta U = \Delta X \frac{\partial U}{\partial X} + \Delta Y \frac{\partial U}{\partial Y}\)\(\Delta V\)類似。當然這樣屬於一種線性近似,僅在較小範圍內擁有較高的準確度。這種線性近似會將螢幕空間的正方形投影至紋理圖的一個平行四邊形,並將其中的UV值進行平均化的操作。當平行四邊形面積較大且紋理圖較大時,這種平均化操作會帶來較大的開銷。因此我們也有下文提供的一系列優化方法。

3.2 重建濾波器

在紋理轉換中我們會更多的碰見在畫素格中散落分佈的情況,而不是恰好在畫素點上。現在我們採用的方法是bilinearly interpolation, 即已知輸入UV的情況下,尋找其UV值所在的紋理影象素格,並基於其四個角的紋理影象素顏色進行bilinear interpolation,程式碼如下。這個流程與不同尺寸圖片之間互相轉換的流程類似(超取樣等)。

3.3 Mipmap

詳情見GAMES101紋理對映部分,簡單來說與LOD(Level Of Details)類似,根據不同情況選用尺寸大小不同的紋理圖進行對映。不同紋理圖的大小應該以兩倍縮小來進行層次轉移,這樣可以保證紋理從高往低改變時不會落在高紋理的紋理畫素格內,而是一定落在高紋理畫素的值上。假設我們有1024*1024的圖片,那麼接下來的Mipmap層分別有邊長512,256,128,64,32,16,8,4,2,1。 Mipmap的設計意義和LOD有些差距,Mipmap是為了在搜尋一定區域內的紋理平均值時直接搜尋已經計算好的低清紋理圖,而LOD是為了省模型精度帶來的計算空間。因為mipmap只能搜尋正方形的平均值,長方形的變體稱為各向異性(anisotropic),還有各類不規則圖形的變體例如EWA過濾。從程式碼上可以看出,當我們的目標大小與偏移合適時,我們根據Footprint的長邊長度D來算出我們需要第幾層的Mipmap,並直接搜尋對應Mipmap層中的值。對於大小和偏移非恰好對準的情況可以搜尋GAMES101,簡單而言我們可以在Mipmap的臨近層之間形成trilinear interpolation。

4 紋理對映的應用

  • 更改PBR的引數,包括但不限於顏色,粗糙度,金屬度,高光等
  • 更改法向量
  • 更改模型,包括但不限於位移量(Displacement)
  • 儲存需要複雜計算的數值,例如點光源形成的shadow map(陰影圖)以及environment map(環境圖)。原文所說的shadow mapping和紋理的關係較弱,是光柵化中從光源出發繪畫出shadow map,儲存每個畫素格的光照深度。在螢幕空間中判斷某點是否處於某光源所帶來的影子中時,我們將該點在光源視角下的深度與光源的shadow map深度值進行對比,前者大於後者則說明發生了遮擋,因此在影子中。有些紋理圖是為了簡略計算光影,例如將天氣系統的24小時分別繪製一份陰影圖,並在時間流逝中進行插值來計算影子。Environment Map往往被用於計算球形物體收到某環境造成的反射效果,如果不是球形物體則加以變形,它類似一種假設總能反射時環境造成的底色變化。

5. 程式化生成(Procedural )三維紋理

三維紋理指我們不使用二維的紋理圖片,而是生成真正的三維模型來儲存紋理值。這種辦法可以有效的提高準確度,但是額外一維的空間會帶來大量的空間開銷。因此我們引入了程式化生成三維紋理的方法,即用數學語言或一個固定函式來快速生成整個紋理再加以查詢,而不是每次尋找對應值。

5.1 條紋

對於條紋的交替我們可以用週期性三角函式sin是否大於0來實現,條紋交替的頻率和長度則可以由三角函式的長度來改變,漸變等效果也可以用三角函式的直接值而不是是否大於0來實現。

5.2 噪聲

Perlin Noise(柏林噪聲),Worley Noise增加了更多的不規則度。這部分本書介紹的比較少,推薦看其他地方的更細節化的處理。值得注意的是噪聲有很多應用,比如遊戲中血液和屍體如何慢慢消失,以及程式化生成Minecraft的地形。