上一篇傳送門:
https://blog.csdn.net/qq_27534999/article/details/100661120
本篇會在上一篇的基礎上,添加高光與平滑效果。
一、高光
塞爾達中的頭髮高光應該是用普通高光加上額外貼圖控制的,這邊先做普通的高光。
還是用老辦法,視角方向和光照方向相加後歸一化,之後與法線點乘:
fixed3 worldHalfDir = normalize(worldLightDir + worldViewDir); //高光計算用
...
fixed spec = dot(worldNormal, worldHalfDir);
fixed4 specular = _Specular * lerp(0,1,step(0, spec+_SpecularScale-1)) * step(0.001, _SpecularScale);
...
fixed4 rim = light * rimStep * 0.5 * diffStep * _RimColor;
效果如下,臉部其實不應有高光的,但模型是一體的,沒法分別設置參數。
建議把需要高光的模型單獨拆出來,設置合適的 SpecularScale 值。(或者用下一篇講的頂點色控制)
二、平滑
細心的你肯定會發現鋸齒感嚴重,此時需要做一些平滑處理。(雖然塞爾達荒野之息貌似對鋸齒不作處理……)
方法也很簡單,就是使用 smoothstep 函數。
smoothstep函數具體細節可以看這裏:https://blog.csdn.net/u010333737/article/details/82859246
fixed w = fwidth(spec)*2.0;
模仿 Unity Shader 入門精要,計算一個 w值,然後將代碼中部分 step 改爲 smoothstep,用上 w值即可(也可用一個較小的值替代 w值,實際測試效果差別不大)。
fixed4 specular = _Specular * lerp(0,1,smoothstep(-w, w, spec+_SpecularScale-1)) * step(0.001, _SpecularScale);
...
fixed diffStep = smoothstep(-w+_ShadowThreshold, w+_ShadowThreshold, diffValue);
...
fixed rimStep = smoothstep(-w+_RimThreshold, w+_RimThreshold, rimValue);
將之前的 step 改爲 smoothstep後,邊緣會更加平滑、柔和:
三、成果
至此,完整 Shader 如下:
Shader "Custom/ToonShadingSimple_v2"
{
Properties
{
[Header(Main)]
_MainTex ("Texture", 2D) = "white" {}
_Color ("Color", Color) = (1.0, 1.0, 1.0, 1.0)
_RimColor ("RimColor", Color) = (1.0, 1.0, 1.0, 1.0)
_ShadowThreshold ("ShadowThreshold", Range(-1.0, 1.0)) = 0.2
_ShadowBrightness ("ShadowBrightness", Range(0.0, 1.0)) = 0.6
_RimThreshold ("RimThreshold", Range(0.0, 1.0)) = 0.35
_RimPower ("RimPower", Range(0.0, 16)) = 4.0
_Specular ("Specular", Color) = (1, 1, 1, 1)
_SpecularScale("Specular Scale", Range(0, 0.1)) = 0.02
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
Cull Back
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float3 worldNormal : TEXCOORD1;
float3 worldPos : TEXCOORD2;
UNITY_FOG_COORDS(3)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
fixed4 _RimColor;
fixed _ShadowThreshold;
fixed _ShadowBrightness;
fixed _RimThreshold;
half _RimPower;
fixed4 _Specular;
fixed _SpecularScale;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed3 worldNormal = normalize(i.worldNormal); //法線 N
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); //光照方向 L
fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); //視角方向 V
fixed3 worldHalfDir = normalize(worldLightDir + worldViewDir); //高光計算用
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
fixed spec = dot(worldNormal, worldHalfDir);
// w值也可用一個較小的值代替,效果差別不大
fixed w = fwidth(spec)*2.0;
fixed4 specular = _Specular * lerp(0,1,smoothstep(-w, w, spec+_SpecularScale-1)) * step(0.001, _SpecularScale);
fixed diffValue = dot(worldNormal, worldLightDir);
fixed diffStep = smoothstep(-w+_ShadowThreshold, w+_ShadowThreshold, diffValue);
fixed4 light = _LightColor0 * 0.5 + 0.5;
fixed4 diffuse = light * col * (diffStep + (1 - diffStep) * _ShadowBrightness) * _Color;
// 模仿參考文章的方法,感覺效果不是太好
// fixed rimValue = 1 - dot(worldNormal, worldViewDir);
// fixed rimStep = step(_RimThreshold, rimValue * pow(dot(worldNormal,worldLightDir), _RimPower));
fixed rimValue = pow(1 - dot(worldNormal, worldViewDir), _RimPower);
fixed rimStep = smoothstep(-w+_RimThreshold, w+_RimThreshold, rimValue);
fixed4 rim = light * rimStep * 0.5 * diffStep * _RimColor;
fixed4 final = diffuse + rim + specular;
// apply fog
UNITY_APPLY_FOG(i.fogCoord, final);
return final;
}
ENDCG
}
}
}
謝謝觀賞!~
下一篇傳送門:
https://blog.csdn.net/qq_27534999/article/details/100985558
參考資料:
1、https://roystan.net/articles/toon-shader.html
2、《Unity Shader 入門精要》