Cg 程式設計/應用矩陣變換
在著色器中應用傳統的頂點變換(參見 “頂點變換”部分)或任何其他由矩陣表示的變換,通常透過在著色器的統一變數中指定相應的矩陣,然後將矩陣與向量相乘來完成。但是,在細節上有一些差異。在這裡,我們討論點的變換(即具有第 4 個座標等於 1 的 4D 向量)、方向的變換(即嚴格意義上的向量:3D 向量或具有第 4 個座標等於 0 的 4D 向量)以及表面法向量的變換(即指定與平面正交的方向的向量)。
本節假定您對 Cg 語法有一定的瞭解,如 “向量和矩陣操作”部分 中所述。
對於點,變換通常用 4×4 矩陣表示,因為它們可能包括由 3D 向量 t 在第 4 列進行平移
(投影矩陣還將在最後一行包含其他不等於 0 的值。)
三維點用具有第 4 個座標等於 1 的四維向量表示
為了應用變換,矩陣 乘以向量
用 4×4 矩陣對由 4D 向量表示的點進行變換的 Cg 程式碼非常簡單
float4x4 mymatrix;
float4 point;
float4 transformed_point = mul(mymatrix, point);
如果給定一個轉置矩陣,則可以使用 transpose 函式計算所需的矩陣。但是,更常見的是,如 “向量和矩陣運算”部分 所述,從左側對轉置矩陣進行向量乘法。
float4x4 matrix_transpose;
float4 point;
float4 transformed_point = mul(point, matrix_transpose);
變換方向
[edit | edit source]三維空間中的方向用 3D 向量或第四個座標為 0 的 4D 向量表示。(可以將它們視為無窮遠處的點;類似於地平線上的一個點,我們無法確定其在空間中的位置,只能確定找到它的方向。)
在 3D 向量的情況下,我們可以透過將其與 3×3 矩陣相乘來進行變換
float3x3 mymatrix;
float3 direction;
float3 transformed_direction = mul(mymatrix, direction);
或者,如果我們將 3D 向量轉換為第四個座標等於 0 的 4D 向量,則可以使用 4×4 矩陣進行變換
float4x4 mymatrix;
float3 direction;
float4 transformed_direction = mul(mymatrix, float4(direction, 0.0));
或者,也可以將 4×4 矩陣轉換為 3×3 矩陣。
另一方面,可以將 4D 向量直接與 4×4 矩陣相乘。也可以將其轉換為 3D 向量,以便將其與 3×3 矩陣相乘
float3x3 mymatrix;
float4 direction; // 4th component is 0
float4 transformed_direction = float4(mul(mymatrix, direction.xyz), 0.0);
變換法線向量
[edit | edit source]與方向類似,表面法線向量(簡稱“法線向量”)用 3D 向量或第四個分量為 0 的 4D 向量表示。但是,它們的變換方式不同。(數學原因是它們表示一個稱為協向量、協變向量、一形式或線性泛函的東西。)
要理解法線向量的變換,請考慮表面法線向量的主要特徵:它與表面正交。當然,此特徵在變換下仍然成立,即變換後的法線向量應該與變換後的表面正交。如果表面在區域性用切線向量表示,則此特徵要求變換後的法線向量與變換後的方向向量正交,如果原始法線向量與原始方向向量正交。
數學上來說,法線向量 n 與方向向量 v 正交,如果它們的點積為 0。事實證明,如果 v 透過 3×3 矩陣 進行變換,則法線向量必須透過 的 **轉置逆矩陣** 進行變換:。我們可以透過檢查變換後的法線向量 n 與變換後的方向向量 v 的點積來輕鬆測試這一點
第一步中我們用到了 = ,然後 = ,然後 ,然後 (即單位矩陣)。
計算表明,變換後的向量的點積實際上與原始向量的點積相同;因此,變換後的向量是正交的當且僅當原始向量是正交的。這正是它應該做的。
因此,為了在 Cg 中變換法向量,轉置逆矩陣通常被指定為一個統一變數(連同用於變換方向和點的原始矩陣),並像任何其他變換一樣應用。
float3x3 matrix_inverse_transpose;
float3 normal;
float3 transformed_normal = mul(matrix_inverse_transpose, normal);
對於 4×4 矩陣,可以透過新增 0 將法向量轉換為 4D 向量。
float4x4 matrix_inverse_transpose;
float3 normal;
float3 transformed_normal = mul(matrix_inverse_transpose, float4(normal, 0.0)).xyz;
或者,可以將矩陣轉換為 3×3 矩陣。
如果已知逆矩陣,則可以從左側乘以法向量來應用轉置逆矩陣,如 “向量和矩陣運算”部分 所述。因此,為了將法向量乘以轉置逆矩陣,我們可以從左側將其乘以逆矩陣。
float3x3 matrix_inverse;
float3 normal;
float3 transformed_normal = mul(normal, matrix_inverse);
在將 4×4 矩陣乘以 4D 法向量(從左側或右側)的情況下,應確保所得向量的第 4 個分量為 0。實際上,在某些情況下,需要丟棄計算出的第 4 個分量(例如,透過將結果轉換為 3D 向量)。
float4x4 matrix_inverse;
float4 normal;
float4 transformed_normal = float4(mul(normal, matrix_inverse).xyz, 0.0);
請注意,透過這種變換,法向量的單位長度歸一化不會保留。因此,法向量通常在變換後歸一化為單位長度(例如,使用內建 Cg 函式 normalize)。
用正交矩陣變換法向量
[edit | edit source]當變換矩陣 是正交的時候,會出現一種特殊情況。在這種情況下, 的逆是轉置矩陣;因此, 的逆的轉置是兩次轉置的矩陣,即原始矩陣,即對於正交矩陣
因此,對於正交矩陣,法向量用與方向和點相同的矩陣進行變換。
float3x3 mymatrix; // orthogonal matrix
float3 normal;
float3 transformed_normal = mul(mymatrix, normal);
用逆矩陣變換點
[edit | edit source]有時需要應用逆變換。在大多數情況下,最佳解決方案是為逆矩陣定義另一個統一變數,並在主應用程式中設定逆矩陣。然後,著色器可以像任何其他矩陣一樣應用逆矩陣。這比在著色器中計算逆矩陣效率高得多。
但是,存在一個特殊情況:如果矩陣 具有上面所示的形式(即第 4 行為 (0,0,0,1))
具有正交 3×3 矩陣 (即 的行(或列)向量是歸一化的並且彼此正交;例如,這通常是檢視變換的情況,請參閱 “頂點變換”部分),那麼逆矩陣由(因為對於正交矩陣 )
對於用 4D 向量 表示的點 乘以 3D 向量 p ,我們得到
注意向量 t 只是矩陣 的第 4 列,可以透過以下方式訪問:
float4x4 m;
float4 last_column = float4(m[0][3], m[1][3], m[2][3], m[3][3]);
如上所述,用一個轉置矩陣乘以一個向量可以透過將向量從左側乘以矩陣來輕鬆表達,這對應於轉置矩陣與對應列向量的矩陣向量積
利用 Cg 的這些特性,(p - t) 可以輕鬆高效地實現,如下所示(注意,結果的第 4 個分量必須單獨設定為 1)
float4x4 m; // upper, left 3x3 matrix is orthogonal;
// 4th row is (0,0,0,1)
float4 point; // 4th component is 1
float4 last_columm = float4(m[0][3], m[1][3], m[2][3], m[3][3]);
float4 point_transformed_with_inverse = float4(mul(point - last_column, m).xyz, 1.0);
使用逆矩陣變換方向
[edit | edit source]與點的情況一樣,用逆矩陣變換方向的最佳方法通常是在主應用程式中計算逆矩陣,並透過另一個統一變數將其傳遞給著色器。
例外情況是正交 3×3 矩陣 (即所有行(或列)都已歸一化,並且彼此正交)或形式為 4×4 矩陣 的
其中 是正交 3×3 矩陣。在這種情況下,逆矩陣 等於轉置矩陣 。
如上所述,在 Cg 中,用轉置矩陣乘以向量的最佳方法是從左到右乘以原始矩陣,因為這被解釋為行向量與原始矩陣的乘積,對應於轉置矩陣與列向量的乘積。
因此,用轉置矩陣(即在正交矩陣的情況下為逆矩陣)進行變換寫為
float4x4 mymatrix; // upper, left 3x3 matrix is orthogonal
float4 direction; // 4th component is 0
float4 direction_transformed_with_inverse = float4(float3(mul(direction, mymatrix)), 0.0);
請注意,結果的第 4 個分量必須單獨設定為 0,因為 mul(direction, mymatrix) 的第 4 個分量對於方向的變換沒有意義。(但是,它對於平面方程的變換是有意義的,這裡沒有討論。)
3x3 矩陣和 3D 向量的版本只需要在 3D 和 4D 向量之間進行不同的強制轉換操作。
用逆變換變換法線向量
[edit | edit source]假設逆矩陣 可用,但需要對應於 的變換。此外,我們希望將此變換應用於法線向量。在這種情況下,我們可以透過從左到右乘以法線向量到逆矩陣(如上所述)來應用逆矩陣的轉置
float4x4 matrix_inverse;
float3 normal;
float3 transformed_normal = mul(float4(normal, 0.0), matrix_inverse).xyz;
(或透過強制轉換矩陣)。
進一步閱讀
[edit | edit source]法線向量的變換也描述在“OpenGL 4.5 Compatibility Profile Specification”的第 12.1.2 節中,該規範可在 Khronos OpenGL 網站 上獲得。
“OpenGL Programming Guide”的免費 HTML 版本的附錄 F 中提供了更易於理解的法線向量變換描述,可在 網上 獲得。
< Cg 程式設計