/*===============================================================================*\
|########################   [GLSL PSX FX SHADER V1.2]    #########################|
|########################	   [BY ASMODEAN@PCSX2]	     #########################|
||																				 ||
||			  		   ,---.  .---..-.   .-.  ,---..-.   .-.					 ||
||					   | .-.\( .-._)) \_/ /   | .-' ) \_/ / 					 ||
||					   | |-' )) \  (_)   /    | `-.(_)   /  					 ||
||					   | |--'_ \ \   / _ \    | .-'  / _ \  					 ||
||					   | | ( `-'  ) / / ) \   | |   / / ) \ 					 ||
||					   /(   `----' `-' (_)-'  )\|  `-' (_)-'					 ||
||					  (__)                   (__)           					 ||
||																				 ||
||		  This program is free software; you can redistribute it and/or			 ||
||		  modify it under the terms of the GNU General Public License			 ||
||		  as published by the Free Software Foundation; either version 2		 ||
||		  of the License, or (at your option) any later version.				 ||
||																				 ||
||		  This program is distributed in the hope that it will be useful,		 ||
||		  but WITHOUT ANY WARRANTY; without even the implied warranty of		 ||
||		  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the			 ||
||		  GNU General Public License for more details.							 ||
||																				 ||
|#################################################################################|
\*===============================================================================*/
#version 430 compatibility

/*------------------------------------------------------------------------------
								[EXTENSIONS]
------------------------------------------------------------------------------*/

#extension GL_NV_gpu_shader5 : enable
#extension GL_ARB_gpu_shader5 : enable
#extension GL_NV_draw_buffers : enable
#extension GL_ARB_draw_buffers : enable
#extension GL_ARB_sample_shading : enable
#extension GL_ARB_shader_precision : enable
#extension GL_ARB_conservative_depth : enable 
#extension GL_EXT_framebuffer_object : enable
#extension GL_ARB_uniform_buffer_object : enable
#extension GL_ARB_vertex_attrib_64bit : enable
#extension GL_NV_explicit_multisample : enable
#extension GL_ARB_shader_texture_lod : enable
#extension GL_ARB_texture_multisample : enable
#extension GL_ARB_texture_rectangle : enable

precision highp int;
precision highp float;

/*------------------------------------------------------------------------------
						[DEFINITIONS & ON/OFF OPTIONS]
------------------------------------------------------------------------------*/

//---------------------------#[CHOOSE EFFECTS]#--------------------------------\\

//-#[ANTIALIASING TECHNIQUES]  [1=ON/0=OFF]  *READ: For best results, only enable one type of AA at one time.
#define NFAA_ENABLED				1		//#Normal Filter Anti Aliasing (Subtle AA, with good edge detection, no blur (3D Only)).
#define SSAA_ENABLED				0		//#Shader-Based SuperSampling, Scales well with high resolutions. (Soft SS AA (2D & 3D)).
#define FXAA_ENABLED				1		//#HQ Fast Approximate Anti Aliasing (Heavy duty AA, more blur than the others, but also better jaggy removal. (2D & 3D)).
#define SPRITE_INTERPOLATION		0		//#2DAA Interpolation Smoothing, Nice for Sprite games(2D only). ||READ:**To enable this, you also need to set it to 1 in the Vertex shader (.slv)**||

//-#[LIGHTING & COLOUR] 	   [1=ON/0=OFF]  *READ: These can all be turned on & off independently of each other. || For HDR - the luma, tonemap, and bloom passes should be used together, for best results.
#define LUMA_SHARPEN				1		//#HQ Luma-Based Sharpen, looks similar to a negative mip LOD Bias, enhances texture fidelity. 
#define HDR_GAMMA_PASS				1		//#Scene Gamma range correction pass of the HDR.
#define HDR_TONEMAP_PASS			1		//#Scene tonemapping pass of the HDR.
#define HDR_BLOOM_PASS				1 		//#Bloom high pass of the HDR.
#define DYNAMIC_CONTRAST			1		//#S-Curve Dynamic Scene Contrast Enhancement.
#define INTELLI_VIBRANCE			1		//Dynamic per pixel vibrance. It can also be used as color offset calibration, due to the HDR skewing the gamut.
#define LIGHT_ATTENUATION			1		//Light Attenuation, (lighting enhancement) doesn't work for all games, is why it's optional.
#define TOON_SHADER					0		//#A Cartoon-Style look, I'm still working on improving this, but it works fine for now.
#define SUBPIXEL_DITHERING			0		//#Subpixel Dithering, can help to reduce color banding.

//-#[TEXTURE FILTERING] 	   [1=ON/0=OFF]  *READ: The use of filters requires an AA technique to be turned on. For best results, only enable one type of Filter at one time.
#define TEXTURE_FILTER_ENABLED		1		//#<-**Don't turn this off, for now**. Enable or Disable The texture filtering from the two options below.
#define FILTER_TYPE_BICUBIC			0		//#CatMullRom BiCubic texture filter ||Choose one type of filter, don't enable both together.||
#define FILTER_TYPE_BILINEAR		0		//#BiLinear texture filter (recommended) ||Choose one type of filter, don't enable both together.||

/*------------------------------------------------------------------------------
					[SHADER FX CONFIG OPTIONS & LOCAL VARS]
------------------------------------------------------------------------------*/

#if (NFAA_ENABLED == 1)
	float NFWidth = 4096.0; 		//X res(density)
	float NFHeight = 2048.0;		//Y res(density)
	float filterStrength = 10.0;	//AA Strength 
	float filterSpread = 0.1;		//Filter Width
	
	bool debug = false;				//Shows the edge detection.
	bool useColor = false;			//Uses color detection, instead of luma edge detection (more coverage, less accuracy).
#endif

#if (SSAA_ENABLED == 1)
	float w = 1.0; //Filter Width
	float Width = 2560.0;
	float Height = 1440.0;
#endif

#if (FXAA_ENABLED == 1)
	#define FXAA_REDUCE_MIN (1.0/64.0)
	#define FXAA_REDUCE_MUL (1.0/64.0)
	#define FXAA_SPAN_MAX (2)
	#define FXAA_GATHER4_ALPHA 1
	#define FXAA_QUALITY__SUBPIX (3/4)
	#define FXAA_QUALITY__EDGE_THRESHOLD (1.0/32.0)
	#define FXAA_QUALITY__EDGE_THRESHOLD_MIN (1.0/32.0)
#endif

#if (LUMA_SHARPEN == 1)
	#define Sharp_Strength 0.34   	//[0.10 to 3.00]
	#define Sharp_Clamp 0.015  		//[0.000 to 1.000]
	#define Pattern 5 				//1 = Normal, 2 = Wider, 3 = Pyramid shaped, 4 = 9-tap guassian, 5 = HQ 9-tap texture fetch.
	#define Offset_Bias 4.0 		//Offset Bias [0.0 to 6.0]
	#define Show_Sharpen 0
#endif

#if (HDR_BLOOM_PASS == 1)
	#define BloomType BlendScreen	//The type of blend for the bloom -[BlendScreen, BlendOverlay, BlendSoftLight] (Default: BlendScreen)
	#define BloomPower 0.250f     	//[0.100 to 0.900] Strength of the bloom. blooms naturally, per environment. You will need to adjust for each bloom type.
	#define BlurSpread 1.000f    	//[0.000 to 4.000] Width of the bloom's 'glow' filtering. 0.000 = off.
#endif

#if (HDR_TONEMAP_PASS == 1)
	#define TonemappingType 0		//The type of tonemapping used. 0 = Scene tonemapping, most compatible (Default). 1 = local tonemapping, can be more accurate, but it can look odd, in games with low resolution textures.
	#define Luma 100.00f			//Luma Percent Avg Correction(luma correction) Higher values = more luma, lower = less luma. Adjust by small amounts, eg: increments of 1
	#define Exposure 100.00f		//White Percent Avg Correction (brightness correction) Higher values = more exposure, lower = less exposure. Adjust by small amounts, eg: increments of 1
	#define WhitePoint 1.00f		//Whitepoint Avg (fog correction) Adjust by small amounts, eg: increments of 0.01
	#define BlackLevel 0.013f		//black levels for (Shadow correction) Adjust by small amounts, eg: increments of 0.01
#endif

#if (HDR_GAMMA_PASS == 1)
	const float Luminance = 1.000;		 //Luma
	const float lumaFactor = 1.000;		 //Luma Scale Factor
	const float GammaCorrection = 0.90;  //Percent of Gamma for correction

	uniform float key = 1.00;
	uniform float threshold = 1.00;
	uniform float phi = 1.00;
#endif

#if (DYNAMIC_CONTRAST == 1)
	#define CurveType 0				//[0|1|2] Choose what to apply contrast to. 0 = Luma, 1 = Chroma, 2 = both Luma and Chroma. Default is 0 (Luma)
	#define CurvesContrast 0.33		//[-1.00 to 1.00] The amount of contrast you want
	#define CurvesFormula 1			//[1|2] The contrast s-curve you want to use.
#endif

#if (INTELLI_VIBRANCE == 1)
	#define Vibrance -0.02 		//[-1.00 to 1.00] Intelligently saturates (or desaturates if you use negative values) the pixels depending on their original saturation.
#endif

#if (LIGHT_ATTENUATION == 1)
	uniform float shininess = 50.0;
#endif

#if (SUBPIXEL_DITHERING == 1)
	#define DitherMethod 2  		//[1 or 2] 1 = Ordering dithering (good and very fast), 2 = Random dithering (even better dithering but not as fast)
#endif

/*------------------------------------------------------------------------------
							[GLOBAL VARIABLES]
------------------------------------------------------------------------------*/

uniform sampler2D colorSampler;
uniform sampler2D phongSampler;
uniform sampler2D LumaSampler;
uniform sampler2D ImageSampler;
uniform sampler2D AvgLumSampler;
uniform sampler2D txLogLumSampler;
uniform sampler2D txRelLumAvgSampler;
uniform samplerBuffer lumCurveSampler;

const float TextureSize = 4096;
const float texelSize = (1.0 / TextureSize);
//const ivec2 BufferSize = textureSize(colorSampler, 0);

precise vec2 texcoord = gl_TexCoord[0].st;
precise out vec4 gl_FragData[gl_MaxDrawBuffers];
vec4 texcolor = texture(colorSampler, texcoord);

/*------------------------------------------------------------------------------
                       [TEXTURE FILTER FUNCTIONS]
------------------------------------------------------------------------------*/

#if (TEXTURE_FILTER_ENABLED == 1)
uniform float fWidth = 4096.0;	 //Filter X res
uniform float fHeight = 2048.0;	 //Filter Y res

vec4 cubic(float x)
{
    float x2 = x * x;
    float x3 = x2 * x;
    vec4 w;
    w.x =   -x3 + 3*x2 - 3*x + 1;
    w.y =  3*x3 - 6*x2       + 4;
    w.z = -3*x3 + 3*x2 + 3*x + 1;
    w.w =  x3;
    return w / 6.f;
}

float CatMullRom(float x)
{
    float B = 0.0;
    float C = 0.5;
    float f = x;
	
    if(f < 0.0)
    {
        f = -f;
    }
    if(f < 1.0)
    {
        return ((12.0 - 9.0 * B - 6.0 * C) * (f * f * f) + 
		(-18.0f + 12.0f * B + 6.0f * C) * (f * f) + 
		(6.0f - 2.0f * B)) / 6.0f;
    }
    else if(f >= 1.0 && f < 2.0)
    {
        return (( -B - 6 * C) * (f * f * f) + 
		(6 * B + 30 * C) * (f *f) + 
		(-(12 * B) - 48 * C) * f + 8 * B + 24 * C)/ 6.0;
    }
    else
    {
        return 0.0;
    }
}

#if (FILTER_TYPE_BICUBIC == 1)
vec4 BiCubic(sampler2D textureSampler, vec2 TexCoord)
{
	float texelSizeX = (1.0 / fWidth); //size of one texel 
	float texelSizeY = (1.0 / fHeight); //size of one texel
	
    vec4 nSum = vec4( 0.0, 0.0, 0.0, 0.0 );
    vec4 nDenom = vec4( 0.0, 0.0, 0.0, 0.0 );
	
    float a = fract( TexCoord.x * fWidth ); // get the decimal part
    float b = fract( TexCoord.y * fHeight ); // get the decimal part

	int nX = int(TexCoord.x * fWidth);
	int nY = int(TexCoord.y * fHeight);
	
	vec2 TexCoord1 = vec2( float(nX) / fWidth + 0.5 / fWidth,
					       float(nY) / fHeight + 0.5 / fHeight );

    for(int m = -1; m <=2; m++)
    {
        for(int n =-1; n<= 2; n++)
        {
			vec4 vecData = texture(textureSampler, TexCoord1 + vec2(texelSizeX * float( m ), texelSizeY * float( n )));
			float f  = CatMullRom( float( m ) - a );
			vec4 vecCooef1 = vec4( f,f,f,f );
			float f1 = CatMullRom( -( float( n ) - b ) );
			vec4 vecCoeef2 = vec4( f1, f1, f1, f1 );
            nSum = nSum + ( vecData * vecCoeef2 * vecCooef1  );
            nDenom = nDenom + (( vecCoeef2 * vecCooef1 ));
        }
    }
    return nSum / nDenom;
}
#endif
#if (FILTER_TYPE_BILINEAR == 1)
vec4 tex2DBiLinear(sampler2D textureSampler_i, vec2 texCoord_i)
{	
	float texelSizeX = (1.0 / fWidth); //size of one texel 
	float texelSizeY = (1.0 / fHeight); //size of one texel 

	int nX = int( texCoord_i.x * fWidth );
	int nY = int( texCoord_i.y * fHeight );
	
	vec2 texCoord_New = vec2( ( float( nX ) + 0.5 ) / fWidth,
							  ( float( nY ) + 0.5 ) / fHeight );
	// Take nearest two data in current row.
    vec4 p0q0 = texture(textureSampler_i, texCoord_New);
    vec4 p1q0 = texture(textureSampler_i, texCoord_New + vec2(texelSizeX, 0));

	// Take nearest two data in bottom row.
    vec4 p0q1 = texture(textureSampler_i, texCoord_New + vec2(0, texelSizeY));
    vec4 p1q1 = texture(textureSampler_i, texCoord_New + vec2(texelSizeX , texelSizeY));

    float a = fract(texCoord_i.x * fWidth); // Get Interpolation factor for X direction.
											 // Fraction near to valid data.
	// Interpolation in X direction.
    vec4 pInterp_q0 = mix( p0q0, p1q0, a ); // Interpolates top row in X direction.
    vec4 pInterp_q1 = mix( p0q1, p1q1, a ); // Interpolates bottom row in X direction.

    float b = fract(texCoord_i.y * fHeight); // Get Interpolation factor for Y direction.
	
    return mix(pInterp_q0, pInterp_q1, b); // Interpolate in Y direction.
}
#endif
#endif

/*------------------------------------------------------------------------------
                       [COLOR CORRECTION FUNCTIONS]
------------------------------------------------------------------------------*/

#define GammaCorrection(color, gamma) pow(color, 1.0 / gamma)

float RGBLuminance(vec4 color)
{
   vec3 lumCoeff = vec3(0.212656, 0.715158, 0.072186);
   color.a = dot(color.rgb, lumCoeff);
   return color.a;
}

float BlendScreen(float base, float blend)
{
	return (1.0f - ((1.0f - base) * (1.0f - blend)));
}

vec4 ExpandRGBE(vec4 RGBE)
{
	return vec4(ldexp(RGBE.xyz, ivec3(255.0 * RGBE.w - 128.0 )), 1.0);
}

float LinearToSRGB(float c)
{
	#define EXP (1.0 / 2.4)
	#define A (0.055)
	#define B (1.0 + A)

	float v = c;
	if (v < 0.0031308)
		v = 12.92 * v;
	else
		v = B * pow (v , EXP) - A;

	return v;
}

vec4 Desaturate(vec3 color, float Desaturation)
{
	vec3 grayXfer = vec3(0.3, 0.59, 0.11);
	vec3 gray = vec3(dot(grayXfer, color));
	return vec4(mix(color, gray, Desaturation), 1.0);
}

vec3 RGBToHSL(vec3 color)
{
	vec3 hsl; // init to 0 to avoid warnings ? (and reverse if + remove first part)
	
	float fmin = min(min(color.r, color.g), color.b);    //Min. value of RGB
	float fmax = max(max(color.r, color.g), color.b);    //Max. value of RGB
	float delta = fmax - fmin;             //Delta RGB value

	hsl.z = (fmax + fmin) / 2.0; // Luminance

	if (delta == 0.0)		//This is a gray, no chroma...
	{
		hsl.x = 0.0;	// Hue
		hsl.y = 0.0;	// Saturation
	}
	else                                    //Chromatic data...
	{
		if (hsl.z < 0.5)
			hsl.y = delta / (fmax + fmin); // Saturation
		else
			hsl.y = delta / (2.0 - fmax - fmin); // Saturation
		
		float deltaR = (((fmax - color.r) / 6.0) + (delta / 2.0)) / delta;
		float deltaG = (((fmax - color.g) / 6.0) + (delta / 2.0)) / delta;
		float deltaB = (((fmax - color.b) / 6.0) + (delta / 2.0)) / delta;

		if (color.r == fmax )
			hsl.x = deltaB - deltaG; // Hue
		else if (color.g == fmax)
			hsl.x = (1.0 / 3.0) + deltaR - deltaB; // Hue
		else if (color.b == fmax)
			hsl.x = (2.0 / 3.0) + deltaG - deltaR; // Hue

		if (hsl.x < 0.0)
			hsl.x += 1.0; // Hue
		else if (hsl.x > 1.0)
			hsl.x -= 1.0; // Hue
	}

	return hsl;
}

float HueToRGB(float f1, float f2, float hue)
{
	if (hue < 0.0)
		hue += 1.0;
	else if (hue > 1.0)
		hue -= 1.0;
	float res;
	if ((6.0 * hue) < 1.0)
		res = f1 + (f2 - f1) * 6.0 * hue;
	else if ((2.0 * hue) < 1.0)
		res = f2;
	else if ((3.0 * hue) < 2.0)
		res = f1 + (f2 - f1) * ((2.0 / 3.0) - hue) * 6.0;
	else
		res = f1;
	return res;
}

vec3 HSLToRGB(vec3 hsl)
{
	vec3 rgb;
	
	if (hsl.y == 0.0)
		rgb = vec3(hsl.z); // Luminance
	else
	{
		float f2;
		
		if (hsl.z < 0.5)
			f2 = hsl.z * (1.0 + hsl.y);
		else
			f2 = (hsl.z + hsl.y) - (hsl.y * hsl.z);
			
		float f1 = 2.0 * hsl.z - f2;
		
		rgb.r = HueToRGB(f1, f2, hsl.x + (1.0/3.0));
		rgb.g = HueToRGB(f1, f2, hsl.x);
		rgb.b= HueToRGB(f1, f2, hsl.x - (1.0/3.0));
	}
	
	return rgb;
}

// For all settings: 1.0 = 100% 0.5=50% 1.5 = 150%
vec3 ContrastSaturationBrightness(vec3 color, float brt, float sat, float con)
{
	// Increase or decrease theese values to adjust r, g and b color channels seperately
	const float AvgLumR = 0.5;
	const float AvgLumG = 0.5;
	const float AvgLumB = 0.5;
	
	const vec3 LumCoeff = vec3(0.2125, 0.7154, 0.0721);
	
	vec3 AvgLumin = vec3(AvgLumR, AvgLumG, AvgLumB);
	vec3 brtColor = color * brt;
	vec3 intensity = vec3(dot(brtColor, LumCoeff));
	vec3 satColor = mix(intensity, brtColor, sat);
	vec3 conColor = mix(AvgLumin, satColor, con);
	return conColor;
}

vec3 BlendHue(vec3 base, vec3 blend)
{
	vec3 baseHSL = RGBToHSL(base);
	return HSLToRGB(vec3(RGBToHSL(blend).r, baseHSL.g, baseHSL.b));
}

vec3 BlendColor(vec3 base, vec3 blend)
{
	vec3 blendHSL = RGBToHSL(blend);
	return HSLToRGB(vec3(blendHSL.r, blendHSL.g, RGBToHSL(base).b));
}

/*------------------------------------------------------------------------------
                             [NFAA CODE SECTION]
------------------------------------------------------------------------------*/

#if (NFAA_ENABLED == 1)
precise vec2 rcpres = vec2(1.0/NFWidth, 1.0/NFHeight);

	float GetColorLuminance(vec3 i_vColor)
	{
		return dot(i_vColor, vec3(0.2126f, 0.7152f, 0.0722f));
	}

	vec2 findContrastByLuminance(vec2 XYCoord) 
	{
		// Normal offsets, scale by filter spread
		vec2 upOffset = vec2( 0, rcpres.y ) * filterSpread;
		vec2 rightOffset = vec2( rcpres.x, 0 ) * filterSpread;

		float topHeight         = GetColorLuminance( texture( colorSampler, XYCoord + upOffset              ).rgb );
		float bottomHeight      = GetColorLuminance( texture( colorSampler, XYCoord - upOffset              ).rgb );
		float rightHeight       = GetColorLuminance( texture( colorSampler, XYCoord + rightOffset           ).rgb );
		float leftHeight        = GetColorLuminance( texture( colorSampler, XYCoord - rightOffset           ).rgb );
		float leftTopHeight     = GetColorLuminance( texture( colorSampler, XYCoord - rightOffset + upOffset).rgb );
		float leftBottomHeight  = GetColorLuminance( texture( colorSampler, XYCoord - rightOffset - upOffset).rgb );
		float rightBottomHeight = GetColorLuminance( texture( colorSampler, XYCoord + rightOffset + upOffset).rgb );
		float rightTopHeight    = GetColorLuminance( texture( colorSampler, XYCoord + rightOffset - upOffset).rgb );

		// Normal map creation
		float sum0 = rightTopHeight    + bottomHeight + leftTopHeight;
		float sum1 = leftBottomHeight  + topHeight    + rightBottomHeight;
		float sum2 = leftTopHeight     + rightHeight  + leftBottomHeight;
		float sum3 = rightBottomHeight + leftHeight   + rightTopHeight;

		// Subtract the opposite sample set for final vectors
		float vector1 = (sum0 - sum1) * filterStrength;
		float vector2 = (sum3 - sum2) * filterStrength;

		vec2 Vectors = vec2(vector1, vector2);

		return Vectors;
	}

	vec2 findContrastByColor(vec2 XYCoord)
	{
		// Normal offsets, scale by filter spread
		vec2 upOffset = vec2( 0, rcpres.y ) * filterSpread;
		vec2 rightOffset = vec2( rcpres.x, 0 ) * filterSpread;

		vec3 topHeight         = texture( colorSampler, XYCoord + upOffset              ).rgb;
		vec3 bottomHeight      = texture( colorSampler, XYCoord - upOffset              ).rgb;
		vec3 rightHeight       = texture( colorSampler, XYCoord + rightOffset           ).rgb;
		vec3 leftHeight        = texture( colorSampler, XYCoord - rightOffset           ).rgb;
		vec3 leftTopHeight     = texture( colorSampler, XYCoord - rightOffset + upOffset).rgb;
		vec3 leftBottomHeight  = texture( colorSampler, XYCoord - rightOffset - upOffset).rgb;
		vec3 rightBottomHeight = texture( colorSampler, XYCoord + rightOffset + upOffset).rgb;
		vec3 rightTopHeight    = texture( colorSampler, XYCoord + rightOffset - upOffset).rgb;

		// Normal map creation
		vec3 sum0 = rightTopHeight    + bottomHeight + leftTopHeight;
		vec3 sum1 = leftBottomHeight  + topHeight    + rightBottomHeight;
		vec3 sum2 = leftTopHeight     + rightHeight  + leftBottomHeight;
		vec3 sum3 = rightBottomHeight + leftHeight   + rightTopHeight;

		// Subtract the opposite sample set for final vectors
		float vector1 = length(sum0 - sum1) * filterStrength;
		float vector2 = length(sum3 - sum2) * filterStrength;

		vec2 Vectors = vec2(vector1, vector2);

		return Vectors;
	}

	vec4 getNFAA(vec4 o_Color, vec2 Vectors) 
	{
		
		filterStrength += (filterSpread/2);
		
		if (useColor)
		{ 
			Vectors = findContrastByColor(texcoord.xy);
		}
		else
		{
			Vectors = findContrastByLuminance(texcoord.xy);
		}

		float filterClamp = filterStrength/filterSpread;

		Vectors.xy = clamp(Vectors, -vec2(filterClamp,filterClamp), vec2(filterClamp,filterClamp));

		vec2 Normal = vec2(Vectors.x, Vectors.y) * rcpres;
		
		#if (TEXTURE_FILTER_ENABLED == 1 && FILTER_TYPE_BICUBIC == 1)
			vec4 Scene0 = BiCubic(colorSampler, texcoord.xy);
			vec4 Scene1 = texture(colorSampler, texcoord.xy + Normal.xy, 0.0);
			vec4 Scene2 = texture(colorSampler, texcoord.xy - Normal.xy, 0.0);
			vec4 Scene3 = texture(colorSampler, texcoord.xy + vec2(Normal.x, -Normal.y), 0.0);
			vec4 Scene4 = texture(colorSampler, texcoord.xy - vec2(Normal.x, -Normal.y), 0.0);
		#elif (TEXTURE_FILTER_ENABLED == 1 && FILTER_TYPE_BILINEAR == 1)
			vec4 Scene0 = tex2DBiLinear(colorSampler, texcoord.xy);
			vec4 Scene1 = texture(colorSampler, texcoord.xy + Normal.xy, 0.0);
			vec4 Scene2 = texture(colorSampler, texcoord.xy - Normal.xy, 0.0);
			vec4 Scene3 = texture(colorSampler, texcoord.xy + vec2(Normal.x, -Normal.y), 0.0);
			vec4 Scene4 = texture(colorSampler, texcoord.xy - vec2(Normal.x, -Normal.y), 0.0);
		#else
			vec4 Scene0 = texture(colorSampler, texcoord.xy, 0.0);
			vec4 Scene1 = texture(colorSampler, texcoord.xy + Normal.xy, 0.0);
			vec4 Scene2 = texture(colorSampler, texcoord.xy - Normal.xy, 0.0);
			vec4 Scene3 = texture(colorSampler, texcoord.xy + vec2(Normal.x, -Normal.y), 0.0);
			vec4 Scene4 = texture(colorSampler, texcoord.xy - vec2(Normal.x, -Normal.y), 0.0);
		#endif

		// Final color
		o_Color = (Scene0 + Scene1 + Scene2 + Scene3 + Scene4) * 0.2;

		// Debug Output
		if (debug) 
		{
			o_Color .xyz = normalize(vec3(Vectors.x, Vectors.y, 1.0) * 0.5 + 0.5);
		}

		return o_Color;
	}
#endif

/*------------------------------------------------------------------------------
                             [SSAA CODE SECTION]
------------------------------------------------------------------------------*/

#if (SSAA_ENABLED == 1)
precise vec2 inverse_buffer_size = vec2(1.0/Width, 1.0/Height);

	float lumRGB(vec3 v)
	{   
		return dot(v, vec3(0.2126f, 0.7152f, 0.0722f));
	}
	
	vec4 getSSAA(vec4 oColor, vec2 txCoord) 
	{
	 
		float   t = lumRGB(texture(colorSampler, texcoord + vec2(0.0, -1.0) * w * inverse_buffer_size, 0.0).xyz),
				l = lumRGB(texture(colorSampler, texcoord + vec2(-1.0, 0.0) * w * inverse_buffer_size, 0.0).xyz),
				r = lumRGB(texture(colorSampler, texcoord + vec2(1.0, 0.0) * w * inverse_buffer_size, 0.0).xyz),
				b = lumRGB(texture(colorSampler, texcoord + vec2(0.0, 1.0) * w * inverse_buffer_size, 0.0).xyz);
	 
		vec2    n = vec2(-(t - b), r - l);

		float   nl = length(n);
		
		if	(nl < (1.0 / 16.0))
			return texture(colorSampler, texcoord, 0.0);
	 
		else
		{
			n *= inverse_buffer_size / nl;
			
			#if (TEXTURE_FILTER_ENABLED == 1 && FILTER_TYPE_BICUBIC == 1)
				vec4	o = BiCubic(colorSampler, texcoord),
						t0 = texture(colorSampler, texcoord + n * 0.5, 0.0) * 0.9,
						t1 = texture(colorSampler, texcoord - n * 0.5, 0.0) * 0.9,
						t2 = texture(colorSampler, texcoord + n, 0.0) * 0.75,
						t3 = texture(colorSampler, texcoord - n, 0.0) * 0.75; 
			#elif (TEXTURE_FILTER_ENABLED == 1 && FILTER_TYPE_BILINEAR == 1)
				vec4	o = tex2DBiLinear(colorSampler, texcoord),
						t0 = tex2DBiLinear(colorSampler, texcoord + n * 0.5) * 0.9,
						t1 = texture(colorSampler, texcoord - n * 0.5, 0.0) * 0.9,
						t2 = texture(colorSampler, texcoord + n, 0.0) * 0.75,
						t3 = texture(colorSampler, texcoord - n, 0.0) * 0.75; 
			#else
				vec4	o = texture(colorSampler, texcoord, 0.0),
						t0 = texture(colorSampler, texcoord + n * 0.5, 0.0) * 0.9,
						t1 = texture(colorSampler, texcoord - n * 0.5, 0.0) * 0.9,
						t2 = texture(colorSampler, texcoord + n, 0.0) * 0.75,
						t3 = texture(colorSampler, texcoord - n, 0.0) * 0.75; 
			#endif						
					
			oColor = (o + t0 + t1 + t2 + t3) / 4.3;
			
			return oColor;
	}
	}
#endif

/*------------------------------------------------------------------------------
                             [FXAA CODE SECTION]
------------------------------------------------------------------------------*/

#if (FXAA_ENABLED == 1)
#define FXAA_QUALITY__PS 	13
#define FXAA_QUALITY__P0 	1.0
#define FXAA_QUALITY__P1 	1.0
#define FXAA_QUALITY__P2 	1.0
#define FXAA_QUALITY__P11	1.0

precise in vec4 posPos;
uniform float rt_w = 2560.0;
uniform float rt_h = 1440.0;
#define FxaaInt2 ivec2
#define Fxaavec2 vec2
#define Fxaavec4 vec4
#define FxaaSat(x) clamp(x, 0.0, 1.0)
#define FxaaLerp(x,y,s) mix(x,y,s)
#define FxaaTexLod0(t, p) textureLod(t, p, 0.0)
#define FxaaTexOff(t, p, o, r) textureLodOffset(t, p, 0.0, o)
#define FxaaTexAlpha4(t, p, r) textureGather(t, p, 3)
#define FxaaTexOffAlpha4(t, p, o, r) textureGatherOffset(t, p, o, 3)
	
	vec3 FxaaPixelShader(vec4 posPos, sampler2D tex, vec2 rcpFrame)
	{	
		vec2 posM;
		posM.x = posPos.x;
		posM.y = posPos.y;
		
		#if (GL_NV_gpu_shader5)
		vec4 luma4A = FxaaTexAlpha4(tex, posM, rcpFrame.xy);
		vec4 luma4B = FxaaTexOffAlpha4(tex, posM, FxaaInt2(-1, -1), rcpFrame.xy);
		#endif
		
		vec4 rgbyM = FxaaTexLod0(tex, posM);
 
        float lumaS = FxaaTexOff(tex, posM, FxaaInt2( 0, 1), rcpFrame.xy).w;
        float lumaE = FxaaTexOff(tex, posM, FxaaInt2( 1, 0), rcpFrame.xy).w;
        float lumaN = FxaaTexOff(tex, posM, FxaaInt2( 0,-1), rcpFrame.xy).w;
        float lumaW = FxaaTexOff(tex, posM, FxaaInt2(-1, 0), rcpFrame.xy).w;
		
		vec3 rgbNW = FxaaTexLod0(tex, posPos.zw).xyz;
		vec3 rgbNE = FxaaTexOff(tex, posPos.zw, FxaaInt2(1,0), rcpFrame.xy).xyz;
		vec3 rgbSW = FxaaTexOff(tex, posPos.zw, FxaaInt2(0,1), rcpFrame.xy).xyz;
		vec3 rgbSE = FxaaTexOff(tex, posPos.zw, FxaaInt2(1,1), rcpFrame.xy).xyz;
		vec3 rgbM  = FxaaTexLod0(tex, posPos.xy).xyz;

		vec3 luma = vec3(0.2125, 0.7154, 0.0721);
		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 maxSM = max(lumaS, lumaM);
		float minSM = min(lumaS, lumaM);
		float maxESM = max(lumaE, maxSM);
		float minESM = min(lumaE, minSM);
		float maxWN = max(lumaN, lumaW);
		float minWN = min(lumaN, lumaW);
		float rangeMax = max(maxWN, maxESM);
		float rangeMin = min(minWN, minESM);
		float rangeMaxScaled = rangeMax * FXAA_QUALITY__EDGE_THRESHOLD;
		float range = rangeMax - rangeMin;
		float rangeMaxClamped = max(FXAA_QUALITY__EDGE_THRESHOLD_MIN, rangeMaxScaled);
		bool earlyExit = range < rangeMaxClamped;
		
		float lumaNS = lumaN + lumaS;
		float lumaWE = lumaW + lumaE;
		float subpixRcpRange = 1.0/range;
		float subpixNSWE = lumaNS + lumaWE;
		float edgeHorz1 = (-2.0 * lumaM) + lumaNS;
		float edgeVert1 = (-2.0 * lumaM) + lumaWE;

		float lumaNESE = lumaNE + lumaSE;
		float lumaNWNE = lumaNW + lumaNE;
		float edgeHorz2 = (-2.0 * lumaE) + lumaNESE;
		float edgeVert2 = (-2.0 * lumaN) + lumaNWNE;

		float lumaNWSW = lumaNW + lumaSW;
		float lumaSWSE = lumaSW + lumaSE;
		float edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2);
		float edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2);
		float edgeHorz3 = (-2.0 * lumaW) + lumaNWSW;
		float edgeVert3 = (-2.0 * lumaS) + lumaSWSE;
		float edgeHorz = abs(edgeHorz3) + edgeHorz4;
		float edgeVert = abs(edgeVert3) + edgeVert4;
		
		float subpixNWSWNESE = lumaNWSW + lumaNESE;
		float lengthSign = rcpFrame.x;
		bool horzSpan = edgeHorz >= edgeVert;
		float subpixA = subpixNSWE * 2.0 + subpixNWSWNESE;
		
		if(!horzSpan) lumaN = lumaW;
		if(!horzSpan) lumaS = lumaE;
		if(horzSpan) lengthSign = rcpFrame.y;
		float subpixB = (subpixA * (1.0/12.0)) - lumaM;
		
		float gradientN = lumaN - lumaM;
		float gradientS = lumaS - lumaM;
		float lumaNN = lumaN + lumaM;
		float lumaSS = lumaS + lumaM;
		bool pairN = abs(gradientN) >= abs(gradientS);
		float gradient = max(abs(gradientN), abs(gradientS));
		if(pairN) lengthSign = -lengthSign;
		float subpixC = FxaaSat(abs(subpixB) * subpixRcpRange);
		
		vec2 posB;
		posB.x = posM.x;
		posB.y = posM.y;
		vec2 offNP;
		offNP.x = (!horzSpan) ? 0.0 : rcpFrame.x;
		offNP.y = ( horzSpan) ? 0.0 : rcpFrame.y;
		if(!horzSpan) posB.x += lengthSign * 0.5;
		if( horzSpan) posB.y += lengthSign * 0.5;
		
		vec2 posN;
		posN.x = posB.x - offNP.x * FXAA_QUALITY__P0;
		posN.y = posB.y - offNP.y * FXAA_QUALITY__P0;
		vec2 posP;
		posP.x = posB.x + offNP.x * FXAA_QUALITY__P0;
		posP.y = posB.y + offNP.y * FXAA_QUALITY__P0;
		float subpixD = ((-2.0)*subpixC) + 3.0;
		float lumaEndN = FxaaTexLod0(tex, posN).w;
		float subpixE = subpixC * subpixC;
		float lumaEndP = FxaaTexLod0(tex, posP).w;
		
		if(!pairN) lumaNN = lumaSS;
		float gradientScaled = gradient * 1.0/4.0;
		float lumaMM = lumaM - lumaNN * 0.5;
		float subpixF = subpixD * subpixE;
		bool lumaMLTZero = lumaMM < 0.0;
		
		lumaEndN -= lumaNN * 0.5;
		lumaEndP -= lumaNN * 0.5;
		bool doneN = abs(lumaEndN) >= gradientScaled;
		bool doneP = abs(lumaEndP) >= gradientScaled;
		if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P1;
		if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P1;
		bool doneNP = (!doneN) || (!doneP);
		if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P1;
		if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P1;
		
		if(doneNP)
		{
			if(!doneN) lumaEndN = FxaaTexLod0(tex, posN.xy).w;
			if(!doneP) lumaEndP = FxaaTexLod0(tex, posP.xy).w;
			if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;
			if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;
			doneN = abs(lumaEndN) >= gradientScaled;
			doneP = abs(lumaEndP) >= gradientScaled;
			if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P11;
			if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P11;
			doneNP = (!doneN) || (!doneP);
			if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P11;
			if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P11;
		}
		
		float dstN = posM.x - posN.x;
		float dstP = posP.x - posM.x;
		if(!horzSpan) dstN = posM.y - posN.y;
		if(!horzSpan) dstP = posP.y - posM.y;
		
		bool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero;
		float spanLength = (dstP + dstN);
		bool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero;
		float spanLengthRcp = 1.0/spanLength;

		bool directionN = dstN < dstP;
		float dst = min(dstN, dstP);
		bool goodSpan = directionN ? goodSpanN : goodSpanP;
		float subpixG = subpixF * subpixF;
		float pixelOffset = (dst * (-spanLengthRcp)) + 0.5;
		float subpixH = subpixG * FXAA_QUALITY__SUBPIX;

		float pixelOffsetGood = goodSpan ? pixelOffset : 0.0;
		float pixelOffsetSubpix = max(pixelOffsetGood, subpixH);
		if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign;
		if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign;

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

		vec2 dir; 
		dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));
		dir.y =  ((lumaNW + lumaSW) - (lumaNE + lumaSE));

		float dirReduce = max(
			(lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * 
			FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);
		float rcpDirMin = 1.0/(min(abs(dir.x), abs(dir.y)) + dirReduce);
		dir = min(Fxaavec2( FXAA_SPAN_MAX,  FXAA_SPAN_MAX), 
			  max(Fxaavec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), 
			  dir * rcpDirMin)) * rcpFrame.xy;

		vec3 rgbA = (1.0/2.0) * (
			FxaaTexLod0(tex, posPos.xy + dir * (1.0/3.0 - 0.5)).xyz +
			FxaaTexLod0(tex, posPos.xy + dir * (2.0/3.0 - 0.5)).xyz);
		vec3 rgbB = rgbA * (1.0/2.0) + (1.0/4.0) * (
			FxaaTexLod0(tex, posPos.xy + dir * (0.0/3.0 - 0.5)).xyz +
			FxaaTexLod0(tex, posPos.xy + dir * (3.0/3.0 - 0.5)).xyz);
		float lumaB = dot(rgbB, luma);
		
		if((lumaB < lumaMin) || (lumaB > lumaMax)) return rgbA;
		return rgbB;
	}

	vec4 PostFX(sampler2D tex, vec2 uv)
	{
		vec4 FxaaOutput = texture(colorSampler, texcoord);
		vec2 rcpFrame = vec2(1.0/rt_w, 1.0/rt_h);
		
		FxaaOutput.rgb = FxaaPixelShader(posPos, tex, rcpFrame);
		FxaaOutput.a = dot(FxaaOutput.rgb, vec3(0.212656, 0.715158, 0.072186));
		
		return FxaaOutput;
	}

	vec4 getFXAA(vec4 FxaaColor, vec2 Tex) 
	{
		return PostFX(colorSampler, texcoord);
	}
#endif

/*------------------------------------------------------------------------------
						[DYNAMIC CONTRAST CODE SECTION]
						[Adapted from CeeJay's SweetFX]
    [http://www.guru3d.com/files_details/sweetfx_shader_suite_download.html]
------------------------------------------------------------------------------*/

#if (DYNAMIC_CONTRAST == 1)
	vec4 CurvesPass(vec4 colorInput, vec2 t)
	{
		vec3 lumCoeff = vec3(0.212656, 0.715158, 0.072186);
		float Curves_contrast_blend = CurvesContrast;
		float PI = acos(-1); //3.1415926589

		#if (CurveType != 2)
			float luma = dot(lumCoeff, colorInput.rgb);
			vec3 chroma = colorInput.rgb - luma;
		#endif

		#if (CurveType == 2)
			vec3 x = colorInput.rgb;
		#elif (CurveType == 1)
			vec3 x = chroma;
			x = x * 0.5 + 0.5;
		#else
			float x = luma;
		#endif

		// -- Curve 1 -- Cubic Bezier spline
		#if (CurvesFormula == 1)
			float a = 0.00; //start point
			float b = 0.00; //control point 1
			float c = 1.00; //control point 2
			float d = 1.00; //endpoint

			float r  = (1-x);
			float r2 = r*r;
			float r3 = r2 * r;
			float x2 = x*x;
			float x3 = x2*x;

			x = a*(1-x)*(1-x)*(1-x) + 3*b*(1-x)*(1-x)*x + 3*c*(1-x)*x*x + d*x*x*x;
		#endif

		// -- Curve 2 -- Cubic Bezier spline II
		#if (CurvesFormula == 2)
			vec3 a = vec3(0.00,0.00,0.00); //start point
			vec3 b = vec3(0.25,0.15,0.85); //control point 1
			vec3 c = vec3(0.75,0.85,0.15); //control point 2
			vec3 d = vec3(1.00,1.00,1.00); //endpoint

			vec3 ab = mix(a,b,x);           // point between a and b (green)
			vec3 bc = mix(b,c,x);           // point between b and c (green)
			vec3 cd = mix(c,d,x);           // point between c and d (green)
			vec3 abbc = mix(ab,bc,x);       // point between ab and bc (blue)
			vec3 bccd = mix(bc,cd,x);       // point between bc and cd (blue)
			vec3 dest = mix(abbc,bccd,x);   // point on the bezier-curve (black)
			x = dest;
		#endif

		#if (CurveType == 2) //Both Luma and Chroma
			vec3 color = x; 
			colorInput.rgb = mix(colorInput.rgb, color, Curves_contrast_blend);
		#elif (CurveType == 1) //Only Chroma
			x = x * 2 - 1;
			vec3 color = luma + x; //Luma + Chroma
			colorInput.rgb = mix(colorInput.rgb, color, Curves_contrast_blend);
		#else // CurveType == 0 //Only Luma
			x = mix(luma, x, Curves_contrast_blend);
			colorInput.rgb = x + chroma; //Luma + Chroma 
			colorInput.a = RGBLuminance(colorInput);
		#endif

		colorInput = clamp(colorInput, 0.0 , 1.0);
		
		return colorInput;
	}
