// ENBSeries effect file
// visit http://enbdev.com for updates
//==================================================================
// Sharpen + ColorCorrection + White Balance + Film Grain + Vignette
//
// Поддержка именно effect.txt для мультипроходных эффектов.
// ENB ALight
// Copyright (c) 2007-2017 Boris Vorontsov
// Copyright (c) 2019-2026 Anton Vorontsov
//
//==================================================================

bool bEnableColorCorr < string UIName="Enable Color Correction"; > = {true};
float fTemp < string UIName="CC: Temperature (Cold-Hot)"; string UIWidget="Spinner"; float UIMin=-1.0; float UIMax=1.0; > = {0.2};
float4 ScreenSize,Timer; // x = время в мс, w = время кадра
bool bEnableGrain < string UIName="Enable Film Grain"; > = {true};
float fGrainStr < string UIName="Grain: Intensity"; string UIWidget="Spinner"; float UIMin=0.0; float UIMax=0.1; > = {0.05};
float sharpStr < string UIName="Sharp: strength"; string UIWidget="Spinner"; float UIMin=0.0; float UIMax=1.0; > = {0.45};
float fvignette < string UIName="Vignette: Amount"; string UIWidget="Spinner"; float UIMin=0.0; float UIMax=1.0; > = {0.37};

texture2D texColor;
sampler2D SamplerColor = sampler_state { Texture = <texColor>; MinFilter = LINEAR; MagFilter = LINEAR; MipFilter = NONE; AddressU = Clamp; AddressV = Clamp; SRGBTexture = FALSE; };

struct VS_OUTPUT_POST { float4 vpos : POSITION; float2 txcoord : TEXCOORD0; };
struct VS_INPUT_POST { float3 pos : POSITION; float2 txcoord : TEXCOORD0; };

VS_OUTPUT_POST VS_PostProcess(VS_INPUT_POST IN) 
{
    VS_OUTPUT_POST OUT;
    OUT.vpos = float4(IN.pos.xyz, 1.0);
    OUT.txcoord = IN.txcoord.xy;
    return OUT;
}

// Вспомогательная функция для шума Film Grain
float GetFGNoise(float2 co) 
{
    return frac(sin(dot(co.xy ,float2(12.9898,78.233))) * 43758.5453);
}

// FXAA с настройками четкости
float4 PS_FXAA(VS_OUTPUT_POST i) : COLOR {
    #define FXAA_SUBPIX_SHIFT (1.0/4.0) // Смещение для четкости
    #define FXAA_REDUCE_MIN   (1.0/64.0)
    #define FXAA_REDUCE_MUL   (1.0/64.0)
    #define FXAA_SPAN_MAX     4.0        // Радиус поиска краев

    // Расчет шага кадра (размер пикселя)
    float2 pixelStep = float2(1.0/ScreenSize.x, 1.0/(ScreenSize.x/ScreenSize.z));

    float4 uvCoords;
    uvCoords.xy = i.txcoord.xy;
    uvCoords.zw = i.txcoord.xy - (pixelStep * (0.5 + FXAA_SUBPIX_SHIFT));

    //Выборка соседних пикселей (North-West, North-East, South-West, South-East, Middle)
    float3 rgbNW = tex2D(SamplerColor, uvCoords.zw).xyz;
    float3 rgbNE = tex2D(SamplerColor, uvCoords.zw + float2(pixelStep.x, 0.0)).xyz;
    float3 rgbSW = tex2D(SamplerColor, uvCoords.zw + float2(0.0, pixelStep.y)).xyz;
    float3 rgbSE = tex2D(SamplerColor, uvCoords.zw + pixelStep).xyz;
    float3 rgbM  = tex2D(SamplerColor, uvCoords.xy).xyz;

    //Коэффициенты яркости (Luma)
    float3 luma = float3(0.299, 0.587, 0.114);
    float lumaNW = dot(rgbNW, luma);
    float lumaNE = dot(rgbNE, luma);
    float lumaSW = dot(rgbSW, luma);
    float lumaSE = dot(rgbSE, luma);
    float lumaM  = dot(rgbM,  luma);

    float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));
    float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));

    // Определение направления поиска краев
    float2 edgeDir;
    edgeDir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));
    edgeDir.y =  ((lumaNW + lumaSW) - (lumaNE + lumaSE));

    float edgeDirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);
    float inverseDirMin = 1.0 / (min(abs(edgeDir.x), abs(edgeDir.y)) + edgeDirReduce);

    edgeDir = min(float2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(float2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), edgeDir * inverseDirMin)) * pixelStep;

    // Финальное смешивание для сглаживания
    float3 colorLowRes = 0.5 * (tex2D(SamplerColor, i.txcoord.xy + edgeDir * (1.0/3.0 - 0.5)).xyz + tex2D(SamplerColor, i.txcoord.xy + edgeDir * (2.0/3.0 - 0.5)).xyz);
    float3 colorHighRes = colorLowRes * 0.5 + 0.25 * (tex2D(SamplerColor, i.txcoord.xy + edgeDir * (0.0/3.0 - 0.5)).xyz + tex2D(SamplerColor, i.txcoord.xy + edgeDir * (3.0/3.0 - 0.5)).xyz);

    float lumaFinal = dot(colorHighRes, luma);
    if ((lumaFinal < lumaMin) || (lumaFinal > lumaMax)) return float4(colorLowRes, 1.0);

    return float4(colorHighRes, 1.0);
}

