3D數(shù)學-基礎紋理

3D數(shù)學-基礎紋理

好記性不如爛筆頭啊,還是記錄一下!


概述

紋理最初的目的就是使用一張圖片來控制模型的外觀。使用紋理映射(texture mapping)技術,我們可以把一張圖“黏”在模型表面,逐紋素(texel)(紋素的名字是為了和像素進行區(qū)分)地控制模型的顏色。

在美術人員建模的時候,通常會在建模軟件中利用紋理展開技術把紋理映射坐標(texture——mapping coordinates)存儲在每個頂點上。紋理映射坐標定義了該頂點在紋理中對應的2D坐標。通常,這些坐標使用一個二維變量(u,v)來表示,其中u是橫向坐標,而v是縱向坐標。因此,紋理映射坐標被稱為UV坐標,如圖:

3D數(shù)學-基礎紋理_1.png

盡管紋理的大小可以是多種多樣的,可以是256*256或者1024*1024,但頂點的UV坐標的范圍通常都被歸一化到[0,1]范圍內。


漫反射紋理

漫反射紋理(Diffuse Map)是最基礎的一種紋理。其實就是一張覆蓋物體的圖像,讓我們能夠逐片段索引其獨立的顏色值。在光照場景中,它通常只是用采樣到的顏色值取代光照模型中的漫反射部分的顏色值?;貞浺幌禄A光照中的光照模型:

c_{diffuse}=(c_{light} \cdot m_{diffuse}) \cdot max(0, n \cdot l)

如果對這個公式有疑問可以參考《3D數(shù)學-基礎光照》

采樣的顏色值替換m_{diffuse}部分,漫反射紋理通常是紋理是什么樣子,渲染到物體上就是什么樣子,例如:

3D數(shù)學-基礎紋理_2.png

渲染出來會是這樣:

3D數(shù)學-基礎紋理_3.png

鏡面光紋理

大家可能會注意到,應用了鏡面高光后看起來會有點奇怪,因為這張圖片中有兩種材質,木材和鋼材,但是木頭不應該有這么強的鏡面高光的。所以我們想讓物體的某些部分以不同的強度顯示鏡面高光,我們就可以使用一張專門用于鏡面高光的紋理貼圖。我們可以使用一張黑白紋理來定義物體每個部分的鏡面光強度,例如上面渲染的木箱它的鏡面光紋理(Specular Map)

3D數(shù)學-基礎紋理_4.png

鏡面高光的強度可以通過圖像每個像素的亮度來獲取。鏡面光紋理上的每個像素可以由一個顏色向量來表示。比如黑色代表顏色向量<0, 0, 0>、灰色代表顏色向量<0.5, 0.5, 0.5>,那么我們用光照模型計算出來的高光部分c_{specular}就可以點乘這個向量,這樣就可以方便的控制不同部分的反光強度。在鏡面光紋理(Specular Map)中,一個像素越白,說明說明物體表面的鏡面光強度越大,經(jīng)過鏡面光紋理的后的渲染,如圖:

3D數(shù)學-基礎紋理_5.png

經(jīng)過鏡面光紋理的處理,看起來更逼真一些,但是感覺面還是平平的缺少一些細節(jié)。


法線紋理

現(xiàn)實中的物體表面并非平坦的,想提升一個表面的細節(jié)主要有兩種方式。第一種通過增加頂點來提升面數(shù)來增加一個表面的細節(jié),這種方式表現(xiàn)力強,但是比較耗費性能。另一種方法就是使用凹凸映射(bump mapping),給模型提供更多的細節(jié)表現(xiàn)。這種方法不會真的改變模型的頂點位置,只是讓模型看起來好像是“凹凸不平”的,但可以從模型的輪廓處看出“破綻”

在光照模型的中,影響光照強度的是輻照度(irradiance),而輻照度(irradiance)通常是由光源方向l和表面法線n點積來計算。我們通常無法改變光的強度,但是我們可以改變表面法線n來控制一個表面的光照強度,表面只有一個法向量,使得這個平面被同樣的一種輻照度(irradiance)照亮。如果每個fragment都有自己不同的法線會怎樣,我們就可以根據(jù)表面細微的細節(jié),來改變這些法線,這樣在一個表面上就可以產(chǎn)生出表面并不平坦的錯覺,如圖:

3D數(shù)學-基礎紋理_6.png

每個fragment都有自己不同的法線,也就可以看成這個表面是由很多微小的(垂直于法向量的)平面組成,物體經(jīng)過光照模型計算后,表面細節(jié)會得到極大的提升,這種每個fragment使用各自的法線,替代一個面上所有fragment使用同一個法線的技術叫做法線紋理(normal mapping)凹凸映射(bump mapping),下圖展示了沒有使用法線紋理和使用了法線紋理的區(qū)別:

3D數(shù)學-基礎紋理_7.png

