Unity3D學習筆記3——Unity Shader的初步使用


1. 概述

在上一篇文章《Unity3D學習筆記2——繪制一個帶紋理的面》中介紹了如何繪制一個帶紋理材質的面,並且通過調整光照,使得材質生效(變亮)。不過,上篇文章隱藏了一個很重要的細節——Unity Shader。Shader(着色器)是渲染管線中可被用戶編程的階段,依靠着色器可以控制渲染管線的細節。現代圖像渲染技術,都把Shader封裝成與Material(材質)相關的組件。所以這篇文章,我們就初步學習下在Unity中使用Shader。

2. 詳論

2.1. 創建材質

在上一章中,材質、以及材質相關的資源是在Unity3D編輯器中創建,在C#腳本中直接引用的。這里為了學習使用Shader,我們使用自定義的Shader,可以在C#腳本中創建材質。修改上一章代碼的材質部分:

Shader shader = Shader.Find("Custom/MainShader");
Material material = new Material(shader);
        
Texture2D texture = Resources.Load<Texture2D>("ImageDemo");
material.mainTexture = texture;

MeshRenderer meshRenderer = newGameObject.AddComponent<MeshRenderer>();      
meshRenderer.material = material;

可以看到,要創建一個Material,首先得創建一個Shader。我們在Project視圖中右鍵菜單->Create->Standard Surface Shader,創建一個標准表面着色器MainShader:

imagelink1

雙擊打開這個Shader,可以看到這個Shader的具體內容。標准着色器很復雜,我們清空里面的內容,填入我們這個更簡單的着色器示例:

Shader "Custom/MainShader"
{
    Properties
    {       
        _MainTex ("Texture", 2D) = "white" {}      
    }
    SubShader
    {
        Tags{"Queue" = "Geometry"}
		
		Cull Back

		Pass
		{
		    CGPROGRAM
    
			#pragma vertex vert	
			#pragma fragment frag

			sampler2D _MainTex;

			//頂點着色器輸入
			struct a2v
			{
				float4  position : POSITION;
				float3  normal: NORMAL;
				float2  texcoord : TEXCOORD0;	
 			};

			//頂點着色器輸出
			struct v2f
			{
				float4 position: SV_POSITION;
				float2 texcoord: TEXCOORD0;
			};

			v2f vert(a2v v)
			{
				v2f o;	
				o.position = mul(UNITY_MATRIX_MVP, v.position);									
				o.texcoord = v.texcoord;

				return o;
			}

			fixed4 frag(v2f i) : SV_Target 
			{
				return tex2D(_MainTex, i.texcoord);			
			}
   
			ENDCG
		}
    }
    FallBack "Diffuse"
}

2.2. 着色器

Unity使用的着色器語言叫做ShaderLab,它是圖形渲染中Shader(例如GLSL,HLSL以及CG)的更高級更抽象一級的封裝。ShaderLab是個非常簡單的說明性描述語言,通過嵌套在花括號中的語義來描述Unity Shader文件。

2.2.1. 名稱

通過Shader語義指定Unity Shader的名稱:

Shader "Custom/MainShader"
{

}

這個名稱非常重要,在Unity編輯器中需要通過這個名字來引用Shader。

2.2.2. 屬性

Shader語義塊的第一個語義塊是Properties語義塊,它連接着材質和Unity3d編輯器,設置了這個屬性就能夠通過材質面板調整材質,調整材質的本質就是調整Shader。Properties的定義通常描述如下:

Properties {
	Name ("display name",PropertyType) = DefaultValue
}

Name指的是在Shader中使用的名稱,display name指的是顯示在材質面板的名稱。PropertyType則有點容易混淆,它指的是顯示在材質面板中的屬性類型,借用一下《Unity Shader入門精要》的圖表:

imagelink2

2.2.3. SubShader

每個Unity Shader都至少包含一個SubShader語義塊,Unity會優先選擇第一個能夠在當前平台下運行的SubShader作為最終渲染效果的Shader。

這個語義塊下面又會包含三個語義塊:

2.2.3.1. 標簽(Tags)

