跳轉到內容

GLSL 程式設計/應用矩陣變換

來自華夏公益教科書,開放的書籍,為開放的世界

在著色器中應用傳統的頂點變換(參見 “頂點變換”部分)或任何其他由矩陣表示的變換通常透過在著色器的統一變數中指定相應的矩陣,然後將該矩陣乘以一個向量來完成。然而,在細節上有一些差異。這裡,我們討論點的變換(即 4D 向量,其第 4 個座標等於 1),方向的變換(即嚴格意義上的向量:3D 向量或第 4 個座標等於 0 的 4D 向量),以及表面法向量的變換(即指定與平面正交的方向的向量)。

本節假設您對 “向量和矩陣運算”部分 中描述的 GLSL 語法有所瞭解。

變換點

[編輯 | 編輯原始碼]

對於點,變換通常用 4×4 矩陣表示,因為它們可能包括 3D 向量 t 在第 4 列的平移

     

(投影矩陣還將在最後一行包含其他不等於 0 的值。)

三維點用第 4 個座標等於 1 的四維向量表示

為了應用變換,矩陣 乘以向量

  

使用 GLSL 程式碼將 4x4 矩陣應用於由 4D 向量表示的點非常簡單。

mat4 matrix;
vec4 point;
vec4 transformed_point = matrix * point;

變換方向

[edit | edit source]

三維空間中的方向可以用 3D 向量或第四個座標為 0 的 4D 向量表示。(可以將它們視為無窮遠處的點,類似於地平線上的一點,我們無法確定其空間位置,只能確定其方向。)

對於 3D 向量,我們可以使用 3x3 矩陣變換它

mat3 matrix;
vec3 direction; 
vec3 transformed_direction = matrix * direction;

或者使用 4x4 矩陣,如果我們將 3D 向量轉換為第四個座標為 0 的 4D 向量。

mat4 matrix;
vec3 direction;
vec3 transformed_direction = vec3(matrix * vec4(direction, 0.0));

或者,也可以將 4x4 矩陣轉換為 3x3 矩陣。

另一方面,4D 向量可以直接乘以 4x4 矩陣。也可以將其轉換為 3D 向量,以便使用 3x3 矩陣進行乘法。

mat3 matrix;
vec4 direction; // 4th component is 0
vec4 transformed_direction = vec4(matrix * vec3(direction), 0.0);

變換法向量

[edit | edit source]

與方向類似,表面法向量(或簡稱為“法向量”)由 3D 向量或第四個分量為 0 的 4D 向量表示。但是,它們變換方式不同。(數學原因是它們表示的是協向量、協變向量、一形式或線性泛函。)

要理解法向量的變換,請考慮表面法向量的主要特徵:它與表面正交。當然,這種特徵在變換後仍然應該成立,即變換後的法向量應該與變換後的表面正交。如果表面由切向量區域性表示,則此特徵要求變換後的法向量與變換後的方向向量正交,如果原始法向量與原始方向向量正交。

從數學角度講,法向量 n 與方向向量 v 正交,如果它們的點積為 0。事實證明,如果 v 由 3x3 矩陣 變換,則法向量必須由 的 **轉置逆矩陣** 變換: 。我們可以透過檢查變換後的法向量 n 和變換後的方向向量 v 的點積來輕鬆測試這一點。

                 

第一步,我們使用了 = ,然後 = ,然後 ,然後 (即單位矩陣)。

計算表明,變換後的向量的點積實際上與原始向量的點積相同;因此,變換後的向量是正交的,當且僅當原始向量是正交的。正如預期的那樣。

因此,為了在 GLSL 中變換法向量,轉置逆矩陣 通常被指定為一個 uniform 變數(以及用於變換方向和點的原始矩陣),並像任何其他變換一樣應用。

mat3 matrix_inverse_transpose;
vec3 normal;
vec3 transformed_normal = matrix_inverse_transpose * normal;

對於一個 4x4 矩陣,法向量可以透過新增 0 來轉換為一個 4D 向量。

