本篇介紹創建一個最簡單的純色着色器,麻雀雖小五臟俱全。
在unity裏創建一個BaseColor.shader文件,使用編輯器填入下列代碼。所有代碼說明見註釋
Shader "ShaderTutorial/BaseColor" // Shader在面板上顯示的名稱
{
/*
材質面板屬性
*/
Properties
{
/*
材質面板屬性定義,格式:變量名("<顯示變量名>",變量類型)=<缺省變量值>
*/
_BaseColor("BaseColor",Color) = (1,1,1,1)
}
/*
子着色器
*/
SubShader
{
Tags { "RenderType" = "Opaque" } // 標籤,這裏"RenderType"標明渲染物體類型爲"Opaque",非透明物體
LOD 100 // shader LOD(level of detail ) ,可以通過這個對畫質進行分檔
Pass
{
CGPROGRAM // CG 代碼段的開始標誌
#pragma vertex vert // 預編譯指令,指示頂點着色器(vertex)使用名稱爲vert的函數
#pragma fragment frag // 預編譯指令,告訴編譯器,片元着色器(frament)使用名稱爲frag的函數
#include "UnityCG.cginc" // 預編譯指令,引用文件"UnityCG.cginc",這裏包含一些預定義的一些變量和函數
/*
變量聲明,前面Properties中定義的,暴露給編輯器材質面板的變量,
必須在這裏再次聲明,因爲這裏纔是真正的變量聲明位置
*/
half4 _BaseColor;
/*
結構體,這個結構體是頂點着色器的輸入數據,
是從應用階段(cpu)傳輸給頂點處理階段(gpu)的頂點屬性,
因此這裏命名爲appdata,其他任意名稱也ok
*/
struct appdata
{
/*
這裏變量聲明後加的":POSITION",是語義說明,
用來告訴GPU,頂點的座標放在positionOS這個變量裏。
*/
float4 positionOS : POSITION;
};
/*
結構體,這個結構體是頂點着色器的輸出以及片元着色器的輸入,因此命名爲v2f(vector to fragment ),其他任意名稱也ok
*/
struct v2f
{
float4 positionCS : SV_POSITION; // 這裏的語義說明":SV_POSITION",是告訴GPU,把光柵化階段之後的裁剪空間頂點座標放在positionCS這個變量裏。
};
/*
頂點着色器函數,每個模型頂點需要調用一次該頂點着色器, 以appdata作爲輸入,v2f作爲輸出
*/
v2f vert (appdata v)
{
v2f o;
/*
使用UnityCG.cginc中定義的函數,UnityObjectToClipPos(),
把模型空間的頂點座標轉換到裁剪空間
*/
o.positionCS = UnityObjectToClipPos(v.positionOS);
/*
上述函數的實現如下,
實際上就是把頂點座標先從模型空間轉換到世界空間(乘以模型空間到世界空間的變換矩陣-unity_ObjectToWorld),
然後從世界空間轉換到齊次裁剪空間(世界空間座標,乘以觀察投影矩陣-UNITY_MATRIX_VP)。
這裏需要注意的是,
很多教程中會告訴你使用UNITY_MATRIX_MVP矩陣直接乘以模型空間座標,
這種方式雖然結果上是正確的,
但是因爲UNITY_MATRIX_MPV矩陣實際上是UNITY_MATIRX_VP和unity_ObjectToWrold相乘的預編譯指令,
因此直接使用MVP矩陣會導致運算爲4x4矩陣與4x4矩陣的相乘結果再乘以四維向量,
運算次數遠多於4x4矩陣乘以兩次四維向量。
因此建議使用unity內置的UnityObjectToClipPos方法或者下面的實現,進行空間變換,可以得到更好的性能
o.positionCS = mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, float4(v.positionOS.xyz, 1.0)));
*/
return o;
}
/*
片元着色器函數,以v2f作爲輸入,這裏v2f,是經過光柵化之後的插值過的v2f。
每一個片元(類似於屏幕像素,只是此時還沒有輸送給屏幕顏色緩衝)都需要執行一次片元着色器
*/
half4 frag (v2f i) : SV_Target
{
/*
"SV_Target"同樣是語義說明,用來標記返回類型half4,
說明這裏返回結果保存到SV_Target0(這裏0省掉了),
指向的顏色緩衝區的對應位置
*/
/*
這裏的返回值使用了half4,而不是常見的fixed4格式,
因爲目前的幾乎所有顯卡都不再支持fixed4類型格式,
而編譯器會內部把fixed 類型 轉爲 half 類型。
但是除非你非常瞭解unity shader編譯器在目標平臺的編譯結果,
建議使用half4代替fixed4類型。
*/
half4 col = _BaseColor.rgba;
/*
把我們定義的變量類型_BaseColor賦值給col,並返回。
這裏".rgba"的寫法或者".xyzw"是CG或者HLSL語言對向量的特有操作,
表示把向量的指定分量(例如 .r .xy .xx .a 等 賦值給對應變量)
*/
return col;
}
ENDCG // CG代碼段的結束標誌
}
}
}
這個着色器在Unity內的預覽如下:
可以看到
- 顏色來源於我們定義的變量_BaseColor(顯示名稱爲BaseColor)。
- 渲染隊列Render Queue 默認來自於我們定義的Tags “RenderType” =“Opaque”,事實上這裏可以直接定義Tag “RenderQueue”,這會在之後的文章說明