#endif

/*------------------------------------------------------------------------------
                          [LUMA SHARPEN CODE SECTION]
						[Adapted from CeeJay's SweetFX]
	[http://www.guru3d.com/files_details/sweetfx_shader_suite_download.html]
------------------------------------------------------------------------------*/

#if (LUMA_SHARPEN == 1)
	#define BUFFER_RCP_WIDTH (1.0 / 3584)
	#define BUFFER_RCP_HEIGHT (1.0 / 3072)
	#define px BUFFER_RCP_WIDTH
	#define py BUFFER_RCP_HEIGHT
	#define pixel vec2(px,py)
	#define CoefLuma vec3(0.2126, 0.7152, 0.0722)
	#define sharp_strength_luma (CoefLuma * Sharp_Strength)
	
	vec4 LumaSharpen(vec4 inputcolor, vec2 tex) 
	{
		vec3 ori = texture(colorSampler, tex).rgb; 

		// -- Pattern 1 -- A 9 tap gaussian using 4+1 texture fetches.
		#if (Pattern == 1)
			vec3 blur_ori = texture(colorSampler, tex + vec2(px,-py) * 0.5 * Offset_Bias).rgb;
			blur_ori += texture(colorSampler, tex + vec2(-px,-py) * 0.5 * Offset_Bias).rgb;   
			blur_ori += texture(colorSampler, tex + vec2(px,py) * 0.5 * Offset_Bias).rgb;
			blur_ori += texture(colorSampler, tex + vec2(-px,py) * 0.5 * Offset_Bias).rgb;

			blur_ori *= 0.25;
		#endif 

		// -- Pattern 2 -- An experimental 17 tap gaussian using 4+1 texture fetches.
		#if (Pattern == 2)
			vec3 blur_ori = texture(colorSampler, tex + vec2(0.4*px,-1.2*py)* Offset_Bias).rgb;
			blur_ori += texture(colorSampler, tex + vec2(-1.2*px,-0.4*py) * Offset_Bias).rgb;
			blur_ori += texture(colorSampler, tex + vec2(1.2*px,0.4*py) * Offset_Bias).rgb;
			blur_ori += texture(colorSampler, tex + vec2(-0.4*px,1.2*py) * Offset_Bias).rgb;
			blur_ori *= 0.25;

			sharp_strength_luma *= 0.51;
		#endif

		// -- Pattern 3 -- A 9 tap high pass (pyramid filter) using 4+1 texture fetches.
		#if (Pattern == 3)
			vec3 blur_ori = texture(colorSampler, tex + vec2(0.5 * px,-py * Offset_Bias)).rgb;
			blur_ori += texture(colorSampler, tex + vec2(Offset_Bias * -px,0.5 * -py)).rgb;
			blur_ori += texture(colorSampler, tex + vec2(Offset_Bias * px,0.5 * py)).rgb;
			blur_ori += texture(colorSampler, tex + vec2(0.5 * -px,py * Offset_Bias)).rgb;	
			blur_ori /= 4.0;

			sharp_strength_luma *= 0.666;
		#endif

		// -- Pattern 4 -- A (slower) 9 tap gaussian using 9 texture fetches.
		#if (Pattern == 4)
			half3 blur_ori = texture(colorSampler, tex + vec2(-px,py) * Offset_Bias).rgb;
			blur_ori += texture(colorSampler, tex + vec2(px,-py) * Offset_Bias).rgb;
			blur_ori += texture(colorSampler, tex + vec2(-px,-py)  * Offset_Bias).rgb;
			blur_ori += texture(colorSampler, tex + vec2(px,py) * Offset_Bias).rgb;

			half3 blur_ori2 = texture(colorSampler, tex + vec2(0,py) * Offset_Bias).rgb;
			blur_ori2 += texture(colorSampler, tex + vec2(0,-py) * Offset_Bias).rgb;
			blur_ori2 += texture(colorSampler, tex + vec2(-px,0) * Offset_Bias).rgb;
			blur_ori2 += texture(colorSampler, tex + vec2(px,0) * Offset_Bias).rgb;
			blur_ori2 *= 2.0;

			blur_ori += blur_ori2;
			blur_ori += (ori * 4);
			blur_ori /= 16.0;
		#endif

		// -- Pattern 5 -- A (slower) 9 tap high pass using 9 texture fetches.
		#if (Pattern == 5)
			vec3 blur_ori = texture(colorSampler, tex + vec2(-px,py) * Offset_Bias).rgb;
			blur_ori += texture(colorSampler, tex + vec2(px,-py) * Offset_Bias).rgb;
			blur_ori += texture(colorSampler, tex + vec2(-px,-py)  * Offset_Bias).rgb;
			blur_ori += texture(colorSampler, tex + vec2(px,py) * Offset_Bias).rgb;

			blur_ori += ori.rgb;

			blur_ori += texture(colorSampler, tex + vec2(0,py) * Offset_Bias).rgb;
			blur_ori += texture(colorSampler, tex + vec2(0,-py) * Offset_Bias).rgb;
			blur_ori += texture(colorSampler, tex + vec2(-px,0) * Offset_Bias).rgb;
			blur_ori += texture(colorSampler, tex + vec2(px,0) * Offset_Bias).rgb;

			blur_ori /= 9;
		#endif

		vec3 sharp = ori - blur_ori;  
		float sharp_luma = dot(sharp, sharp_strength_luma);
		sharp_luma = clamp(sharp_luma, -Sharp_Clamp, Sharp_Clamp);

		vec4 done = inputcolor + sharp_luma; 

		#if Show_Sharpen == 1
		done = clamp(0.5 + (sharp_luma * 4), 0.0, 1.0).rrrr;
		#endif

		return clamp(done, 0.0, 1.0);
	}
#endif

/*------------------------------------------------------------------------------
                             [DITHER CODE SECTION]
						[Adapted from CeeJay's SweetFX]
    [http://www.guru3d.com/files_details/sweetfx_shader_suite_download.html]
------------------------------------------------------------------------------*/

#if (SUBPIXEL_DITHERING == 1)
	vec4 Dither(vec4 colorInput, vec2 tex)
	{
		
		vec4 color = colorInput;

		float dither_size = 2.0;  //Size of the dithering grid - I'm using a 2x2 grid here.
		float dither_bit  = 8.0;  //Number of bits per channel. Should be 8 for most monitors.

		#if DitherMethod == 2 //random dithering

		 //make some noise
		 float noise = fract(sin(dot(vec4(tex,-tex.yx), vec4(vec2(12.9898,78.233),vec2(12.9898,78.233)* acos(-1)))) * 43758.5453); //pseudo random number generator
		 
		 noise -= 0.5;
		 
		 //Calculate how big the shift should be
		 float dither_shift = (noise) * (1.0 / (pow(2,dither_bit) - 1.0)); // using noise to determine shift. The noise ahould vary between +- 0.5.
																		   
		 //Shift the individual colors differently, thus making it even harder to see the dithering Pattern
		 vec3 dither_shift_RGB = vec3(-dither_shift, dither_shift, -dither_shift); //subpixel dithering
		 
		 color.rgb += dither_shift_RGB;
		 
		#else //DitherMethod == 1 , Ordered dithering
		 //Calculate grid position
		 float grid_position = fract(dot(tex,(screen_size / dither_size)) + (0.5 / dither_size)); //returns 0.25 and 0.75

		 //Calculate how big the shift should be
		 float dither_shift = (0.25) * (1.0 / (pow(2,dither_bit) - 1.0)); // 0.25 seems good both when using math and when eyeballing it. So does 0.75 btw.

		 //Shift the individual colors differently, thus making it even harder to see the dithering Pattern
		 vec3 dither_shift_RGB = vec3(dither_shift, -dither_shift, dither_shift); //subpixel dithering

		 //modify shift acording to grid position.
		 dither_shift_RGB = mix(2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position); //shift acording to grid position.
		 
		 //shift the color by dither_shift
		 //color.rgb += mix(2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position); //shift acording to grid position.
		 color.rgb += dither_shift_RGB;
		#endif

		/*debugging purposes only*/
		//colorInput.rgb = (dither_shift_RGB * 2.0 * (pow(2,dither_bit) - 1.0) ) + 0.5; //visualize the shift
		//colorInput.rgb = grid_position; //visualize the grid
		//colorInput.rgb = noise; //visualize the noise

		colorInput.rgb = color.rgb;

		return colorInput;
		
	}
#endif

/*------------------------------------------------------------------------------
                    [SPRITE INTERPOLATION AA CODE SECTION]
------------------------------------------------------------------------------*/

