

#include "/lib/Head/Common.inc"
#include "/lib/Head/Uniforms.inc"

// Dirty Lens 
#if DIRT_STYLE > 0
    #if DIRT_STYLE == 1
        uniform sampler2D colortex10;  // astralex
        #define DIRTY_LENS_SAMPLER colortex10
    #elif DIRT_STYLE == 2
        uniform sampler2D colortex11; // bodycam
        #define DIRTY_LENS_SAMPLER colortex11
    #elif DIRT_STYLE == 3
        uniform sampler2D colortex12; // big
        #define DIRTY_LENS_SAMPLER colortex12
    #elif DIRT_STYLE == 4
        uniform sampler2D colortex13; // blink
        #define DIRTY_LENS_SAMPLER colortex13
    #endif
#endif

//--// Internal Settings //---------------------------------------------------//

/*
	const int 	colortex0Format 			= RGBA16F;
	const int 	colortex1Format 			= RGBA16F;
	const int 	colortex2Format 			= RGBA16F;
	const int 	colortex3Format 			= RGBA16;
	const int 	colortex4Format 			= R11F_G11F_B10F;
	const int 	colortex5Format 			= RGBA16F;
	const int 	colortex6Format 			= RGB8;
	const int 	colortex7Format 			= RGB8;

	const bool	colortex0Clear				= false;
	const bool	colortex1Clear				= false;
	const bool	colortex2Clear				= false;
	const bool	colortex4Clear				= false;
	const bool  colortex5Clear				= false;
	const bool 	colortex7Clear				= true;

	const float shadowIntervalSize 			= 2.0;
	const float ambientOcclusionLevel 		= 0.05f;
	const float	sunPathRotation				= -35.0;
	const float eyeBrightnessHalflife 		= 10.0;
	const float wetnessHalflife				= 180.0;
	const float drynessHalflife				= 60.0;
	const bool 	shadowHardwareFiltering1 	= true;
*/

#ifdef PARALLAX
/*
	const int 	colortex7Format 			= RGBA8;
*/
#endif

//----------------------------------------------------------------------------//

//#define PURKINJE_SHIFT

#if !defined IS_OVERWORLD
	#undef PURKINJE_SHIFT
#endif

// ВЫБОР ТОНМАППИНГ ОПЕРАТОРА
#define TONEMAP AcademyFit // [AcademyCustom AcademyFit AgX_Minimal AgX_Full ReinhardSimple ExtendedReinhard Filmic HableFilmic HejlDawson JodieReinhard BlueShadowCustom BlueShadowIntense BlueShadowSubtle]

//#define CINEMATIC_EFFECT

#define COLOR_GRADING	1 // [0 1]
#define BRIGHTNESS 		1.0  // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0]
#define GAMMA 			1.0  // [0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0]
#define CONTRAST		0.8  // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0]
#define SATURATION 		1.0  // [0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0]
#define WHITE_BALANCE	6500 // [2500 3000 3500 4000 4100 4200 4300 4400 4500 4600 4700 4800 4900 5000 5100 5200 5300 5400 5500 5600 5700 5800 5900 6000 6100 6200 6300 6400 6500 6600 6700 6800 6900 7000 7100 7200 7300 7400 7500 7600 7700 7800 7900 8000 8100 8200 8300 8400 8500 8600 8700 8800 8900 9000 9100 9200 9300 9400 9500 9600 9700 9800 9900 10000 10100 10200 10300 10400 10500 10600 10700 10800 10900 11000 11100 11200 11300 11400 11500 11600 11700 11800 11900 12000]

//#define VIGNETTE_ENABLED
#define VIGNETTE_STRENGTH 1.0 // [0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.5 3.0 3.5 4.0 5.0]

// ==== НАСТРОЙКИ МЯГКОГО БЛУМА (SEUS PTGI STYLE) ====
//#define SOFT_BLOOM_ENABLED
#define SOFT_BLOOM_QUALITY 3      // [1 2 3 4 5]
#define SOFT_BLOOM_AMOUNT 0.5     // [0.0 0.25 0.5 0.75 1.0]
#define SOFT_BLOOM_RADIUS 1.0     // [0.5 0.75 1.0 1.25 1.5]

// ==== НАСТРОЙКИ BLUE SHADOW  ====
#define BLUE_SHADOW_STRENGTH 0.35  // [0.0 0.1 0.2 0.3 0.35 0.4 0.5 0.6 0.7 0.8 0.9 1.0]
#define BLUE_SHADOW_HUE_R 0.4      // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0]
#define BLUE_SHADOW_HUE_G 0.5      // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0]
#define BLUE_SHADOW_HUE_B 0.9      // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0]
#define BLUE_SHADOW_RANGE 0.3      // [0.0 0.1 0.2 0.3 0.4 0.5]

