Unity Shader 7 CG 語法

1 Cg語法基礎

如C++、C#和Java等高級語言一樣,Cg語言也有自己的數據類型和關鍵字。掌握和理解這些關鍵字是寫好Cg程序的基礎。

1.2、Cg的數據類型與關鍵字

基本數據類型:Cg支持7種基本的數據類型

1、float,32位浮點數據,一個符號位。浮點數據類型被所有的圖形接口支持;
2、half,16位浮點數據;
3、int,32位整形數據
4,fixed,12位定點數,
5、bool,布爾數據,被所有的圖形接口支持;
6、sampler*,紋理對象的句柄,分爲sampler、sampler1D、sampler2D、sampler3D、samplerCUBE和samplerRECT。

內置的數據類型:基於基礎數據類型,如float3,表示float類型的三維向量;同理,bool2表示布爾類型的二維向量。
注:向量最長不能超過四元,如float5 vector;//編譯錯誤

向量的賦值,float2 a=float(1.0,1.0); //編譯通過
float2 a=float(1.0f,1.0f); //編譯錯誤
float3 b=float(a,0.0); //編譯通過
矩陣數據類型,float1X1 m1; //即float m1,一維矩陣
float3X4 m34 //34階矩陣
注:X是字符,不是乘號,最大的維數爲4
4階
float3 x和floatx[3]是不同的,前者爲向量是內置的數據類型,而數組則是一種數據結構,不是內置的數據類型。

類型轉換:Cg中的類型轉換有強制轉換和隱式轉換;如果是隱式轉換則數據類型從低精度向高精度轉換。如:
float a=1.0;
half b=2.0;
float c=a+b; //等價於float c=a+(float)b;

Cg語言中可以對常量數據加上類型後綴表示該數據類型的數據,如:
float a=1.0h; //1.0h爲half類型常量數據

這樣的後綴類型有三種:

f:表示float;
h:表示half;
x:表示fixed;
Swizzle操作符:Cg語言中的其他操作符和高級CPU語言C++類似,包括關係操作符、邏輯操作符和位移操作符以及條件操作符。而Swizzle操作符是Cg語言中特有的,它可以將一個向量的成員取出組成一個新的向量。對於座標或者角度等其他多維向量,Swizzle操作符(.)後接x、y、z、w分別表示原始向量的第一個、第二個、第三個和第四個元素;同樣,對於顏色可以後接r、g、b和a來表示同樣的索引。
例如:

          float4(a,b,c,d).xwz  等價於 float(a,d,c)

          float4(a,b,c,d).xxy  等價於 float(a,a,b)

輸入數據關鍵字:Cg中輸入數據流一般分爲兩類

1、varying 參數:在Cg程序中通過語義進行綁定變量, Cg語言提供了一組語義詞,用以表示參數是由頂點的那些數據初始化的,一旦這個變量使用了語義詞進行綁定,那麼這個變量值被初始化的同時也意味着它有了特殊的含義,如表示位置、法線等含義。語義提供了一種使用隨頂點變化或隨片段變化而變化的值來初始化Cg程序參數的方法,這些數據都是從應用程序輸入到GPU的數據,如頂點位置、法向量、紋理座標數據等。

2、Uniform參數:Uniform是用來限制一個變量的初始值的來源,當聲明一個變量爲Uniform類型的時候,表示這個變量的初始值來自於外部的其他環境。除了獲取初始值的這點之外,Uniform關鍵字聲明的變量和其他變量是完全一樣的。通常用Uniform來定義一些與三維渲染有關的離散信息數據,並通常不會隨着圖元信息的變化而變化,如材質對光的反射信息。Uniform表示一個參數,通常使用uniform表示函數的形參,不能定義一個uniform表示的局部變量。

Unity的內置Uniform輸入參數如下:
uniform float4 _Time, _SinTime, _CosTime; // 時間量
uniform float4 _ProjectionParams; // x = 1 or -1 (如果投影翻轉就是-1)