SubShader的標簽用於用於標識何時以何種方式被渲染到渲染引擎,它由一系列鍵值對組成。Queue是最常用的標簽,用於標識渲染物體在渲染隊列中的位置:
imagelink3

我們這里,把這個渲染物體放到Geometry隊列中,這個位置通常放置不透明物體的渲染:

Tags{"Queue" = "Geometry"}

2.2.3.2. 渲染狀態(RenderSetup)

渲染狀態用於設置圖形硬件的各種狀態,例如是否應開啟 Alpha 混合或是否應使用深度測試等。在像OpenGL這樣的圖形接口中,通常是以函數的形式進行調用的,Unity3d將其放在Shader里面,也有一定的道理。

這里的渲染狀態設置成將背面裁剪掉:

Cull Back

2.2.3.3. 通道(Pass)

在Pass語義塊中,才是像OpenGL/DirectX中使用的Shader。OpenGL使用的着色器語言叫做GLSL,DirectX使用的着色器語言叫做HLSL,Unity3D則推薦使用Cg語言,這是一種類C語言,與HLSL非常相似。Cg語言代碼段在Pass語義塊中被包裹在CGPROGRAM和ENDCG之間:

CGPROGRAM
//...
ENDCG

2.2.4. 回退(FallBack)

FallBack定義了一種退化策略,由於不同機器支持的性能特性不同,如果之前的子着色器都不生效,那么就使用這個着色器,通常這個着色器是內置的:

FallBack "Diffuse"

2.3. 渲染管線

圖形渲染引擎的渲染管線其實是個內涵非常豐富的概念,再次借用《Unity Shader入門精要》的插圖,渲染管線的描述大致如下:

imagelink4

當然只看這個圖是不夠的,但是我們可以直接從代碼層面去了解它。鑲嵌在CGPROGRAM和ENDCG之間的CG代碼,體現的正是渲染管線的思維。

首先,通過編譯指令,分別指定頂點着色器程序和片元着色器程序:

#pragma vertex vert	
#pragma fragment frag

vert就是頂點着色器的函數,在這個着色器程序中指定了計算了頂點坐標和紋理坐標:

v2f vert(a2v v)
{
	v2f o;	
	o.position = mul(UNITY_MATRIX_MVP, v.position);									
	o.texcoord = v.texcoord;

	return o;
}

傳入參數是一個結構體,POSITION,NORMAL,TEXCOORD0是Unity Shader中固定的語義,分別代表這位置、法向量以及紋理坐標,他們也被稱為頂點屬性。還記得在上一篇文章《Unity3D學習筆記2——繪制一個帶紋理的面》中創建Mesh時給Mesh創建的成員變量vertices、uv和normals吧?給他們傳入的數據正是在這里用到了。

//頂點着色器輸入
struct a2v
{
	float4  position : POSITION;
	float3  normal: NORMAL;
	float2  texcoord : TEXCOORD0;	
};

傳出參數則是另外一個結構體:

//頂點着色器輸出
struct v2f
{
	float4 position: SV_POSITION;
	float2 texcoord: TEXCOORD0;
};

SV_POSITION表示的是裁剪空間坐標,也就是在頂點着色器中計算的頂點值。這個計算內容的內涵也挺豐富的,簡單來說,創建Mesh時的頂點坐標,經過一個模型變換(Model)、視圖變換(View)、投影變換(Projection),最終變成了裁剪空間坐標系中的坐標,體現在着色器中,就是內置的MVP矩陣UNITY_MATRIX_MVP。

剩下的就是片元着色器函數的部分了。在這個着色器中,_MainTex也就是我們先前創建的,並且傳遞到材質中的紋理,通過將頂點着色器中傳遞過來的紋理坐標進行采樣,得到具體的片元顏色:

sampler2D _MainTex;

fixed4 frag(v2f i) : SV_Target 
{
	return tex2D(_MainTex, i.texcoord);			
}

最終顯示的效果如下:
imagelink5

可以看到這里顯示的就是圖片本身的顏色,這是因為在着色器中只是采樣了圖片的顏色,並沒有光照計算的參與。也就是在圖形引擎中,任何效果的設置只是表象,任何效果的實現都會歸結到着色器中。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM