天天看點

UnityShader學習記錄(二)

在接觸了一段時間Shader之後,簡單的shader基本上是可以自行完成,比如漫反射,高光反射,紋理算法,凹凸算法,漸變紋理,遮罩紋理,透明效果,雙面渲染的透明效果等等,記錄一下。

ps:本篇文章采自《UnityShader入門精要》一書。

漫反射計算有兩種,一種是逐頂點計算,一種是逐像素點計算。

逐頂點計算:

計算公式為 Diffuse = (light * diffuse)*max(0,n,l)

其中light表示入射光線的顔色和強度,diffuse表示材質的漫反射系數,n表示表面法線,l表示光源方向,max方法是為了防止出現點積的結果為負數,鎖定最小值為0,在Unity中有saturate函數代替max的使用。

漫反射顔色diffuse是自行定義的屬性,頂點法線n使用UnityShader語義NORMAL得到,光源方向l可以用_WorldSpaceLightPos0得到,光源顔色和強度light使用_LightColor0得到。換算之後,最終的計算公式變成了:

Diffuse = _LightColor0.rgb * diffuse.rgb*saturate(dot(n,l));

代碼如下:

Shader "Unlit/Diffuse"
{

	//逐頂點光照

	Properties{
		_DiffuseColor("DiffuseColor",Color) = (1,1,1,1)
	}
		SubShader{
			Pass{
		//指明這個Pass的光照類型
		Tags {"LightMode" = "ForwardBase"}

		CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "Lighting.cginc"
		//與屬性同名同類型的變量
			fixed4 _DiffuseColor;

			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};
			struct v2f {
				float4 pos : SV_POSITION;
				fixed3 color : COLOR;
			};

			v2f vert(a2v v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				fixed3 worldNormal = normalize(mul(v.normal,(float3x3)unity_WorldToObject));
				fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
				fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.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"
}
           

逐像素計算:

逐像素計算與逐頂點計算之間的唯一差別在于,逐像素計算時把所有的漫反射計算的操作放在了片段着色器上面,而逐頂點計算是把所有的漫反射計算的操作放在了頂點着色器上。當逐像素計算時,頂點着色器需要提供所有計算元素的同坐标空間下的坐标位置資訊,計算方式與上面的公式相同。

代碼如下:

Shader "Unlit/Diffuse_pixel"
{
	Properties{
		_Diffuse("Diffuse",Color) = (1,1,1,1)
	}
		SubShader{
			Pass{
			Tags{ "LightMode" = "ForwardBase" }
			CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#include "Lighting.cginc"
				//漫反射系數(顔色)
				fixed4 _Diffuse;

				struct a2v {
					float4 vertex : POSITION;
					float3 normal : NORMAL;
				};
				struct v2f {
					float4 pos : SV_POSITION;
				
					fixed3 worldNormal : TEXCOORD0;
					fixed3 worldLight : TEXCOORD1;
				};

				v2f vert(a2v v){
					v2f o;
					o.pos = UnityObjectToClipPos(v.vertex);
					o.worldNormal = UnityObjectToWorldNormal(v.normal);
					o.worldLight = _WorldSpaceLightPos0.xyz;
					return o;
				}


			fixed4 frag(v2f f) : SV_Target{
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				fixed3 worldNormal = normalize(f.worldNormal);
				fixed3 worldLight = normalize(f.worldLight);
				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight));
				fixed3 color = ambient + diffuse;
				return fixed4(color,1.0);
			}

		ENDCG
		}
	}
	FallBack "Diffuse"
}
           

這裡有個問題點,在逐像素光照時,因為整體的平滑效果提升,會出現看不見的地方産生一片純黑的樣子,為了解決這個問題,使用了一個叫做半蘭伯特的公式:saturate(0.5 * dot(n,l)+0.5)

改變的位置就在fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight));這裡的saturate函數中,半蘭伯特公式與蘭伯特公式相比較,不需要再擔心出現點乘為負數的問題,同時解決了因為逐像素光照産生的一片黑的問題,具體代碼如下:

Shader "Unlit/Diffuse_Half"
{
	Properties{
		_Diffuse("Diffuse",Color) = (1,1,1,1)
	}
	SubShader{
		Pass{
		Tags{"LightModle" = "ForwardBase"}
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "Lighting.cginc"
			fixed4 _Diffuse;

			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};
			struct v2f {
				float4 pos : SV_POSITION;
			
				float3 worldNormal : TEXCOORD0;
				float3 worldLight : TEXCOORD1;
			};

			v2f vert(a2v v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				o.worldLight = _WorldSpaceLightPos0.xyz;
				return o;
			}

			fixed4 frag(v2f f):SV_Target {
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				fixed3 worldNormal = normalize(f.worldNormal);
				fixed3 worldLight = normalize(f.worldLight);
				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(0.5*(dot(worldNormal, worldLight))+0.5);
				fixed3 color = ambient + diffuse;
				return fixed4(color, 1.0);
			}
			
		ENDCG
		}
	}
	FallBack "Deffuse"
}
           
UnityShader學習記錄(二)

高光反射分為三種,逐頂點,逐像素還有一種Blinn-Phone

逐頂點計算:

高光反射的計算公式為:Specular = light * specular * max(0,v,r)gloss

高光反射計算需要四個參數,light 入射光線的顔色和強度,specular 材質的高光反射系數,v 視角方向,r 反射方向,其中反射方向 r 可以用另外一個公式求得:reflect(i,n),i 表示入射方向,n 表示法線方向。

視角方向 v :用_WorldSpaceCameraPos可求得世界空間下的錄影機位置,再把頂點位置從模型空間通過矩陣計算轉換為世界空間下的位置資訊,再與_WorldSpaceCameraPos相減就能求得世界空間下的視角方向。在UnityCG中有直接求得視角方向的函數——UnityWorldSpaceViewDir(float4 v),隻需要輸入一個世界空間下的頂點位置坐标資訊就可以求得世界空間下的視角方向了。

反射方向 r :用reflect函數可以求得,因為reflect函數的入射方向要求是由光源指向交點處,是以要把_worldLightDir取反再傳給reflect函數操作就行。

轉換為Unity中的計算方法就是:

light = _LightColot0.rgb;

specular = 自定義屬性 

v = normalize(_WorldSpaceCameraPos.xyz - mul(_Objec2World,v.vertex.xyz));或v = normalize(UnityWorldSpaceViewDir(v.vertex));

r = normalize(reflect(_WorldLightDir,worldNormal));

Specular = light * specular * pow(saturate(dot(v,r)),Gloss);

pow函數表示求幾次幂,Gloss表示次幂數,完整代碼如下:

Shader "Unlit/Specular_Vertex"
{
	Properties{
		_Diffuse("Diffuse",Color) = (1,1,1,1)
		_Specular("Specular",Color) = (1,1,1,1)
		_Gloss("Gloss",Range(8,256)) = 20
	}
		SubShader{
			Pass{
				Tags{"LightModle" = "ForwarBase"}
			CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#include "Lighting.cginc"
				struct a2v{
					float4 vertex : POSITION;
					float3 normal : NORMAL;
				};
				struct v2f {
					float4 pos : SV_POSITION;
					fixed3 color : COLOR;
				};
				fixed3 _Diffuse;
				fixed3 _Specular;
				float _Gloss;
				v2f vert(a2v v) {
					v2f o;
					o.pos = UnityObjectToClipPos(v.vertex);
					fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
					fixed3 worldNormal = normalize(UnityObjectToWorldNormal(v.normal));
					fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
					fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));

					fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
					fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld,v.vertex).xyz);
					fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
					o.color = ambient + diffuse + specular;
					return o;
				}
				fixed4 frag(v2f i) :SV_Target{
					return fixed4(i.color,1.0);
				}
			ENDCG
		}
	}
	Fallback"Specular"	
}
           

逐像素計算:

與漫反射的差別是一樣的隻是把主要的計算放在了片段着色器上面,效果更為真實,具體代碼如下:
Shader "Unlit/Specular_Pixel"
{
	Properties
	{
		_Diffuse("Diffuse",Color) = (1,1,1,1)
		_Specular("Specular",Color) = (1,1,1,1)
		_Gloss("Gloss",Range(8.0,256)) = 20
	}
		SubShader
		{
			Tags { "LightModle" = "ForwardBase" }

			Pass
			{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "Lighting.cginc"

			fixed3 _Diffuse;
			fixed3 _Specular;
			float _Gloss;

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

			struct v2f
			{
				float4 vertex : SV_POSITION;
				fixed3 worldNormal : TEXCOORD0;
				fixed3 worldPos : TEXCOORD1;
			};

			
			v2f vert (a2v v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.worldNormal =UnityObjectToWorldNormal(v.normal);
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				return o;
			}
			
			fixed4 frag (v2f f) : SV_Target
			{
				//環境光照
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				//漫反射光照
				fixed3 worldNormal = normalize(f.worldNormal);
				fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
				//高光反射光照
				fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
				fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - f.worldPos.xyz);
				fixed3 specular = _LightColor0.rgb * _Specular * pow( saturate(dot(viewDir, reflectDir)) , _Gloss);
				//最終光照
				fixed3 color = ambient + diffuse + specular;
				return fixed4(color, 1.0);

			}
			ENDCG
		}
	}
}
           

Blinn-Phone光照模型:

Blinn-Phone在Phone光照模型基礎上更為真實,上面的逐頂點和逐像素都是Phone光照模型

計算公式為:Specular = light * specular * max(0,n,h)gloss

這裡出現一個h,h表示half,計算公式為:h = (v+l)/ |(v+l)|  即  h = normalize(光照方向+視角方向)

完整代碼如下:

Shader "Unlit/Specular_Blinn_Phone"
{
	Properties
	{
		_Diffuse("Diffuse",Color) = (1,1,1,1)
		_Specular("Specular",Color) = (1,1,1,1)
		_Gloss("Gloss",Range(8.0,256)) = 20
	}
	SubShader
	{ 
		Tags{ "LightModle" = "ForwardBase" }
		Pass
		{ 
			
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "Lighting.cginc"

			fixed3 _Diffuse;
			fixed3 _Specular;
			float _Gloss;

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

			struct v2f
			{
				float4 vertex : SV_POSITION;
				fixed3 worldNormal : TEXCOOD0;
				fixed3 worldPos : TEXCOOD1;
			};

			
			v2f vert (a2v v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				return o;
			}
			
			fixed4 frag (v2f f) : SV_Target
			{	//環境光反射
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				//漫反射
				fixed3 worldNormal = normalize(f.worldNormal);
				fixed3 worldLightPos = normalize(_WorldSpaceLightPos0.xyz);
				fixed3 diffuse = _LightColor0.rgb * _Diffuse * saturate(dot(worldNormal, worldLightPos));
				//高光反射
				fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - f.worldPos.xyz);
				fixed3 halfDir = normalize(worldLightPos + viewDir);
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow( saturate(0.5 *dot(worldNormal, halfDir)+0.5), _Gloss);
				fixed3 color = ambient + diffuse + specular;
				return fixed4(color, 1.0);
			}
			ENDCG
		}
	}
}
           
UnityShader學習記錄(二)

 紋理算法

紋理替換了漫反射,相當于漫反射就是紋理是以,先在頂點着色器中計算紋理的uv坐标:

o.uv= v.texcoord.xy*_MainTex_ST.xy+_MainTex_ST.zw;

在片段着色器中捕獲圖樣:

tex2D(_MainTex,i.uv).rgb * _Color.rgb;

這裡注意一個點,環境光也需要乘圖樣,不然會出現物體表面霧狀

紋理需要一個float類型的變量存儲矩陣的變換資訊,比如上面的_MainTex_ST,這個名稱不可以随意起,紋理名_ST是命名規範,用來計算變換後的紋理坐标,使紋理從頂點坐标變換到最終的紋理坐标,ST表示先縮放(Sacle)再位移(Transform),如果先位移再縮放的話坐标就會發生改變,變換矩陣就不正确了。

