【Unity Shader】绒毛草坪

2023-10-26 17:32:57 浏览数 (3)

这是一个一年前就想实现的效果,不过当时技术力太差,一直没能实现理想的效果。

初学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);

看着还行,勉强能用

0 人点赞