//#define DEBUG_COUNTER

//----------------------------------------------------------------------------//

out vec3 sceneColor;

/* DRAWBUFFERS:3 */

in vec2 screenCoord;

//----// FUNCTIONS //-----------------------------------------------------------------------------//

float ScreenToViewSpace(in float depth) {
    depth = depth * 2.0 - 1.0;
    return 1.0 / (depth * gbufferProjectionInverse[2][3] + gbufferProjectionInverse[3][3]);
}

// ====================================================================
// УЛУЧШЕННАЯ СИСТЕМА БЛУМА (COD:AW STYLE)
// ====================================================================

// Бикубическая интерполяция для качественного апсемплинга
vec4 BicubicTexture(sampler2D tex, vec2 coord, vec2 texSize) {
    vec2 texPixelSize = 1.0 / texSize;
    coord = coord * texSize - 0.5;

    vec2 p = floor(coord);
    vec2 f = coord - p;

    vec2 ff = f * f;
    vec4 w0, w1;
    
    w0.xz = 1.0 - f;
    w0.xz *= w0.xz * w0.xz;
    w1.yw = ff * f;
    w1.xz = 3.0 * w1.yw + 4.0 - 6.0 * ff;
    w0.yw = 6.0 - w1.xz - w1.yw - w0.xz;

    vec4 s = w0 + w1;
    vec4 c = p.xxyy + vec2(-0.5, 1.5).xyxy + w1 / s;
    c *= texPixelSize.xxyy;

    vec2 m = s.xz / (s.xz + s.yw);
    
    return mix(
        mix(textureLod(tex, c.yw, 0.0), textureLod(tex, c.xw, 0.0), m.x),
        mix(textureLod(tex, c.yz, 0.0), textureLod(tex, c.xz, 0.0), m.x),
        m.y
    );
}

// Вычисление смещения тайлов для каждого LOD уровня
vec2 CalculateTileOffset(int lod) {
	vec2 lodMult = floor(lod * 0.5 + vec2(0.0, 0.5));
	vec2 offset = vec2(1.0 / 3.0, 2.0 / 3.0) * (1.0 - exp2(-2.0 * lodMult));
	return lodMult * 16.0 * screenPixelSize + offset;
}

// Апсемплинг для одного LOD уровня
vec3 BloomUpSample(in sampler2D tex, in int lod, in vec2 coord) {
    float scale = exp2(-lod);
    vec2 sampleCoord = coord * scale + CalculateTileOffset(lod - 1);
    return BicubicTexture(tex, sampleCoord, screenSize).rgb;
}

// ====================================================================
// ГЛАВНАЯ ФУНКЦИЯ БЛУМА
// ====================================================================

