Unity3D ShaderLab布料着色器

布料着色器是我们在虚拟现实中经常使用的着色器。本篇就来完成一个较为简单的布料着色器。

新建Shader,Material,InteractiveCloth【布料】。完成的代码如下

 

Shader "91YGame/ClothShader" {
    Properties {
        //参数;
        _MainTint("Base Color",Color)=(1,1,1,1)
        _BumpMap ("Normal Map", 2D) = "bump" {}
        _DetailBump("Detail Normal",2D)=""{}
        _DetailTex("Fabric Weave",2D)=""{}
        _FresnelColor("Fresnel Color",Color)=(1,1,1,1)
        _FresnelPower("Fresnel Power",Range(0.01,10))=3
        _RimPower("Rim Falloff",Range(0.01,10))=3
        _SpecIntesity("Specular Intensity",Range(0.01,1))=0.3
        _SpecWidth("Specular Width",Range(0.01,1))=0.2

    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200
        
        CGPROGRAM
        #pragma surface surf VelvetCloth
        #pragma target 3.0
        //变量;
        sampler2D _BumpMap;
        sampler2D _DetailBump;
        sampler2D _DetailTex;
        float4 _MainTint;
        float4 _FresnelColor;
        float _FresnelPower;
        float _RimPower;
        float _SpecIntesity;
        float _SpecWidth;
        //输入结构体;
        struct Input {
            float2 uv_BumpMap;
            float2 uv_DetailBump;
            float2 uv_DetailTex;
        };

        inline fixed4 LightingVelvetCloth(SurfaceOutput s,fixed3 lightDir, fixed3 viewDir, fixed atten){
            //光照向量;
            viewDir =normalize(viewDir);
            lightDir = normalize(lightDir);
            half3 halfVec = normalize(lightDir+viewDir);
            fixed NdotL = max(0.01,dot(s.Normal,lightDir));
            //计算高光;
            float NdotH = max(0.01,dot(s.Normal,halfVec));
            float spec = pow(NdotH,s.Specular*128)*s.Gloss;
            //视线越贴近布料表面,纤维吸收的背面光就越多,高光也就越强;
            float HdotV = pow(1-max(0,dot(halfVec,viewDir)),_FresnelPower);
            float NdotE = pow(1-max(0,dot(s.Normal,viewDir)),_RimPower);
            float finalSpecMask = NdotE*HdotV;
            //最终颜色;
            fixed4 c;
            c.rgb = (s.Albedo*NdotL*_LightColor0.rgb)+(spec*(finalSpecMask*_FresnelColor))*(atten*2);
            c.a =1;
            return c;
        }

        void surf (Input IN, inout SurfaceOutput o) {
            half4 c = tex2D (_DetailTex, IN.uv_DetailTex);
            //提取法线;
            fixed3 normals = UnpackNormal(tex2D(_BumpMap,IN.uv_BumpMap)).rgb;
            fixed3 detailNormals = UnpackNormal(tex2D(_DetailBump,IN.uv_DetailBump)).rgb;
            fixed3 finalNormals = float3(normals.x+detailNormals.x,normals.y+detailNormals.y,normals.z+detailNormals.z);
            o.Normal = normalize(finalNormals);
            o.Specular = _SpecWidth;
            o.Gloss = _SpecIntesity;
            o.Albedo = c.rgb*_MainTint;
            o.Alpha = c.a;
        }
        ENDCG
    } 
    FallBack "Diffuse"
}

 

 

返回Unity,设置变量。最终效果如下:

在上面的实现过程中,我们将两张不同平铺率的法线贴图结合到一起。计算出新的法线向量。

所以使用UnpackNormal从变化法线贴图中提取出法线向量在产生新的法线贴图。然后使用normalize对最后的向量进行归一化,这样可以避免法线贴图看起来杂乱。

最后 将菲涅尔计算和高光计算的结果进行合并,创建出布料的纤维效果。