卡通风格渲染模型
Shader "Unlit/KaTong"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}//主帖图
		_Color("Color Tint",Color) = (1,1,1,1)//颜色
		_Ramp("Ramp Texture",2D) = "white" {}//渐变贴图
		_Outline("Outline",Range(0,1)) = 0.1//描边大小
		_OutlineColor ("Outline Color",Color) = (1,1,1,1) //描边的颜色
		_Specular ("Specular",Color) = (1,1,1,1) //高光颜色
		_SpecularScale("Specular Scale",Range(0,0.1)) = 0.01 //高光缩放,范围
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		LOD 100

		Pass
		{
			NAME "OUTLINE"
			Cull Front // 渲染背面,大一点,把边缘露出来
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"

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

			struct v2f
			{
				float4 vertex : SV_POSITION;
			};

			float4 _OutlineColor;
			float _Outline;
			
			v2f vert (appdata v)
			{
				v2f o;
				float4 pos = mul(UNITY_MATRIX_MV, v.vertex);//变换顶点到观察空间
				//变换法线到观察空间,UNITY_MATRIX_IT_MV是专门针对法线的变换矩阵,法线非等比变换会使角度改变,所以使用的矩阵和顶点变换的是不一样的
				float3 normal = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);
				//对法线的z轴处理在normalize 是为了防止背面顶点扩张挡住正面
				normal.z = -0.5;
				pos = pos + float4(normalize(normal), 0) * _Outline;
				o.vertex = mul(UNITY_MATRIX_P, pos);//在变换到检查矩阵
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				//渲染成描边颜色就可以了
				return float4(_OutlineColor.rgb,1);
			}
			ENDCG
		}

		Pass{
			Tags{"LightMode"="ForwardBase"}

			Cull Back

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma multi_compile_fwdbase

			#include "UnityCG.cginc"
			#include "Lighting.cginc"
			#include "AutoLight.cginc"

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float2 uv:TEXCOORD0;
				float3 worldNormal:TEXCOORD1;
				float3 worldPos : TEXCOORD2;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _Ramp;
			float4 _Color;
			float4 _Specular;
			fixed _SpecularScale;

			v2f vert(appdata_base v)
			{
				//单单的变换坐标,计算世界顶点位置和世界顶点法线给片元
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
				o.worldNormal = mul(unity_ObjectToWorld, v.normal);
				o.worldPos = mul(unity_ObjectToWorld, v.vertex);
				return o;
			}

			float4 frag(v2f i):SV_Target
			{
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
				fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
				fixed3 worldHalfDir = 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(_Ramp, float2(diff, diff)).rgb; //融合渐变纹理和环境颜色

				//卡通的高光都是一块一块的,也就是让它不是0就是1,然后在边缘的地方进行一些过度,减少突变
				fixed spec = dot(worldNormal, worldHalfDir);
				fixed w = fwidth(spec) * 2.0; //邻域像素之间的近似倒数值,用来作为平滑的区域
				//smoothstep 在[-w,w]外区域外的话 >w就为1,<-w的话就为0 在这区间的话就平滑为0-1
				fixed3 specular = _Specular.rgb * lerp(0, 1, smoothstep(-w, w, spec + _SpecularScale - 1)) * step(0.0001, _SpecularScale);
				//step(0.0001, _SpecularScale) 是为了_SpecularScale为0时,消除高光

				return fixed4(ambient + diffuse + specular, 1.0);
			}
			ENDCG
		}
	}
	Fallback Off
}


一个卡通效果思路就是,在漫反射的时候使用一个渐变纹理控制色调,高光是区块性的,再加一个描边效果。这里使用的是过程式几何轮廓线渲染,就是使用一个Pass用来渲染较大的背面当作边缘线,再用另外一个Pass渲染正面。

blob.png


首页 我的博客
粤ICP备17103704号