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渲染正面。