具體代碼如下:

Shader "Unlit/TextureShader"
{
	Properties
	{
		
		//紋理貼圖
		_MainTex("Albedo",2D) = "White"{}
		//紋理顔色
		_Color("Color Tint",Color) = (1,1,1,1)
		_Specular("Specular",Color) = (1,1,1,1)
		_Gloss("Gloss",Range(8,256)) = 20
	}
		SubShader
		{
			Pass
			{
			Tags{ "LightModle" = "ForwardBase" }
				CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#include "Lighting.cginc"


			fixed4 _Specular;
			float _Gloss;
			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed4 _Color;

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

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;

				float2 uv : TEXCOORD2;
			};

			
			v2f vert (a2v v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;

				o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
				return o;
			}
			
			fixed4 frag (v2f f) : SV_Target
			{ 
				
				fixed3 worldNormalDir = normalize(f.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(f.worldPos));
				//圖檔
				fixed3 albedo = tex2D(_MainTex, f.uv).rgb * _Color.rgb;
				//環境光
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
				//漫反射
				fixed3 diffuse = _LightColor0.rgb * albedo * saturate(0.5 * (dot(worldNormalDir, worldLightDir))+0.5);
				//高光反射
				fixed3 viewDir = normalize(UnityWorldSpaceViewDir(f.worldPos));
				fixed3 halfDir = normalize(viewDir + worldLightDir);
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow( saturate(dot(halfDir,worldNormalDir)) , _Gloss);

				fixed3 color = ambient + diffuse + specular;
				return fixed4(color,1.0);
			}
			ENDCG
		}
	}
		FallBack "Specular"
}
           
UnityShader學習記錄(二)

凹凸映射: 

凹凸映射是計算表面凹凸質感,也就是對法線的一個操作計算。

先計算出從切線空間到世界空間的舉證,這個變換矩陣可用頂點的切線,副切線和法線在世界空間下的表示得出,最後把法線紋理中的法線方向從切線空間變換到世界空間就行。

其中副法線可以用法線和切線的叉乘得到,再與切線的w軸相乘确定防線

切線的計算可以用TANGENT語義得到

因為這裡需要的變換矩陣是有xyz三軸,同時,一個寄存器最多可以存放float4大小的變量,是以變換矩陣用3個寄存器儲存,同時為了不浪費空間,w軸是存放頂點在世界坐标的位置

凹凸映射的計算方式:

法線貼圖采樣:bump = UnpackNormal(tex2D(normalTex,uv));

凹凸深度計算:bump.xy *= _BumpScale;

z分量的計算:sqrt(1.0 - saturate(dot(bump.xy,bump.xy)));

代碼如下:

Shader "Unlit/TextureNormalMaterialSelf_2"
{
	Properties{
		_MainTex("MainTex",2D) = "white"{}
		_Color("Color Tint",Color) = (1,1,1,1)
		_BumpTex("Normal Map",2D) = "bump"{}
		_BumpScale("BumpScale",float) = 1
		_Specular("Specular",Color) = (1,1,1,1)
		_Gloss("Gloss",Range(8,256)) = 20
	}
		SubShader{
			Pass{
			Tags{ "LightMode" = "ForwardBase" }
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "Lighting.cginc"
			struct a2v {
				float4 vertex : POSITION;
				float4 normal : NORMAL;
				float4 texcoord : TEXCOORD0;
				float4 tangent : TANGENT;
			};
			struct v2f {
				float4 pos : SV_POSITION;
				float4 uv : TEXCOORD0;
				float4 matrix_x : TEXCOORD1;
				float4 matrix_y : TEXCOORD2;
				float4 matrix_z : TEXCOORD3;
				
			};
			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed3 _Color;
			sampler2D _BumpTex;
			float4 _BumpTex_ST;
			float _BumpScale;
			fixed4 _Specular;
			float _Gloss;
			v2f vert(a2v v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
				o.uv.zw = v.texcoord.xy * _BumpTex_ST.xy + _BumpTex_ST.zw;

				float3 worldPos = UnityObjectToWorldDir(v.vertex);
				float3 worldNormal = UnityObjectToWorldNormal(v.normal);
				//float3 worldTangent = UnityObjectToWorldDir(v.tangent);
				float3 worldBinormal = cross(worldNormal, v.tangent) * v.tangent.w;
				
				o.matrix_x = float4(v.tangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
				o.matrix_y = float4(v.tangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
				o.matrix_z = float4(v.tangent.z, worldBinormal.z, worldNormal.z, worldPos.z);
				

				return o;
			}
			fixed4 frag(v2f i) :SV_Target{

				float3 worldPos = float3(i.matrix_x.w,i.matrix_y.w,i.matrix_z.w);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(worldPos));
				fixed3 worldViewDir =normalize(UnityWorldSpaceViewDir(worldPos));
				fixed3 halfDir = normalize(worldViewDir + worldLightDir);
				fixed3 bump = UnpackNormal(tex2D(_BumpTex, i.uv.zw));
				bump.xy *= _BumpScale;
				bump.z = sqrt(1.0 - dot(bump.xy, bump.xy));
				bump = normalize(fixed3(dot(i.matrix_x.xyz, bump), dot(i.matrix_y.xyz, bump), dot(i.matrix_z.xyz, bump)));

				fixed3 albedo = tex2D(_MainTex, i.uv.xy).rgb * _Color.rgb;
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
				fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(bump, worldLightDir));
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(halfDir,bump)), _Gloss);
				fixed3 color = ambient + diffuse + specular;
				return fixed4(color,1);
			}
				ENDCG

		}
	}
}
           
UnityShader學習記錄(二)

 漸變紋理計算:

漸變紋理就和字面意思是一樣的,顔色的漸變,漸變紋理圖渲染在模型上面,與紋理的計算基本一緻,差別在于漫反射的計算,計算方式以半蘭伯特算法計算,同時,漸變紋理也以半蘭伯特的x,y軸為uv坐标進行采樣:

tex2D(_RampTex,fixed2(halfLambert,halfLambert)).rgb

環境光和高光與之前計算方式一緻。

代碼如下:

Shader "Unlit/RampTextureShader_1"
{
	Properties{
		_RampTex("Ramp Tex",2D) = "white"{}
		_Color("Color Tint",Color) = (1,1,1,1)
		_Specular("Specular",Color) = (1,1,1,1)
		_Gloss("Gloss",Range(8.0, 256)) = 20
	}
		SubShader{
			Pass{
				Tags{ "LightMode" = "ForwardBase" }
				CGPROGRAM
	#pragma vertex vert 
	#pragma fragment frag
	#include "Lighting.cginc"
				sampler2D _RampTex;
				float4 _RampTex_ST;
				fixed4 _Color;
				fixed4 _Specular;
				float _Gloss;
				struct a2v {
					float4 vertex : POSITION;
					float3 normal : NORMAL;
				};
				struct v2f {
					float4 pos :SV_POSITION;
					float3 worldNormal : TEXCOORD0;
					fixed3 worldPos : TEXCOORD1;
				};
				v2f vert(a2v v) {
					v2f o;
					o.pos = UnityObjectToClipPos(v.vertex);
					o.worldNormal = UnityObjectToWorldNormal(v.normal);
					o.worldPos = UnityObjectToWorldDir(o.pos);
					return o;
				}
				fixed4 frag(v2f i) :SV_Target{

				//	fixed3 worldPos = normalize(i.worldPos.xyz);
					fixed3 worldNormal = normalize(i.worldNormal);
					fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
					
					fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
					
					fixed halfLambert = 0.5 * dot(worldNormal, worldLightDir) + 0.5;

					fixed3 ramp = tex2D(_RampTex, fixed2(halfLambert, halfLambert)).rgb * _Color.rgb;

					
					
					fixed3 diffuse = _LightColor0.rgb * ramp ;
					
					fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
					fixed3 halfDir = normalize(viewDir + worldLightDir);
					fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(halfDir, worldNormal)),_Gloss);
					fixed3 color = ambient + diffuse + specular;

					return fixed4(color,1);
				}
			ENDCG
		}
	}
}
           
