#version 130

in vec4 posPos;

uniform sampler2D tex0; // 0
//uniform float vx_offset;
uniform vec2 dt;
uniform float FXAA_SPAN_MAX = 8.;
uniform float FXAA_REDUCE_MUL = 1. / 8.;

#define FxaaInt2 ivec2
//#define vec2 vec2

vec3 FxaaPixelShader(vec4 posPos, // Output of FxaaVertexShader interpolated across screen.
					sampler2D tex, // Input texture.
					vec2 rcpFrame) { // Constant {1.0/frameWidth, 1.0/frameHeight}.
/*---------------------------------------------------------*/
//	#define FXAA_REDUCE_MIN   (1. / 128.)
	#define FXAA_REDUCE_MIN   (1. / 128.)
	//#define FXAA_REDUCE_MUL   (1.0/8.0)
	//#define FXAA_SPAN_MAX     8.0
/*---------------------------------------------------------*/
	vec3 rgbNW = texture2D(tex, posPos.zw).xyz;
	vec3 rgbNE = textureOffset(tex, posPos.zw, ivec2(1, 0)).xyz;
	vec3 rgbSW = textureOffset(tex, posPos.zw, ivec2(0, 1)).xyz;
	vec3 rgbSE = textureOffset(tex, posPos.zw, ivec2(1, 1)).xyz;
	vec3 rgbM  = texture2D(tex, posPos.xy).xyz;
/*---------------------------------------------------------*/
	vec3 luma = vec3(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)));
/*---------------------------------------------------------*/
	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. / (min(abs(dir.x), abs(dir.y)) + dirReduce);
	dir = min(vec2( FXAA_SPAN_MAX,  FXAA_SPAN_MAX),
		max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),
		dir * rcpDirMin)) * rcpFrame.xy;
/*--------------------------------------------------------*/
	vec3 rgbA = (1. / 2.) * (
		texture2D(tex, posPos.xy + dir * (1. / 3. - 0.5)).xyz +
		texture2D(tex, posPos.xy + dir * (2. / 3. - 0.5)).xyz);
	vec3 rgbB = rgbA * (1. / 2.) + (1. / 4.) * (
		texture2D(tex, posPos.xy + dir * (0. / 3. - 0.5)).xyz +
		texture2D(tex, posPos.xy + dir * (3. / 3. - 0.5)).xyz);
	float lumaB = dot(rgbB, luma);
	if ((lumaB < lumaMin) || (lumaB > lumaMax)) return rgbA;
	return rgbB;
}


