法線貼圖的一點理解

切線空間

假設三角形的三點爲P1 P2 P3,設該三角形所對應的一個空間爲(T,B,N),其中T和B構成的平面爲三角形所在的平面,而T和B分別對應U和V方向。

切線空間的作用是讓任何向量從Tangent Space變換到World Space。

法線貼圖

有時候需要模擬一些場景,比如帶有浮雕的牆面、帶有花紋的茶壺等,如果使用多個三角形模擬平面的凹凸感,則計算量較大並且影響性能。研究人員發現,人眼對於物體表面的凹凸感是通過表面光照明暗變化體現的。如果可以通過一幅貼圖實現光照明暗的變化,就能模擬平面表面的凹凸感。法線貼圖就是爲了解決這一問題而產生的。

使用法線貼圖可以使用更少的頂點表現出更多的細節。

紋理座標定義在切線空間中,U座標對應切線空間的T軸,V座標對應切線空間的B軸,頂點法向量N對應切線空間的N軸。

法線貼圖的原理

一般3D場景中模擬光照時是在世界空間中計算的,而從法線貼圖中取出的法線向量是位於切線空間的,採用法線貼圖時,必須將法線向量光照相關變量變換到同一空間中才能得到正確的結果。

一般有兩種方式:1、將法線向量變換到世界空間;2、將光照相關向量變換到法線向量所在的切線空間

  • 世界空間中計算:在頂點着色器中計算得到TBN矩陣, 並將其傳入到片元着色器,然後把採樣得到的法線用TBN矩陣從切線空間轉入到世界空間。這種方式是對每個片元進行變換。
  • 切線空間中計算:在頂點着色器中用TBN矩陣的逆矩陣將所有相關的世界空間向量轉化到切線空間,然後將切線空間下的光源位置、觀察位置、以及頂點位置發送給片元着色器。這種方式是對每個頂點進行變換。

效率對比

在着色器執行的過程中,影響頂點着色器效率的主要因素是輸入的頂點的個數。影響片元着色器效率的主要因素是傳入到片元着色器中的像素的個數。一般情況下,頂點着色器中輸入的頂點的數量遠遠低於片元着色器中輸入的像素的數量,所以爲了提高效率,都將在片元着色器中做的運算移到頂點着色器中去算,這樣會極大的提高其效率。

一般情況下在法線貼圖的計算中,都在切線空間中進行計算。

計算TBN矩陣

TBN矩陣是將任一向量從世界空間轉換到切線空間下。
計算過程:

// 將向量從切線空間轉到世界空間
vec3 N = normalize(mat3(Model) * in_Normal);
vec3 T = normalize(mat3(Model) * in_Tangent);
vec3 B = normalize(mat3(Model) * in_Binormal);// or vec3 B = normalize(corss(T,N));
mat3 TBN = mat3(T,B,N); 

通過這種轉換,就可以把在切線空間中Normal Map中的法線轉換到對應的世界空間下,與這個空間下的光線進行計算,從而能夠算出對應此點的正確光照。Normal Map的法線爲什麼存儲在“切空間”下,但它不能直接被使用。經過這樣的轉換,就可以與世界空間聯繫起來了。

不過經常用的,還不是把Normal值轉換到世界空間下,而是反過來,把光線“反”轉換到到切線空間下,與Normal計算出光照值。爲什麼?因爲一個模型可能有非常多的點,對應normal map中的normal值數量必然也是巨大的,如果把每一根normal都做轉換,這種計算量的成本是相當高的。但反過來,光線就那麼幾條,轉換一次光線就能給所有切線空間下的normal使用,相對來說這種計算要“便宜”得多,所以反轉光線值這種做法是更爲常見的做法。

因爲TBN矩陣中三個向量正交,那麼它的逆矩陣就是它的轉置矩陣:

// 將向量從世界空間轉到切線空間
vec3 N = normalize(mat3(Model) * in_Normal);
vec3 T = normalize(mat3(Model) * in_Tangent);
vec3 B = normalize(mat3(Model) * in_Binormal);// or vec3 B = normalize(corss(T,N));
mat3 TBNinverse = inverse(mat3(T,B,N)); 

注意在片元着色器中從法線貼圖中採出來的normal並不一定是歸一化之後的,所以需要:

// 根據傳入的法線貼圖使用得到其切線空間下的法線位置
vec3 getNormalFromMap()
{
    vec3 tangentNormal = texture(normalMap, TexCoords).xyz * 2.0 - 1.0;
    return normalize(tangentNormal); 
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章