float4 PS_Vignette(VS_OUTPUT_POST IN) : COLOR 
{
    float2 coord = IN.txcoord.xy;
    float3 baseColor = tex2D(SamplerColor, coord).rgb;
    float2 pixelScale = float2(1.0/ScreenSize.x, 1.0/(ScreenSize.x/ScreenSize.z));

    // Аккуратный расчет резкости (Шарпенинг)
    float3 blurSample = tex2D(SamplerColor, coord + float2(pixelScale.x, -pixelScale.y)).rgb;
    blurSample += tex2D(SamplerColor, coord + float2(-pixelScale.x, -pixelScale.y)).rgb;
    blurSample += tex2D(SamplerColor, coord + float2(pixelScale.x, pixelScale.y)).rgb;
    blurSample += tex2D(SamplerColor, coord + float2(-pixelScale.x, pixelScale.y)).rgb;
    blurSample *= 0.25;

    float3 edgeDiff = (baseColor - blurSample) * sharpStr;
    baseColor += clamp(edgeDiff, -0.05, 0.05); 

    // 2. Цветокоррекция и Баланс белого
    if (bEnableColorCorr) 
    {
        // Температура (White Balance)
        float3 warm = float3(1.0, 0.9, 0.75); // Теплый / Оранжевый
        float3 cold = float3(0.75, 0.9, 1.0); // Холодный / Синий
        baseColor *= lerp(float3(1.0, 1.0, 1.0), (fTemp > 0 ? warm : cold), abs(fTemp));
    }

    // 3. Film Grain
    if (bEnableGrain) 
    {
        float noise = GetFGNoise(coord + Timer.x);
        // Зерно сильнее в тенях и средних тонах, меньше в ярком свету
        float grainMask = 1.0 - dot(baseColor, 0.333);
        baseColor += (noise - 0.5) * fGrainStr * grainMask;
    }

    // Виньетка
    float2 vignetteUV = (coord - 0.5) * 1.05;
    float vignettePower = saturate(dot(vignetteUV, vignetteUV));
    baseColor = lerp(baseColor, 0.0, vignettePower * fvignette);

    return float4(baseColor, 1.0);
}

technique PostProcess 
{
    pass P0 
    {
        VertexShader = compile vs_3_0 VS_PostProcess();
        PixelShader = compile ps_3_0 PS_FXAA();
    }
}

technique PostProcess2 
{
    pass P0 
    {
        VertexShader = compile vs_3_0 VS_PostProcess();
        PixelShader = compile ps_3_0 PS_Vignette();
    }
}