Toony Colors Pro 裏海浪的shader分析

先看下效果:

這個海浪除了正常的座標點週期性變化之外,這個拍岸的浪花和靠近陸地部分的深度很吸引我。果斷找到這個shader做了下分析。發現拍岸浪和顏色,都是根據水深計算的。而水深是根據場景的深度計算的。

COMPUTE_EYEDEPTH(o.sPos.z);//將z值賦值爲點到視點的深度。這行先計算水面的視深度

float sceneZ = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(IN.sPos));這裏計算場景深度,用這兩個對比,今兒算出水深。由於水是transparent的,所以不用寫入shadow,就是用這個技巧來計算的。
            

下面直接上shader和shader裏的註釋了。

// Upgrade NOTE: upgraded instancing buffer 'Props' to new syntax.

// Toony Colors Pro+Mobile 2
// (c) 2014-2017 Jean Moreno

Shader "Toony Colors Pro 2/Examples/Cat Demo/Water"
{
    Properties
    {
        [TCP2HelpBox(Warning,Make sure that the Camera renders the depth texture for this material to work properly.You can use the script __TCP2_CameraDepth__ for this.)]
    [TCP2HeaderHelp(BASE, Base Properties)]
    //TOONY COLORS
    _HColor("Highlight Color", Color) = (0.6,0.6,0.6,1.0)
    _SColor("Shadow Color", Color) = (0.3,0.3,0.3,1.0)

        //DIFFUSE
        _MainTex("Main Texture (RGB)", 2D) = "white" {}
    [TCP2Separator]

    //TOONY COLORS RAMP
    _RampThreshold("Ramp Threshold", Range(0,1)) = 0.5
    _RampSmooth("Ramp Smoothing", Range(0.001,1)) = 0.1
[TCP2Separator]
[TCP2HeaderHelp(WATER)]
    _Color("Water Color", Color) = (0.5,0.5,0.5,1.0)

    [Header(Depth Color)]
    _DepthColor("Depth Color", Color) = (0.5,0.5,0.5,1.0)
    [PowerSlider(5.0)] _DepthDistance("Depth Distance", Range(0.01,3)) = 0.5

    [Header(Foam)]
    _FoamSpread("Foam Spread", Range(0.01,5)) = 2
    _FoamStrength("Foam Strength", Range(0.01,1)) = 0.8
    _FoamColor("Foam Color (RGB) Opacity (A)", Color) = (0.9,0.9,0.9,1.0)
    [NoScaleOffset]
    _FoamTex("Foam (RGB)", 2D) = "white" {}
    _FoamSmooth("Foam Smoothness", Range(0,0.5)) = 0.02
    _FoamSpeed("Foam Speed", Vector) = (2,2,2,2)

    [Header(Vertex Waves Animation)]
    _WaveSpeed("Speed", Float) = 2
    _WaveHeight("Height", Float) = 0.1
    _WaveFrequency("Frequency", Range(0,10)) = 1

    [Header(UV Waves Animation)]
    _UVWaveSpeed("Speed", Float) = 1
    _UVWaveAmplitude("Amplitude", Range(0.001,0.5)) = 0.05
    _UVWaveFrequency("Frequency", Range(0,10)) = 1
[TCP2Separator]
[TCP2HeaderHelp(RIM, Rim)]
//RIM LIGHT
_RimColor("Rim Color", Color) = (0.8,0.8,0.8,0.6)
_RimMin("Rim Min", Range(0,1)) = 0.5
_RimMax("Rim Max", Range(0,1)) = 1.0
[TCP2Separator]
//Avoid compile error if the properties are ending with a drawer
[HideInInspector] __dummy__("unused", Float) = 0
    }

        SubShader
{
    Tags {"Queue" = "Geometry" "RenderType" = "Opaque"}


    CGPROGRAM

    #pragma surface surf ToonyColorsWater keepalpha vertex:vert nolightmap
    #pragma target 3.0

    //================================================================
    // VARIABLES

    fixed4 _Color;
    sampler2D _MainTex;
    float4 _MainTex_ST;
    sampler2D_float _CameraDepthTexture;
    fixed4 _DepthColor;
    half _DepthDistance;
    half4 _FoamSpeed;
    half _FoamSpread;
    half _FoamStrength;
    sampler2D _FoamTex;
    fixed4 _FoamColor;
    half _FoamSmooth;
    half _WaveHeight;
    half _WaveFrequency;
    half _WaveSpeed;
    half _UVWaveAmplitude;
    half _UVWaveFrequency;
    half _UVWaveSpeed;

    fixed4 _RimColor;
    fixed _RimMin;
    fixed _RimMax;


    struct Input
    {
        half2 texcoord;
        half3 viewDir;
        half2 sinAnim;
        float4 sPos;
    };

    //================================================================
    // CUSTOM LIGHTING

    //Lighting-related variables
    half4 _HColor;
    half4 _SColor;
    half _RampThreshold;
    half _RampSmooth;

    // Instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
    // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
    // #pragma instancing_options assumeuniformscaling
    UNITY_INSTANCING_BUFFER_START(Props)
        // put more per-instance properties here
    UNITY_INSTANCING_BUFFER_END(Props)

        //Custom SurfaceOutput
        struct SurfaceOutputWater
        {
            half atten;
            fixed3 Albedo;
            fixed3 Normal;
            fixed3 Emission;
            fixed Alpha;
        };

        inline half4 LightingToonyColorsWater(inout SurfaceOutputWater s, half3 viewDir, UnityGI gi)
        {
            half3 lightDir = gi.light.dir;
        #if defined(UNITY_PASS_FORWARDBASE)
            half3 lightColor = _LightColor0.rgb;
            half atten = s.atten;
        #else
            half3 lightColor = gi.light.color.rgb;
            half atten = 1;
        #endif

            s.Normal = normalize(s.Normal);
            fixed ndl = max(0, dot(s.Normal, lightDir));
            #define NDL ndl
            #define        RAMP_THRESHOLD    _RampThreshold
            #define        RAMP_SMOOTH        _RampSmooth

            fixed3 ramp = smoothstep(RAMP_THRESHOLD - RAMP_SMOOTH * 0.5, RAMP_THRESHOLD + RAMP_SMOOTH * 0.5, NDL);
        #if !(POINT) && !(SPOT)
            ramp *= atten;
        #endif
        #if !defined(UNITY_PASS_FORWARDBASE)
            _SColor = fixed4(0,0,0,1);
        #endif
            _SColor = lerp(_HColor, _SColor, _SColor.a);    //Shadows intensity through alpha
            ramp = lerp(_SColor.rgb, _HColor.rgb, ramp);
            fixed4 c;
            c.rgb = s.Albedo * lightColor.rgb * ramp;
            c.a = s.Alpha;

        #ifdef UNITY_LIGHT_FUNCTION_APPLY_INDIRECT
            c.rgb += s.Albedo * gi.indirect.diffuse;
        #endif
            return c;
        }

        void LightingToonyColorsWater_GI(inout SurfaceOutputWater s, UnityGIInput data, inout UnityGI gi)
        {
            gi = UnityGlobalIllumination(data, 1.0, s.Normal);

            gi.light.color = _LightColor0.rgb;    //remove attenuation
            s.atten = data.atten;    //transfer attenuation to lighting function
        }

        //================================================================
        // VERTEX FUNCTION


        struct appdata_tcp2
        {
            float4 vertex : POSITION;
            float3 normal : NORMAL;
            float4 texcoord : TEXCOORD0;
            float4 texcoord1 : TEXCOORD1;
            float4 texcoord2 : TEXCOORD2;
    #if UNITY_VERSION >= 550
            UNITY_VERTEX_INPUT_INSTANCE_ID
    #endif
        };

            #define TIME (_Time.y)

        void vert(inout appdata_tcp2 v, out Input o)
        {
            UNITY_INITIALIZE_OUTPUT(Input, o);

            //Main texture UVs
            float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
            half2 mainTexcoords = worldPos.xz * 0.1;//因爲水面的尺寸是10.不乘以0.1就會導致repeat。比較難看
            o.texcoord.xy = TRANSFORM_TEX(mainTexcoords.xy, _MainTex);//根據texture的縮放
            half2 x = ((v.vertex.xy + v.vertex.yz) * _UVWaveFrequency) + (TIME.xx * _UVWaveSpeed);
            o.sinAnim = x;
            //vertex waves
            float3 _pos = worldPos.xyz * _WaveFrequency;
            float _phase = TIME * _WaveSpeed;
            half4 vsw_offsets = half4(1.0, 2.2, 0.6, 1.3);
            half4 vsw_ph_offsets = half4(1.0, 1.3, 2.2, 0.4);
            half4 waveXZ = sin((_pos.xxzz * vsw_offsets) + (_phase.xxxx * vsw_ph_offsets));
            // float waveFactorX = (waveXZ.x + waveXZ.y) * _WaveHeight / 2;
            // float waveFactorZ = (waveXZ.z + waveXZ.w) * _WaveHeight / 2;
            float waveFactorX = dot(waveXZ.xy, 1) * _WaveHeight / 2;
            float waveFactorZ = dot(waveXZ.zw, 1) * _WaveHeight / 2;
        #define VSW_STRENGTH 1
            v.vertex.y += (waveFactorX + waveFactorZ) * VSW_STRENGTH;
            half4 waveXZn = cos((_pos.xxzz * vsw_offsets) + (_phase.xxxx * vsw_ph_offsets)) * (vsw_offsets / 2);
            float xn = -_WaveHeight * (waveXZn.x + waveXZn.y);
            float zn = -_WaveHeight * (waveXZn.z + waveXZn.w);
            v.normal = normalize(float3(xn, 1, zn));
            float4 pos = UnityObjectToClipPos(v.vertex);
            o.sPos = ComputeScreenPos(pos);
            COMPUTE_EYEDEPTH(o.sPos.z);//將z值賦值爲點到視點的深度
        }

        //================================================================
        // SURFACE FUNCTION

        void surf(Input IN, inout SurfaceOutputWater o)
        {

            half2 uvDistort = ((sin(0.9*IN.sinAnim.xy) + sin(1.33*IN.sinAnim.xy + 3.14) + sin(2.4*IN.sinAnim.xy + 5.3)) / 3) * _UVWaveAmplitude;
            IN.texcoord.xy += uvDistort.xy;
            half ndv = saturate(dot(IN.viewDir, o.Normal));//法線和視角的點乘。
            fixed4 mainTex = tex2D(_MainTex, IN.texcoord.xy);
            float sceneZ = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(IN.sPos));//此點的深度值,這裏是關鍵。
            //SAMPLE_DEPTH_TEXTURE_PROJ這個函數使用的是投影渲染獲取的深度,這個水不投影,因爲最後一行的 Fallback "Diffuse"是註釋掉的,
            //所以算深度的時候不包括水。partZ是水的深度值。兩個差值,就可以算出水的深度。
            //這裏本來發現一個小問題,就是給陸地的fallback關閉,發現還是能正常渲染。後來發現,surface shader中使用addshadow也是會產生陰影和深度通道的。
            //如果給這兩個都關閉的話,就會沒有深度不同的效果了。 
            //具體參考https://docs.unity3d.com/Manual/SL-CameraDepthTexture.html頁面的DepthTextureMode.Depth texture這小結。總算搞明白了。
            //根據這個水深,再去設置水的顏色和波浪
            if (unity_OrthoParams.w > 0)
            {
                //orthographic camera
            #if defined(UNITY_REVERSED_Z)
                sceneZ = 1.0f - sceneZ;
            #endif
                sceneZ = (sceneZ * _ProjectionParams.z) + _ProjectionParams.y;
            }
            else
                //perspective camera
                sceneZ = LinearEyeDepth(sceneZ);
            float partZ = IN.sPos.z;
            float depthDiff = abs(sceneZ - partZ);//這裏算出了水的深度
            depthDiff *= ndv * 2;
            //Depth-based foam
            half2 foamUV = IN.texcoord.xy;
            foamUV.xy += TIME.xx*_FoamSpeed.xy*0.05;
            fixed4 foam = tex2D(_FoamTex, foamUV);
            foamUV.xy += TIME.xx*_FoamSpeed.zw*0.05;
            fixed4 foam2 = tex2D(_FoamTex, foamUV);
            foam = (foam + foam2) / 2;
            float foamDepth = saturate(_FoamSpread * depthDiff);
            half foamTerm = (smoothstep(foam.r - _FoamSmooth, foam.r + _FoamSmooth, saturate(_FoamStrength - foamDepth)) * saturate(1 - foamDepth)) * _FoamColor.a;
            //Alter color based on depth buffer (soft particles technique)
            mainTex.rgb = lerp(_DepthColor.rgb, mainTex.rgb, saturate(_DepthDistance * depthDiff));    //N.V corrects the result based on view direction (depthDiff tends to not look consistent depending on view angle)));
            _Color = lerp(fixed4(1,1,1,1), _Color, mainTex.a);
            o.Albedo = lerp(mainTex.rgb * _Color.rgb, _FoamColor.rgb, foamTerm);
            o.Alpha = mainTex.a * _Color.a;
            o.Alpha = lerp(o.Alpha, _FoamColor.a, foamTerm);
            //o.Albedo = fixed4(0, 0, abs(sceneZ - partZ), 1);
            //Rim
            half3 rim = smoothstep(_RimMax, _RimMin, 1 - Pow4(1 - ndv)) * _RimColor.rgb * _RimColor.a;
            o.Emission += rim.rgb;
        }

        ENDCG

}

//Fallback "Diffuse"
CustomEditor "TCP2_MaterialInspector_SG"
}
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章