可以看到細節(jié)獲得了極大的提升,性能消耗確不大。因為我們只需要改變每個fragment的法線向量,不需要更改光照模型?,F(xiàn)在我們是為每個fragment傳遞一個法線,不再使用插值的表面法線。這樣光照使表面的每個fragment擁有了自己的細節(jié)。

為使法線紋理工作,我們需要為每個fragment提供一個法線。2D紋理不僅可以儲存顏色和光照數(shù)據(jù),還可以儲存法線向量。這樣我們可以從2D紋理中采樣得到特定紋理的法線向量。像漫反射紋理(Diffuse Map)鏡面光紋理(Specular Map)一樣,我們可以使用一張2D紋理來儲存法線數(shù)據(jù)。

由于法線向量是個幾何工具,而紋理通常只用于存儲顏色信息,用紋理存儲法線向量不是非常直觀。法線方向的分量范圍在[-1, 1],而像素的分量范圍為[0, 1],因此我們需要做一個映射,通常使用的映射就是:

pixel = normal \times 0.5 + 0.5

這就要求我們在對法線紋理采樣后,還需要對結果進行一次反映射的過程來得到原先法線的方向。反映射的過程就是使用上面映射函數(shù)的逆函數(shù):

normal = pixel \times 2 - 1

將法線向量變換為這樣的RGB顏色,我們就能把根據(jù)表面形狀的fragment的法線保存在2D紋理中,例如上面渲染的磚塊的例子:

3D數(shù)學-基礎紋理_8.png

一般來說,法線紋理都會是這種偏藍色調的紋理。這是因為所有法線的指向都偏向z<0, 0, 1>,映射到像素即為<0.5, 0.5, 1>,也就是淺藍色。這些淺藍色實際上說明fragment的大部分法線是和模型本身法線一樣,不需要改變。法線向量從z軸方向向其他方向輕微的偏移,顏色也就發(fā)生了變化,這樣看起來編有了一種深度。然后我們就可以用采樣的顏色值,反映射出對應的法線向量,然后進行光照計算,就可以得到上面渲染的表現(xiàn)效果。

這個方法看起來很完美,這樣使用有很大的限制,如果法線紋理的所有向量都是基于z<0, 0, 1>的偏移,那么必須模型表面的法向量必須是指向z<0, 0, 1>。如果將表面旋轉使得法向量指向y<0, 1, 0>,計算出來的光照完全不對,如圖:

3D數(shù)學-基礎紋理_9.png

有一個解決方案是為每個表面制作一張單獨的法線紋理。這樣的話,如果一個立方體我們就需要6張法線紋理,如果一個模型上有無數(shù)朝向不同方向的面,這就不靠譜了。

另一個解決方案是,在一個不同的坐標空間中進行光照,這個坐標空間里,法線紋理向量總是指向這個坐標控件的正z<0, 0, 1>方向。所有的光照向量都是相對于這個正z<0, 0, 1>方向進行變換。這樣我們就能始終使用同樣的法線紋理,不管朝向問題。這就是切線空間(tangent space)。

切線空間(tangent space)

切線空間(tangent space)也稱為圖像空間(image space),法線紋理中的法線向量都是定義在切線空間(tangent space)中,頂點法線永遠指向z<0, 0, 1>方向:

3D數(shù)學-基礎紋理_10.png

然后我們需要確定切線(Tagent)方向:

3D數(shù)學-基礎紋理_11.png

然而垂直與法線的切線有很多條,理論上哪一條都行。但我們需要保持連續(xù)一致性,以免銜接出現(xiàn)瑕疵。標準的做法是將切線方向和紋理空間對齊:

3D數(shù)學-基礎紋理_12.png

定義一個空間坐標系需要三個基向量,因此我們還得計算副切線(Bitangent)

我們得到這三個基向量后就可以構建TBN矩陣T代表tangentB代表bitangent、N代表normal),可以用這個矩陣把任意向量從切線空間(tangent space)轉換到模型空間,然后我們只需要對TBN矩陣求逆就可以實現(xiàn)從模型空間轉換到切線空間(tangent space)的變換矩陣。

現(xiàn)在我們現(xiàn)在已知法線(Normal),需要將切線(Tangent)副切線(Bitangent)對齊到紋理空間的u軸和v

3D數(shù)學-基礎紋理_13.png

假設一個fragment進行采樣:

3D數(shù)學-基礎紋理_14.png

P_{1}<U_{1}, V_{1}>是切線空間中的TB平面上的一個坐標點

P_{2}<U_{2}, V_{2}>是切線空間中的TB平面上的一個坐標點

P_{3}<U_{3}, V_{3}>是切線空間中的TB平面上的一個坐標點

E_{1}是連接P_{1}P_{2}的的直線

E_{2}是連接P_{2}P_{3}的的直線

則有以下關系式:

\begin{cases} \Delta U_{1} = |U_{2} - U_{1}| \\[2ex] \Delta V_{1} = |V_{2} - V_{1}| \\[2ex] \Delta U_{2} = |U_{3} - U_{2}| \\[2ex] \Delta V_{2} = |V_{3} - V_{2}| \end{cases}