void CalculateBloomFog(inout vec3 color, in ivec2 texel) {
	vec3 bloomData = vec3(0.0);
	vec3 fogBloom = vec3(0.0);

	// Поддержка дождя (как в референсном шейдере)
	#if defined IS_OVERWORLD
		float rainAlpha = clamp(texelFetch(colortex0, texel, 0).a * wetness, 0.0, 1.0);
	#else
		const float rainAlpha = 0.0;
	#endif

	// ================================================================
	// АПСЕМПЛИНГ ВСЕХ LOD УРОВНЕЙ С АДАПТИВНЫМИ ВЕСАМИ
	// ================================================================
	
	// LOD 1
	vec3 sampleTile = BloomUpSample(colortex4, 1, screenCoord);
	bloomData += sampleTile;
	fogBloom += sampleTile;

	// LOD 2 - адаптивный вес в зависимости от дождя
	sampleTile = BloomUpSample(colortex4, 2, screenCoord);
	bloomData += sampleTile * mix(1.0, 0.83333333, rainAlpha);
	fogBloom += sampleTile * 1.5;

	// LOD 3
	sampleTile = BloomUpSample(colortex4, 3, screenCoord);
	bloomData += sampleTile * mix(1.0, 0.69444444, rainAlpha);
	fogBloom += sampleTile * 2.25;

	// LOD 4
	sampleTile = BloomUpSample(colortex4, 4, screenCoord);
	bloomData += sampleTile * mix(1.0, 0.57870370, rainAlpha);
	fogBloom += sampleTile * 3.375;

	// LOD 5
	sampleTile = BloomUpSample(colortex4, 5, screenCoord);
	bloomData += sampleTile * mix(1.0, 0.48225309, rainAlpha);
	fogBloom += sampleTile * 5.0625;

	// LOD 6
	sampleTile = BloomUpSample(colortex4, 6, screenCoord);
	bloomData += sampleTile * mix(1.0, 0.40187757, rainAlpha);
	fogBloom += sampleTile * 7.59375;

	// LOD 7
	sampleTile = BloomUpSample(colortex4, 7, screenCoord);
	bloomData += sampleTile * mix(1.0, 0.33489798, rainAlpha);
	fogBloom += sampleTile * 11.328125;

	// Нормализация весов (адаптивная)
	bloomData *= mix(0.23118661, 1.4 / 7.0, rainAlpha);
	fogBloom *= 0.03108305;

	// ================================================================
	// МЯГКИЙ БЛУМ (SEUS PTGI STYLE) - ОПЦИОНАЛЬНО
	// ================================================================
	
	#ifdef SOFT_BLOOM_ENABLED
		vec3 softBloom = vec3(0.0);
		float totalWeight = 0.0;
		
		#if SOFT_BLOOM_QUALITY >= 1
			float weight1 = 1.0;
			softBloom += BloomUpSample(colortex4, 2, screenCoord) * weight1;
			totalWeight += weight1;
		#endif
		
		#if SOFT_BLOOM_QUALITY >= 2
			float weight2 = 1.2;
			softBloom += BloomUpSample(colortex4, 3, screenCoord) * weight2;
			totalWeight += weight2;
		#endif
		
		#if SOFT_BLOOM_QUALITY >= 3
			float weight3 = 1.5;
			softBloom += BloomUpSample(colortex4, 4, screenCoord) * weight3;
			totalWeight += weight3;
		#endif
		
		#if SOFT_BLOOM_QUALITY >= 4
			float weight4 = 1.8;
			softBloom += BloomUpSample(colortex4, 5, screenCoord) * weight4;
			totalWeight += weight4;
		#endif
		
		#if SOFT_BLOOM_QUALITY >= 5
			float weight5 = 2.0;
			softBloom += BloomUpSample(colortex4, 6, screenCoord) * weight5;
			totalWeight += weight5;
		#endif
		
		softBloom /= totalWeight;
		softBloom *= SOFT_BLOOM_AMOUNT * (SOFT_BLOOM_RADIUS * 0.5);
		
		// Смешиваем с основным блумом
		bloomData = mix(bloomData, softBloom, 0.7);
		bloomData += softBloom * 0.3;
	#endif

	// ================================================================
	// DIRTY LENS (ГРЯЗНЫЕ ЛИНЗЫ) - ОПЦИОНАЛЬНО
	// ================================================================
	
	#if DIRT_STYLE > 0
		float newAspectRatio = 1.777777777777778 / aspectRatio;
		vec2 scale = vec2(max(newAspectRatio, 1.0), max(1.0 / newAspectRatio, 1.0));
		vec2 dirtCoord = (screenCoord - 0.5) / scale + 0.5;

		float dirt = texture2D(DIRTY_LENS_SAMPLER, dirtCoord).r;

		float brightness = length(bloomData / (1.0 + bloomData));
		dirt *= brightness * DIRTY_LENS_STRENGTH;

		#ifdef DIRTY_LENS_BLOOM_BOOST
			bloomData *= (dirt * DIRTY_LENS_BLOOM_STRENGTH + 1.0);
			fogBloom *= (dirt * (DIRTY_LENS_BLOOM_STRENGTH * 0.5) + 1.0);
		#else
			bloomData *= (dirt * 16.0 + 1.0);
			fogBloom *= (dirt * 8.0 + 1.0);
		#endif
	#endif

	fogBloom += bloomData;

	// ================================================================
	// СМЕШИВАНИЕ С ТУМАННЫМ БЛУМОМ
	// ================================================================
	
	#ifdef BLOOMY_FOG
		float fogTransmittance = texelFetch(colortex6, texel, 0).x;
		color = mix(fogBloom * 0.5, color, fogTransmittance);
	#endif

	// ================================================================
	// ПРИМЕНЕНИЕ БЛУМА С АДАПТАЦИЕЙ ПОД ЭКСПОЗИЦИЮ
	// ================================================================
	
	float bloomAmount = BLOOM_AMOUNT * 0.15;

	float exposure = texelFetch(colortex5, ivec2(0), 0).a;
	bloomAmount /= fma(max(exposure, 1.0), 0.7, 0.3);

	color += bloomData * bloomAmount;
	
	// ================================================================
	// ДОПОЛНИТЕЛЬНЫЙ БЛУМ ДЛЯ ДОЖДЯ/СНЕГА
	// ================================================================
	
	#if !defined IS_NETHER
		if (isEyeInWater == 0 && wetness > 1e-2) {
			float rain = texelFetch(colortex0, texel, 0).b * 0.35;
			fogBloom *= 1.0 + weatherSnowySmooth * 2.0;
			color = color * (1.0 - rain) + fogBloom * fma(clamp(exposure, 0.6, 2.0), 0.15, 0.3) * rain;
		}
	#endif
}

