Shader "Custom/LavaFlow" {
    Properties{
        _TilingLava("TilingLava", Float) = 10
        _TilingRock("TilingRock", Float) = 1
        _MainTex("LavaTex", 2D) = "white" {}
        _RockTex("RockTex", 2D) = "white" {}
        _NormalTex("Normal", 2D) = "bump" {}
        _NormalRockTex("NormalRockTex",2D) = "bump"{}
        _RockMask("RockMask",2D) = "white" {}
        _EnhanceScale("EnhanceScale",Float) = 1
        [NoScaleOffset] _FlowMap("Flow (RG, A noise)", 2D) = "black" {}
        [NoScaleOffset] _DerivHeightMap("Deriv (AG) Height (B)", 2D) = "black" {}
        _UJump("U jump per phase", Range(-0.25, 0.25)) = 0.25
        _VJump("V jump per phase", Range(-0.25, 0.25)) = 0.25
        _Speed("Speed", Float) = 1
        _FlowStrength("Flow Strength", Float) = 1
        _FlowOffset("Flow Offset", Float) = 0
        _FlowVelocityX("FlowVelocityX",Float) = 0
        _FlowVelocityY("FlowVelocityY",Float) = 0
        _HeightScale("Height Scale, Constant", Float) = 0.25
        _HeightScaleModulated("Height Scale, Modulated", Float) = 0.75
        _Glossiness("Smoothness", Range(0,1)) = 0.5
        _Metallic("Metallic", Range(0,1)) = 0.0
        _OpacityDepth("OpacityDepth", Float) = 0.2
    }
    SubShader
    {
        Tags
        {
            "IgnoreProjector" = "True"
            "Queue" = "Transparent"
            "RenderType" = "Transparent"
            "PreviewType" = "Plane"
            "RenderPipeline" = "UniversalPipeline"
        }
        Pass
        {
            Name "LavaShading"
            Tags {
                "LightMode" = "TransparentDepthFog"
            }
            Blend SrcAlpha OneMinusSrcAlpha
            ZWrite Off

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "UnityLightingCommon.cginc"
            #include "UnityPBSLighting.cginc"
            #include "AutoLight.cginc"
            #pragma multi_compile_fwdbase
            #pragma multi_compile_fog
            #pragma only_renderers d3d9 d3d11 glcore gles gles3 metal d3d11_9x n3ds wiiu 
            #pragma target 3.0
            #define PI 3.1415926f

            sampler2D _MainTex, _RockTex,_NormalTex,_NormalRockTex,_FlowMap, _DerivHeightMap,_RockMask;
            float _UJump, _VJump, _TilingLava, _TilingRock, _Speed, _FlowStrength, _FlowOffset;
            float _HeightScale, _HeightScaleModulated;
            float _FlowVelocityX;
            float _FlowVelocityY;
            half _Glossiness;
            half _Metallic;
            float _EnhanceScale;
            struct VertexInput {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float4 tangent : TANGENT;
                float2 texcoord0 : TEXCOORD0;
            };
            struct VertexOutput {
                float4 pos : SV_POSITION;
                float2 uv0 : TEXCOORD0;
                float4 posWorld : TEXCOORD1;
                float3 normalDir : TEXCOORD2;
                float4 screenPos : TEXCOORD3;
                float4 projPos : TEXCOORD4;
                float4 tangent : TEXCOORD5;
                float4 flow : TEXCOORD6;
            };
            //<光照计算
            float3 Diffuse_Lambert(float3 DiffuseColor)
            {
                return DiffuseColor * (1 / PI);
            }
            float D_GGX(float a2, float NoH)
            {
                float d = (NoH * a2 - NoH) * NoH + 1;	// 2 mad
                return a2 / (PI * d * d);					// 4 mul, 1 rcp
            }
            float Vis_SmithJointApprox(float a2, float NoV, float NoL)
            {
                float a = sqrt(a2);
                float Vis_SmithV = NoL * (NoV * (1 - a) + a);
                float Vis_SmithL = NoV * (NoL * (1 - a) + a);
                return 0.5 * rcp(Vis_SmithV + Vis_SmithL);
            }
            float3 F_Schlick(float3 SpecularColor, float VoH)
            {
                float Fc = pow(1 - VoH,5);
                return saturate(50.0 * SpecularColor.g) * Fc + (1 - Fc) * SpecularColor;

            }
            float3 SpecularGGX(float Roughness, float3 SpecularColor, float NoH, float NoL, float NoV,float VoH)
            {
                float a2 = pow(Roughness,4);
                float D = D_GGX(a2, NoH);
                float Vis = Vis_SmithJointApprox(a2, NoV, NoL);
                float3 F = F_Schlick(SpecularColor, VoH);
                return (D * Vis) * F;
            }
            //光照计算>
            //half3 BlendNormals(half3 n1, half3 n2) {
            //    return normalize(half3(n1.xy + n2.xy, n1.z * n2.z));
            //}
            float3 CreateBinormal(float3 normal, float3 tangent, float binormalSign) {
                return cross(normal, tangent.xyz) *
                    (binormalSign * unity_WorldTransformParams.w);
            }
            void InitializeFragmentNormal(inout VertexOutput i,float3 tangentSpaceNormal) {
                float3 binormal = CreateBinormal(i.normalDir, i.tangent.xyz, i.tangent.w);
                i.normalDir = normalize(
                    tangentSpaceNormal.x * i.tangent +
                    tangentSpaceNormal.y * binormal +
                    tangentSpaceNormal.z * i.normalDir
                );
            }
            float3 UnpackDerivativeHeight(float4 textureData) {
                float3 dh = textureData.agb;
                dh.xy = dh.xy * 2 - 1;
                return dh;
            }
            float3 FlowUVW(
                float2 uv, float2 flowVector, float2 jump,
                float flowOffset, float tiling, float time, bool flowB
            ) {
                float phaseOffset = flowB ? 0.5 : 0;
                float progress = frac(time + phaseOffset);
                float3 uvw;
                uvw.xy = uv - flowVector * (progress + flowOffset);
                uvw.xy *= tiling;
                uvw.xy += phaseOffset;
                uvw.xy += (time - progress) * jump;
                uvw.z = 1 - abs(1 - 2 * progress);
                return uvw;
            }
            float3 FlowUVWRock(
                float2 uv, float2 flowVector, float2 jump,
                float flowOffset, float tiling, float time, bool flowB
            ) {
                float phaseOffset = flowB ? 0.5 : 0;
                float progress = frac(time*0.0 + phaseOffset);
                float3 uvw;
                uvw.xy = uv - flowVector * (progress + flowOffset);
                uvw.xy *= tiling;
                uvw.xy += phaseOffset;
                uvw.xy += (time - progress) * jump;
                uvw.z = 1 - abs(1 - 2 * progress);
                return uvw;
            }
            //<begin 关照算法
            float3 Diffuse_Burley(float3 DiffuseColor, float Roughness, float NoV, float NoL, float VoH)
            {
                float FD90 = 0.5 + 2 * VoH * VoH * Roughness;
                float FdV = 1 + (FD90 - 1) * pow(1 - NoV,5);
                float FdL = 1 + (FD90 - 1) * pow(1 - NoL,5);
                return DiffuseColor * ((1 / PI) * FdV * FdL);
            }
            //end>
            uniform sampler2D _CameraDepthTexture;
            uniform float _OpacityDepth;
            float4 GetLavaColor(VertexOutput i,float2 uvOffset,float4 flow, float2 jump, float tiling, float time)
            {
                float3 uvwA = FlowUVW(
                    i.uv0 + uvOffset, flow.xy, jump,
                    _FlowOffset, tiling, time, false
                );
                float3 uvwB = FlowUVW(
                    i.uv0 + uvOffset, flow.xy, jump,
                    _FlowOffset, tiling, time, true
                ); 
                float finalHeightScale =
                    flow.z * _HeightScaleModulated + _HeightScale;

                float3 dhA =
                    UnpackDerivativeHeight(tex2D(_DerivHeightMap, uvwA.xy)) *
                    (uvwA.z * finalHeightScale);
                float3 dhB =
                    UnpackDerivativeHeight(tex2D(_DerivHeightMap, uvwB.xy)) *
                    (uvwB.z * finalHeightScale);
                float3 Normal = normalize(float3(-(dhA.xy + dhB.xy), 1));
                //float3 L = _WorldSpaceLightPos0.xyz;
                //float3 V = normalize(_WorldSpaceCameraPos - i.posWorld.xyz);;
                //float3 H = normalize(L + V);

                fixed4 texA = tex2D(_MainTex, uvwA.xy) * uvwA.z;
                fixed4 texB = tex2D(_MainTex, uvwB.xy) * uvwB.z;
                fixed3 normalA = UnpackNormal(tex2D(_NormalTex, uvwA.xy)) * uvwA.z;
                fixed3 normalB = UnpackNormal(tex2D(_NormalTex, uvwB.xy)) * uvwB.z;
                float3 tangentNormal = normalize(normalA + normalB);
                tangentNormal = BlendNormals(Normal, tangentNormal);
                InitializeFragmentNormal(i, tangentNormal);
                fixed4 mainColor = (texA + texB) * dot(_WorldSpaceLightPos0.xyz, i.normalDir);
                return mainColor;
            }
            float4 GetRockColor(VertexOutput i, float2 uvOffset, float4 flow, float2 jump, float tiling, float time,out float4 maskcolor)
            {
                float3 uvwA = FlowUVWRock(
                    i.uv0 + uvOffset, flow.xy, jump,
                    _FlowOffset, tiling, time, false
                );
                float3 uvwB = FlowUVWRock(
                    i.uv0 + uvOffset, flow.xy, jump,
                    _FlowOffset, tiling, time, true
                );
                float finalHeightScale =
                    flow.z * _HeightScaleModulated + _HeightScale;

                float3 dhA =
                    UnpackDerivativeHeight(tex2D(_DerivHeightMap, uvwA.xy)) *
                    (uvwA.z * finalHeightScale);
                float3 dhB =
                    UnpackDerivativeHeight(tex2D(_DerivHeightMap, uvwB.xy)) *
                    (uvwB.z * finalHeightScale);
                float3 Normal = normalize(float3(-(dhA.xy + dhB.xy), 1));
                //float3 L = _WorldSpaceLightPos0.xyz;
                //float3 V = normalize(_WorldSpaceCameraPos - i.posWorld.xyz);;
                //float3 H = normalize(L + V);

                fixed4 texA = tex2D(_RockTex, uvwA.xy) * uvwA.z;
                fixed4 texB = tex2D(_RockTex, uvwB.xy) * uvwB.z;
                fixed4 maskA = tex2D(_RockMask, uvwA.xy) * uvwA.z;
                fixed4 maskB = tex2D(_RockMask, uvwB.xy) * uvwB.z;
                fixed3 normalA = UnpackNormal(tex2D(_NormalRockTex, uvwA.xy)) * uvwA.z;
                fixed3 normalB = UnpackNormal(tex2D(_NormalRockTex, uvwB.xy)) * uvwB.z;
                float3 tangentNormal = normalize(normalA + normalB);
                tangentNormal = BlendNormals(Normal, tangentNormal);
                InitializeFragmentNormal(i, tangentNormal);
                //fixed4 mainColor = lerp((texA + texB) * dot(_WorldSpaceLightPos0.xyz, i.normalDir), 0, (maskA + maskB) * 0.5);
                //fixed4 mainColor = (texA + texB) * dot(_WorldSpaceLightPos0.xyz, i.normalDir);// *(1 - saturate(maskA + maskB));
                fixed4 mainColor = max(texA, texB);
                maskcolor = max(maskA, maskB);
                return mainColor;
            }
            VertexOutput vert(VertexInput v) 
            {
                VertexOutput o = (VertexOutput)0;
                o.uv0 = v.texcoord0;
                o.tangent = float4(UnityObjectToWorldDir(v.tangent.xyz), v.tangent.w);
                o.normalDir = UnityObjectToWorldNormal(v.normal);
                o.posWorld = mul(unity_ObjectToWorld, v.vertex);
                o.pos = UnityObjectToClipPos(v.vertex);
                o.posWorld.w = o.pos.z;
                o.projPos = ComputeScreenPos(o.pos);
                COMPUTE_EYEDEPTH(o.projPos.z);
                o.screenPos = o.pos;
                o.flow = tex2Dlod(_FlowMap, float4(v.texcoord0.xy, 0, 0));
                return o;
            }
            float4 frag(VertexOutput i) : COLOR
            {
                float sceneZ = max(0,LinearEyeDepth(UNITY_SAMPLE_DEPTH(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)))) - _ProjectionParams.g);
                float viewDistance = UNITY_Z_0_FAR_FROM_CLIPSPACE(i.posWorld.w);
                float partZ = viewDistance;
                float alpha = smoothstep(0.0,1.0,saturate((sceneZ - partZ) / _OpacityDepth));
                //<begin
                //float4 flow = tex2D(_FlowMap, i.uv0);
                float2 uvOffset = _Time.y * float2(_FlowVelocityX,_FlowVelocityY);
                float4 flow = i.flow;
                flow.xy = flow.xy * 2 - 1;
                //提升熔岩部分的扭曲程度
                flow *= _FlowStrength;
                float noise = flow.a;
                //float time = _Time.y * _Speed + noise;
                float time = _Time.y * _Speed + noise;
                float2 jump = float2(_UJump, _VJump);
                float4 maskColor;
                fixed4 mainColor = GetLavaColor(i, uvOffset, flow, jump, _TilingLava, time);
                fixed4 rockColor = GetRockColor(i, uvOffset * 0.4, flow, jump, _TilingRock, time, maskColor);
                mainColor *= (1 + _EnhanceScale * mainColor.r);
                //return rockColor;
                //提升熔岩部分的亮度
                mainColor *= (1 + _EnhanceScale * mainColor.r);
                float3 c = lerp(rockColor, mainColor, smoothstep(0, 0.5, maskColor.r));
                //float3 c = maskColor.rrr;
                //根据与地形的深度差调整颜色
                alpha = smoothstep(0.4,0.8, alpha);
                c = lerp(4* c, c, alpha);
                //return float4(alpha, alpha, alpha, 1);
                return float4(c, 1);
            }
            ENDHLSL
        }
    }
}
