1. 程式人生 > >CG 程式設計

CG 程式設計

1. Cg用例
Cg是用於GPU程式設計的語言。Cg程式看起來非常像C程式。這兒有一個Cg頂點程式:

void simpleTransform(float4 objectPosition: POSITION,
float4 color: COLOR,
float4 decalCoord: TEXCOORD0,
float4 lightMapCoord: TEXCOORD1,
out float4 clipPosition: POSITION,
out float4 oColor: COLOR,
out float4 oDecalCoord: TEXCOORD0,
out float4 oLightMapCoord: TEXCOORD1,
uniform float brightness,
uniform float4x4 modelViewProjection)
{
clipPosition = mul(modelViewProjection, objectPosition);
oColor = brightness * color;
oDecalCoord = decalCoord;
oLightMapCoord = lightMapCoord;
}

1.1 頂點程式的講解
這個程式把一個頂點的物件空間位置通過一個包含世界、觀察和投射串聯轉換的4x4矩陣進行轉換。得出的結果向量是頂點在夾持空間的位置。每個頂點的顏色在輸出前都通過一個浮點引數進行放縮。同樣,兩個紋理座標集自然也傳過去了。

Cg支援標量資料型別,比如float,但也很好地支援向量資料型別。float4表示包含四個浮點數的向量。float4x4表示一個矩陣。mul是一個標準庫函式,完成矩陣和向量乘法。Cg提供像C++的函式過載;mul是一個過載函式,所以它可以用於向量和矩陣相乘的所有組合情況。

Cg提供和C一樣的操作符。但是不像C,Cg的操作符可以接受和返回標量和向量。比如,標量brightness對向量color進行了放縮,就像你期望的。

Cg中,用uniform修飾符宣告一個引數表明它的值是由外部的資料來源初始化的,而且在給定這批向量的處理中保持不變。從這點看來,Cg中的uniform修飾符和RenderMan中的uniform修飾符不同,但使用的場合一樣。實際上,外部資料來源是你的應用程式載入的一些OpenGL或Direct3D狀態。比如,你的程式必須提供modelViewProjection矩陣和brightness標量。Cg執行庫提供一個API把你程式的狀態轉化成編譯的程式需要的適當的API狀態。

跟在objectPosition、color、decalCoord和lightMapCoord引數後面的POSITION、COLOR、TEXCOORD0和TEXCOORD1標示符叫做輸入語義。它們表明引數是怎麼由每個頂點不同的資料初始化的。在OpenGL中,glVertex命令需要POSITION輸入語義;glColor命令需要COLOR語義;glMultiTexCoord命令需要TEXCOORDn語義。

out修飾符表明clipPosition、oColor、oDecalCoord和oLightMapCoord引數是由這段程式輸出的。跟在引數後面的語義就是輸出語義。這些語義分別表明程式輸出一個轉換後的夾持空間位置和顏色量。同樣,兩個紋理座標集也傳遞過去了。得出的結果頂點提供給圖元彙編程式來生成一個光柵化的圖元。

編譯這個程式需要程式原始碼、要編譯的入口函式名和profile名(vs_1_1)。

然後Cg編譯器可以把上面的Cg程式編譯為下面的DirectX 8 vertex shader:

vs.1.1
mov oT0, v7
mov oT1, v8
dp4 oPos.x, c1, v0
dp4 oPos.y, c2, v0
dp4 oPos.z, c3, v0
dp4 oPos.w, c4, v0
mul oD0, c0.x, v5

Profile用來表明程式要被編譯為用於哪個API執行環境的。同一個程式可以編譯為用於DirectX 9 vertex shader profile(vs_2_0),多廠商OpenGL頂點程式擴充套件(arbvp1)或NVIDIA私有的OpenGL擴充套件(vp20和vp30)。

編譯Cg程式的過程可以在你使用Cg的程式初始化的時候進行。Cg執行庫包含Cg編譯器,也以API的方式包含一個非常簡單的過程,可以讓你配置編譯過的程式是用於OpenGL還是Direct3D的。

1.2 片元程式的講解
除了寫處理頂點的程式之外,你還可以寫處理片元的程式。這兒有一個Cg片元程式:

float4 brightLightMapDecal(float4 color: COLOR,
float4 decalCoord: TEXCOORD0,
float4 lightMapCoord: TEXCOORD1,
uniform sampler2D decal,
uniform sampler2D lightMap) : COLOR
{
float4 d = tex2Dproj(decal, decalCoord);
float4 lm = tex2Dproj(lightMap, lightMapCoord);
return 2.0 * color * d * lm;
}

輸入的引數是顏色插值和兩個紋理座標集,由它們的輸入語義定義。

sampler2D型別對應於一個2D紋理單元。Cg標準庫程例tex2Dproj可以進行2D紋理對映查詢。兩次tex2Dproj呼叫對decal和light map紋理進行取樣,然後把結果分別賦值給區域性變數d和lm。

這段程式把兩個紋理結果,顏色插值和常數2.0相乘,得出RGBA顏色的結果。這段程式返回一個float4和返回值是COLOR的語義,也就是片元最後的顏色。

當要編譯為arbfp1多廠商OpenGL片元profile時,Cg編譯器把brightLightMapDecal轉化成以下程式碼:

