https://blog.csdn.net/zhangxiao13627093203/article/details/52850518
轉載請注明出處凱爾八阿哥專欄
1.2、Cg語法基礎
?如C++、C#和Java等高級語言一樣,Cg語言也有自己的數據類型和關鍵字。掌握和理解這些關鍵字是寫好Cg程序的基礎。
1.2.1、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??????????????????? //3*4階矩陣
注:X是字符,不是乘號,最大的維數為4*4階
矩陣的初始化 float 2*2 m22={1.0,2.0,3.0,2.3};
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)
注:Swizzle操作符只對結構體和向量使用,不能對數組使用。
輸入數據關鍵字: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.2、輸入輸出和語義
?輸入輸出:在第一節中我們了解到對于程圖形渲染管線,可編程控制的部分只有兩個,頂點著色器和片段著色器。對于編程控制這兩個部分,首要的任務就是要怎么給它們傳參數。Cg語言的參數傳遞同樣也有“值傳遞”和“引用傳遞”之分。因為GPU不支持指針,所以Cg語言采用了如下的方式來修辭參數傳遞:
1、in:修辭一個形參只是用于輸入,進入函數體時被初始化,且該形參值的改變不會影響實參值,傳遞方式為值傳遞。
2、out:修辭一個形參只是用于輸出,進入函數體時沒有被初始化,一般為函數的返回值。
3、inout:修辭一個形參即用于輸入也用于輸出,這是典型的引用傳遞。
語義:表示圖元數據的含義(頂點的位置、法向量或者紋理信息),也表明這些圖元數據存放的硬件資源。因為頂點著色器的輸出即是片段著色器的輸入,所以頂點著色器的輸出必須和片段著色器的輸入語義是一致的。語義是頂點程序和片段程序之間輸入\輸出數據和寄存器之間的橋梁,因此語義只對這兩個處理階段有意義,并且只在入口函數才有效,在內部函數無效。語義概念的提出和圖形流水線工作機制大有關系。從前面所講的GPU 處理流程中可以看出,一個階段處理數據,然后傳輸給下一個階段,那么每個階段之間的接口是如何確定的呢?例如:頂點處理器的輸入數據是處于模型空間的頂點數據(位置、法向量),輸出的是投影坐標和光照顏色;片段處理器要將光照顏色做為輸入,問題是“片段處理器怎么知道光照顏色值的存放位置”。在高級語言中(C/C++),數據從接口的一端流向另一端,是因為提供了數據存放的內存位置(通常是指針信息);由于Cg 語言并不支持指針機制,且圖形硬件處理過程中,數據通常暫存在寄存器中,故而在Cg 語言中,通過引入語義綁定(binding semantics)機制,指定數據存放的位置,實際上就是將輸入\輸出數據和寄存器做一個映射關系(在OpenGL Cg profiles 中是這樣的,但在DirectX-based Cgprofiles 中則并沒有這種映射關系)。根據輸入語義,圖形處理器從某個寄存器取數據;然后再將處理好的數據,根據輸出語義,放到指定的寄存器。
語義詞和語義綁定:Unity的頂點著色器(程序)中常用的語言詞有如下:
1、POSITION\SV_POSITION :模型坐標的位置,
2、TANGENT:正交于表面法線的向量
3、NORMAL:表面法線向量,需要進行歸一化
4、TEXCOORDi:第i組紋理坐標(也即UV坐標,坐標范圍在0~1之間),i是0~7中的一個數字
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][ :][=]
voidvert(float4 obj_position:POSITION,
? float4 obj_normal:NORMAL,
outfloat4 outPos:POSITION,
? uniform float4 uColor:COLOR
{
...
}
其中,const作為可選項,const修飾符同C和C++語言里的一樣,表示這個變量的值是不能改變的;in、out、inout作為可選項,說明數據的調用方式;
uniform也是可選項,表示變量的值的初始化是來自外部應用程序;type是必選項,聲明數據的類型;identifier是必選項,形參變量名:一個冒號“:”加上一個綁定語義,是可選項;最后是初始化參數,是可選項。
參數1、2、5綁定到輸入語義;參數3、4綁定到輸出語義;盡管參數1和3的綁定語義詞一樣,但前者是輸入語義,后者是輸出語義,所以這兩個參數數據所對應存儲的硬件位置是不一樣的。
2、綁定語義可以放在結構體的成員變量后面:
Struct
{
[:binding-semantic>];
};。
如在結構體vertexOutput中兩個成員變量分別綁定語義SV_POSITOIN和TEXCOORD0,這個結構體在頂點程序vert中作為輸出,為頂點程序的輸出語義,同時頂點程序的輸入即是片段程序的輸入,該結構體也為片段程序的輸出語義。
structvertexOutput {
? ? ? ? ? ? float4 pos : SV_POSITION;
? ? ? ? ? ? float4 col : TEXCOORD0;
? ? ? ? };
vertexOutputvert(appdata_full input)
? ? ? ? {
? ? ? ? ? ? vertexOutput output;
? ? ? ? ? ? output.pos =? mul(UNITY_MATRIX_MVP, input.vertex);
? ? ? ? ? ? output.col = input.texcoord;
returnoutput;
? ? ? ? }
float4frag(vertexOutput input) : COLOR
? ? ? ? {
returninput.col;
? ? ? ? }
3、綁定語義詞可以放在函數聲明的后面,其形式為:?
() [:
{
}
如下面的代碼中,片段程序的后面帶有“COLOR”語義,表示該程序最后要返回一個顏色的值給GPU,所以在最后,返回的變量值類型是float4類型的。那這里為什么不直接使用float4呢,語義綁定,顧名思義綁定的變量是要有含義的,直接使用發咯float4就不能理解它到達表示什么含義,因為float4類型的變量可以是顏色、位置等。
float4frag(vertexOutput input) : COLOR
? ? ? ? {
float4 oColor =float4(1,0,0,1);
returnoColor;
? ? ? ? }