前言:我又來翻譯了。。。
今天的主題是法線貼圖。法線貼圖和 Phong 著色之間的主要區(qū)別是什么?關(guān)鍵是我們擁有的信息密度。對(duì)于 Phong 著色,我們使用三角形網(wǎng)格的每個(gè)頂點(diǎn)給出的法向量(并在三角形內(nèi)插入),而法線貼圖紋理提供密集信息,大大改善了渲染細(xì)節(jié)。
好吧,我們已經(jīng)在上一課中應(yīng)用了法線貼圖,但是我們使用全局坐標(biāo)系來存儲(chǔ)紋理。今天我們談?wù)摰氖乔芯€空間法線貼圖。
所以,我們有兩個(gè)紋理,左邊的一個(gè)是在全局框架中給出的(從RGB 到 XYZ 法線的直接轉(zhuǎn)換),而在 Darboux 框架中是右邊的:

為了使用正確的紋理,我們繪制每個(gè)像素,我們計(jì)算切線空間。在此基礎(chǔ)上,一個(gè)矢量(通常為z)與我們的表面正交,另外兩個(gè)坐標(biāo)軸給出了與當(dāng)前點(diǎn)相切的平面。然后我們從紋理中讀取(擾動(dòng)的)法向量,將其坐標(biāo)從 Darboux 框架轉(zhuǎn)換為全局系統(tǒng)坐標(biāo),我們已經(jīng)完成了。通常,法線貼圖提供法向量的小擾動(dòng),因此紋理呈主導(dǎo)藍(lán)色。
好吧,為什么這么亂?為什么不像以前那樣使用全球系統(tǒng)?想象一下,我們想要為我們的模型制作動(dòng)畫。例如,我采取了黑人模型并張開嘴。很明顯,要修改法向量。

左圖給頭部開口,但是沒有改變的(全局框架)正常紋理。仔細(xì)檢查下唇的內(nèi)部。光線直接照在他的臉上;當(dāng)嘴巴閉合時(shí),下唇的背面自然不會(huì)被點(diǎn)亮的。現(xiàn)在嘴巴張開,但嘴唇?jīng)]有亮起......正確的圖像是用切線空間法線貼圖計(jì)算的。
因此,如果我們有一個(gè)動(dòng)畫模型,那么為了在全局幀中進(jìn)行正確的法線貼圖,我們需要每幀動(dòng)畫有一個(gè)紋理,而切線空間相應(yīng)地變形為模型,而且我們只需要一個(gè)紋理!
(這兩段話完全沒有理解)
這是另一個(gè)例子:

這些是暗黑破壞神模型的紋理。請(qǐng)注意,紋理中只繪制了一只手,而尾部只有一側(cè)。畫家對(duì)雙臂和兩側(cè)使用相同的紋理,這意味著在全局坐標(biāo)系中我可以為尾部的左側(cè)提供法向矢量。要么是正確的,要么不是兩者兼而有之!武器也是如此。我需要左側(cè)和右側(cè)的不同信息,例如,檢查左側(cè)圖像中的左右顴骨,自然法線向量指向相反的方向!
讓我們完成動(dòng)機(jī)部分并直接進(jìn)行計(jì)算。
(看不懂)
開始,Phong 著色
好的,這是起點(diǎn)。著色器非常簡(jiǎn)單,它是 Phong 著色。
struct Shader : public IShader {
mat<2,3,float> varying_uv; // triangle uv coordinates, written by the vertex shader, read by the fragment shader
mat<3,3,float> varying_nrm; // normal per vertex to be interpolated by FS
virtual Vec4f vertex(int iface, int nthvert) {
varying_uv.set_col(nthvert, model->uv(iface, nthvert));
varying_nrm.set_col(nthvert, proj<3>((Projection*ModelView).invert_transpose()*embed<4>(model->normal(iface, nthvert), 0.f)));
Vec4f gl_Vertex = Projection*ModelView*embed<4>(model->vert(iface, nthvert));
varying_tri.set_col(nthvert, gl_Vertex);
return gl_Vertex;
}
virtual bool fragment(Vec3f bar, TGAColor &color) {
Vec3f bn = (varying_nrm*bar).normalize();
Vec2f uv = varying_uv*bar;
float diff = std::max(0.f, bn*light_dir);
color = model->diffuse(uv)*diff;
return false;
}
};
這是渲染圖像:

出于教育和調(diào)試目的,我將去除皮膚紋理并應(yīng)用具有水平紅色和垂直藍(lán)線的常規(guī)網(wǎng)格

讓我們記住 Phong 著色的工作原理:

對(duì)于三角形的每個(gè)頂點(diǎn),我們有它的坐標(biāo) p,紋理坐標(biāo) uv 和法向量。對(duì)于著色當(dāng)前片段,我們的軟件光柵化器為我們提供了片段(alpha,beta,gamma)的重心坐標(biāo)。這意味著片段的坐標(biāo)可以獲得為p = alpha p0 + beta p1 + gamma p2。然后我們以相同的方式插入紋理坐標(biāo)和法線向量:

