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"
}

 


高光反射分为三种,逐顶点,逐像素还有一种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
		}
	}
}


 纹理算法

纹理替换了漫反射,相当于漫反射就是纹理所以,先在顶点着色器中计算纹理的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"
}


凹凸映射: 

凹凸映射是计算表面凹凸质感,也就是对法线的一个操作计算。

先计算出从切线空间到世界空间的举证,这个变换矩阵可用顶点的切线,副切线和法线在世界空间下的表示得出,最后把法线纹理中的法线方向从切线空间变换到世界空间就行。

其中副法线可以用法线和切线的叉乘得到,再与切线的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

		}
	}
}


 渐变纹理计算:

渐变纹理就和字面意思是一样的,颜色的渐变,渐变纹理图渲染在模型上面,与纹理的计算基本一致,区别在于漫反射的计算,计算方式以半兰伯特算法计算,同时,渐变纹理也以半兰伯特的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
		}
	}
}


 遮罩纹理算法:

遮罩纹理计算与凹凸纹理相似,区别在于遮罩纹理的高光计算,先捕获遮罩图样取得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
		}
	}
}


 到此,简单的纹理就基本完毕,之后会有透明效果的Shader,因为透明效果比较重要,所以单另放在下一个文章中进行记录,以上的所有脚本有的没有写注释,仔细看过之后也就明白了,比较简单易懂,这篇文章就到此结束吧。

Over!

猜你喜欢

转载自blog.csdn.net/qq_38186269/article/details/82758025