跳轉到內容

Cg 程式設計/向量和矩陣操作

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

Cg 的語法與 C(因此 C++ 和 Java)非常相似;但是,它包含用於浮點向量和矩陣的內建資料型別和函式,這些函式是 Cg 特有的。這些將在本文中討論。Cg 的完整描述可以在 Nvidia 的 Cg 教程Nvidia 的 Cg 語言規範 中找到。

具有全精度的浮點資料型別

[編輯 | 編輯原始碼]

在 Cg 中,型別 float2float3float4 代表 2D、3D 和 4D 浮點向量。向量變數的定義與您預期的一樣,就像 C、C++ 或 Java 具有這些型別一樣

float2 a2DVector;
float3 three_dimensional_vector;
float4 vector4;

浮點 2×2、3×3 和 4×4 矩陣的資料型別分別為:float2x2float3x3float4x4

float2x2 m2x2;
float3x3 linear_mapping;
float4x4 trafo;

還存在用於具有不同行數和列數的矩陣的型別,例如 3 行 4 列:float3x4

具有有限精度的的資料型別

[編輯 | 編輯原始碼]

除了 float 資料型別之外,還有額外的 half 型別(halfhalf2half3half4half2x2half3x3half4x4 等)和 fixed 型別(fixedfixed2fixed3fixed4 等),它們代表有限精度和範圍的數字(即標量)、向量和矩陣。它們通常提供更好的效能;因此,為了獲得最佳效能,您應該使用 half 型別之一代替相應的 float 型別(特別是如果資料表示幾何位置或紋理座標)甚至 fixed 型別之一(通常如果資料表示顏色)如果有限精度和範圍不會導致渲染偽影。

在這裡,程式碼將始終使用 float 型別以避免任何與有限精度相關的錯誤並保持儘可能簡單。還存在用於整數和布林向量的型別,這些型別這裡將不討論。

建構函式

[編輯 | 編輯原始碼]

向量可以透過與資料型別同名的建構函式進行初始化和轉換

float2 a = float2(1.0, 2.0);
float3 b = float3(-1.0, 0.0, 0.0);
float4 c = float4(0.0, 0.0, 0.0, 1.0);

請注意,某些 Cg 編譯器可能會在使用整數初始化浮點向量時報錯;因此,最好始終包含小數點。

將低維向量轉換為高維向量是透過向這些建構函式提供正確數量的元件來實現的

float2 a = float2(0.1, 0.2);
float3 b = float3(0.0, a); // = float3(0.0, 0.1, 0.2)
float4 c = float4(b, 1.0); // = float4(0.0, 0.1, 0.2, 1.0)

同樣,矩陣可以被初始化和構造。請注意,在矩陣建構函式中指定的這些值將被消耗以填充第一行,然後是第二行,依此類推。

float3x3 m = float3x3(
   1.1, 1.2, 1.3, // first row (not column as in GLSL!)
   2.1, 2.2, 2.3, // second row
   3.1, 3.2, 3.3  // third row
);
float3 row0 = float3(0.0, 1.0, 0.0);
float3 row1 = float3(1.0, 0.0, 0.0);
float3 row2 = float3(0.0, 0.0, 1.0);
float3x3 n = float3x3(row0, row1, row2); // sets rows of matrix n

Cg(但不是所有版本的 HLSL)也接受 float4 建構函式中的一個浮點數,以將所有元件設定為相同的值

float4 a = float4(0.0); // = float4(0.0, 0.0, 0.0, 0.0)

此外,Cg(但不是所有版本的 HLSL)可以使用向量建構函式將高維向量轉換為低維向量

float4 a = float4(-1.0, 2.5, 4.0, 1.0);
float3 b = float3(a); // = float3(-1.0, 2.5, 4.0)
float2 c = float2(b); // = float2(-1.0, 2.5)

如果從較大的矩陣構造較小的矩陣,則將選擇較大矩陣的頂部左子矩陣

float3x3 m = float3x3(
   1.1, 1.2, 1.3,
   2.1, 2.2, 2.3,
   3.1, 3.2, 3.3 
);
float2x2 n = float2x2(m); // = float2x2(1.1, 1.2, 2.1, 2.2)