#if (SPRITE_INTERPOLATION == 1)
	vec4 getSpI(vec4 SpriteColor, vec2 txCoord)
	{	
		/*This code is courtesy of guest.r*/
		
		vec3 c11 = texture(colorSampler, gl_TexCoord[0].xy).xyz;
		vec3 s00 = texture(colorSampler, gl_TexCoord[1].xy).xyz; 
		vec3 s20 = texture(colorSampler, gl_TexCoord[2].xy).xyz; 
		vec3 s22 = texture(colorSampler, gl_TexCoord[3].xy).xyz; 
		vec3 s02 = texture(colorSampler, gl_TexCoord[4].xy).xyz; 
		vec3 c00 = texture(colorSampler, gl_TexCoord[5].xy).xyz; 
		vec3 c22 = texture(colorSampler, gl_TexCoord[6].xy).xyz; 
		vec3 c20 = texture(colorSampler, gl_TexCoord[5].zw).xyz;
		vec3 c02 = texture(colorSampler, gl_TexCoord[6].zw).xyz;
		vec3 c10 = texture(colorSampler, gl_TexCoord[1].zw).xyz; 
		vec3 c21 = texture(colorSampler, gl_TexCoord[2].zw).xyz; 
		vec3 c12 = texture(colorSampler, gl_TexCoord[3].zw).xyz; 
		vec3 c01 = texture(colorSampler, gl_TexCoord[4].zw).xyz;     
		vec3 dt = vec3(1.0,1.0,1.0);
	 
		float d1=dot(abs(c00-c22),dt)+0.001;
		float d2=dot(abs(c20-c02),dt)+0.001;
		float hl=dot(abs(c01-c21),dt)+0.001;
		float vl=dot(abs(c10-c12),dt)+0.001;
		float m1=dot(abs(s00-s22),dt)+0.001;
		float m2=dot(abs(s02-s20),dt)+0.001;
	 
		c11 =.5*(m2*(s00+s22)+m1*(s02+s20))/(m1+m2);

		float k1 = max(dot(abs(c00-c11),dt),dot(abs(c22-c11),dt))+0.01;k1=1.0/k1;
		float k2 = max(dot(abs(c20-c11),dt),dot(abs(c02-c11),dt))+0.01;k2=1.0/k2;
		float k3 = max(dot(abs(c01-c11),dt),dot(abs(c21-c11),dt))+0.01;k3=1.0/k3;
		float k4 = max(dot(abs(c01-c11),dt),dot(abs(c12-c11),dt))+0.01;k4=1.0/k4;
	 
		c11 = 0.5*(k1*(c00+c22)+k2*(c20+c02)+k3*(c01+c21)+k4*(c10+c12))/(k1+k2+k3+k4);
	 
		vec3 mn1 = min(min(c00,c01),c02);
		vec3 mn2 = min(min(c10,c11),c12);
		vec3 mn3 = min(min(c20,c21),c22);
		vec3 mx1 = max(max(c00,c01),c02);
		vec3 mx2 = max(max(c10,c11),c12);
		vec3 mx3 = max(max(c20,c21),c22);
	 
		mn1 = min(min(mn1,mn2),mn3);
		mx1 = max(max(mx1,mx2),mx3);
	 
		float filterparam = 10.0; 
	 
		vec3 dif1 = abs(c11-mn1) + 0.001*dt;
		vec3 dif2 = abs(c11-mx1) + 0.001*dt;
	 
		dif1=vec3(pow(dif1.x,filterparam),pow(dif1.y,filterparam),pow(dif1.z,filterparam));
		dif2=vec3(pow(dif2.x,filterparam),pow(dif2.y,filterparam),pow(dif2.z,filterparam));
	 
		c11.r = (dif1.x*mx1.x + dif2.x*mn1.x)/(dif1.x + dif2.x);
		c11.g = (dif1.y*mx1.y + dif2.y*mn1.y)/(dif1.y + dif2.y);
		c11.b = (dif1.z*mx1.z + dif2.z*mn1.z)/(dif1.z + dif2.z);
		
		SpriteColor = vec4(c11, 1.0);
	 
		return SpriteColor;
	} 
#endif

/*------------------------------------------------------------------------------
					[HDR LUMA CORRECTION PASS CODE SECTION]
------------------------------------------------------------------------------*/

#if (HDR_GAMMA_PASS == 1)
#define DO_TONEMAP
#define DO_GLOBAL
#define DO_LOCAL
#define DO_CHROMINANCE
#define DO_VARYING
#define K 4
	
	vec3 RGB2XYZ(const vec3 RGB)
	{
	  const mat3 mRGB2XYZ = mat3
	  (
		vec3(0.51413640, 0.26506800, 0.02411880),
		vec3(0.32387860, 0.67023428, 0.12281780),
		vec3(0.16036376, 0.06409157, 0.84442666)
	  );
	  vec3 XYZ = mRGB2XYZ * RGB;
	  return(XYZ);
	}
	
	vec3 XYZ2RGB(const vec3 XYZ)
	{
	  const mat3 mXYZ2RGB = mat3
	  (
		vec3( 2.5651, -1.0217,  0.0753),
		vec3(-1.1665,  1.9777, -0.2543),
		vec3(-0.3986,  0.0439,  1.1892)
	  );
	  vec3 RGB = mXYZ2RGB * XYZ;
	  return(RGB);
	}
	
	struct Yxy_t
	{
	  float Y;
	  float x;
	  float y;
	};

	Yxy_t XYZ2Yxy(const vec3 XYZ)
	{
	  Yxy_t Yxy;
	  float W = XYZ.x + XYZ.y + XYZ.z;
	  Yxy.Y = XYZ.y;
	  Yxy.x = XYZ.x / W;
	  Yxy.y = XYZ.y / W;
	  return(Yxy);
	}

	Yxy_t RGB2Yxy(const vec3 RGB)
	{
	  vec3 XYZ = RGB2XYZ(RGB);
	  Yxy_t Yxy = XYZ2Yxy(XYZ);
	  return(Yxy);
	}

	vec3 Yxy2XYZ(const Yxy_t Yxy)
	{
	  vec3 XYZ;
	  XYZ.x = Yxy.Y * (Yxy.x / Yxy.y);
	  XYZ.y = Yxy.Y;
	  XYZ.z = (XYZ.x / Yxy.x) - XYZ.x - XYZ.y;
	  return(XYZ);
	}

	vec3 Yxy2RGB(const Yxy_t Yxy)
	{
	  vec3 XYZ = Yxy2XYZ(Yxy);
	  vec3 RGB = XYZ2RGB(XYZ);
	  return(RGB);
	}

	struct Lab_t
	{
	  float L;
	  float a;
	  float b;
	};

	const vec3 D50 = vec3( 96.4212, 100.0, 82.51880 );
	const vec3 D55 = vec3( 95.6797, 100.0, 92.14810 );
	const vec3 D65 = vec3( 95.0429, 100.0, 108.8900 );
	const vec3 D75 = vec3( 94.9722, 100.0, 122.6394 );

	const float e = 216.0 / 24389.0;
	const float k = 24389.0 / 27.0;
	
	Lab_t XYZ2Lab(const vec3 XYZ)
	{
	  const vec3 W = D65;

	  const vec3 r = XYZ / W;

	  vec3 f;
	  f.x = (r.x > e) ? pow(r.x, 1.0/3.0) : (k*r.x + 16.0)/116.0;
	  f.y = (r.y > e) ? pow(r.y, 1.0/3.0) : (k*r.y + 16.0)/116.0;
	  f.z = (r.z > e) ? pow(r.z, 1.0/3.0) : (k*r.z + 16.0)/116.0;

	  Lab_t Lab;
	  Lab.L = 116.0*f.y - 16.0;
	  Lab.a = 500.0*(f.x - f.y);
	  Lab.b = 200.0*(f.y - f.z);

	  return(Lab);
	}

	vec3 Lab2XYZ(const Lab_t Lab)
	{
	  const vec3 W = D65;

	  vec3 f;
	  f.y = (Lab.L + 16.0) / 116.0;
	  f.x = (Lab.a/500.0) + f.y;
	  f.z = f.y - (Lab.b/200.0);

	  vec3 r;
	  r.x = (pow(f.x, 3.0) > e) ? pow(f.x, 3.0) : (116.0*f.x - 16.0)/k;
	  r.y = (pow(f.y, 3.0) > e) ? pow(f.y, 3.0) : Lab.L / k;
	  r.z = (pow(f.z, 3.0) > e) ? pow(f.z, 3.0) : (116.0*f.z - 16.0)/k;

	  vec3 XYZ;
	  XYZ = r * W;

	  return(XYZ);
	}

	Lab_t RGB2Lab(const vec3 RGB)
	{
	  vec3 XYZ = RGB2XYZ(RGB);
	  Lab_t Lab = XYZ2Lab(XYZ);
	  return(Lab);
	}

	vec3 Lab2RGB(const Lab_t Lab)
	{
	  vec3 XYZ = Lab2XYZ(Lab);
	  vec3 RGB = XYZ2RGB(XYZ);
	  return(RGB);
	}

	Lab_t Yxy2Lab(const Yxy_t Yxy)
	{
	  vec3 XYZ = Yxy2XYZ(Yxy);
	  Lab_t Lab = XYZ2Lab(XYZ);
	  return(Lab);
	}

	Yxy_t Lab2Yxy(const Lab_t Lab)
	{
	  vec3 XYZ = Lab2XYZ(Lab);
	  Yxy_t Yxy = XYZ2Yxy(XYZ);
	  return(Yxy);
	}

	float E(const float I)
	{
	  return( ((70.0)/(1.0 + pow(10.0/I, 0.383))) + 22.0 );
	}

	void ChrominanceAdaption(inout vec3 XYZ, const float lambda)
	{
	  Lab_t Lab = XYZ2Lab(XYZ);
	  float rho = min(E(lambda)/E(10.0), 1.0);
	  if (Lab.a > 0.0)
		Lab.a *= rho;
	  XYZ = Lab2XYZ(Lab);
	}

	float RelativeLuminance(const float lum, const float key, const float logavglum)
	{
	  float rLum = (lum*key) / logavglum;
	  return(rLum);
	}

	float Global(const float lum, const float rLum)
	{
	  float dLum = rLum / (1.0+rLum);
	  return(dLum);
	}
	
	#define vec float
	#define EXT r
	#define sampler sampler2DRect
	#define texFetch texture2DRect

	uniform sampler txSATLum;
	uniform vec2 SATSIZE;

	vec texSATArea(const sampler SAT, vec2 amin, vec2 amax, out vec2 area)
	{
	  amax = min(amax, SATSIZE-vec2(0.5));
	  vec accum = texFetch(SAT, amax).EXT;
	  amin = max(amin, vec2(0.5));
	  amin -= vec2(1);
	  accum -= texFetch(SAT, vec2(amin.x,amax.y)).EXT;
	  accum -= texFetch(SAT, vec2(amax.x,amin.y)).EXT;
	  accum += texFetch(SAT, amin).EXT;
	  area = vec2(amax.x-amin.x, amax.y-amin.y);
	  return(accum);
	}

	vec texSATDiff(const sampler SAT, vec2 center, vec2 diff, out vec2 area)
	{
	  vec2 amin = center-diff;
	  vec2 amax = center+diff;
	  vec sum = texSATArea(SAT, amin, amax, area);
	  return(sum);
	}
	
	float Activity(const float Aa, const float Bb, const float ii, const float kkey, const float pphi)
	{
	  float scale = pow(1.6, ii);
	  float num = (Aa - Bb);
	  float sharp = pow(2.0, pphi)*kkey;
	  float denum = (sharp/(scale*scale))+Aa;
	  float activity = abs(num/denum);
	  return(activity);
	}
	
	float LumScale(const sampler SAT, vec2 coord, const int i)
	{
	  //const int sizes [8] = { 1, 3, 5, 7, 11, 17, 27, 43 };
	  const int radii [8] = int[]( 0, 1, 2, 3, 5, 8, 13, 21 );
	  vec2 area;
	  float lum = texSATDiff(SAT, coord, vec2(radii[i]), area);
	  lum /= (area.x*area.y);
	  return(lum);
	}

	#define SCALES 8
	vec3 MaxScaleLuminance(const sampler SAT, vec2 coord, const float rLum)
	{
	  float lums [8];
	  lums[0] = rLum;

	  float add_back = texture(txRelLumAvgSampler, vec2(0.0)).r;

	  for (int i=SCALES-1; i>0; --i)
		lums[i] = LumScale(SAT, coord, i) + add_back;

	  vec3 lummax = vec3(lums[SCALES-1], SCALES-1, 1.0);
	  for (int i=0; i<SCALES-1; ++i)
	  {
		float activity = Activity(lums[i], lums[i+1], i, key, phi);
		if (activity > threshold)
		{
		  #if (GL_NV_gpu_shader5)
			lummax = vec3(lums[i], i, float(i)/float(SCALES-1)); //intelligent method (works on NVIDIA)
			break;
		  #else
		  if (lummax.x == lums[SCALES-1])
			lummax = vec3(lums[i], i, float(i)/float(SCALES-1)); //Radeon-friendly.
		  #endif
		}
	  }

	  return(lummax);
	}

	float Local(const float lum, const float rLum, const float maxlum)
	{
	  float dLum = rLum / (1.0+maxlum);
	  return(dLum);
	}
	
	vec4 LumCorrectPass(vec4 HDRColor, vec2 Tex)
	{		
		//HDRColor *= pow(Luminance, (1.0/lumaFactor));
		
		Yxy_t Yxy = RGB2Yxy(HDRColor.rgb);
		float rLum = vec3(0.2126, 0.7152, 0.0722);
		float dLum = Yxy.Y;
		vec3 maxLum = vec3(0.0);

		#ifdef DO_TONEMAP
			vec2 avgs = texture(AvgLumSampler, vec2(0.5)).ra;
			float chromaLum = avgs.x;
			rLum = RelativeLuminance(Yxy.Y, key, avgs.y);

		#ifdef DO_GLOBAL
			dLum = Global(Yxy.Y, rLum);
		#endif

		#ifdef DO_LOCAL
			maxLum = MaxScaleLuminance(txSATLum, gl_TexCoord[1].xy, rLum);
			dLum = Local(Yxy.Y, rLum, maxLum.x);

		#ifdef DO_VARYING
			chromaLum = maxLum.x*(avgs.x/key);
		#endif
		#endif

		Yxy.Y = dLum;
		vec3 XYZ = Yxy2XYZ(Yxy);

		#ifdef DO_CHROMINANCE
		  ChrominanceAdaption(XYZ, chromaLum);
		#endif

		HDRColor.rgb + XYZ2RGB(XYZ);
		HDRColor.a = RGBLuminance(HDRColor);
		HDRColor = pow(HDRColor, vec4(1.0/GammaCorrection));
		clamp(HDRColor, 0.001, 1.0);
		#endif

		return HDRColor;
	}
