这是一个一年前就想实现的效果,不过当时技术力太差,一直没能实现理想的效果。
初学Shader,这篇文章可能有不少错误,还请各位大佬批评指正。
后来参考了这篇文章的思路,相信很多人都看过,核心思想就是通过多个PASS的堆叠实现出类似毛发的效果:
先打个简单的底色,这里最好要加上AO贴图。
AO贴图
代码语言:javascript复制 v2f vert(appdata_base v)
{
v2f o;
o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag(v2f i): SV_Target
{
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
fixed3 worldView = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
fixed3 worldHalf = normalize(worldView worldLight);
fixed shadow = SHADOW_ATTENUATION(i);
fixed3 col = tex2D(_MainTex, i.uv.xy).rgb * _Color;
fixed ambient = lerp(0,1,(tex2D(_AOTex,i.uv.xy*5).r tex2D(_AOTex,i.uv.xy*5).g tex2D(_AOTex,i.uv.xy*5).b)*.657);
fixed4 ambientCol =fixed4((ambient * _AOColor).rgb,ambient*0.5);
fixed diffuse =lerp(0,1,saturate(dot(worldNormal, worldLight))*shadow);
fixed4 diffuseCol =fixed4((diffuse * _LightColor0).rgb ((1-diffuse) * _ShadowColor * 0.5).rgb,diffuse);
fixed specular = pow(saturate(dot(worldNormal, worldHalf)), _Shininess*2.5)*0.85;
col = col*(1 - ambientCol.a) ambientCol * ambientCol.a;
col = col * diffuseCol * lerp(0.86,1.4,specular-ambient*0.2) ;
fixed3 color = col diffuse;
return fixed4(col ,1);
}
可以得到这样的效果:
现在开始最核心的部分,利用多pass进行类似毛发的效果。由于每一个PASS进行的操作过于雷同,不妨把每个pass的内容写进一个单独.cginc文件中,在shader里include一下就好了。Shader只需要传入一个FURSTEP控制每个PASS相对于上一个PASS的移动量即可。
代码语言:javascript复制 Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//...其他一些include
#define FURSTEP 0.03
#include "FurPass.cginc"
ENDCG
}
在vert中对每一个pass沿着法线方向做偏移操作,顺便加上一个风动效果:
代码语言:javascript复制v2f vert(appdata_base v)
{
v2f o;
v.vertex.x = ( sin(_WindDistribution _Time.y * _WindFrequency) * _WindAmplitude) * v.texcoord.y*FURSTEP;
v.vertex.y = ( cos( _WindDistribution _Time.y * _WindFrequency) * _WindAmplitude) * v.texcoord.y*FURSTEP;
float3 P = v.vertex.xyz v.normal * _FurLength * FURSTEP;
P = clamp(mul(unity_WorldToObject, _ForceGlobal).xyz _ForceLocal.xyz, -1, 1) * pow(FURSTEP, 3) * _FurLength;
o.pos = UnityObjectToClipPos(float4(P, 1.0));
o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv.zw = TRANSFORM_TEX(v.texcoord, _FurTex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
TRANSFER_SHADOW(o)
return o;
}
一定要记得根据草的noise采样图对每一个pass进行透明度处理:
代码语言:javascript复制 fixed3 noise = tex2D(_FurTex, i.uv.zw * _FurThinness).rgb;
fixed alpha = clamp(noise - (FURSTEP * FURSTEP) * _FurDensity, 0, 1);
clip(alpha-_Cutoff);
先给个最简单的纯色看看效果:
再根据FURSTEP附上不同的明暗:
代码语言:javascript复制col -= (pow(1 - FURSTEP, 1.2)) * _FurShading;
开始有内味了。
再根据AO贴图给草坪加上AO:
代码语言:javascript复制 fixed ambient = saturate((tex2D(_AOTex,i.uv.xy*5).r tex2D(_AOTex,i.uv.xy*5).g tex2D(_AOTex,i.uv.xy*5).b)*.357);
fixed3 ambientTmp = dot(UNITY_LIGHTMODEL_AMBIENT.xyz,(1,1,1))<0.8?UNITY_LIGHTMODEL_AMBIENT.xyz (1.5,1.5,1.5):UNITY_LIGHTMODEL_AMBIENT;
fixed4 ambientCol = fixed4((ambient * _AOColor * pow(1.5,FURSTEP 2)).rgb,ambient);
ambientCol.rgb = ambientCol.rgb*(1 - 0.53) ambientTmp * 0.53;
col = col*(1 - ambientCol.a) ambientCol * ambientCol.a;
加上简单的光照:
代码语言:javascript复制 fixed shadow = SHADOW_ATTENUATION(i);
fixed diffuse = saturate(saturate(dot(worldNormal, worldLight)) * shadow);
fixed4 diffuseCol = fixed4((diffuse * _LightColor0).rgb ((1-diffuse) * _ShadowColor * 0.5).rgb,diffuse*pow(1.5,FURSTEP 3));
col *= diffuseCol;
现在有影子了:
再加上一个简单的specular:
代码语言:javascript复制 fixed specular = pow(saturate(dot(worldNormal, worldHalf)), _Shininess * 1.55);
col *= lerp(0.86,1.4,specular-ambient*0.2);
看着还行,勉强能用