// y = 近平面; z = 遠平面; w = 1/遠平面
uniform float4 _ScreenParams; // x = width; y = height; z = 1 +1/width; w = 1 + 1/height
uniform float3_WorldSpaceCameraPos;
uniform float4x4 _Object2World; //模型矩陣
uniform float4x4 _World2Object; // 模型矩陣的逆
uniform float4 _LightPositionRange; // xyz = pos, w = 1/range
uniform float4 _WorldSpaceLightPos0; // 光源的位置和方向
uniform float4x4 UNITY_MATRIX_MVP; // 模型視圖投影矩陣
uniform float4x4 UNITY_MATRIX_MV; // 模型視圖矩陣
uniform float4x4 UNITY_MATRIX_V; // 視圖矩陣
uniform float4x4 UNITY_MATRIX_P; // 投影矩陣
uniform float4x4 UNITY_MATRIX_VP; // 視圖投影矩陣
uniform float4x4 UNITY_MATRIX_T_MV; // 模型視圖矩陣的轉置矩陣
uniform float4x4 UNITY_MATRIX_IT_MV; // 模型視圖矩陣的逆矩陣的轉置矩陣
uniform float4x4 UNITY_MATRIX_TEXTURE0; // 貼圖紋理矩陣
uniform float4x4 UNITY_MATRIX_TEXTURE1; //貼圖紋理矩陣
uniform float4x4 UNITY_MATRIX_TEXTURE2; //貼圖紋理矩陣
uniform float4x4 UNITY_MATRIX_TEXTURE3; //貼圖紋理矩陣
uniform float4 UNITY_LIGHTMODEL_AMBIENT; // 環境色

這些變量在Unity中可以直接使用,本章第三節中將會展現如何在Unity中使用這些內置的Uniform變量。

1.2 輸入輸出和語義

輸入輸出:在第一節中我們瞭解到對於程圖形渲染管線,可編程控制的部分只有兩個,頂點着色器和片段着色器。對於編程控制這兩個部分,首要的任務就是要怎麼給它們傳參數。Cg語言的參數傳遞同樣也有“值傳遞”“引用傳遞”之分。因爲GPU不支持指針,所以Cg語言採用瞭如下的方式來修辭參數傳遞:

1、in:修辭一個形參只是用於輸入,進入函數體時被初始化,且該形參值的改變不會影響實參值,傳遞方式爲值傳遞。

2、out:修辭一個形參只是用於輸出,進入函數體時沒有被初始化,一般爲函數的返回值。

3、inout:修辭一個形參即用於輸入也用於輸出,這是典型的引用傳遞。

語義:表示圖元數據的含義(頂點的位置、法向量或者紋理信息),也表明這些圖元數據存放的硬件資源。因爲頂點着色器的輸出即是片段着色器的輸入,所以頂點着色器的輸出必須和片段着色器的輸入語義是一致的。語義是頂點程序和片段程序之間輸入輸出數據和寄存器之間的橋樑,因此語義只對這兩個處理階段有意義,並且只在入口函數纔有效,在內部函數無效。語義概念的提出和圖形流水線工作機制大有關係。從前面所講的GPU處理流程中可以看出,一個階段處理數據,然後傳輸給下一個階段,那麼每個階段之間的接口是如何確定的呢?例如:頂點處理器的輸入數據是處於模型空間的頂點數據(位置、法向量),輸出的是投影座標和光照顏色;片段處理器要將光照顏色做爲輸入,問題是“片段處理器怎麼知道光照顏色值的存放位置”。在高級語言中(C/C++),數據從接口的一端流向另一端,是因爲提供了數據存放的內存位置(通常是指針信息);由於Cg 語言並不支持指針機制,且圖形硬件處理過程中,數據通常暫存在寄存器中,故而在Cg 語言中,通過引入語義綁定(binding semantics)機制,指定數據存放的位置,實際上就是將輸入輸出數據和寄存器做一個映射關係(在OpenGL Cg profiles 中是這樣的,但在DirectX-based Cgprofiles 中則並沒有這種映射關係)。根據輸入語義,圖形處理器從某個寄存器取數據;然後再將處理好的數據,根據輸出語義,放到指定的寄存器。
1、POSITIONSV_POSITION :模型座標的位置,
2、TANGENT:正交於表面法線的向量
3、NORMAL:表面法線向量,需要進行歸一化
4、TEXCOORDi:第i組紋理座標(也即UV座標,座標範圍在01之間),i是07中的一個數字
5、COLOR:顏色
6、PSIZE:點的大小
7、BLENDINDICES:通用屬性,可以用它和TANGENT來替換TEXCOORDi
注:SV_POSTION和POSTION的唯一區別是用在頂點着色器中作爲輸出語義時,SV_POSTION表示的頂點位置會被固定,不能被改變。如果作爲片段着色器的輸入語義就是一樣的,都可以被改變。

