在渲染中,有很多的計(jì)算是在視角空間中完成的。這是因?yàn)楣庹胀ǔJ窃谶@個(gè)空間中計(jì)算的,否則,像一些依賴視角的效果,比如高光,將會(huì)比較難以實(shí)現(xiàn)。
因此,我們需要一種方法將法線轉(zhuǎn)換到視角空間。我們可以通過下面的寫法把一個(gè)頂點(diǎn)轉(zhuǎn)換到視角空間:
vertexEyeSpace = gl_ModelViewMatrix * gl_Vertex;
那么,我們?yōu)槭裁床荒苤苯影堰@個(gè)應(yīng)用到法線向量呢?首先,法線是一個(gè)3維向量,而ModelView矩陣是一個(gè)4x4矩陣;其次,因?yàn)榉ň€是一個(gè)向量,所以我們只需要轉(zhuǎn)換它的方向。ModelView矩陣左上方的3x3子矩陣包含了方向的信息,那么我們?yōu)槭裁床荒苁褂眠@個(gè)子矩陣去轉(zhuǎn)換法線呢?
這可以通過下面的代碼很簡(jiǎn)單的實(shí)現(xiàn):
normalEyeSpace = vec3(gl_ModelViewMatrix * vec4(gl_Normal,0.0));
但上面的代碼只在部分情況下可以正常工作。
讓我們來看下一個(gè)潛在的問題:

在上面的圖中,我們看到一個(gè)帶了一個(gè)法線和切線向量的三角形。在下面的圖中,顯示了如果ModelView矩陣包含了非均勻縮放會(huì)發(fā)生什么樣的偏差。

注意:如果是均勻縮放,那么法線的方向?qū)?huì)保持,雖然長(zhǎng)度會(huì)受到影響,但可以簡(jiǎn)單的通過標(biāo)準(zhǔn)化修復(fù)。
在上面的圖中,ModelView矩陣應(yīng)用到了所有的向量,包括法線,結(jié)果是顯而易見的錯(cuò)誤的:轉(zhuǎn)換后的法線不再與表面垂直了。
我們知道,一個(gè)向量可以表示為兩個(gè)點(diǎn)之間的差異。比如切線向量,它可以通過計(jì)算三角形一條邊上的兩個(gè)頂點(diǎn)之間的差距來獲得。如果P1和P2是定義一條邊的兩個(gè)頂點(diǎn),那么我們知道:
T = P2 - P1
如果把這個(gè)向量表示為包含4個(gè)元素且最后一個(gè)元素為0的元組,那么我們可以把這個(gè)等式兩邊都乘以ModelView矩陣
T * ModelView = (P2 - P1) * ModelView
結(jié)果是
T * ModelView = P2 * ModelView - P1 * ModelView
T' = P'2 - P'1
因?yàn)?strong>P'1和P'2為轉(zhuǎn)換后的三角形的頂點(diǎn),所以T'仍然與三角形的邊相切。因此,ModelView保持了切線。但它卻沒有保持法線。
使用和T向量一樣的方法,我們可以找到兩個(gè)點(diǎn)Q1和Q2,讓以下等式成立
N = Q2 - Q1
主要的問題是,就如上面的圖所展示的那樣,由轉(zhuǎn)換后的的點(diǎn)定義的向量,Q2 - Q1,不一定仍然與邊垂直。法線向量不是如切線向量那樣,定義為兩個(gè)點(diǎn)之間的差距,它定義為垂直于一個(gè)表面的向量。
現(xiàn)在我們知道了,我們不能在所有情況下使用ModelView來轉(zhuǎn)換法線向量。那么我們應(yīng)該使用什么樣的矩陣呢?
假設(shè)G為一個(gè)3X3的矩陣,讓我們看看如何計(jì)算它來正確的轉(zhuǎn)換法線向量。
我們知道,在矩陣轉(zhuǎn)換之前,因?yàn)榍芯€和法線向量是垂直的,所以T.N = 0。我們也知道,轉(zhuǎn)換之后,它們也一定要垂直,所以N'.T'也一定要等于0。我們把ModelView的左上方的3x3矩陣稱為M矩陣,T可以安全的乘以M(T是一個(gè)向量,所以w元素是0).
我們假設(shè)G是正確轉(zhuǎn)換法線向量N的矩陣,那么就有了下面的等式
N'.T' = (GN).(MT) = 0
把點(diǎn)乘轉(zhuǎn)換為叉乘,那么
(GN).(MT) = (GN)T * (MT)
注意為了叉乘向量,第一個(gè)向量的轉(zhuǎn)置是必須的。我們知道兩個(gè)矩陣相乘的轉(zhuǎn)置,等于轉(zhuǎn)置后矩陣的相乘,但順序要相反,因此:
(GN)T * (MT) = NTGTMT
我們前面已經(jīng)說過了N和T的點(diǎn)乘是0,所以如果
GTM = I
那么我們有
N'.T' = N.T = 0
這正是我們要的,所以我們可以根據(jù)M來計(jì)算G。
GTM = I <==> G = (M-1)T
因此M矩陣的逆矩陣的轉(zhuǎn)置,是正確轉(zhuǎn)換法線的矩陣。
前面我們說直接使用ModelView矩陣在部分情況下可以正常工作。當(dāng)ModelView左上方的3x3矩陣是正交矩陣的時(shí)候,我們有:
M-1 = MT ==> G == M
這是因?yàn)檎痪仃嚨霓D(zhuǎn)置與逆矩陣是一樣的。那么什么是正交矩陣呢?正交矩陣就是矩陣的每行/列都是單位長(zhǎng)度,并且互相垂直的矩陣。這意味這兩個(gè)向量,被正交矩陣轉(zhuǎn)換前后,它們之間的角度是一樣的,因此轉(zhuǎn)換后的法線依然垂直于切線。此外,它還保持了向量的長(zhǎng)度。
那么我們?nèi)绾伪WCM是正交的呢?只要我們限制我們的幾何變換僅限于旋轉(zhuǎn)和位移,比如在OpenGL程序中我們僅僅使用glRotate和glTranslate,不使用glScale,那么可以確保M是正交的。注意:gluLookAt也創(chuàng)建了一個(gè)正交矩陣。