vec3 Fxaa3PixelShader (
	// {xy} = center of pixel
	vec4 pos,
	// {xyzw} = not used on FXAA3 Quality
	//vec4 posPos,
	// {rgb_} = color in linear or perceptual color space
	// {__a} = luma in perceptual color space (not linear)
	sampler2D tex,
	// This must be from a constant/uniform.
	// {x_} = 1.0/screenWidthInPixels
	// {_y} = 1.0/screenHeightInPixels
	vec2 rcpFrame
	// {xyzw} = not used on FXAA3 Quality
	//vec4 rcpFrameOpt
) {
/*--------------------------------------------------------------------------*/
	//#if (FXAA_GATHER4_ALPHA == 1)
		/*vec4 luma4A = FxaaTexOffAlpha4(tex, pos.xy, FxaaInt2(-1, -1), rcpFrame.xy);
		#if (FXAA_DISCARD == 0)
			vec4 rgbyM = texture2D(tex, pos.xy);
		#endif
		vec4 luma4B = FxaaTexAlpha4(tex, pos.xy, rcpFrame.xy);
		float lumaNE = textureOffset(tex, pos.xy, ivec(1, -1)).w;
		float lumaSW = textureOffset(tex, pos.xy, ivec(-1, 1)).w;
		float lumaNW = luma4A.w;
		float lumaN  = luma4A.z;
		float lumaW  = luma4A.x;
		float lumaM  = luma4A.y;
		float lumaE  = luma4B.z;
		float lumaS  = luma4B.x;
		float lumaSE = luma4B.y;*/
	//#else
		vec3 luma = vec3(0.299, 0.587, 0.114);
		float lumaN = dot(textureLodOffset(tex, pos.xy, 0., ivec2(0, -1)).rgb, luma);
		float lumaW = dot(textureLodOffset(tex, pos.xy, 0., ivec2(-1, 0)).rgb, luma);
		vec4 rgbyM = texture2D(tex, pos.xy);
		float lumaE = dot(textureLodOffset(tex, pos.xy, 0., ivec2( 1, 0)).rgb, luma);
		float lumaS = dot(textureLodOffset(tex, pos.xy, 0., ivec2( 0, 1)).rgb, luma);
		float lumaM = dot(rgbyM.rgb, luma);
	//#endif
/*--------------------------------------------------------------------------*/
	float rangeMin = min(lumaM, min(min(lumaN, lumaW), min(lumaS, lumaE)));
	float rangeMax = max(lumaM, max(max(lumaN, lumaW), max(lumaS, lumaE)));
	float range = rangeMax - rangeMin;
	//return vec4(range);
/*--------------------------------------------------------------------------*/
	#define FXAA_QUALITY_EDGE_THRESHOLD_MIN (1.0/12.0)
	#define FXAA_QUALITY_EDGE_THRESHOLD (1.0/6.0)
	if(range < max(FXAA_QUALITY_EDGE_THRESHOLD_MIN, rangeMax * FXAA_QUALITY_EDGE_THRESHOLD))
		/*#if (FXAA_DISCARD == 1)
			FxaaDiscard;
		#else*/
			return rgbyM.rgb;
			//return vec3(0);
		//#endif
/*--------------------------------------------------------------------------*/
	//#if (FXAA_GATHER4_ALPHA == 0)
		float lumaNW = dot(textureLodOffset(tex, pos.xy, 0., ivec2(-1,-1)).rgb, luma);
		float lumaNE = dot(textureLodOffset(tex, pos.xy, 0., ivec2( 1,-1)).rgb, luma);
		float lumaSW = dot(textureLodOffset(tex, pos.xy, 0., ivec2(-1, 1)).rgb, luma);
		float lumaSE = dot(textureLodOffset(tex, pos.xy, 0., ivec2( 1, 1)).rgb, luma);
	//#endif
/*--------------------------------------------------------------------------*/
	#define FXAA_QUALITY_SUBPIX_CAP (3.0/4.0)
	//#define FXAA_QUALITY_SUBPIX_CAP 0
	#define FXAA_QUALITY_SUBPIX_TRIM (1.0/4.0)
	#define FXAA_QUALITY_SUBPIX_TRIM_SCALE  (1.0/(1.0 - FXAA_QUALITY_SUBPIX_TRIM))
/*--------------------------------------------------------------------------*/
	float lumaL = (lumaN + lumaW + lumaE + lumaS) * 0.25;
	float rangeL = abs(lumaL - lumaM);
	float blendL = clamp((rangeL / range) - FXAA_QUALITY_SUBPIX_TRIM, 0., 1.) * FXAA_QUALITY_SUBPIX_TRIM_SCALE;
	blendL = min(FXAA_QUALITY_SUBPIX_CAP, blendL);
/*--------------------------------------------------------------------------*/
	float edgeVert =
			abs(lumaNW + (-2.0 * lumaN) + lumaNE) +
		10.0 * abs(lumaW  + (-2.0 * lumaM) + lumaE ) +
			abs(lumaSW + (-2.0 * lumaS) + lumaSE);
	float edgeHorz =
			abs(lumaNW + (-2.0 * lumaW) + lumaSW) +
		10.0 * abs(lumaN  + (-2.0 * lumaM) + lumaS ) +
			abs(lumaNE + (-2.0 * lumaE) + lumaSE);
	//return vec3(edgeHorz);
	//float edgeVert = abs(lumaS - lumaN);
	//float edgeHorz = abs(lumaE - lumaW);
	bool horzSpan = edgeHorz >= edgeVert;
/*--------------------------------------------------------------------------*/
	float lengthSign = horzSpan ? -rcpFrame.y : -rcpFrame.x;
	//if (edgeHorz == edgeVert) ;//lengthSign = 0.;
	if(!horzSpan) {
		lumaN = lumaW;
		lumaS = lumaE;
	}
	float gradientN = abs(lumaN - lumaM);
	float gradientS = abs(lumaS - lumaM);
	lumaN = (lumaN + lumaM) * 0.5;
	lumaS = (lumaS + lumaM) * 0.5;
/*--------------------------------------------------------------------------*/
	bool pairN = gradientN >= gradientS;
	if(!pairN) {
		lumaN = lumaS;
		gradientN = gradientS;
		lengthSign = -lengthSign;
	}
	vec2 posN;
	posN = pos.xy + (horzSpan ? vec2(0.0, lengthSign * 0.5) : vec2(lengthSign * 0.5, 0.0));
	//posN.x = pos.x + (horzSpan ? 0.0 : lengthSign * 0.5);
	//posN.y = pos.y + (horzSpan ? lengthSign * 0.5 : 0.0);
/*--------------------------------------------------------------------------*/
	#define FXAA_SEARCH_STEPS     8
	#define FXAA_SEARCH_THRESHOLD (1.0/8.0)
/*--------------------------------------------------------------------------*/
	gradientN *= FXAA_SEARCH_THRESHOLD;
/*--------------------------------------------------------------------------*/
	vec2 posP = posN;
	vec2 offNP = horzSpan ? vec2(rcpFrame.x, 0.0) : vec2(0.0, rcpFrame.y);
	float lumaEndN = 0.;
	float lumaEndP = 0.;
	bool doneN = false;
	bool doneP = false;
	posN += offNP * (-1.5);
	posP += offNP * ( 1.5);
	for(int i = 0; i < FXAA_SEARCH_STEPS; i++) {
		lumaEndN = dot(texture2DLod(tex, posN.xy, 0.).rgb, luma);
		lumaEndP = dot(texture2DLod(tex, posP.xy, 0.).rgb, luma);
		bool doneN2 = abs(lumaEndN - lumaN) >= gradientN;
		bool doneP2 = abs(lumaEndP - lumaN) >= gradientN;
		if(doneN2 && !doneN) posN += offNP;
		if(doneP2 && !doneP) posP -= offNP;
		if(doneN2 && doneP2) break;
		doneN = doneN2;
		doneP = doneP2;
		if(!doneN) posN -= offNP * 2.0;
		if(!doneP) posP += offNP * 2.0;
	}
/*--------------------------------------------------------------------------*/
	float dstN = horzSpan ? pos.x - posN.x : pos.y - posN.y;
	float dstP = horzSpan ? posP.x - pos.x : posP.y - pos.y;
/*--------------------------------------------------------------------------*/
	bool directionN = dstN < dstP;
	lumaEndN = directionN ? lumaEndN : lumaEndP;
/*--------------------------------------------------------------------------*/
	if(((lumaM - lumaN) < 0.0) == ((lumaEndN - lumaN) < 0.0))
		lengthSign = 0.0;
/*--------------------------------------------------------------------------*/
	float spanLength = (dstP + dstN);
	dstN = directionN ? dstN : dstP;
	float subPixelOffset = 0.5 + (dstN * (-1.0/spanLength));
	subPixelOffset += blendL * (1.0/8.0);
	subPixelOffset *= lengthSign;
	vec3 rgbF = texture2DLod(tex, pos.xy + (horzSpan ? vec2(0., subPixelOffset) : vec2(subPixelOffset, 0.)), 0.).xyz;
/*--------------------------------------------------------------------------*/
	/*#if (FXAA_LINEAR == 1)
		lumaL *= lumaL;
	#endif*/
	float lumaF = dot(rgbF, vec3(0.299, 0.587, 0.114)) + (1.0/(65536.0*256.0));
	float lumaB = mix(lumaF, lumaL, blendL);
	float scale = min(4.0, lumaB/lumaF);
	rgbF *= scale;
	return vec3(rgbF);//, lumaM.rgb);
	//, lumaM.rgb);
}


void main() {
	gl_FragColor.rgb = Fxaa3PixelShader(posPos, tex0, dt);
}