向量的元件可以透過使用 [] 運算子進行陣列索引(索引從 0 開始)或使用 . 運算子和元素名稱 x, y, z, wr, g, b, a(或 s, t, p, q)進行訪問

float4 v = float4(1.1, 2.2, 3.3, 4.4);
float a = v[3]; // = 4.4 
float b = v.w; // = 4.4
float c = v.a; // = 4.4

也可以透過擴充套件 . 符號來構造新的向量

float4 v = float4(1.1, 2.2, 3.3, 4.4);
float3 a = v.xyz; // = float3(1.1, 2.2, 3.3) 
float3 b = v.bgr; // = float3(3.3, 2.2, 1.1)

矩陣被認為由行向量組成,這些行向量可以透過使用 [] 運算子進行陣列索引進行訪問。得到的(行)向量的元素可以透過上面討論的方式進行訪問

float3x3 m = float3x3(
   1.1, 1.2, 1.3, // first row 
   2.1, 2.2, 2.3, // second row
   3.1, 3.2, 3.3  // third row
);
float3 row2 = m[2]; // = float3(3.1, 3.2, 3.3)
float m20 = m[2][0]; // = 3.1
float m21 = m[2].y; // = 3.2

運算子

[編輯 | 編輯原始碼]

如果二元運算子 *, /, +, -, =, *=, /=, +=, -= 用於相同型別的向量之間,它們將按元件執行

float3 a = float3(1.0, 2.0, 3.0);
float3 b = float3(0.1, 0.2, 0.3);
float3 c = a + b; // = float3(1.1, 2.2, 3.3)
float3 d = a * b; // = float3(0.1, 0.4, 0.9)

請特別注意,a * b 代表兩個向量的按元件乘積,這線上性代數中並不常見。對於矩陣,這些運算子也按元件執行。同樣,兩個矩陣的按元件乘積線上性代數中並不常見。

對於通常的矩陣-向量積或矩陣-矩陣積,請參見下面的內建 mul 函式。對於向量之間的點積和叉積,請參見下面的內建函式 dotcross

* 運算子也可以用於將浮點數(即標量)乘以向量或矩陣的所有元件(從左或右)

float3 a = float3(1.0, 2.0, 3.0);
float2x2 m = float2x2(1.0, 0.0, 0.0, 1.0);
float s = 10.0;
float3 b = s * a; // float3(10.0, 20.0, 30.0)
float3 c = a * s; // float3(10.0, 20.0, 30.0)
float2x2 m2 = s * m; // = float2x2(10.0, 0.0, 0.0, 10.0)
float2x2 m3 = m * s; // = float2x2(10.0, 0.0, 0.0, 10.0)

內建向量和矩陣函式

[編輯 | 編輯原始碼]

按元件函式

[編輯 | 編輯原始碼]

以下函式按元件對型別 floatfloat2float3float4halfhalf2half3half4fixedfixed2fixed3fixed4 的變數執行操作,這些變量表示為 TYPE

TYPE min(TYPE a, TYPE b) // returns a if a < b, b otherwise
TYPE max(TYPE a, TYPE b) // returns a if a > b, b otherwise
TYPE clamp(TYPE a, TYPE minVal, TYPE maxVal) 
   // = min(max(a, minVal), maxVal) 
TYPE lerp(TYPE a, TYPE b, TYPE wb) // = a * (TYPE(1.0, 1.0, ...) - wb) + b * wb
TYPE lerp(TYPE a, TYPE b, float wb) // = a * TYPE(1.0 - wb, 1.0 - wb, ...) + b * TYPE(wb, wb, ...)

還有更多內建函式,這些函式也按元件執行,但對向量不太有用,例如 abssignfloorceilroundfracfmodstepsmoothstepsqrtpowexpexp2loglog2radians(將度數轉換為弧度)、degrees(將弧度轉換為度數)、sincostanasinacosatanatan2

矩陣函式

[編輯 | 編輯原始碼]