#endif

/*------------------------------------------------------------------------------
                        [HDR BLOOM PASS CODE SECTION]
------------------------------------------------------------------------------*/

#if (HDR_BLOOM_PASS == 1)
const float blurspread = BlurSpread;

	vec4 BloomPass(vec4 color, vec2 t)
	{	
		#define BUFFER_WIDTH (1.0 / 2560)
		#define BUFFER_HEIGHT (1.0 / 1440)
		#define bpx BUFFER_WIDTH
		#define bpy BUFFER_HEIGHT
		#define bpixel vec2(bpx,bpy)
		
		float bloomPower = BloomPower;
		vec4 blur = color * 0.22520613262190495;
		color.a = RGBLuminance(color);

		const vec2 dx = vec2(bpixel.x * blurspread, 0.0);
		const vec2 dy = vec2(0.0, bpixel.y * 2.0 * blurspread);
		const vec2 dx2 = 2 * dx;
		const vec2 dy2 = 2 * dy;
		
		vec4 colorU = texture(colorSampler, t + dy);
		
		blur += 0.002589001911021066 * texture(colorSampler, t -dx2 +dy2); 
		blur += 0.010778807494659370 * texture(colorSampler, t -dx +dy2); 
		blur += 0.024146616900339800 * texture(colorSampler, t +dy2); 
		blur += 0.010778807494659370 * texture(colorSampler, t +dx +dy2); 
		blur += 0.002589001911021066 * texture(colorSampler, t +dx2 +dy2); 

		blur += 0.010778807494659370 * texture(colorSampler, t -dx2 +dy);
		blur += 0.044875475183061630 * texture(colorSampler, t -dx +dy);
		blur += 0.100529757860782610 * texture(colorSampler, t +dy);
		blur += 0.044875475183061630 * texture(colorSampler, t +dx +dy);
		blur += 0.010778807494659370 * texture(colorSampler, t +dx2 +dy);

		blur += 0.024146616900339800 * texture(colorSampler, t -dx2);
		blur += 0.100529757860782610 * texture(colorSampler, t -dx);	
		blur += 0.100529757860782610 * texture(colorSampler, t +dx);
		blur += 0.024146616900339800 * texture(colorSampler, t +dx2);

		blur += 0.010778807494659370 * texture(colorSampler, t -dx2 -dy);
		blur += 0.044875475183061630 * texture(colorSampler, t -dx -dy);
		blur += 0.100529757860782610 * texture(colorSampler, t -dy);
		blur += 0.044875475183061630 * texture(colorSampler, t +dx -dy);
		blur += 0.010778807494659370 * texture(colorSampler, t +dx2 -dy);

		blur += 0.002589001911021066 * texture(colorSampler, t -dx2 -dy2);
		blur += 0.010778807494659370 * texture(colorSampler, t -dx -dy2);
		blur += 0.024146616900339800 * texture(colorSampler, t -dy2);
		blur += 0.010778807494659370 * texture(colorSampler, t +dx -dy2);
		blur += 0.002589001911021066 * texture(colorSampler, t +dx2 -dy2);				
		
		vec4 newcolor = vec4(BloomType(color.r, blur.r),
								 BloomType(color.g, blur.g),
								 BloomType(color.b, blur.b),
								 RGBLuminance(color));
		
		color = mix(color, newcolor, BloomPower);
	
	return color;
	}
#endif

/*------------------------------------------------------------------------------
                        [HDR TONEMAP PASS CODE SECTION]
						[Adapted from CeeJay's SweetFX]
	[http://www.guru3d.com/files_details/sweetfx_shader_suite_download.html]
------------------------------------------------------------------------------*/

#if (HDR_TONEMAP_PASS == 1)
	vec4 TonemapPass(vec4 colorInput, vec2 t)
	{
		// Average scene luminance is stored in R channel
		float avgLuminance = Luma;
		float exposure = Exposure;
		float whitePoint = WhitePoint;
		float blackLevel = BlackLevel;

		//TEXCOORD mapping swap
		vec2 colorTexel = vec2(0,0);
		
		#if(TonemappingType == 1)
		// Local tone mapping
			vec4 ColorTone = texture(phongSampler, t);
		#else
		// Scene tone mapping
			vec4 ColorTone = texture(colorSampler, t);
		#endif

		vec3 lumCoeff = vec3(0.212656f, 0.715158f, 0.072186f);
		
		// Detect dark areas, depending on mapping type
		if(ColorTone.r < 0.25f && ColorTone.g < 0.25f && ColorTone.b < 0.25f)
		{
			colorTexel = t;
			ColorTone = texture(colorSampler, colorTexel);
			colorInput.rgb -= blackLevel;
		}
		
		// RGB -> XYZ conversion
		const mat3 RGB2XYZ = mat3(0.4124564f,  0.3575761f,  0.1804375f,
								  0.2126729f,  0.7151522f,  0.0721750f,
								  0.0193339f,  0.1191920f,  0.9503041f);
								  
		vec3 XYZ = colorInput.rgb*RGB2XYZ;
	  
		// XYZ -> Yxy conversion
		vec3 Yxy = lumCoeff;
		Yxy.r = XYZ.g;                            // copy luminance Y
		Yxy.g = XYZ.r / (XYZ.r + XYZ.g + XYZ.b ); // x = X / (X + Y + Z)
		Yxy.b = XYZ.g / (XYZ.r + XYZ.g + XYZ.b ); // y = Y / (X + Y + Z)
		
		// (Lp) Map average luminance to the middlegrey zone by scaling pixel luminance
		float Lp = Yxy.r * exposure / avgLuminance;                       
		// (Ld) Scale all luminance within a displayable range of 0 to 1
		Yxy.r = (Lp * (1.0f + Lp/(whitePoint * whitePoint)))/(1.0f + Lp);
	  
		// Yxy -> XYZ conversion
		XYZ.r = Yxy.r * Yxy.g / Yxy. b;               // X = Y * x / y
		XYZ.g = Yxy.r;                                // copy luminance Y
		XYZ.b = Yxy.r * (1 - Yxy.g - Yxy.b) / Yxy.b;  // Z = Y * (1-x-y) / y
		
		// XYZ -> RGB conversion
		const mat3 XYZ2RGB  = mat3(3.2404542f, -1.5371385f, -0.4985314f,
								  -0.9692660f,  1.8760108f,  0.0415560f, 
								   0.0556434f, -0.2040259f,  1.0572252f);
								   
		colorInput.rgb = XYZ*XYZ2RGB;
		colorInput.a = RGBLuminance(colorInput);
		
		return colorInput;
	}
#endif

/*------------------------------------------------------------------------------
						[DYNAMIC VIBRANCE CODE SECTION]
						[Adapted from CeeJay's SweetFX]
	[http://www.guru3d.com/files_details/sweetfx_shader_suite_download.html]
------------------------------------------------------------------------------*/

