1. 程式人生 > >unity shader (4)--實現漫反射模型

unity shader (4)--實現漫反射模型

摘自馮樂樂的《unity shader 入門精要》

首先給shader其一個名字

Shader "Custom/DiffuseVertxLevelMat"

為了得到並控制材質漫反射的顏色,在Properties語義塊中宣告如下
			#include "Lighting.cginc"

Properties
	{
		_Diffuse("Diffuse",Color)=(1,1,1,1)
	}

因為這是頂點/片元著色器的程式碼,所以要把程式碼寫在Pass語義塊中,,而非SubShader語義塊,並且在第一行宣告光照模式為ForwardBase:
SubShader
	{
		Pass
		{
			Tags{"LightMode"="ForwardBase"}
			

然後使用CGPROGRAM和ENDCG來包圍程式碼塊,定義頂點著色器和片元著色器程式碼,如下:
CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag

為了使用unity內建的一些變數,然後包含:
#include "Lighting.cginc"
最後是核心的程式碼,
fixed4 _Diffuse;

			struct a2v
			{
				float4 vertex:POSITION;
				float4 normal:NORMAL;
			};

			struct v2f
			{
				float4 pos:SV_POSITION;
				fixed3 color:COLOR;
			};

			v2f vert(a2v v)
			{
				v2f o;

				//Transform the vertex from object space to projection space
				//變換物件空間的頂點投影空間
				o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
				
				//get ambient term
				//獲得環境光照
				fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;

				//transform the normal from object space world space
				//把區域性法線轉換到世界法線
				fixed3 worldNormal=normalize(mul(v.normal,(float3x3)_World2Object));

				//get the light direction in world space
				//獲得全域性光照
				fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);

				//compute diffuse term
				//計算漫反射
				fixed3 diffuse =_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLight));

				o.color=ambient+diffuse;
				return o;
			}

			fixed4 frag(v2f i):SV_Target
			{
				return fixed4(i.color,1.0);
			}
			ENDCG
		}		
	}
最後補上完整的程式碼:
Shader "Custom/DiffuseVertexLevel" {
	Properties
	{
		_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
	}

	SubShader
	{
		Pass  //   頂點/片元著色器的程式碼需要寫在Pass語義塊中
		{
			//光照模型
			Tags { "LightMode" = "ForwardBase" }

			CGPROGRAM

			//頂點著色器 的名稱
			#pragma vertex vert
			//片元著色器 的名稱
			#pragma fragment frag

			//使用Unity內建的一些變數,如後面的_LightColor0,所以需要
			//包含 Lighting.cginc
			#include "Lighting.cginc"

			//定義一個Properties語義塊中宣告的屬性一樣型別匹配的變數
			//通過這種方式,我們可以得到漫反射公式中需要的引數,材質漫反射屬性,
			//由於顏色屬性的範圍在0到1之間,因此我們使用fixed精度變數來儲存它
			fixed4 _Diffuse;

			//頂點著色器的輸入結構體
			struct a2v
			{
				float4 vertex : POSITION;
				//用於訪問頂點法線,通過NORMAL語義來告訴Unity把模型頂點法線資訊儲存到normal變數中
				float3 normal : NORMAL;
			};
			//頂點著色器的輸出結構體(同事也說片元著色器的輸入結構體)
			struct v2f
			{
				float4 pos : SV_POSITION;
				//為了接受頂點著色器計算得到的光照顏色並作用於片元著色器,
				//我們定義了一個color變數,且並不是必須使用COLOR語義,
				//有的是使用TEXCROOD0語義
				fixed3 color : COLOR;
			};

			//頂點著色器最基本的任務就是把頂點位置從模型空間轉換到裁剪空間中
			// 所以使用unity內建的 UNITY_MATRIX_MVP  完成 模型*世界*投影矩陣來完成轉換
			v2f vert(a2v v)
			{
				v2f o;
				
				//Transform the vertex from object space to projection space
				//變換物件空間的頂點投影空間
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				//get ambient term
				//獲得環境光照
				//獲取環境光
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;


				//transform the normal from object space world space
				//把區域性法線轉換到世界法線
				fixed3 worldNormal = normalize(mul(v.normal, (float3x3)_World2Object));

				//  _WorldSpaceLightPos0 獲取光源方向
				//get the light direction in world space
				//獲得全域性光照
				fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);


				//compute diffuse term
				//計算漫反射
				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight));

				o.color = ambient + diffuse;

				return o;
			}

			fixed4 frag(v2f i) : SV_Target
			{
				return fixed4(i.color, 1.0);
			}

			ENDCG
		}
	}

	FallBack "Diffuse"
}


在計演算法線和光源方向之間的點積時,我們需要選擇它們所在的座標系,我們選擇了世界座標系,而由a2v得到的
頂點法線是位於模型空間下的,因此我們首先需要把法線轉換到世界空間中。所以使用_World2Object把模型空間
轉換到世界空間,然後通過調換它在mul函式中的位置,得到和轉置矩陣相同的矩陣乘法。由於法線是一個三維向量,
因此我們只需要擷取_World2Object的前三行前三列即可

然後就是效果顯示的對照圖:


效果比想象中的要好很多啊!!!
最後,學習unity對於英語和碼程式碼的速度和準確度有很高的要求,畢竟沒有很好的IDE。