對於線性代數中通常的矩陣-矩陣積,應使用 mul 函式。它透過將一行(第一個矩陣)乘以一列(第二個矩陣)來計算結果矩陣的元件,例如

  

在Cg中

float2x2 a = float2x2(1., 2.,  3., 4.);
float2x2 b = float2x2(10., 20.,  30., 40.);
float2x2 c = mul(a, b); // = float2x2(
   // 1. * 10. + 2. * 30., 1. * 20. + 2. * 40., 
   // 3. * 10. + 4. * 30., 3. * 20. + 4. * 40.)

此外,mul 函式可以用於對應維度的矩陣-向量乘積,例如:

  

在Cg中

float2 v = float2(10., 20.);
float2x2 m = float2x2(1., 2.,  3., 4.);
float2 w = mul(m, v); // = float2x2(1. * 10. + 2. * 20., 3. * 10. + 4. * 20.)

請注意,向量必須作為第二個引數,才能從右側乘以矩陣。

如果向量被指定為第一個引數,它將從 **左側** 乘以矩陣。

float2 v = float2(10., 20.);
float2x2 m = float2x2(1., 2.,  3., 4.);
float2 w = mul(v, m); // = float(10. * 1. + 20. * 3., 10. * 2. + 20. * 4.)

從左側乘以行向量到矩陣相當於從右側乘以列向量到 **轉置** 矩陣。

用分量表示

        

因此,將一個向量從左側乘以矩陣,相當於將它從右側乘以轉置矩陣,而無需顯式計算轉置矩陣。

還有一個函式 transpose 用於計算轉置矩陣。

float2x2 m = float2x2(1., 2.,  3., 4.);
float2x2 n = transpose(m); // = float2x2(1., 3., 2., 4)

幾何函式

[edit | edit source]

以下函式對於向量運算特別有用。TYPE 可以是以下任意值:float, float2, float3, float4, half, half2, half3, half4, fixed, fixed2, fixed3fixed4(每行只允許一個)。

float3 cross(float3 a, float3 b) 
   // = float3(a[1] * b[2] - a[2] * b[1], 
   // a[2] * b[0] - a[0] * b[2], 
   // a[0] * b[1] - a[1] * b[0]) 
float dot(TYPE a, TYPE b) // = a[0] * b[0] + a[1] * b[1] + ... 
float length(TYPE a) // = sqrt(dot(a, a))
float distance(TYPE a, TYPE b) // = length(a - b)
TYPE normalize(TYPE a) // = a / length(a)
TYPE faceforward(TYPE n, TYPE i, TYPE nRef) 
   // returns n if dot(nRef, i) < 0, -n otherwise
TYPE reflect(TYPE i, TYPE n) // = i - 2. * dot(n, i) * n  
   // this computes the reflection of vector 'i' 
   // at a plane of normalized(!) normal vector 'n'

物理函式

[edit | edit source]

該函式

TYPE refract(TYPE i, TYPE n, float r)

計算折射光線的方向,如果 i 指定了入射光線的歸一化(!)方向,而 n 指定了兩種光學介質(例如空氣和水)介面的歸一化(!)法向量。向量 n 應該指向 i 所來自的一側,即 ni 的點積應該為負。浮點數 r 是光線來自的介質的折射率與表面另一側的介質的折射率之比。因此,如果光線來自空氣(折射率約為 1.0)並撞擊水錶面(折射率為 1.33),則比率 r 為 1.0 / 1.33 = 0.75。該函式的計算結果為

float d = 1.0 - r * r * (1.0 - dot(n, i) * dot(n, i));
if (d < 0.0) return TYPE(0.0, 0.0, ...); // total internal reflection
return r * i - (r * dot(n, i) + sqrt(d)) * n;

如程式碼所示,如果發生全內反射 (參見維基百科條目),即光線沒有穿過兩種材料之間的介面,則該函式將返回一個長度為 0 的向量。

進一步閱讀

[edit | edit source]

關於 Cg 的完整描述可以在 Nvidia 的 Cg 教程(包括 附錄 E 中的所有標準庫函式)和 Nvidia 的 Cg 語言規範 中找到。

< Cg 程式設計

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