// ====================================================================
// ОСТАЛЬНЫЕ ФУНКЦИИ
// ====================================================================

const mat3 rgbToXyz = mat3(
	vec3(0.4124564, 0.3575761, 0.1804375),
	vec3(0.2126729, 0.7151522, 0.0721750),
	vec3(0.0193339, 0.1191920, 0.9503041)
);

const mat3 xyzToRgb = mat3(
	vec3(3.2409699419, 	-1.5373831776, -0.4986107603),
	vec3(-0.9692436363,  1.8759675015,  0.0415550574),
	vec3(0.0556300797, 	-0.2039769589,  1.0569715142)
);

#ifdef PURKINJE_SHIFT
	vec3 PurkinjeShift(in vec3 color) {
		const vec3 rodResponse = vec3(7.15e-5, 4.81e-1, 3.28e-1);
		vec3 xyz = color * rgbToXyz;

		vec3 scotopicLuminance = max0(xyz * (1.33 * (1.0 + (xyz.y + xyz.z) / xyz.x) - 1.68));

		float purkinje = dot(rodResponse, scotopicLuminance * xyzToRgb);
		return mix(color, purkinje * vec3(0.5, 0.7, 1.0), expf(-purkinje * 90.0));
	}
#endif

#if defined COLOR_GRADING && WHITE_BALANCE != 6500
	mat3 ChromaticAdaptationMatrix(vec3 srcXyz, vec3 dstXyz) {
		const mat3 bradfordConeResponse = mat3(
			0.89510, -0.75020,  0.03890,
			0.26640,  1.71350, -0.06850,
			-0.16140,  0.03670,  1.02960
		);

		vec3 srcLms = srcXyz * bradfordConeResponse;
		vec3 dstLms = dstXyz * bradfordConeResponse;
		vec3 quotient = dstLms / srcLms;

		mat3 vonKries = mat3(
			quotient.x, 0.0, 0.0,
			0.0, quotient.y, 0.0,
			0.0, 0.0, quotient.z
		);

		return (bradfordConeResponse * vonKries) * inverse(bradfordConeResponse);
	}

	mat3 WhiteBalanceMatrix() {
		vec3 srcXyz = Blackbody(float(WHITE_BALANCE)) * rgbToXyz;
		vec3 dstXyz = Blackbody(6500.0) 			  * rgbToXyz;

		return rgbToXyz * ChromaticAdaptationMatrix(srcXyz, dstXyz) * xyzToRgb;
	}
#endif

vec3 Contrast(in vec3 color) {
	const float logMidpoint = log2(0.16);
	color = log2(color + 1e-6) - logMidpoint;
	return max0(exp2(color * CONTRAST + logMidpoint) - 1e-6);
}

#include "/lib/Post/ACES.glsl"
#include "/lib/Post/AgX.glsl"

// ====================================================================
// НОВЫЕ ТОНМАППИНГ ОПЕРАТОРЫ
// ====================================================================

#if TONEMAP == ReinhardSimple
vec3 ReinhardSimple(in vec3 color) {
    color *= 1.2;
    vec3 result = color / (1.0 + color);
    
    #ifdef COLOR_GRADING
		result = Contrast(result * BRIGHTNESS);
		result = pow(result, vec3(rcp(GAMMA)));
		result = ColorSaturation(result, SATURATION);
		#if WHITE_BALANCE != 6500
			result *= WhiteBalanceMatrix();
		#endif
	#endif
    
    return LinearToSRGB(saturate(result));
}
#endif