就可以得出一下關系:

\begin{cases} E_{1} = \Delta U_{1}T + \Delta V_{1}B \\[2ex] E_{2} = \Delta U_{2}T + \Delta V_{2}B \end{cases}

我們也可以寫成這樣:

\begin{cases} (E_{1x}, E_{1y}, E_{1z}) = \Delta U_{1}(T_{x}, T_{y}, T_{z}) + \Delta V_{1}(B_{x}, B_{y}, B_{z}) \\[2ex] (E_{2x}, E_{2y}, E_{2z}) = \Delta U_{2}(T_{x}, T_{y}, T_{z}) + \Delta V_{2}(B_{x}, B_{y}, B_{z}) \end{cases}

這樣我們就可以方便的寫成矩陣的形式:

\begin{bmatrix} E_{1x} & V_{1x} \\[2ex] E_{1y} & V_{1y} \\[2ex] E_{1z} & V_{2z} \end{bmatrix}= \begin{bmatrix} \Delta U_{1} & \Delta U_{2} \\[2ex] \Delta V_{1} & \Delta V_{2} \end{bmatrix} \cdot \begin{bmatrix} T_{x} & B_{x} \\[2ex] T_{y} & B_{y} \\[2ex] T_{z} & B_{z} \end{bmatrix}

然后我們可以進行變換,成為:

\begin{bmatrix} \Delta U_{1} & \Delta U_{2} \\[2ex] \Delta V_{1} & \Delta V_{2} \end{bmatrix}^{-1} \cdot \begin{bmatrix} E_{1x} & V_{1x} \\[2ex] E_{1y} & V_{1y} \\[2ex] E_{1z} & V_{2z} \end{bmatrix}= \begin{bmatrix} T_{x} & B_{x} \\[2ex] T_{y} & B_{y} \\[2ex] T_{z} & B_{z} \end{bmatrix}

然后就可以計算出delta紋理坐標矩陣的逆矩陣,就可以計算出T、B,計算逆矩陣的方法這里就不詳細介紹了,主要方式是計算矩陣的行列式,然后用1除以行列式再乘以它的伴隨矩陣(Adjugate Matrix)

\begin{bmatrix} T_{x} & B_{x} \\[2ex] T_{y} & B_{y} \\[2ex] T_{z} & B_{z} \end{bmatrix}= \frac{1}{\Delta U_{1}\Delta V_{2}-\Delta U_{2}\Delta V_{1}} \begin{bmatrix} \Delta V_{2} & -\Delta V_{1} \\[2ex] -\Delta U_{2} & \Delta U_{1} \end{bmatrix} \cdot \begin{bmatrix} E_{1x} & V_{1x} \\[2ex] E_{1y} & V_{1y} \\[2ex] E_{1z} & V_{2z} \end{bmatrix}

這樣我們就計算出了TBN矩陣,我們只需要得到TBN矩陣的逆矩陣就可是實現(xiàn)模型空間轉換到切線空間(tangent space),在理想的情況下TBN矩陣是正交矩陣,我們就可以通過求轉置矩陣來獲得逆矩陣,即:

\begin{bmatrix} T_{x} & B_{x} & N_{x} \\[2ex] T_{y} & B_{y} & N_{y} \\[2ex] T_{z} & B_{z} & N_{z} \end{bmatrix}^{T}= \begin{bmatrix} T_{x} & T_{y} & T_{z} \\[2ex] B_{x} & B_{y} & B_{z} \\[2ex] N_{x} & N_{y} & N_{z} \end{bmatrix}

然而實際情況計算出的TBN矩陣往往不是正交矩陣,所以我們需要對這個矩陣進行格拉姆-施密特正交化過程(Gram-Schmidt process)來進行正交化:

3D數(shù)學-基礎紋理_15.png

t_{o} = normalize(t - n \times (n \cdot t) \\[2ex] b_{o} = n \times t_{o}

這樣我們就得到了正交的TBN矩陣,一般來說有兩種方式來使用它:

  1. 我們直接使用TBN矩陣,可以將切線坐標空間的向量轉換到世界坐標空間。因此我們可以將從法線紋理中采樣到的法線乘以TBN矩陣轉換到世界空間中,這樣法線、光照參數(shù)都在一個坐標系中,就可以進行光照模型的計算了。
  2. 我們也可以使用TBN的逆矩陣,可以將世界坐標空間的向量轉換到切線坐標空間.我們使用這個矩陣將光照參數(shù)轉換到切線空間中,然后就進行光照模型的計算了。

本節(jié)教程就到此結束,希望大家繼續(xù)閱讀我之后的教程。

謝謝大家,再見!


飲水思源

參考文獻:

《3D游戲與圖形學中的數(shù)學方法》
《Unity Shader 入門精要》
《法線貼圖》
《法線貼圖》


版權聲明:原創(chuàng)技術文章,撰寫不易,轉載請注明出處!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容