# 【Shader练习-妲己】

游戏《王者荣耀》里的英雄“妲己”的渲染效果实现。

全屏显示

包括了以下练习内容:

  1. 卡通漫反射
  2. 镜面反射
  3. 环境遮蔽
  4. 环境球
  5. 皮肤颜色调整
  6. 尾巴绒毛
  7. 增加阴影
  8. 菲涅尔反射
Shader "Vp/DajiShader" {
    Properties {
        [NoScaleOffset]_DiffuseTex("Diffuse(RGB)", 2D) = "white" {}
        [NoScaleOffset]_NormalTex("Normal", 2D) = "bump" {}

        [NoScaleOffset]_SpecularTex("Specular", 2D) = "black" {}
        [NoScaleOffset]_GlossTex("Gloss", 2D) = "black" {}

        [NoScaleOffset]_AOTex("环境遮蔽", 2D) = "white" {}

        [NoScaleOffset]_SkinMaskTex("皮肤遮罩", 2D) = "white" {}
        _SkinColor("SkinColor", Color) = (1, 1, 1, 1)
        _SkinColorIns("SkinColorIns", Range(0, 1)) = 0.1

        _Ambient("Ambient", Color) = (0.3, 0.3, 0.3, 1)
        _AmbientBorder("AmbientBorder", Range(0.01, 1)) = 0.3
        _AmbientBlur("AmbientBlur", Range(0, 0.5)) = 0.1
        _SpecularPow("SpecularPow", Range(0, 100)) = 8

        _FresnelPow("FresnelPow", Range(0, 100)) = 8
        _FresnelIns("FresnelIns", Range(0, 1)) = 0.5

        _Cubemap ("RGB:环境球贴图", cube) = "_Skybox" {}
        _CubemapMip ( "环境球Mip" , Range(0,7)) = 0
    }
    SubShader {
        Tags {
            "RenderType"="Opaque"
        }
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode"="ForwardBase"
            }
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "AutoLight.cginc"// 用于阴影
            #include "Lighting.cginc"// 用于阴影
            #pragma multi_compile_fwdbase_fullshadows
            #pragma target 3.0

            uniform sampler2D _DiffuseTex;
            uniform sampler2D _NormalTex;
            uniform half4 _Ambient;
            uniform float _AmbientBorder;
            uniform float _AmbientBlur;

            uniform sampler2D _SpecularTex;
            uniform sampler2D _GlossTex;
            uniform float _SpecularPow;

            uniform sampler2D _AOTex;
            uniform sampler2D _SkinMaskTex;
            uniform float4 _SkinColor;
            uniform float _SkinColorIns;

            uniform float _FresnelPow;
            uniform float _FresnelIns;

            samplerCUBE _Cubemap;
            uniform float _CubemapMip;

            /** 具有阴影和非阴影交汇的卡通漫反射 */
            half4 toondiffuse(float2 uv,float3 normal, float3 lightDir)
            {
                // 环境遮蔽
                half4 var_AOTex = tex2D(_AOTex,uv);
                half ao = var_AOTex.r;

                // 皮肤
                half4 var_SkinMaskTex = tex2D(_SkinMaskTex,uv);
                half4 SkinCol = var_SkinMaskTex.r * _SkinColor;
                
                // 光照模型漫反射颜色
                float ndl = max(0, dot(normal, lightDir)) * ao;
                float td = smoothstep(_AmbientBorder - _AmbientBlur, _AmbientBorder + _AmbientBlur, ndl);
                half4 var_DiffuseTex = tex2D(_DiffuseTex,uv) + SkinCol * _SkinColorIns;

                half4 a = _Ambient * var_DiffuseTex;// 环境光+漫反射
                half4 rd = lerp(a, var_DiffuseTex, td);// 环境光与漫反射进行边缘混合
                return  rd;
            }

            struct VertexInput {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
                float4 tangent : TANGENT;
            };
            struct VertexOutput {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : TEXCOORD1;
                float3 tangent : TEXCOORD2;
                float3 btangent : TEXCOORD3;
                float3 posWS : TEXCOORD4;
                LIGHTING_COORDS(5,6)// 用于阴影
            };

            VertexOutput vert (VertexInput v) {
                VertexOutput o = (VertexOutput)0;
                o.pos = UnityObjectToClipPos( v.vertex );
                TRANSFER_VERTEX_TO_FRAGMENT(o)// 用于阴影
                o.uv = v.uv;
                o.normal = UnityObjectToWorldNormal(v.normal);
                o.tangent = normalize(mul(unity_ObjectToWorld,float4(v.tangent.xyz,0.0)).xyz);
                o.btangent = normalize(cross(o.normal,o.tangent) * v.tangent.w);
                o.posWS = mul(unity_ObjectToWorld,v.vertex).xyz;

                return o;
            }
            half4 frag(VertexOutput i) : COLOR {
                // 采样纹理准备
                float3 nDirTS = UnpackNormal(tex2D(_NormalTex, i.uv));
                half4 var_SpecularTex = tex2D(_SpecularTex,i.uv);
                half4 var_GlossTex = tex2D(_GlossTex,i.uv);
                float attenuation = LIGHT_ATTENUATION(i);// 用于阴影
                // 法线贴图的法线
                float3x3 TBN = float3x3(
                    i.tangent.x,i.tangent.y,i.tangent.z,
                    i.btangent.x,i.btangent.y,i.btangent.z,
                    i.normal.x,i.normal.y,i.normal.z
                );
                float3 tex_normal = normalize(mul(nDirTS,TBN));

                // 向量准备
                float3 vDirWS = normalize(_WorldSpaceCameraPos.xyz - i.posWS.xyz);
                float3 lDirWS = normalize(_WorldSpaceLightPos0.xyz);
                float3 rDirWS = reflect(-lDirWS, tex_normal);
                float3 vrDirWS = reflect(-vDirWS, tex_normal);

                // 环境球
                float3 var_CubemapSpecular = texCUBElod(_Cubemap, float4(vrDirWS, lerp(_CubemapMip, 0.0, var_SpecularTex.r))).rgb;

                // 漫反射
                half4 difCol = toondiffuse(i.uv,tex_normal,lDirWS);

                // 镜面反射
                float vdotr = dot(vDirWS, rDirWS);
                float phong = pow(max(0.0, vdotr),_SpecularPow) * var_GlossTex.r;
                half3 phongCubemap = var_CubemapSpecular * phong * max(var_SpecularTex.r , var_GlossTex.r);
                phongCubemap = phongCubemap * 5;
                // 菲涅尔反射(非金属部分都具有菲涅尔反射)
                float nDotv = 1-max(0,dot(tex_normal,vDirWS));
                float fresnel = pow(nDotv,_FresnelPow) * (1-var_SpecularTex.r) * _FresnelIns;

                // 返回(漫反射+光高模型)*阴影
                return (difCol + half4(phongCubemap,1.0) + fresnel) * attenuation;
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}