mat4 matrix_inverse_transpose;
vec3 normal;
vec3 transformed_normal = vec3(matrix_inverse_transpose * vec4(normal, 0.0));

或者,矩陣可以轉換為一個 3x3 矩陣。

如果逆矩陣已知,法向量可以從左側乘以應用轉置逆矩陣。一般來說,用向量乘以轉置矩陣可以透過將向量放在矩陣左側來輕鬆表示。原因是向量-矩陣乘積只對行向量(即轉置列向量)有意義,並且對應於轉置矩陣與相應列向量的矩陣-向量乘積。

由於 GLSL 不區分列向量和行向量,因此結果只是一個向量。

因此,為了用轉置逆矩陣乘以法向量,我們可以從左側乘以逆矩陣。

mat3 matrix_inverse;
vec3 normal;
vec3 transformed_normal = normal * matrix_inverse;

對於將 4x4 矩陣乘以 4D 法向量(從左側或右側),應確保結果向量的第 4 個分量為 0。事實上,在某些情況下,需要丟棄計算出的第 4 個分量(例如,透過將結果轉換為 3D 向量)。

mat4 matrix_inverse;
vec4 normal;
vec4 transformed_normal = vec4(vec3(normal * matrix_inverse), 0.0);

請注意,對法向量進行任何單位化操作都不會保留這種變換。因此,法向量通常在變換後被單位化(例如,使用內建 GLSL 函式 normalize)。

使用正交矩陣變換法向量

[編輯 | 編輯原始碼]

當變換矩陣 是正交的時候,就會出現一種特殊情況。在這種情況下, 的逆矩陣是轉置矩陣;因此, 逆矩陣的轉置是兩次轉置的矩陣,即原始矩陣,也就是說,對於一個正交矩陣

  

因此,在**正交**矩陣的情況下,法向量會與方向和點使用相同的矩陣進行變換。

mat3 matrix; // orthogonal matrix
vec3 normal;
vec3 transformed_normal = matrix * normal;

使用逆矩陣變換點

[編輯 | 編輯原始碼]

有時需要應用逆變換。在大多數情況下,最佳的解決方案是為逆矩陣定義另一個統一變數,並在主應用程式中設定逆矩陣。然後,著色器可以像任何其他矩陣一樣應用逆矩陣。這比在著色器中計算逆矩陣效率高得多。

但是,有一個特殊情況:如果矩陣 具有上面顯示的形式(即第 4 行為 (0,0,0,1))

其中 是一個 3×3 正交矩陣(即 的行向量(或列向量)是歸一化且相互正交的;例如,這通常是檢視變換的情況,請參閱“頂點變換”部分),則逆矩陣由下式給出(因為 對於正交矩陣 )

  

對於使用點 進行的乘法,該點由 4D 向量 表示,其中 3D 向量 p ,我們得到

     

請注意,向量t 只是矩陣 的第 4 列,它可以在 GLSL 中方便地訪問。

mat4 matrix;
vec4 last_column = matrix[3]; // indices start with 0 in GLSL

如上所述,將轉置矩陣與向量相乘可以很容易地用將向量放在矩陣左側來表示,因為向量-矩陣乘積只有對行向量(即轉置列向量)才有意義,並且對應於轉置矩陣與相應列向量進行矩陣-向量乘積。

利用 GLSL 的這些特性,項 (p - t) 可以用以下方式輕鬆高效地實現(請注意,結果的第 4 個分量必須單獨設定為 1)。

mat4 matrix; // upper, left 3x3 matrix is orthogonal; 
   // 4th row is (0,0,0,1) 
vec4 point; // 4th component is 1
vec4 point_transformed_with_inverse = vec4(vec3((point - matrix[3]) * matrix), 1.0);

使用逆矩陣變換方向

[edit | edit source]

與點的情況類似,使用逆矩陣變換方向的最佳方法通常是在主應用程式中計算逆矩陣,並透過另一個統一變數將其傳遞給著色器。

