Shader "Hidden/Post FX/Eye Adaptation" 
 | 
{ 
 | 
    Properties 
 | 
    { 
 | 
        _MainTex("Texture", 2D) = "white" {} 
 | 
    } 
 | 
  
 | 
    CGINCLUDE 
 | 
  
 | 
        #pragma target 4.5 
 | 
        #pragma multi_compile __ AUTO_KEY_VALUE 
 | 
        #include "UnityCG.cginc" 
 | 
        #include "Common.cginc" 
 | 
        #include "EyeAdaptation.cginc" 
 | 
  
 | 
        // Eye adaptation pass 
 | 
        float4 _Params; // x: lowPercent, y: highPercent, z: minBrightness, w: maxBrightness 
 | 
        float2 _Speed; // x: down, y: up 
 | 
        float4 _ScaleOffsetRes; // x: scale, y: offset, w: histogram pass width, h: histogram pass height 
 | 
        float _ExposureCompensation; 
 | 
  
 | 
        StructuredBuffer<uint> _Histogram; 
 | 
  
 | 
        float GetBinValue(uint index, float maxHistogramValue) 
 | 
        { 
 | 
            return float(_Histogram[index]) * maxHistogramValue; 
 | 
        } 
 | 
  
 | 
        // Done in the vertex shader 
 | 
        float FindMaxHistogramValue() 
 | 
        { 
 | 
            uint maxValue = 0u; 
 | 
  
 | 
            for (uint i = 0; i < HISTOGRAM_BINS; i++) 
 | 
            { 
 | 
                uint h = _Histogram[i]; 
 | 
                maxValue = max(maxValue, h); 
 | 
            } 
 | 
  
 | 
            return float(maxValue); 
 | 
        } 
 | 
  
 | 
        void FilterLuminance(uint i, float maxHistogramValue, inout float4 filter) 
 | 
        { 
 | 
            float binValue = GetBinValue(i, maxHistogramValue); 
 | 
  
 | 
            // Filter dark areas 
 | 
            float offset = min(filter.z, binValue); 
 | 
            binValue -= offset; 
 | 
            filter.zw -= offset.xx; 
 | 
  
 | 
            // Filter highlights 
 | 
            binValue = min(filter.w, binValue); 
 | 
            filter.w -= binValue; 
 | 
  
 | 
            // Luminance at the bin 
 | 
            float luminance = GetLuminanceFromHistogramBin(float(i) / float(HISTOGRAM_BINS), _ScaleOffsetRes.xy); 
 | 
  
 | 
            filter.xy += float2(luminance * binValue, binValue); 
 | 
        } 
 | 
  
 | 
        float GetAverageLuminance(float maxHistogramValue) 
 | 
        { 
 | 
            // Sum of all bins 
 | 
            uint i; 
 | 
            float totalSum = 0.0; 
 | 
  
 | 
            UNITY_LOOP 
 | 
            for (i = 0; i < HISTOGRAM_BINS; i++) 
 | 
                totalSum += GetBinValue(i, maxHistogramValue); 
 | 
  
 | 
            // Skip darker and lighter parts of the histogram to stabilize the auto exposure 
 | 
            // x: filtered sum 
 | 
            // y: accumulator 
 | 
            // zw: fractions 
 | 
            float4 filter = float4(0.0, 0.0, totalSum * _Params.xy); 
 | 
  
 | 
            UNITY_LOOP 
 | 
            for (i = 0; i < HISTOGRAM_BINS; i++) 
 | 
                FilterLuminance(i, maxHistogramValue, filter); 
 | 
  
 | 
            // Clamp to user brightness range 
 | 
            return clamp(filter.x / max(filter.y, EPSILON), _Params.z, _Params.w); 
 | 
        } 
 | 
  
 | 
        float GetExposureMultiplier(float avgLuminance) 
 | 
        { 
 | 
            avgLuminance = max(EPSILON, avgLuminance); 
 | 
  
 | 
        #if AUTO_KEY_VALUE 
 | 
            half keyValue = 1.03 - (2.0 / (2.0 + log2(avgLuminance + 1.0))); 
 | 
        #else 
 | 
            half keyValue = _ExposureCompensation; 
 | 
        #endif 
 | 
  
 | 
            half exposure = keyValue / avgLuminance; 
 | 
  
 | 
            return exposure; 
 | 
        } 
 | 
  
 | 
        float InterpolateExposure(float newExposure, float oldExposure) 
 | 
        { 
 | 
            float delta = newExposure - oldExposure; 
 | 
            float speed = delta > 0.0 ? _Speed.x : _Speed.y; 
 | 
            float exposure = oldExposure + delta * (1.0 - exp2(-unity_DeltaTime.x * speed)); 
 | 
            //float exposure = oldExposure + delta * (unity_DeltaTime.x * speed); 
 | 
            return exposure; 
 | 
        } 
 | 
  
 | 
        float4 FragAdaptProgressive(VaryingsDefault i) : SV_Target 
 | 
        { 
 | 
            float maxValue = 1.0 / FindMaxHistogramValue(); 
 | 
            float avgLuminance = GetAverageLuminance(maxValue); 
 | 
            float exposure = GetExposureMultiplier(avgLuminance); 
 | 
            float prevExposure = tex2D(_MainTex, (0.5).xx); 
 | 
            exposure = InterpolateExposure(exposure, prevExposure); 
 | 
            return exposure.xxxx; 
 | 
        } 
 | 
  
 | 
        float4 FragAdaptFixed(VaryingsDefault i) : SV_Target 
 | 
        { 
 | 
            float maxValue = 1.0 / FindMaxHistogramValue(); 
 | 
            float avgLuminance = GetAverageLuminance(maxValue); 
 | 
            float exposure = GetExposureMultiplier(avgLuminance); 
 | 
            return exposure.xxxx; 
 | 
        } 
 | 
  
 | 
        // ---- Editor stuff 
 | 
        int _DebugWidth; 
 | 
  
 | 
        struct VaryingsEditorHisto 
 | 
        { 
 | 
            float4 pos : SV_POSITION; 
 | 
            float2 uv : TEXCOORD0; 
 | 
            float maxValue : TEXCOORD1; 
 | 
            float avgLuminance : TEXCOORD2; 
 | 
        }; 
 | 
  
 | 
        VaryingsEditorHisto VertEditorHisto(AttributesDefault v) 
 | 
        { 
 | 
            VaryingsEditorHisto o; 
 | 
            o.pos = UnityObjectToClipPos(v.vertex); 
 | 
            o.uv = v.texcoord.xy; 
 | 
            o.maxValue = 1.0 / FindMaxHistogramValue(); 
 | 
            o.avgLuminance = GetAverageLuminance(o.maxValue); 
 | 
            return o; 
 | 
        } 
 | 
  
 | 
        float4 FragEditorHisto(VaryingsEditorHisto i) : SV_Target 
 | 
        { 
 | 
            const float3 kRangeColor = float3(0.05, 0.4, 0.6); 
 | 
            const float3 kAvgColor = float3(0.8, 0.3, 0.05); 
 | 
  
 | 
            float4 color = float4(0.0, 0.0, 0.0, 0.7); 
 | 
  
 | 
            uint ix = (uint)(round(i.uv.x * HISTOGRAM_BINS)); 
 | 
            float bin = saturate(float(_Histogram[ix]) * i.maxValue); 
 | 
            float fill = step(i.uv.y, bin); 
 | 
  
 | 
            // Min / max brightness markers 
 | 
            float luminanceMin = GetHistogramBinFromLuminance(_Params.z, _ScaleOffsetRes.xy); 
 | 
            float luminanceMax = GetHistogramBinFromLuminance(_Params.w, _ScaleOffsetRes.xy); 
 | 
  
 | 
            color.rgb += fill.rrr; 
 | 
  
 | 
            if (i.uv.x > luminanceMin && i.uv.x < luminanceMax) 
 | 
            { 
 | 
                color.rgb = fill.rrr * kRangeColor; 
 | 
                color.rgb += kRangeColor; 
 | 
            } 
 | 
  
 | 
            // Current average luminance marker 
 | 
            float luminanceAvg = GetHistogramBinFromLuminance(i.avgLuminance, _ScaleOffsetRes.xy); 
 | 
            float avgPx = luminanceAvg * _DebugWidth; 
 | 
  
 | 
            if (abs(i.pos.x - avgPx) < 2) 
 | 
                color.rgb = kAvgColor; 
 | 
  
 | 
            return color; 
 | 
        } 
 | 
  
 | 
    ENDCG 
 | 
  
 | 
    SubShader 
 | 
    { 
 | 
        Cull Off ZWrite Off ZTest Always 
 | 
  
 | 
        Pass 
 | 
        { 
 | 
            CGPROGRAM 
 | 
  
 | 
                #pragma vertex VertDefault 
 | 
                #pragma fragment FragAdaptProgressive 
 | 
  
 | 
            ENDCG 
 | 
        } 
 | 
  
 | 
        Pass 
 | 
        { 
 | 
            CGPROGRAM 
 | 
  
 | 
                #pragma vertex VertDefault 
 | 
                #pragma fragment FragAdaptFixed 
 | 
  
 | 
            ENDCG 
 | 
        } 
 | 
  
 | 
        Pass 
 | 
        { 
 | 
            CGPROGRAM 
 | 
  
 | 
                #pragma vertex VertEditorHisto 
 | 
                #pragma fragment FragEditorHisto 
 | 
  
 | 
            ENDCG 
 | 
        } 
 | 
    } 
 | 
} 
 |