Shader "Hidden/Post FX/Lut Generator"
{
    CGINCLUDE

        #pragma target 3.0
        #pragma multi_compile __ TONEMAPPING_NEUTRAL TONEMAPPING_FILMIC

        #include "UnityCG.cginc"
        #include "ACES.cginc"
        #include "Common.cginc"
        #include "ColorGrading.cginc"
        #include "Tonemapping.cginc"

        half3 _Balance;

        half3 _Lift;
        half3 _InvGamma;
        half3 _Gain;

        half3 _Offset;
        half3 _Power;
        half3 _Slope;

        half _HueShift;
        half _Saturation;
        half _Contrast;

        half3 _ChannelMixerRed;
        half3 _ChannelMixerGreen;
        half3 _ChannelMixerBlue;

        half4 _NeutralTonemapperParams1;
        half4 _NeutralTonemapperParams2;

        sampler2D _Curves;

        half4 _LutParams;

        half3 ColorGrade(half3 color)
        {
            half3 aces = unity_to_ACES(color);

            // ACEScc (log) space
            half3 acescc = ACES_to_ACEScc(aces);

            acescc = OffsetPowerSlope(acescc, _Offset, _Power, _Slope);

            half2 hs = RgbToHsv(acescc).xy;
            half satMultiplier = SecondaryHueSat(hs.x, _Curves);
            satMultiplier *= SecondarySatSat(hs.y, _Curves);
            satMultiplier *= SecondaryLumSat(AcesLuminance(acescc), _Curves);

            acescc = Saturation(acescc, _Saturation * satMultiplier);
            acescc = ContrastLog(acescc, _Contrast);

            aces = ACEScc_to_ACES(acescc);

            // ACEScg (linear) space
            half3 acescg = ACES_to_ACEScg(aces);

            acescg = WhiteBalance(acescg, _Balance);
            acescg = LiftGammaGain(acescg, _Lift, _InvGamma, _Gain);

            half3 hsv = RgbToHsv(max(acescg, 0.0));
            hsv.x = SecondaryHueHue(hsv.x + _HueShift, _Curves);
            acescg = HsvToRgb(hsv);

            acescg = ChannelMixer(acescg, _ChannelMixerRed, _ChannelMixerGreen, _ChannelMixerBlue);

        #if TONEMAPPING_FILMIC

            aces = ACEScg_to_ACES(acescg);
            color = FilmicTonemap(aces);

        #elif TONEMAPPING_NEUTRAL

            color = ACEScg_to_unity(acescg);
            color = NeutralTonemap(color, _NeutralTonemapperParams1, _NeutralTonemapperParams2);

        #else

            color = ACEScg_to_unity(acescg);

        #endif

            // YRGB curves (done in linear/LDR for now)
            color = YrgbCurve(color, _Curves);

            return color;
        }

        half4 FragCreateLut(VaryingsDefault i) : SV_Target
        {
            // 2D strip lut
            half2 uv = i.uv - _LutParams.yz;
            half3 color;
            color.r = frac(uv.x * _LutParams.x);
            color.b = uv.x - color.r / _LutParams.x;
            color.g = uv.y;

            // Lut is in LogC
            half3 colorLogC = color * _LutParams.w;

            // Switch back to unity linear and color grade
            half3 colorLinear = LogCToLinear(colorLogC);
            half3 graded = ColorGrade(colorLinear);

            return half4(graded, 1.0);
        }

    ENDCG

    SubShader
    {
        Cull Off ZWrite Off ZTest Always

        // (0)
        Pass
        {
            CGPROGRAM

                #pragma vertex VertDefault
                #pragma fragment FragCreateLut

            ENDCG
        }
    }
}