#if (INTELLI_VIBRANCE == 1)
	vec4 VibrancePass(vec4 vibranceColor, vec2 t)
	{
		vec4 color = vibranceColor; //original input color
		vec3 lumCoeff = vec3(0.2126, 0.7152, 0.0722);  //Values to calculate luma with

		float luma = dot(lumCoeff, color.rgb); //calculate luma (grey)
		
		float max_color = max(vibranceColor.r, max(vibranceColor.g,vibranceColor.b)); //Find the strongest color
		float min_color = min(vibranceColor.r, min(vibranceColor.g,vibranceColor.b)); //Find the weakest color
		
		float color_saturation = max_color - min_color; //The difference between the two is the saturation
	  
		color.rgb = mix(vec3(luma), color.rgb, (1.0 + (Vibrance * (1.0 - (sign(Vibrance) * color_saturation))))); //extrapolate between luma and original by 1 + (1-saturation) - current
		color.a = RGBLuminance(vibranceColor);
	  
		//color.rgb = mix(luma, color.rgb, 1.0 + (1.0-pow(color_saturation, 1.0 - (1.0-Vibrance))) ); //pow version
	  
		return color; //return the result
	}
#endif

#if (LIGHT_ATTENUATION == 1)
smooth out vec3 normal;
smooth out vec3 tangent;
smooth out vec3 binormal;
smooth out vec4 pos;
smooth out vec4 vertexColor;

uniform sampler2D diffuse;
uniform sampler2D normal_map;
uniform sampler2D specular_map;

uniform vec4 diffuse_color = vec4(1.0, 1.0, 1.0 , 1.0);
uniform vec4 specular_color = vec4(1.0, 1.0, 1.0 , 1.0);
uniform vec4 ambient_color;
	
	float calculateAttenuation(in int i, in float dist)
	{
		return(1.0 / (gl_LightSource[i].constantAttenuation +
					  gl_LightSource[i].linearAttenuation * dist +
					  gl_LightSource[i].quadraticAttenuation * dist * dist));
	}

	void pointLight(in int i, in vec3 bump, in vec3 normal, in vec3 tangent, in vec3 binormal, in vec4 pos, inout vec4 diffuse, inout vec4 specular)
	{
		vec4 color = diffuse_color;
		vec4 matspec = specular_color;
		float shininess = shininess;
		vec4 lightspec = gl_LightSource[i].specular;
		vec4 lpos = gl_LightSource[i].position;
		
		vec3 tmpVec = lpos.xyz - pos.xyz;	
		lpos.x = dot(tmpVec, tangent);
		lpos.y = dot(tmpVec, binormal);
		lpos.z = dot(tmpVec, normal);	
		
		float distSqr = dot(lpos.xyz, lpos.xyz);
		vec3 lVec = lpos.xyz * inversesqrt(distSqr);
		
		tmpVec = -pos.xyz;
		vec3 v;
		v.x = dot(tmpVec, tangent);
		v.y = dot(tmpVec, binormal);
		v.z = dot(tmpVec, normal);	
		
		v = normalize(v);

		float nDotL = dot(lVec, bump);
		if(nDotL > 0.0)
		{
			float dist = length(lpos.xyz);    
			float attenuation = calculateAttenuation(i, dist);

			diffuse  += color * max(0.0, nDotL) * gl_LightSource[i].diffuse * attenuation;

		  if (shininess != 0.0)
		  {
			specular += lightspec * matspec * pow(clamp(dot(reflect(-lVec, bump), v),0.0,1.0), shininess) * attenuation;
		  }
		}
	}

	void spotLight(in int i, in vec3 bump, in vec3 normal, in vec3 tangent, in vec3 binormal, in vec4 pos, inout vec4 diffuse, inout vec4 specular)
	{
		vec4 color = diffuse_color;
		vec4 matspec = specular_color;
		float shininess = shininess;
		vec4 lightspec = gl_LightSource[i].specular;
		vec4 lpos = gl_LightSource[i].position;
		
		vec3 tmpVec = lpos.xyz - pos.xyz;	
		lpos.x = dot(tmpVec, tangent);
		lpos.y = dot(tmpVec, binormal);
		lpos.z = dot(tmpVec, normal);	
		
		float distSqr = dot(lpos.xyz, lpos.xyz);
		vec3 lVec = lpos.xyz * inversesqrt(distSqr);
		
		tmpVec = -pos.xyz;
		vec3 v;
		v.x = dot(tmpVec, tangent);
		v.y = dot(tmpVec, binormal);
		v.z = dot(tmpVec, normal);	
		
		v = normalize(v);
		
		tmpVec = gl_LightSource[i].spotDirection.xyz;
		vec3 lDir;
		lDir.x = dot(tmpVec, tangent);
		lDir.y = dot(tmpVec, binormal);
		lDir.z = dot(tmpVec, normal);	
		
		lDir = normalize(lDir);

		
		float cos_outer_cone_angle = gl_LightSource[i].spotExponent;
		float cos_cur_angle = dot(-lDir, lVec);
		float cos_inner_cone_angle = gl_LightSource[i].spotCosCutoff;

		float cos_inner_minus_outer_angle = cos_inner_cone_angle - cos_outer_cone_angle;
		float spot = clamp((cos_cur_angle - cos_outer_cone_angle) / cos_inner_minus_outer_angle, 0.0, 1.0);
				

		float nDotL = dot(lVec, bump);
		if(nDotL > 0.0)
		{
			float dist = length(lpos.xyz);    
			float attenuation = calculateAttenuation(i, dist);

			diffuse  += color * max(0.0, nDotL) * gl_LightSource[i].diffuse * attenuation * spot;

		  if (shininess != 0.0)
		  {
			specular += lightspec * matspec * pow(clamp(dot(reflect(-lVec, bump), v),0.0,1.0), shininess) * attenuation * spot;
		  }
		}
	}

	void doLights(in int NUM_LIGHTS, in vec3 bump, in vec3 normal, in vec3 tangent, in vec3 binormal, in vec4 pos, inout vec4 diffuse, inout vec4 specular)
	{
		for (int i = 0; i < NUM_LIGHTS; i++) {
			if (gl_LightSource[i].spotCutoff == 180.0) {
				pointLight(i, bump, normal, tangent, binormal, pos, diffuse, specular);
			} else {
				spotLight(i, bump, normal, tangent, binormal, pos, diffuse, specular);
			}
		}
	}

	vec4 pointLights(vec4 pointColor, vec2 txCoord)
	{
		txCoord = texcoord;
		
		vec4 diffuse_val  = vec4(0.0);
		vec4 specular_val = vec4(0.0);
		
		
		
		vec3 bump = normalize( texture(normal_map, txCoord).xyz * 2.0 - 1.0);
			
		doLights(16, bump, normal, tangent, binormal, pos, diffuse_val, specular_val);
		
		specular_val.xyz *= texture(specular_map, txCoord).xyz * gl_FrontMaterial.specular.a;
		
		diffuse_val.a = 1.0;
		specular_val.a = 1.0;
			
		vec4 texColor = texture(diffuse, txCoord);
		
		vec2 light_pos = vec2(0,0);
		vec4 light = texture(colorSampler, texcoord);
			
		pointColor = (diffuse_val  * texColor * vertexColor) +
					 (specular_val * 1.0)+
					 (ambient_color * texColor * vertexColor);

		if(light.r > 0.25f && light.g > 0.25f && light.b > 0.25f)
		{
			light_pos = texcoord;
			light = texture(colorSampler, light_pos);
			pointColor.rgb += (light.rgb*0.0333f);
		}
		
		// fog
		const float LOG2 = 1.442695;
		float z = gl_FragCoord.z / gl_FragCoord.w;
		float fogFactor = exp2( -gl_Fog.density * 
					   gl_Fog.density * 
					   z * 
					   z * 
					   LOG2 );

		fogFactor = clamp(fogFactor, 0.0, 1.0);

		pointColor = mix(gl_Fog.color, pointColor, fogFactor);
		pointColor.a = diffuse_color.a * texColor.a;	

		return pointColor;
	}
#endif

/*------------------------------------------------------------------------------
                      [MAIN() & COMBINE PASS CODE SECTION]
------------------------------------------------------------------------------*/

void main()
{
	vec4 baseColor = texture(colorSampler, texcoord, 0);
	baseColor.a = dot(baseColor.rgb, vec3(0.212656f, 0.715158f, 0.072186f));
	vec4 lumFactor = texture(LumaSampler, texcoord, 0);

	#if (SPRITE_INTERPOLATION == 1)
		baseColor = getSpI(baseColor, texcoord);
	#endif
	
	#if (NFAA_ENABLED == 1)
		baseColor = getNFAA(baseColor, texcoord);
	#endif
	
	#if (SSAA_ENABLED == 1)
		baseColor = getSSAA(baseColor, texcoord);
	#endif
	
	#if (FXAA_ENABLED == 1)
		baseColor = getFXAA(baseColor, texcoord);
	#endif
	
	#if (LUMA_SHARPEN == 1)
		baseColor = LumaSharpen(baseColor, texcoord);
	#endif
	
	#if (INTELLI_VIBRANCE == 1)
		baseColor = VibrancePass(baseColor, texcoord);
	#endif
	
	#if (HDR_BLOOM_PASS == 1)
		baseColor = BloomPass(baseColor, texcoord);
	#endif
	
	#if (HDR_GAMMA_PASS == 1)
		baseColor = LumCorrectPass(baseColor, texcoord);
	#endif
	
	#if (HDR_TONEMAP_PASS == 1)
		baseColor = TonemapPass(baseColor, texcoord);
	#endif
	
	#if (DYNAMIC_CONTRAST == 1)
		baseColor = CurvesPass(baseColor, texcoord);
	#endif
	
	#if (LIGHT_ATTENUATION == 1)
		baseColor += pointLights(baseColor, texcoord);
	#endif
	
	#if (SUBPIXEL_DITHERING == 1)
		baseColor = Dither(baseColor, texcoord);
	#endif
	
	float lumPoint = dot(baseColor.rgb, vec3(0.2126, 0.7152, 0.0722));
	
	int rOffset = int(baseColor.r * 1024); 
	lumFactor.r = texelFetchBuffer(lumCurveSampler, rOffset).r;
	
	int gOffset = int(baseColor.g * 1024); 
	lumFactor.g = texelFetchBuffer(lumCurveSampler, gOffset).r;

	int bOffset = int(baseColor.b * 1024); 
	lumFactor.b = texelFetchBuffer(lumCurveSampler, bOffset).r;
		
	lumFactor.a = RGBLuminance(baseColor);
	
	lumFactor = clamp(lumFactor, 0.0f, 1.0f);
	baseColor = clamp(baseColor, 0.0f, 1.0f);
	
	gl_FragData[0] = baseColor;
	gl_FragData[1] = vec4(lumPoint, lumPoint, lumPoint, 1.0f);
	gl_FragData[2] = lumFactor;
}
