GLSL教程 (九)其他說明

2021-06-05 17:21:39 字數 2561 閱讀 2830

法線矩陣

在很多頂點shader中都用到了gl_normalmatrix。這裡將介紹這個矩陣是什麼,以及它的作用。

大部分計算是在檢視空間內完成的,主要原因是光照的運算要放在這個空間內,否則一些依賴觀察點座標的效果,比如鏡面反射光就很難實現。

所以我們需要將法線變換到檢視空間。變換乙個頂點到檢視空間的方法如下:

[cpp]view plain

copy

vertexeyespace = gl_modelviewmatrix * gl_vertex;  

對法線也能如此操作嗎?乙個法線是3個浮點數組成的向量,而模型檢視矩陣是乙個4×4的矩陣。另外,因為法線是乙個向量,我們只需要變換它的方向,而模型檢視矩陣中左上方的3×3子矩陣正好包含了旋轉變換。所以可不可以用法線來乘這個子矩陣呢?

下面的**很簡單地實現了這個要求:

[cpp]view plain

copy

normaleyespace = vec3(gl_modelviewmatrix * vec4(gl_normal,0.0));  

這樣的話,gl_normalmatrix還有什麼用呢?只是為了簡化**書寫嗎?實際當然不是這麼簡單,上面的**在某些情況下是有效的,但不能應對所有情況。

讓我們看看潛在的問題:

在上圖中我們可以看到乙個三角面以及它的法線和切線。下圖顯示了如果模型檢視矩陣包含非一致縮放(non-uniform scale)的話會發生什麼。

注意,如果縮放是一致的(uniform),那麼法線方向保持不變,變的只是長度,而且可以通過歸一化修正這個影響。

在上圖中,模型檢視矩陣應用到所有頂點以及法線上,最後的結果明顯錯誤:法線不再與三角面垂直了。

現在我們知道模型檢視矩陣在某些情況下,不能用來變換法線向量。下面的問題就是:那麼該使用哪個矩陣?

考慮乙個3×3矩陣g,讓我們看看要正確變換法線,這個矩陣該是什麼樣子。

我們知道,變換前切線和法線是垂直的,即t•n = 0,在變換後切線和法線同樣應該保持垂直,即t』•n』 = 0。現在假設g是正確變換法線的矩陣,同時模型檢視矩陣的左上3×3子矩陣m可以正確變換切線t(t是乙個向量,所以w成分為0)。因為t可以通過兩個頂點的差來計算,所以變換頂點的矩陣同樣可以用來變換t。由此可以得到如下等式:

向量的點乘相當於向量的內積,所以有:

我們知道相乘的轉置等於分別轉置再交換順序相乘:

已知n和t點乘結果為0,所以如果下式成立就可以滿足等式為0:

即有:可見變換法線的正確矩陣是m的逆的轉置。opengl計算出的這個矩陣就儲存在gl_normalmatrix裡。

在本節開始討論過,某些情況下使用模型檢視矩陣也可以。當模型檢視矩陣的左上3×3子矩陣m正交時,可以得到:

乙個正交矩陣的所有行/列都為單位向量,並且互相正交。當兩個向量乘上正交矩陣時,它們之間的夾角在變換前後不變。由於這種保角變換的關係,所以法線和切線依然儲存垂直。此外,向量的長度也保持不變。

m在什麼時候能確定為正交的呢?當我們把幾何變換限制為旋轉和平移時(在opengl應用程式中只使用glrotate和gltranslate,而不使用glscale),就可以保證m正交。注意:glulookat同樣建立正交矩陣。

關於法線歸一化

當乙個法線到達頂點shader後,我們一般會將它歸一化:

[cpp]view plain

copy

normal = normalize(gl_normalmatrix * gl_normal);  

法線與gl_normalmatrix矩陣相乘,將會被變換到檢視空間。歸一化向量可以保證使用點乘得到余弦值。

我們可以避免歸一化計算嗎?在某些情況下是可行的。如果gl_normalmatrix是正交矩陣,那麼經過變換後輸入法線的長度不會變,依然等於gl_normal。所以如果在opengl程式中法線已經是歸一化的,那麼在shader中就不需要在重複了。

也就是說,如果我們使用glulookat設定照相機,對模型值進行旋轉和平移變換,就可以在shader中避免使用歸一化操作。這對於歸一化過的光線向量也是適用的。

片斷shader的情況

在片斷shader中,我們經常發現需要重新歸一化在頂點shader中歸一化的法線。這是必要的嗎?答案是肯定的。

考慮乙個包含三個不同頂點法線的三角面。片斷shader接收經過插值的法線,插值基於距離三個頂點的遠近。這樣得到的法線方向是對的,但不再是單位長度了。

下圖顯示了原因。圖中黑線表示三角面,頂點法線用藍色表示,插值得到的片斷法線用綠色表示。所有的插值法線排列在黑色的點劃線上。從圖上可以看出綠色的插值法線大小小於單位長度的頂點法線。

注意,如果頂點法線沒有單位化,那麼得到的插值法線的方向也將是錯誤的。所以,即使乙個頂點沒有在頂點shader用到,也可能要對它在頂點shader中進行歸一化。

有一種情況,在片斷shader中可以避免歸一化操作,那就是每個頂點法線方向相同,而且頂點法線是經過歸一化的。此時頂點法線插值得到的結果都相同。

以方向光為例,每個片斷都需要考慮光線方向,如果光線向量已經在之前歸一化了,在片斷shader中就可以避免歸一化這一步。

GLSL教程 (九)其他說明

法線矩陣 在很多頂點shader中都用到了gl normalmatrix。這裡將介紹這個矩陣是什麼,以及它的作用。大部分計算是在檢視空間內完成的,主要原因是光照的運算要放在這個空間內,否則一些依賴觀察點座標的效果,比如鏡面反射光就很難實現。所以我們需要將法線變換到檢視空間。變換乙個頂點到檢視空間的方...

GLSL教程 (九)其他說明

from 法線矩陣 在很多頂點shader中都用到了gl normalmatrix。這裡將介紹這個矩陣是什麼,以及它的作用。大部分計算是在檢視空間內完成的,主要原因是光照的運算要放在這個空間內,否則一些依賴觀察點座標的效果,比如鏡面反射光就很難實現。所以我們需要將法線變換到檢視空間。變換乙個頂點到檢...

GLSL教程 (九)其他說明

法線矩陣 在很多頂點shader中都用到了gl normalmatrix。這裡將介紹這個矩陣是什麼,以及它的作用。大部分計算是在檢視空間內完成的,主要原因是光照的運算要放在這個空間內,否則一些依賴觀察點座標的效果,比如鏡面反射光就很難實現。所以我們需要將法線變換到檢視空間。變換乙個頂點到檢視空間的方...