// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)

    #ifndef UNITY_PBS_LIGHTING_INCLUDED
    #define UNITY_PBS_LIGHTING_INCLUDED
    
    #include "UnityShaderVariables.cginc"
    #include "UnityStandardConfig.cginc"
    #include "UnityLightingCommon.cginc"
    #include "UnityGBuffer.cginc"
    #include "UnityGlobalIllumination.cginc"
    
    //-------------------------------------------------------------------------------------
    // Default BRDF to use:
    #if !defined (UNITY_BRDF_PBS) // allow to explicitly override BRDF in custom shader
        // still add safe net for low shader models, otherwise we might end up with shaders failing to compile
        #if SHADER_TARGET < 30 || defined(SHADER_TARGET_SURFACE_ANALYSIS) // only need "something" for surface shader analysis pass; pick the cheap one
            #define UNITY_BRDF_PBS BRDF3_Unity_PBS
        #elif defined(UNITY_PBS_USE_BRDF3)
            #define UNITY_BRDF_PBS BRDF3_Unity_PBS
        #elif defined(UNITY_PBS_USE_BRDF2)
            #define UNITY_BRDF_PBS BRDF2_Unity_PBS
        #elif defined(UNITY_PBS_USE_BRDF1)
            #define UNITY_BRDF_PBS BRDF1_Unity_PBS
        #else
            #error something broke in auto-choosing BRDF
        #endif
    #endif
    
    //-------------------------------------------------------------------------------------
    // little helpers for GI calculation
    // CAUTION: This is deprecated and not use in Untiy shader code, but some asset store plugin still use it, so let here for compatibility
    
    #if !defined (UNITY_BRDF_GI)
        #define UNITY_BRDF_GI BRDF_Unity_Indirect
    #endif
    
    inline half3 BRDF_Unity_Indirect (half3 baseColor, half3 specColor, half oneMinusReflectivity, half smoothness, half3 normal, half3 viewDir, half occlusion, UnityGI gi)
    {
        return half3(0,0,0);
    }
    
    #define UNITY_GLOSSY_ENV_FROM_SURFACE(x, s, data)               \
        Unity_GlossyEnvironmentData g;                              \
        g.roughness /* perceptualRoughness */   = SmoothnessToPerceptualRoughness(s.Smoothness); \
        g.reflUVW = reflect(-data.worldViewDir, s.Normal);  \
    
    
    #if defined(UNITY_PASS_DEFERRED) && UNITY_ENABLE_REFLECTION_BUFFERS
        #define UNITY_GI(x, s, data) x = UnityGlobalIllumination (data, s.Occlusion, s.Normal);
    #else
        #define UNITY_GI(x, s, data)                                \
            UNITY_GLOSSY_ENV_FROM_SURFACE(g, s, data);              \
            x = UnityGlobalIllumination (data, s.Occlusion, s.Normal, g);
    #endif
    
    // Surface shader output structure to be used with physically
    // based shading model.
    
    //-------------------------------------------------------------------------------------
    // Metallic workflow
    
    struct SurfaceOutputStandard
    {
        fixed3 Albedo;      // base (diffuse or specular) color
        float3 Normal;      // tangent space normal, if written
        half3 Emission;
        half Metallic;      // 0=non-metal, 1=metal
        // Smoothness is the user facing name, it should be perceptual smoothness but user should not have to deal with it.
        // Everywhere in the code you meet smoothness it is perceptual smoothness
        half Smoothness;    // 0=rough, 1=smooth
        half Occlusion;     // occlusion (default 1)
        fixed Alpha;        // alpha for transparencies
    };
    
    inline half4 LightingStandard (SurfaceOutputStandard s, float3 viewDir, UnityGI gi)
    {
        s.Normal = normalize(s.Normal);
    
        half oneMinusReflectivity;
        half3 specColor;
        s.Albedo = DiffuseAndSpecularFromMetallic (s.Albedo, s.Metallic, /*out*/ specColor, /*out*/ oneMinusReflectivity);
    
        // shader relies on pre-multiply alpha-blend (_SrcBlend = One, _DstBlend = OneMinusSrcAlpha)
        // this is necessary to handle transparency in physically correct way - only diffuse component gets affected by alpha
        half outputAlpha;
        s.Albedo = PreMultiplyAlpha (s.Albedo, s.Alpha, oneMinusReflectivity, /*out*/ outputAlpha);
    
        half4 c = UNITY_BRDF_PBS (s.Albedo, specColor, oneMinusReflectivity, s.Smoothness, s.Normal, viewDir, gi.light, gi.indirect);
        c.a = outputAlpha;
        return c;
    }
    
    inline half4 LightingStandard_Deferred (SurfaceOutputStandard s, float3 viewDir, UnityGI gi, out half4 outGBuffer0, out half4 outGBuffer1, out half4 outGBuffer2)
    {
        half oneMinusReflectivity;
        half3 specColor;
        s.Albedo = DiffuseAndSpecularFromMetallic (s.Albedo, s.Metallic, /*out*/ specColor, /*out*/ oneMinusReflectivity);
    
        half4 c = UNITY_BRDF_PBS (s.Albedo, specColor, oneMinusReflectivity, s.Smoothness, s.Normal, viewDir, gi.light, gi.indirect);
    
        UnityStandardData data;
        data.diffuseColor   = s.Albedo;
        data.occlusion      = s.Occlusion;
        data.specularColor  = specColor;
        data.smoothness     = s.Smoothness;
        data.normalWorld    = s.Normal;
    
        UnityStandardDataToGbuffer(data, outGBuffer0, outGBuffer1, outGBuffer2);
    
        half4 emission = half4(s.Emission + c.rgb, 1);
        return emission;
    }
    
    inline void LightingStandard_GI (
        SurfaceOutputStandard s,
        UnityGIInput data,
        inout UnityGI gi)
    {
    #if defined(UNITY_PASS_DEFERRED) && UNITY_ENABLE_REFLECTION_BUFFERS
        gi = UnityGlobalIllumination(data, s.Occlusion, s.Normal);
    #else
        Unity_GlossyEnvironmentData g = UnityGlossyEnvironmentSetup(s.Smoothness, data.worldViewDir, s.Normal, lerp(unity_ColorSpaceDielectricSpec.rgb, s.Albedo, s.Metallic));
        gi = UnityGlobalIllumination(data, s.Occlusion, s.Normal, g);
    #endif
    }
    
    //-------------------------------------------------------------------------------------
    // Specular workflow
    
    struct SurfaceOutputStandardSpecular
    {
        fixed3 Albedo;      // diffuse color
        fixed3 Specular;    // specular color
        float3 Normal;      // tangent space normal, if written
        half3 Emission;
        half Smoothness;    // 0=rough, 1=smooth
        half Occlusion;     // occlusion (default 1)
        fixed Alpha;        // alpha for transparencies
    };
    
    inline half4 LightingStandardSpecular (SurfaceOutputStandardSpecular s, float3 viewDir, UnityGI gi)
    {
        s.Normal = normalize(s.Normal);
    
        // energy conservation
        half oneMinusReflectivity;
        s.Albedo = EnergyConservationBetweenDiffuseAndSpecular (s.Albedo, s.Specular, /*out*/ oneMinusReflectivity);
    
        // shader relies on pre-multiply alpha-blend (_SrcBlend = One, _DstBlend = OneMinusSrcAlpha)
        // this is necessary to handle transparency in physically correct way - only diffuse component gets affected by alpha
        half outputAlpha;
        s.Albedo = PreMultiplyAlpha (s.Albedo, s.Alpha, oneMinusReflectivity, /*out*/ outputAlpha);
    
        half4 c = UNITY_BRDF_PBS (s.Albedo, s.Specular, oneMinusReflectivity, s.Smoothness, s.Normal, viewDir, gi.light, gi.indirect);
        c.a = outputAlpha;
        return c;
    }
    
    inline half4 LightingStandardSpecular_Deferred (SurfaceOutputStandardSpecular s, float3 viewDir, UnityGI gi, out half4 outGBuffer0, out half4 outGBuffer1, out half4 outGBuffer2)
    {
        // energy conservation
        half oneMinusReflectivity;
        s.Albedo = EnergyConservationBetweenDiffuseAndSpecular (s.Albedo, s.Specular, /*out*/ oneMinusReflectivity);
    
        half4 c = UNITY_BRDF_PBS (s.Albedo, s.Specular, oneMinusReflectivity, s.Smoothness, s.Normal, viewDir, gi.light, gi.indirect);
    
        UnityStandardData data;
        data.diffuseColor   = s.Albedo;
        data.occlusion      = s.Occlusion;
        data.specularColor  = s.Specular;
        data.smoothness     = s.Smoothness;
        data.normalWorld    = s.Normal;
    
        UnityStandardDataToGbuffer(data, outGBuffer0, outGBuffer1, outGBuffer2);
    
        half4 emission = half4(s.Emission + c.rgb, 1);
        return emission;
    }
    
    inline void LightingStandardSpecular_GI (
        SurfaceOutputStandardSpecular s,
        UnityGIInput data,
        inout UnityGI gi)
    {
    #if defined(UNITY_PASS_DEFERRED) && UNITY_ENABLE_REFLECTION_BUFFERS
        gi = UnityGlobalIllumination(data, s.Occlusion, s.Normal);
    #else
        Unity_GlossyEnvironmentData g = UnityGlossyEnvironmentSetup(s.Smoothness, data.worldViewDir, s.Normal, s.Specular);
        gi = UnityGlobalIllumination(data, s.Occlusion, s.Normal, g);
    #endif
    }
    
    #endif // UNITY_PBS_LIGHTING_INCLUDED