!!ARBfp1.0
PARAM c0 = {2, 2, 2, 2}; TEMP R0; TEMP R1; TEMP R2;
TXP R0, fragment.texcoord[0], texture[0], 2D;
TXP R1, fragment.texcoord[1], texture[1], 2D;
MUL R2, c0.x, fragment.color.primary;
MUL R0, R2, R0;
MUL result.color, R0, R1;
END

同一個程式可以編譯為用於DirectX 8或9的profile(ps_1_3和ps_2_x)或NVIDIA私有的OpenGL擴充套件(fp20和fp30)。

2. 其他Cg的功能
2.1 從C來的特性
Cg提供結構體和陣列,包含多維陣列。Cg提供所有C的數學操作符(+、*、/等)。Cg提供一個布林型別和那些與布林型別相關的操作符(||、&&、!等)。Cg提供遞增/遞減(++/--)操作符,條件表示式操作符(?:),賦值表示式(+=等),甚至還有C逗號操作符。

Cg提供使用者定義函式(除了已定義的標準庫函式之外),但是不允許遞迴函式。Cg提供C的控制流結構的子集(do、while、for、if、break、continue);其他結構比如goto和switch在當前的Cg實現版本並不支援,但保留了必要的關鍵字。

就像C,Cg對資料型別的精度和範圍並不嚴加限制。實際上,用於編譯的profile的選擇決定了每種資料型別具體的表現。float、half和double用來表示連續的值,理想的浮點數,但這依賴於profile。half指的是16位半精度浮點資料型別(NVIDIA的CineFX架構提供這樣的資料型別)。int是整數,通常用於迴圈和索引。fixed是一個額外的資料型別,用來表示不是浮點數的定點的連續資料。

Cg提供#i nclude、#define、#ifdef等,和C的預處理一樣。Cg支援C和C++的註釋風格。

2.2 C沒有的附加特性
Cg對向量資料型別提供內建的建構函式(類似於C++,但不是由使用者自定義的):

float4 vec1 = float4(4.0, -2.0, 5.0, 3.0);
Swizzling是一種用來重組向量的組成或者構造更短或更長向量的方法。例子如下:

float2 vec2 = vec1.yx;// vec2 = (-2.0, 4.0)
float scalar = vec1.w;// scalar = 3.0
float3 vec3 = scalar.xxx;// vec3 = (3.0, 3.0, 3.0)

矩陣可以用更復雜的swizzling語法。向量和矩陣元素也可以用標準陣列索引語法來訪問。

寫入掩碼可以限制只給向量指定的部分賦值。例子如下:

vec1.xw = vec3;// vec1 = (3.0, -2.0, 5.0, 3.0)
使用.xyzw或.rgba字尾來組成swizzling和寫入掩碼。

Cg標準庫包含大量的內建數學函式(abs、dot、log2、reflect、rsqrt等)和紋理訪問函式(texCUBE、tex3Dproj等)。標準庫廣泛應用了函式過載(類似於C++)來支援不同長度的向量和不同的資料型別。你不必像在C裡一樣使用#i nclude來包含標準庫程例的原型,Cg標準庫程例都是自動生成原型的。

除了用於call-by-result傳參的out修飾符之外,inout修飾符使引數既是call-by-value輸入引數也是call-by-result輸出引數。

關鍵字discard類似於return,但是不返回轉換好的片元就終止處理了。

2.3 不支援的特性
Cg目前還不支援指標和位運算(但是,必要的C操作符和關鍵字為這些目的保留了)。Cg(目前)不支援聯合體和函式變元。

Cg沒有C++“大型的程式設計”特性,比如類、模板、操作符過載、異常處理和名字空間。

Cg標準庫沒有功能性的程例,比如字串處理、檔案輸入/輸出和記憶體配置,這些超出了Cg的專有領域。

但是,Cg保留了所有的C和C++關鍵字,這為從這些語言來的特性可以合入未來的Cg實現作保證。

3. Profile依賴
當你編譯一個C或C++程式時,它不會因為程式過大(在一定前提之內)或因為這個程式的用途而無法通過編譯。對於Cg,一個語法和語義都正確的程式可能仍然不能編譯,因為這還受限於你編譯這個程式的profile。

比如,目前當用頂點profile編譯時,如果訪問紋理就會產生一個錯誤。未來的頂點profile可能會允許紋理訪問,但現在的頂點profile不行。其他錯誤更為固有。比如,一個片元profile不能以TEXCOORD0語義輸出一個引數。其他錯誤可能是因為超出了當前的GPU能力範圍,比如超過最大的指令數或可利用的紋理單元數目。

要知道,這些profile依賴錯誤並不反映出Cg語言的限制,而是目前Cg實現的限制或者你的目標GPU的潛在硬體限制。

4. 相容性和移植性
NVIDIA的Cg實現和Microsoft的高階著色語言(HLSL)非常相似,因為它們是合作開發的。HLSL是和DirectX 9以及Windows作業系統整合在一起的。Cg則提供了多API(OpenGL、DirectX 8和DirectX 9)和多作業系統(Windows、Linux和Mac OS X)的支援。因為Cg對多廠商API有介面,Cg可以在多個廠商的GPU上執行。

5. 更多資訊
可以看《The Cg Tutorial:The Definitive Guide to Programmable Real-Time Graphics》(ISBN 0321194969)