UnityShader學習記錄(二)

 遮罩紋理算法:

遮罩紋理計算與凹凸紋理相似,差別在于遮罩紋理的高光計算,先捕獲遮罩圖樣取得RGB值中的R值,與系數相乘最後高光與遮罩相乘即可。

遮罩紋理的作用是保持物體的表面反射及高光一緻。

在這裡做了個節省資源的做法,uv坐标公用,法線貼圖,紋理貼圖,遮罩貼圖公用了一套uv坐标。

代碼如下:

Shader "Unlit/MaskTextureShader"
{
	Properties
	{
		_Color("Color Tint",Color) = (1,1,1,1)

		_MainTex ("Texture", 2D) = "white" {}
		_BumpMap("Bump Map",2D) = "bump"{}
		_BumpScale("BumpScale",float) = 1
		_SpecularMask("Mask Tex",2D) = "white"{}
		_SpecularScale("MaskSacle",float) = 1
		_Specular("Specular",Color) = (1,1,1,1)
		_Gloss("Gloss",Range(8.0,256)) = 20
	}
		SubShader
		{
			Pass
			{
				Tags{ "LightMode" = "ForwardBase" }
				CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag

				#include "Lighting.cginc"
				fixed4 _Color;
				sampler2D _MainTex;
				float4 _MainTex_ST;
				sampler2D _BumpMap;
				float _BumpScale;
				sampler2D _SpecularMask;
				float _SpecularScale;
				fixed4 _Specular;
				float _Gloss;
			struct a2v
			{
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float4 texcoord : TEXCOORD0;
				float4 tangent : TANGENT;
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float2 uv : TEXCOORD0;
				float3 worldNormal : TEXCOORD1;
				float3 worldPos : TEXCOORD2;
				float4 Ttow0 :TEXCOORD3;
				float4 Ttow1 :TEXCOORD4;
				float4 Ttow2 :TEXCOORD5;

			};

			
			v2f vert (a2v v)
			{
				v2f o;
				
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.worldPos = UnityObjectToWorldDir(o.vertex).xyz;
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				
				float3 worldBinormal = cross(o.worldNormal, v.tangent) * v.tangent.w;

				o.Ttow0 = float4(v.tangent.x, worldBinormal.x, o.worldNormal.x, o.worldPos.x);
				o.Ttow1 = float4(v.tangent.y, worldBinormal.y, o.worldNormal.y, o.worldPos.y);
				o.Ttow2 = float4(v.tangent.z, worldBinormal.z, o.worldNormal.z, o.worldPos.z);


				o.uv.xy = v.texcoord.xy* _MainTex_ST.xy+_MainTex_ST.zw;
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				float3 worldPos = float3(i.Ttow0.w,i.Ttow1.w,i.Ttow2.w);
				fixed3 worldNormal = normalize(i.worldNormal.xyz);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(worldPos));
				fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
				fixed3 halfDir = normalize(viewDir + worldLightDir);

				fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv));
				bump.xy *= _BumpScale;
				bump.z = sqrt(1.0 - saturate(dot(bump.xy, bump.xy)));
				bump = normalize(fixed3(dot(i.Ttow0.xyz, bump), dot(i.Ttow1.xyz, bump), dot(i.Ttow2.xyz, bump)));

				fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;

				fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(bump, worldLightDir));

				fixed3 specularMask = tex2D(_SpecularMask, i.uv).r * _SpecularScale;

				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(halfDir, bump)), _Gloss) * specularMask;
				fixed3 color = ambient + diffuse + specular;

				return fixed4(color,1.0);
			}
			ENDCG
		}
	}
}
           
UnityShader學習記錄(二)

 到此,簡單的紋理就基本完畢,之後會有透明效果的Shader,因為透明效果比較重要,是以單另放在下一個文章中進行記錄,以上的所有腳本有的沒有寫注釋,仔細看過之後也就明白了,比較簡單易懂,這篇文章就到此結束吧。

Over!

繼續閱讀