1、引言
這一篇我們倆瞭解下Cg語言中的函數,這裏涉及到的知識點包括以下內容:
- 定製函數
- 函數的前項聲明
- 函數的傳參
- Cg include
- Cg中的內建函數
2、示例
這我們將一一舉例說明!開始之前我們先搭建好場景,便於演示。創建一個場景,一個cube,一個材質球和一個頂點片元shader。然後該cube使用新建的材質,選擇剛纔闖將的shader,修改代碼如下:
Shader "lxt610/NewSurfaceShaderTest3" {
SubShader
{
Pass
{
CGPROGRAM
#pragma fragment frag
#pragma vertex vert
void vert(in float2 objPos:POSITION,out float4 col:COLOR0,out float4 pos:POSITION)
{
pos = float4(objPos,0,1);
col = float4(1,0,0,1);//紅色
//col = pos;
}
void frag(inout float4 col:COLOR0)
{
}
ENDCG
}
}
}
運行場景後顯示紅色。
2.1、函數
函數的語法格式:
返回值類型 函數名稱([參數列表])
{
//語句塊;
}
下面是一個示例
Shader "lxt610/NewSurfaceShaderTest3" {
SubShader
{
Pass
{
CGPROGRAM
#pragma fragment frag
#pragma vertex vert
void vert(in float2 objPos:POSITION,out float4 col:COLOR0,out float4 pos:POSITION)
{
pos = float4(objPos,0,1);
col = float4(1,0,0,1);//紅色
}
void frag(inout float4 col:COLOR0)
{
func();
}
void func()
{
}
ENDCG
}
}
}
寫完回到unity,編譯完畢發現異常了,提示函數未定義!Cg中需要把聲明的函數放在使用的位置之前,否則就異常了。這時只需要改爲:
void func()
{
}
void frag(inout float4 col:COLOR0)
{
func();
}
就可以了!然而有時候我們就想在後面聲明怎麼辦?
2.2、函數的前項聲明
此時可以考慮函數的前項聲明。使用方式就是:在被調用的函數的位置前面聲明一個前置函數,其函數名返回值類型及參數列表和聲明的函數相同就可以了。eg:
Shader "lxt610/NewSurfaceShaderTest3" {
SubShader
{
Pass
{
CGPROGRAM
#pragma fragment frag
#pragma vertex vert
void vert(in float2 objPos:POSITION,out float4 col:COLOR0,out float4 pos:POSITION)
{
pos = float4(objPos,0,1);
col = float4(1,0,0,1);//紅色
}
void func();
void frag(inout float4 col:COLOR0)
{
func();
}
void func()
{
}
ENDCG
}
}
}
此時我們發現編譯通過!
2.3、函數的參數傳遞
我們再次修改代碼
Shader "lxt610/NewSurfaceShaderTest3" {
SubShader
{
Pass
{
CGPROGRAM
#pragma fragment frag
#pragma vertex vert
void vert(in float2 objPos:POSITION,out float4 col:COLOR0,out float4 pos:POSITION)
{
pos = float4(objPos,0,1);
col = pos;
}
void func(float4 col);
void frag(inout float4 col:COLOR0)
{
func(col);
}
void func(float4 col)
{
col = float4(0,1,0,1);
}
ENDCG
}
}
}
這裏我們發現顏色沒有變化爲想要的顏色,這時由於Cg中這裏的值進行了值Copy,並不是操作的原來的值!要想改變可以使用out關鍵字:
Shader "lxt610/NewSurfaceShaderTest3" {
SubShader
{
Pass
{
CGPROGRAM
#pragma fragment frag
#pragma vertex vert
void vert(in float2 objPos:POSITION,out float4 col:COLOR0,out float4 pos:POSITION)
{
pos = float4(objPos,0,1);
col = pos;
}
void func(out float4 col);
void frag(inout float4 col:COLOR0)
{
func(col);
}
void func(out float4 col)
{
col = float4(0,1,0,1);
}
ENDCG
}
}
}
編譯通過後我們發現顏色變爲了綠色。我們可以知道這裏的out參數只是把數值傳遞進來,把值做了改變,並傳回去了。這個out類似於C#中的ref關鍵字。當我們不需要Copy一個變量,需要這個值得時候可以使用out參數。
當我們需要傳遞一個數組時,不可以像其他語言一樣,需要這樣寫:
float func2(float arr[3])
{
float sum = 0;
for(int i = 0; i < arr.Length; i++)
{
sum += arr[i];
}
return sum;
}
void frag(inout float4 col:COLOR0)
{
//func(col)
float arr[2] = {0.5,0.5};
col.x = func2(arr);
}
這裏需要注意的是:
- 聲明的函數的參數列表中如果是數組時,我們需要指定這個函數的長度,我們同時在調用的時候也必須傳遞給這個函數相同長度的數組
- 同時聲明數組時如果我們是ES3.0需要指定數組的長度,eg:
float arr[2] = {0.5,0.5};
- 我們注意到Cg中數組有一個關鍵字Length,用來表示數組的長度。
最終的代碼和效果如下:
Shader "lxt610/NewSurfaceShaderTest3" {
SubShader
{
Pass
{
CGPROGRAM
#pragma fragment frag
#pragma vertex vert
void vert(in float2 objPos:POSITION,out float4 col:COLOR0,out float4 pos:POSITION)
{
pos = float4(objPos,0,1);
col = pos;
}
void func(out float4 col);
float func2(float arr[2])
{
float sum = 0;
for(int i = 0; i < arr.Length; i++)
{
sum += arr[i];
}
return sum;
}
void frag(inout float4 col:COLOR0)
{
//func(col)
float arr[2] = {0.5,0.5};
col.x = func2(arr);
}
void func(out float4 col)
{
col = float4(0,1,0,1);
}
ENDCG
}
}
}
同樣這裏的數組也是一個值Copye。因此在Cg中參數的傳遞是通過值Copy的形式來傳遞的,需要使用這個參數時可以使用out關鍵字。
2.4、Cg include
當我們寫了很多shader之後,我們發現不同的shader在使用同樣的函數,有沒有什麼辦法解決這樣的問題呢?這裏就需要使用Cg當中的include指令。此時我們不妨打開Unity\Unity2017.4.27f1\Editor\Data\CGIncludes
目錄,可以看到有很多.cginc文件,我們打開UnityCG.cginc,我們發現有很多函數和數據類型,我們可以使用include把這個文件包含進來,就可以使用這裏面的函數和數據類型了。
那麼當我們自己寫shader的時候可不可以使用呢?當然也是可以的了。這裏我們創建一個.cginc文件命名爲lmy225.cginc,用來存儲自己的一些函數。爲了方便查找同時不和官方的cginc混淆這裏我專門創建了一個名爲lmy225的文件夾用來存儲自己的cginc文件,此時把lmy225.cginc放在這個文件夾中,這裏我們可以把:
float func2(float arr[2])
{
float2 sum = 0;
for(int i = 0; i < arr.Length;i++)
{
sum += arr[i];
}
return sum;
}
void func(out float4 c)
{
c = float4(0,1,0,1);
}
放在cginc中,然後回到shader刪除前項聲明,使用下面的include指令:
#include "路徑"
eg:
Shader "lxt610/NewSurfaceShaderTest3" {
SubShader
{
Pass
{
CGPROGRAM
#pragma fragment frag
#pragma vertex vert
#include "lmy225/lmy225.cginc"
void vert(in float2 objPos:POSITION,out float4 col:COLOR0,out float4 pos:POSITION)
{
pos = float4(objPos,0,1);
col = pos;
}
void frag(inout float4 col:COLOR0)
{
//func(col)
float arr[2] = {0.5,0.5};
col.x = func2(arr);
}
ENDCG
}
}
}
編譯運行後我們發現實現了相同的效果!
這裏include指令後面的路徑可以使用\或/,也可以混合使用!
當然這裏的.cginc後綴也可以是別的自己喜歡的後綴。只是改爲其他後綴之後,關鍵字沒有高亮提示了。
2.5、Cg中的內建函數
在cg的內建函數中主要有以下四類:
- 數學函數
- 幾何函數
- 紋理函數
- 導數函數
這裏我們給出Cg標準函數的文檔,需要的同學,可以點擊這裏下載。當然除了上面的還有debug函數,由於功能比較單一,只是把數值通過顏色語義來顯示出來!所以很少使用!
3、結束語
The End
好了,今天的分享就到這裏,如有不足之處,還望大家及時指正,隨時歡迎探討交流!!!
喜歡的朋友們,請幫頂、點贊、評論!您的肯定是我寫作的不竭動力!