1. 程式人生 > >Unity Shader入門教程(二) :實現頂點和片元著色器之間的通訊

Unity Shader入門教程(二) :實現頂點和片元著色器之間的通訊

這篇文章將介紹Pass{}內的程式碼的編寫。我們先看如下Unity Shader程式碼:(此文部分採自《unity shader入門精要》)

關於頂點著色器和片元著色器不懂的學習可以自行搜尋概念的意思,這裡就不介紹了。

Shader "Custom/myTest" {
	SubShader {
		Pass{
			CGPROGRAM

                        //申明頂點著色器函式
			#pragma vertex vert
                        //申明片元著色器函式
			#pragma fragment frag

			//使用一個結構體來定義頂點著色器的輸入
			struct a2v{
				//POSITION語義告訴Unity,用模型空間的頂點座標填充vertex變數
				float4 vertex : POSITION;
				//NORMAL語義告訴Unity,用模型空間的法向量方向填充normal變數
				float3 normal : NORMAL;
				//TEXCOORD0語義告訴Unity,用模型的第一套紋理座標填充texcoord變數
				float4 texcoord : TEXCOORD0;
			};

			//如果沒有自定義的結構體,則可寫成float4 vert(float4 v : POSITION) : SV_POSITION
			float4 vert(a2v v) : SV_POSITION{
				//使用v.vertex來訪問模型空間的頂點座標
				return UnityObjectToClipPos(v.vertex);
			}

			float4 frag() : SV_Target{
				return fixed4(1.0, 1.0, 1.0, 1.0);   //fixed4(r, g, b, a),取值範圍為[0,1]
			}

			ENDCG
		}
	}
	FallBack "Diffuse"
}

其中POSITION、NORMAL等大寫的語義代表CG/HLSL語義,告訴GPU以怎樣的方式進行渲染。該程式碼就表示了頂點/片元著色器以怎樣的方式進行渲染。然而在實踐中,我們往往希望從頂點著色器輸出一些資料,例如把模型的法線、紋理座標等傳遞給片元著色器,這就需要再定義一個新的結構體。修改後程式碼如下:

Shader "Custom/myTest" {
	SubShader {
		Pass{
			CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag

			struct a2v{
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float4 texcoord : TEXCOORD0;
			};

			//使用一個結構體來定義頂點著色器的輸出
			struct v2f{
				//SV_POSITION語義告訴Unity,pos裡面包含了頂點在裁切空間中的位置資訊
				float4 pos : SV_POSITION;
				//COLOR0語義可以用於儲存顏色資訊
				fixed3 color : COLOR0;
			};

			v2f vert(a2v v){
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				//v.normal包含了頂點的法線方向,其分量範圍在[-1.0,1.0]
				//下面的程式碼把分量範圍對映到了[0.0, 1.0]
				//儲存到o.color中傳遞給片元著色器
				o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5);
				
				return o;
			}

			fixed4 frag(v2f i) : SV_Target{
				//將插值後的i.color顯示到螢幕上
				return fixed4(i.color, 1.0);
			}

			ENDCG
		}
	}
	FallBack "Diffuse"
}

在上面程式碼中,頂點著色器的輸出結構中,必須包含一個變數,它的語義是SV_POSITION。否則,渲染器將無法得到裁切空間中的頂點座標,也就無法將頂點渲染到螢幕上。COLOR0語義中的資料則可以由使用者自己定義,但一般是儲存顏色。