#if TONEMAP == ExtendedReinhard
vec3 ExtendedReinhard(in vec3 color) {
    color *= 1.15;
    
    float luminance = GetLuminance(color);
    float whitePoint = 2.5;
    float numerator = luminance * (1.0 + (luminance / (whitePoint * whitePoint)));
    float denominator = 1.0 + luminance;
    
    vec3 result = color * (numerator / denominator);
    
    #ifdef COLOR_GRADING
		result = Contrast(result * BRIGHTNESS);
		result = pow(result, vec3(rcp(GAMMA)));
		result = ColorSaturation(result, SATURATION);
		#if WHITE_BALANCE != 6500
			result *= WhiteBalanceMatrix();
		#endif
	#endif
    
    return LinearToSRGB(saturate(result));
}
#endif

#if TONEMAP == Filmic
vec3 Filmic(in vec3 color) {
    color *= 1.5;
    
    const float A = 0.22;
    const float B = 0.30;
    const float C = 0.10;
    const float D = 0.20;
    const float E = 0.01;
    const float F = 0.30;
    
    vec3 result = ((color * (A * color + C * B) + D * E) / 
                   (color * (A * color + B) + D * F)) - E / F;
    
    #ifdef COLOR_GRADING
		result = Contrast(result * BRIGHTNESS);
		result = pow(result, vec3(rcp(GAMMA)));
		result = ColorSaturation(result, SATURATION);
		#if WHITE_BALANCE != 6500
			result *= WhiteBalanceMatrix();
		#endif
	#endif
    
    return LinearToSRGB(saturate(result));
}
#endif

#if TONEMAP == HableFilmic
vec3 HableFilmic(in vec3 color) {
    color *= 2.0;
    
    const float A = 0.15;
    const float B = 0.50;
    const float C = 0.10;
    const float D = 0.20;
    const float E = 0.02;
    const float F = 0.30;
    
    vec3 x = color;
    vec3 curr = ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F;
    
    float W = 11.2;
    vec3 whiteScale = 1.0 / (((vec3(W) * (A * W + C * B) + D * E) / 
                              (vec3(W) * (A * W + B) + D * F)) - E / F);
    
    vec3 result = curr * whiteScale;
    
    #ifdef COLOR_GRADING
		result = Contrast(result * BRIGHTNESS);
		result = pow(result, vec3(rcp(GAMMA)));
		result = ColorSaturation(result, SATURATION);
		#if WHITE_BALANCE != 6500
			result *= WhiteBalanceMatrix();
		#endif
	#endif
    
    return LinearToSRGB(saturate(result));
}
#endif

#if TONEMAP == HejlDawson
vec3 HejlDawson(in vec3 color) {
    color *= 1.3;
    
    vec3 x = max(vec3(0.0), color - 0.004);
    vec3 result = (x * (6.2 * x + 0.5)) / (x * (6.2 * x + 1.7) + 0.06);
    
    #ifdef COLOR_GRADING
		result = Contrast(result * BRIGHTNESS);
		result = pow(result, vec3(rcp(GAMMA)));
		result = ColorSaturation(result, SATURATION);
		#if WHITE_BALANCE != 6500
			result *= WhiteBalanceMatrix();
		#endif
	#endif
    
    return saturate(result);
}
#endif

#if TONEMAP == JodieReinhard
vec3 JodieReinhard(in vec3 color) {
    color *= 1.1;
    
    float luminance = GetLuminance(color);
    
    vec3 tc = color / (1.0 + color);
    float tl = luminance / (1.0 + luminance);
    
    vec3 result = mix(color / (1.0 + luminance), tc, tc);
    
    #ifdef COLOR_GRADING
		result = Contrast(result * BRIGHTNESS);
		result = pow(result, vec3(rcp(GAMMA)));
		result = ColorSaturation(result, SATURATION);
		#if WHITE_BALANCE != 6500
			result *= WhiteBalanceMatrix();
		#endif
	#endif
    
    return LinearToSRGB(saturate(result));
}
#endif

#if TONEMAP == BlueShadowCustom
vec3 BlueShadowCustom(in vec3 color) {
    color *= 1.15;
    
    float luminance = GetLuminance(color);
    
    float shadowMask = 1.0 - smoothstep(0.0, BLUE_SHADOW_RANGE, luminance);
    shadowMask = pow(shadowMask, 2.2);
    
    vec3 blueTint = vec3(BLUE_SHADOW_HUE_R, BLUE_SHADOW_HUE_G, BLUE_SHADOW_HUE_B);
    
    vec3 colorWithBlueShadows = mix(color, color * blueTint, shadowMask * BLUE_SHADOW_STRENGTH);
    
    vec3 result = colorWithBlueShadows / (1.0 + colorWithBlueShadows);
    
    float deepShadowMask = pow(shadowMask, 3.0);
    result = mix(result, result * vec3(0.5, 0.6, 1.2), deepShadowMask * 0.2);
    
    #ifdef COLOR_GRADING
		result = Contrast(result * BRIGHTNESS);
		result = pow(result, vec3(rcp(GAMMA)));
		result = ColorSaturation(result, SATURATION);
		#if WHITE_BALANCE != 6500
			result *= WhiteBalanceMatrix();
		#endif
	#endif
    
    return LinearToSRGB(saturate(result));
}
#endif