請(qǐng)注意,藍(lán)線和紅線相應(yīng)地是 u 和 v 的等值線。因此,對(duì)于我們表面的每個(gè)點(diǎn),我們定義了一個(gè)所謂的 Darboux 框架,其中 x 和 y 軸平行于藍(lán)色和紅色線,z 軸垂直于表面。這是切線空間法線貼圖所在的框架。
如何從三個(gè)樣本重建(3D)線性函數(shù)
好的,所以我們的目標(biāo)是為我們繪制的每個(gè)像素計(jì)算三個(gè)向量(切線基礎(chǔ))。讓我們把它擱置一段時(shí)間,想象一個(gè)線性函數(shù) f,對(duì)于每個(gè)點(diǎn)(x,y,z)給出一個(gè)實(shí)數(shù) f(x,y,z)= Ax + By + Cz + D。唯一的問題是我們不知道 A,B,C 和 D,但是我們知道在空間的三個(gè)不同點(diǎn)(p0,p1,p2)有三個(gè)函數(shù)值:


將 f 想象為傾斜平面的高度圖是很方便的。我們?cè)谄矫嫔闲迯?fù)了三個(gè)不同的(非共線)點(diǎn),我們知道這些點(diǎn)中的 f 的值。三角形內(nèi)的紅線表示等高 f0,f0 + 1 米,f0 + 2 米等。對(duì)于線性函數(shù),我們的等值線是平行(直線)線。
事實(shí)上,我對(duì)方向更感興趣,正交于等值線。如果我們沿著 iso 移動(dòng),高度不會(huì)改變(嗯,這是一個(gè)iso?。?。如果我們偏離 iso 一點(diǎn)點(diǎn),高度開始變化一點(diǎn)點(diǎn)。當(dāng)我們正交于等值線時(shí),我們獲得最陡的上升。
讓我們回想一下,函數(shù)最陡峭的上升方向就是它的梯度。對(duì)于線性函數(shù)f(x,y,z) = Ax+By+Cz+D,其梯度是常數(shù)向量(A,B,C)。回想一下,我們不知道(A,B,C)的值。我們只知道該函數(shù)的三個(gè)樣本。我們可以重建 A,B 和 C 嗎?當(dāng)然可以。
所以,我們有三個(gè)點(diǎn) p0,p1,p2 和三個(gè)值 f0,f1,f2。我們需要找到最陡上升的矢量(A,B,C)。讓我們考慮另一個(gè)定義為g(p)= f(p)-f(p0)的函數(shù):

顯然,我們只是簡(jiǎn)單地平移了我們的傾斜平面,而沒有改變它的傾斜度,因此 f 和 g 的最陡上升方向是相同的。
讓我們重寫 g 的定義

請(qǐng)注意,p ^ x中的上標(biāo) x 表示點(diǎn) p 的 x 坐標(biāo)而不是冪。因此,函數(shù)g 只是向量 (p - p0) 和 (A B C) 之間的點(diǎn)積。我們?nèi)匀徊恢?(A,B,C)!
好的,讓我們回想一下我們所知道的。我們知道如果我們從點(diǎn) p0 到點(diǎn) p2,那么函數(shù) g 將從零到 f2-f0 。換句話說,矢量 (p2 - p0) 和(ABC) 之間的點(diǎn)積等于 f2 - f0。 (p1 - p0) 也是如此。因此,我們正在尋找向量 ABC ,與法向量 n 正交并且遵守點(diǎn)積的兩個(gè)約束。

讓我們以矩陣形式重寫:

因此,我們得到了一個(gè)易于求解的線性矩陣方程 Ax = b:

請(qǐng)注意,我使用字母 A 表示兩種不同的東西,其含義應(yīng)從上下文中清楚。因此,我們的 3x3 矩陣 A 乘以未知向量 x = (A, B, C),給出向量 b = (f1 - f0, f2 - f0, 0)。當(dāng)我們將 A 的逆乘以 b 時(shí),未知向量 x 變?yōu)橐阎?/p>
還要注意,矩陣 A 與函數(shù) f 沒有任何關(guān)系。它只包含有關(guān)我們?nèi)切蔚囊恍┬畔ⅰ?/p>
讓我們計(jì)算 Darboux 基礎(chǔ)并應(yīng)用法線的擾動(dòng)
因此,Darboux 是向量三元組 (i,j,n),其中 n - 是原始法向量,i,j 可以如下計(jì)算:

這是提交,使用切線空間中的法線貼圖,在這里您可以檢查相對(duì)于起點(diǎn)(Phong 著色)的差異。
直截了當(dāng)?shù)膩戆?,我?jì)算矩陣 A。
mat<3,3,float> A;
A[0] = ndc_tri.col(1) - ndc_tri.col(0);
A[1] = ndc_tri.col(2) - ndc_tri.col(0);
A[2] = bn;
然后計(jì)算 Darboux 的兩個(gè)未知向量 (i, j):
一旦我們得到所有切線基礎(chǔ),我從紋理中讀取擾動(dòng)法線并應(yīng)用從切線基礎(chǔ)到全局坐標(biāo)的基礎(chǔ)變化?;叵胍幌拢乙呀?jīng)描述了如何改變基礎(chǔ)。
這是最后的渲染圖像,與 Phong shading 比較一下吧!