例外情況是正交 3×3 矩陣 (即所有行(或列)都已歸一化並且彼此正交)或形式為 的 4×4 矩陣

其中 是正交 3×3 矩陣。在這些情況下,逆矩陣 等於轉置矩陣

如上所述,在 GLSL 中,用向量乘以轉置矩陣的最佳方法是從左側乘以原始矩陣,因為這被解釋為行向量與原始矩陣的乘積,它對應於轉置矩陣與列向量的乘積。

因此,用轉置矩陣(即正交矩陣的情況下的逆矩陣)進行變換寫成

mat4 matrix; // upper, left 3x3 matrix is orthogonal
vec4 direction; // 4th component is 0
vec4 direction_transformed_with_inverse = vec4(vec3(direction * matrix), 0.0);

請注意,結果的第 4 個分量必須單獨設定為 0,因為 direction * matrix 的第 4 個分量對於方向的變換沒有意義。(然而,對於此處未討論的平面方程的變換,它是有意義的。)

針對 3x3 矩陣和 3D 向量的版本只需要在 3D 和 4D 向量之間進行不同的強制轉換操作。

使用逆變換變換法向量

[edit | edit source]

假設逆矩陣 可用,但需要與 對應的變換。此外,我們想將這種變換應用於法向量。在這種情況下,我們只需透過從左邊將法向量乘以逆矩陣來應用逆矩陣的轉置(如上所述)

mat4 matrix_inverse;
vec3 normal;
vec3 transformed_normal = vec3(vec4(normal, 0.0) * matrix_inverse);

(或透過轉換矩陣)。

內建矩陣變換

[edit | edit source]

一些框架(特別是 OpenGL 相容性配置檔案,但既不是 OpenGL 核心配置檔案也不是 OpenGL ES 2.x)在 GLSL 著色器中提供了一些內建的制服來訪問某些頂點變換。它們不應該被宣告,但這裡有宣告來指定它們的型別

uniform mat4 gl_ModelViewMatrix;
uniform mat4 gl_ProjectionMatrix;
uniform mat4 gl_ModelViewProjectionMatrix;
uniform mat4 gl_TextureMatrix[gl_MaxTextureCoords];
uniform mat3 gl_NormalMatrix; // transpose of the inverse of the
   // upper left 3x3 matrix of gl_ModelViewMatrix
uniform mat4 gl_ModelViewMatrixInverse;
uniform mat4 gl_ProjectionMatrixInverse;
uniform mat4 gl_ModelViewProjectionMatrixInverse;
uniform mat4 gl_TextureMatrixInverse[gl_MaxTextureCoords];
uniform mat4 gl_ModelViewMatrixTranspose;
uniform mat4 gl_ProjectionMatrixTranspose;
uniform mat4 gl_ModelViewProjectionMatrixTranspose;
uniform mat4 gl_TextureMatrixTranspose[gl_MaxTextureCoords];
uniform mat4 gl_ModelViewMatrixInverseTranspose;
uniform mat4 gl_ProjectionMatrixInverseTranspose;
uniform mat4 gl_ModelViewProjectionMatrixInverseTranspose;
uniform mat4 gl_TextureMatrixInverseTranspose[gl_MaxTextureCoords];

進一步閱讀

[edit | edit source]

法向量變換在“OpenGL 4.1 相容性配置檔案規範”的第 2.12.2 節中進行了描述,該規範可從 Khronos OpenGL 網站 獲取。

對法向量變換更易於理解的描述在“OpenGL 程式設計指南”的免費 HTML 版本的附錄 E 中給出,該版本可在 網上 獲得。

矩陣變換的內建制服在“OpenGL 著色語言 4.10.6 規範”的第 7.4.1 節中進行了描述,該規範可從 Khronos OpenGL 網站 獲取。


< GLSL 程式設計

除非另有說明,否則本頁上的所有示例原始碼均授予公有領域。
華夏公益教科書