頂點着色器的輸出語義詞有:
1、COLOR:顏色
2、FOG:輸入霧座標
3、PSIZE
4、 POSITION
5、TEXCOORD0-TEXCOORD7

片段着色器的輸入語義即爲頂點着色器的輸出語義。

片段着色器的輸出語義如下:
1、COLOR:顏色
2、DEPTH:片段的深度

語言綁定的三種方法:

  1. 綁定語義放在函數的參數列表的參數聲明後面中:
[const][in|out|inout|uniform]<type><identifier>[:<binding-semantic>][=<initializer>]
void vert(float4 obj_position:POSITION,
float4 obj_normal:NORMAL,
out float4 outPos:POSITION,
uniform float4 uColor:COLOR){
...
}

其中,const作爲可選項,const修飾符同C和C++語言裏的一樣,表示這個變量的值是不能改變的;in、out、inout作爲可選項,說明數據的調用方式;

uniform也是可選項,表示變量的值的初始化是來自外部應用程序;type是必選項,聲明數據的類型;identifier是必選項,形參變量名:一個冒號“:”加上一個綁定語義,是可選項;最後是初始化參數,是可選項。

參數1、2、5綁定到輸入語義;參數3、4綁定到輸出語義;儘管參數1和3的綁定語義詞一樣,但前者是輸入語義,後者是輸出語義,所以這兩個參數數據所對應存儲的硬件位置是不一樣的。

  1. 綁定語義可以放在結構體的成員變量後面:
Struct<struct-tag>
{ 
	<type><identifier>[:binding-semantic>]; 
}; 

如在結構體vertexOutput中兩個成員變量分別綁定語義SV_POSITOIN和TEXCOORD0,這個結構體在頂點程序vert中作爲輸出,爲頂點程序的輸出語義,同時頂點程序的輸入即是片段程序的輸入,該結構體也爲片段程序的輸出語義

struct vertexOutput {
            float4 pos : SV_POSITION;
            float4 col : TEXCOORD0;
         };
vertexOutput vert(appdata_full input) 
         {
            vertexOutput output;
 
            output.pos =  mul(UNITY_MATRIX_MVP, input.vertex);
            output.col = input.texcoord;
 
            return output;
         }
 
         float4 frag(vertexOutput input) : COLOR 
         {
            return input.col; 
         }
  1. 綁定語義詞可以放在函數聲明的後面,其形式爲:
<type> <identifier> (<parameter-list>) [:<binding-semantic]
{
    <body>
}

如下面的代碼中,片段程序的後面帶有“COLOR”語義,表示該程序最後要返回一個顏色的值給GPU,所以在最後,返回的變量值類型是float4類型的。那這裏爲什麼不直接使用float4呢,語義綁定,顧名思義綁定的變量是要有含義的,直接使用發咯float4就不能理解它到達表示什麼含義,因爲float4類型的變量可以是顏色、位置等。

float4 frag(vertexOutput input) : COLOR 
         {
            float4 oColor =float4(1,0,0,1);
	     return oColor;
         }

from
凱爾八阿哥專欄

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章