Unity ShaderLab特效教程 着色器实例 代码+注释 【卡通渲染方案1】

如果代码中有什么不清楚请查看以下基础知识

Shader基础知识
unity3d 中 七种坐标知识详解

卡通渲染

卡通渲染通常包括描边和阴影分段,当前这个写的不好,等有时间完善后更新。


Shader "Custom/Cartoon" {
	Properties{
		//主贴图
		_MainTex("MainTex",2D) = "white"{}
		//主颜色
		_Color("Color",Color) = (1,1,1,1)
		//漫反射色调纹理
		_RampTex("Ramp",2D) = "white"{}
		//描边宽度
		_Outline("Outline",Range(0,1)) = 0.1
		//描边颜色
		_OutlineColor("OutlineColor",Color) = (0,0,0,1)
		//高光反射颜色
		_Specular("SpecularColor",Color) = (1,1,1,1)
		//高光阈值
		_SpecularScale("Specular Scale",Range(0,0.1)) = 0.01
	}
		SubShader{
			//该段pass 的作用是描边
			Pass{
				NAME "OUTLINE"
				Cull Front
				CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#include "UnityCG.cginc"

				//定义与unity通信的变量
				fixed _Outline;
				fixed4 _OutlineColor;
				//定义应用到顶点阶段用的结构
				struct a2v {
					float4 vertex:POSITION;
					float3 normal:NORMAL;
				};
				//定义顶点到片元阶段用的结构
				struct v2f {
					float4 pos:SV_POSITION;
				};
				//定点找色器
				v2f vert(a2v v) {
					v2f o;
					//当前的模型观察矩阵,  用于将顶点/方向数据从模型空间变换到观察空间
					float4 pos = mul(UNITY_MATRIX_MV,v.vertex);
					//UNITY_MATRIX_MV的逆转置矩阵,用于将法线从模型空间变换到观察空间,也可以用于得到UNITY_MATRIX_MV的逆矩阵
					float3 normal = mul((float3x3)UNITY_MATRIX_IT_MV,v.normal);
					normal.z = -0.5;
					pos = pos + float4(normalize(normal),0)*_Outline;

					o.pos = mul(UNITY_MATRIX_P,pos);
					return o;
				}

				fixed4 frag(v2f i) :SV_Target{
					return fixed4(_OutlineColor.rgb,1);
				}
				ENDCG
			}
			//该段作用时上色
			Pass{
				Tags{"LightMode" = "ForwardBase"}
				//裁剪后方
				Cull Back
				CGPROGRAM
					#pragma vertex   vert
					#pragma fragment  frag
					#pragma multi_compile_fwdbase
					#include "Lighting.cginc"
					#include "UnityCG.cginc"
					#include "AutoLight.cginc" 

					sampler2D _MainTex;
					float4 _MainTex_ST;
					fixed4 _Color;
					sampler2D _RampTex;
					fixed4 _Specular;
					fixed _SpecularScale;

					//应用到顶点着色器的数据
					struct a2v {
						//顶点位置,法线与贴图
						float4 vertex:POSITION;
						float3 normal:NORMAL;
						float2 texcoord:TEXCOORD0;
					};
					//顶点到片段着色器的数据
					struct v2f {
						//顶点信息、uv信息、世界坐标法线信息和世界坐标信息。
						float4 pos:SV_POSITION;
						float2 uv:TEXCOORD0;
						float3 worldNormal:TEXCOORD1;
						float3 worldPos:TEXCOORD2;
						SHADOW_COORDS(3)
					};
					//顶点着色器正文
					v2f vert(a2v v) {
						v2f o;

						//将模型坐标转换为裁剪坐标
						o.pos = UnityObjectToClipPos(v.vertex);
						o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
						o.worldNormal = mul(v.normal,(float3x3)unity_WorldToObject);
						o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;

						TRANSFER_SHADOW(o);

						return o;
					}
					//片段着色器 指定输出为SV_Target
					fixed4 frag(v2f i) :SV_Target{
						//获取模型在世界空间的法线向量  计算与输入向量方向相同的单位向量
						fixed3 worldNormal = normalize(i.worldNormal);
						//仅用于向前渲染中,输入一个世界空间中的顶点位置, 返回世界空间中从该点到光源的光照方向。没有被归一化
						fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
						//输入一个世界空间中的顶点位置,返回世界空间中从该点到摄像机的观察方向
						fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
						//计算与输入向量方向相同的单位向量
						fixed3 worldHalf = normalize(worldLightDir + worldViewDir);

						fixed4 c = tex2D(_MainTex,i.uv);
						fixed3 albedo = c.rgb*_Color.rgb;

						fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz*albedo;

						UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos);
						fixed diff = dot(worldNormal,worldLightDir);
						diff = (diff*0.5 + 0.5)*atten;

						fixed3 diffuse = _LightColor0.rgb*albedo*tex2D(_RampTex,float2(diff,diff)).rgb;

						fixed spec = dot(worldNormal,worldHalf);
						fixed w = fwidth(spec)*2.0;
						fixed3 specular = _Specular.rgb*lerp(0,1,smoothstep(-w,w,spec + _SpecularScale - 1))*step(0.0001,_SpecularScale);
						//最后添加的step(0.0001,_SpecularScale);是为了控制当Specular为0时,不出现高光效果

						return fixed4(ambient + diffuse + specular,1.0);
					}
				ENDCG
			}
		}
		FallBack "Diffuse"
		//这里的回调需要注意包含能够处理阴影的特殊Pass
}
发布了134 篇原创文章 · 获赞 37 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/lengyoumo/article/details/103955874