#if TONEMAP == BlueShadowIntense
vec3 BlueShadowIntense(in vec3 color) {
    color *= 1.15;
    
    float luminance = GetLuminance(color);
    float shadowMask = 1.0 - smoothstep(0.0, 0.35, luminance);
    shadowMask = pow(shadowMask, 1.8);
    
    vec3 blueTint = vec3(0.3, 0.45, 1.1);
    float tintStrength = 0.5;
    
    vec3 colorWithBlueShadows = mix(color, color * blueTint, shadowMask * tintStrength);
    vec3 result = colorWithBlueShadows / (1.0 + colorWithBlueShadows);
    
    float deepShadowMask = pow(shadowMask, 2.5);
    result = mix(result, result * vec3(0.4, 0.55, 1.3), deepShadowMask * 0.3);
    
    #ifdef COLOR_GRADING
		result = Contrast(result * BRIGHTNESS);
		result = pow(result, vec3(rcp(GAMMA)));
		result = ColorSaturation(result, SATURATION);
		#if WHITE_BALANCE != 6500
			result *= WhiteBalanceMatrix();
		#endif
	#endif
    
    return LinearToSRGB(saturate(result));
}
#endif

#if TONEMAP == BlueShadowSubtle
vec3 BlueShadowSubtle(in vec3 color) {
    color *= 1.15;
    
    float luminance = GetLuminance(color);
    float shadowMask = 1.0 - smoothstep(0.0, 0.25, luminance);
    shadowMask = pow(shadowMask, 2.8);
    
    vec3 blueTint = vec3(0.55, 0.65, 0.95);
    float tintStrength = 0.2;
    
    vec3 colorWithBlueShadows = mix(color, color * blueTint, shadowMask * tintStrength);
    vec3 result = colorWithBlueShadows / (1.0 + colorWithBlueShadows);
    
    #ifdef COLOR_GRADING
		result = Contrast(result * BRIGHTNESS);
		result = pow(result, vec3(rcp(GAMMA)));
		result = ColorSaturation(result, SATURATION);
		#if WHITE_BALANCE != 6500
			result *= WhiteBalanceMatrix();
		#endif
	#endif
    
    return LinearToSRGB(saturate(result));
}
#endif

#ifdef DEBUG_COUNTER
	#include "/lib/Post/PrintFloat.glsl"
#endif

//----// MAIN //----------------------------------------------------------------------------------//
void main() {
	ivec2 texel = ivec2(gl_FragCoord.xy);
	#ifdef MOTION_BLUR
		vec3 color = texelFetch(colortex2, texel, 0).rgb;
	#else
		vec3 color = texelFetch(colortex5, texel, 0).rgb;
	#endif

	#ifdef BLOOM_ENABLED
		CalculateBloomFog(color, texel);
	#endif

	#ifdef PURKINJE_SHIFT
		color = PurkinjeShift(color);
	#endif

	color *= texelFetch(colortex5, ivec2(0), 0).a; // Exposure

	#ifdef VIGNETTE_ENABLED
		color *= expf(-2.0 * dotSelf(screenCoord - 0.5) * VIGNETTE_STRENGTH);
	#endif

	color = TONEMAP(color);

	#ifdef CINEMATIC_EFFECT
		color *= step(abs(screenCoord.y - 0.5) * 2.0, aspectRatio * (9.0 / 21.0)); // 21:9
	#endif

	#ifdef DEBUG_COUNTER
		const float scale = 5.0, size = 1.0 / scale;
		vec2 tCoord = gl_FragCoord.xy * size;

		if (clamp(tCoord, vec2(0.0, 25.0), vec2(40.0, 50.0)) == tCoord) {
			color = min(color * 0.5, 0.8);
		}

		color += PrintFloat(shadowProjection[0].x, vec2(10.0, 35.0) * scale, size);
	#endif

	sceneColor = saturate(color);
}
