[UnityShader笔记] 卡通Shader
Unity的Standard Assets的Effects下面有个ToonShadering的文件夹专门用来处理卡通Shader。
ToonBasic
这里我们先看看其中的ToonBasic。首先导入一个Car资源模型
然后我们用ToonBasic这个Shader替换掉模型中的所有Shader,先看最终的结果:
右边是使用了ToonBase之后的显示效果,发现卡通的效果还不错。下面我们来研究一下这个Shader的源码。
Shader "Toon/Basic" {
Properties {
_Color ("Main Color", Color) = (.5,.5,.5,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
_ToonShade ("ToonShader Cubemap(RGB)", CUBE) = "" { }
}
SubShader {
Tags { "RenderType"="Opaque" }
Pass {
Name "BASE"
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
#include "UnityCG.cginc"
sampler2D _MainTex;
samplerCUBE _ToonShade;
float4 _MainTex_ST;
float4 _Color;
struct appdata {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
float2 texcoord : TEXCOORD0;
float3 cubenormal : TEXCOORD1;
UNITY_FOG_COORDS(2)
};
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
o.cubenormal = mul (UNITY_MATRIX_MV, float4(v.normal,0));
UNITY_TRANSFER_FOG(o,o.pos);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = _Color * tex2D(_MainTex, i.texcoord);
fixed4 cube = texCUBE(_ToonShade, i.cubenormal);
fixed4 c = fixed4(2.0f * cube.rgb * col.rgb, col.a);
UNITY_APPLY_FOG(i.fogCoord, c);
return c;
}
ENDCG
}
}
Fallback "VertexLit"
}
我们查看一下这个Shader的材质球
发现有三个参数供我们使用
_Color ("Main Color", Color) = (.5,.5,.5,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
_ToonShade ("ToonShader Cubemap(RGB)", CUBE) = "" { }
V&F函数为
v2f vert (appdata v)
{
v2f o;
//将顶点从模型空间转换到裁剪空间
o.pos = UnityObjectToClipPos(v.vertex);
//获取贴图的UV
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
//获取cubemap的法线
o.cubenormal = mul (UNITY_MATRIX_MV, float4(v.normal,0));
UNITY_TRANSFER_FOG(o,o.pos);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
//获取贴图的UV颜色
fixed4 col = _Color * tex2D(_MainTex, i.texcoord);
//获取cubemap的颜色
fixed4 cube = texCUBE(_ToonShade, i.cubenormal);
//将cubemap上的颜色*贴图的颜色*2,透明度直接取贴图的alpha
fixed4 c = fixed4(2.0f * cube.rgb * col.rgb, col.a);
UNITY_APPLY_FOG(i.fogCoord, c);
return c;
}
原理:
ToonBasic主要就是通过cubemap来控制颜色的变化,这里为什么将颜色乘以2。我们改为乘以1来做个测试。
发现卡车颜色变暗了,说明2用来控制颜色的亮度。
ToonBasicOutline
接下来我们使用ToonBasicOutline这个Shader进行对比
我们发现ToonBasicOutline就如它的名字一样,和ToonBasic相比多了一个描边效果。同样看一下源码
Shader "Toon/Basic Outline" {
Properties {
_Color ("Main Color", Color) = (.5,.5,.5,1)
_OutlineColor ("Outline Color", Color) = (0,0,0,1)
_Outline ("Outline width", Range (.002, 0.03)) = .005
_MainTex ("Base (RGB)", 2D) = "white" { }
_ToonShade ("ToonShader Cubemap(RGB)", CUBE) = "" { }
}
CGINCLUDE
#include "UnityCG.cginc"
struct appdata {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
UNITY_FOG_COORDS(0)
fixed4 color : COLOR;
};
uniform float _Outline;
uniform float4 _OutlineColor;
v2f vert(appdata v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
float3 norm = normalize(mul ((float3x3)UNITY_MATRIX_IT_MV, v.normal));
float2 offset = TransformViewToProjection(norm.xy);
#ifdef UNITY_Z_0_FAR_FROM_CLIPSPACE //to handle recent standard asset package on older version of unity (before 5.5)
o.pos.xy += offset * UNITY_Z_0_FAR_FROM_CLIPSPACE(o.pos.z) * _Outline;
#else
o.pos.xy += offset * o.pos.z * _Outline;
#endif
o.color = _OutlineColor;
UNITY_TRANSFER_FOG(o,o.pos);
return o;
}
ENDCG
SubShader {
Tags { "RenderType"="Opaque" }
UsePass "Toon/Basic/BASE"
Pass {
Name "OUTLINE"
Tags { "LightMode" = "Always" }
Cull Front
ZWrite On
ColorMask RGB
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
fixed4 frag(v2f i) : SV_Target
{
UNITY_APPLY_FOG(i.fogCoord, i.color);
return i.color;
}
ENDCG
}
}
Fallback "Toon/Basic"
}
对比发现:ToonBasicOutline相对于ToonBasic增加了一个描边和描边颜色的显示。具体如下:
uniform float _Outline;
uniform float4 _OutlineColor;
v2f vert(appdata v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
float3 norm = normalize(mul ((float3x3)UNITY_MATRIX_IT_MV, v.normal));
float2 offset = TransformViewToProjection(norm.xy);
#ifdef UNITY_Z_0_FAR_FROM_CLIPSPACE //to handle recent standard asset package on older version of unity (before 5.5)
o.pos.xy += offset * UNITY_Z_0_FAR_FROM_CLIPSPACE(o.pos.z) * _Outline;
#else
o.pos.xy += offset * o.pos.z * _Outline;
#endif
o.color = _OutlineColor;
UNITY_TRANSFER_FOG(o,o.pos);
return o;
}
描边的颜色直接是使用变量_OutlineColor控制,ToonBasicOutline获取边缘位置增加一层偏移即可。
ToonBasicOutline后面的Pass使用的是ToonBasic的Pass代码块。
SubShader {
Tags { "RenderType"="Opaque" }
UsePass "Toon/Basic/BASE"
ToonLit
采用同样的方法使用Toonlit批量替换模型的Shader,效果如下:
ToonLit的Shader源码如下:
Shader "Toon/Lit" {
Properties {
_Color ("Main Color", Color) = (0.5,0.5,0.5,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
_Ramp ("Toon Ramp (RGB)", 2D) = "gray" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf ToonRamp
sampler2D _Ramp;
// custom lighting function that uses a texture ramp based
// on angle between light direction and normal
#pragma lighting ToonRamp exclude_path:prepass
inline half4 LightingToonRamp (SurfaceOutput s, half3 lightDir, half atten)
{
#ifndef USING_DIRECTIONAL_LIGHT
lightDir = normalize(lightDir);
#endif
half d = dot (s.Normal, lightDir)*0.5 + 0.5;
half3 ramp = tex2D (_Ramp, float2(d,d)).rgb;
half4 c;
c.rgb = s.Albedo * _LightColor0.rgb * ramp * (atten * 2);
c.a = 0;
return c;
}
sampler2D _MainTex;
float4 _Color;
struct Input {
float2 uv_MainTex : TEXCOORD0;
};
void surf (Input IN, inout SurfaceOutput o) {
half4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
Fallback "Diffuse"
}
ToonLitOutline
ToonLitOutline是ToonLit的基础上增加了描边的显示
Shader "Toon/Lit Outline" {
Properties {
_Color ("Main Color", Color) = (0.5,0.5,0.5,1)
_OutlineColor ("Outline Color", Color) = (0,0,0,1)
_Outline ("Outline width", Range (.002, 0.03)) = .005
_MainTex ("Base (RGB)", 2D) = "white" {}
_Ramp ("Toon Ramp (RGB)", 2D) = "gray" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
UsePass "Toon/Lit/FORWARD"
UsePass "Toon/Basic Outline/OUTLINE"
}
Fallback "Toon/Lit"
}
里面的两个pass分别使用ToonLit的Forward和ToonBasic的OUTLINE
效果如下: