//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ENBSeries effect file | ENBSeries 0.101+
// http://enbdev.com
// Author: Pascal Matthäus ( Euda )
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//+++++++++++++++++++++++++++++
// USED EFFECTS
//+++++++++++++++++++++++++++++

#define ENABLE_BLOOM				1
#define	ENABLE_TONEMAP				1
#define ENABLE_UNSHARPMASK			1
#define ENABLE_FILMGRAIN			1
#define ENABLE_TECHNICOLOR			1
#define ENABLE_LENSDIRT				0
#define	ENABLE_LETTERBOX			0

//+++++++++++++++++++++++++++++
// CUSTOM PARAMETERS
//+++++++++++++++++++++++++++++

// ** BLOOM **
#define		bloomPower 				0.550	// Bloom Overall-Power - Too high values will overexpose the bloom, thus leading to a loss of detail in bright areas
#define		bloomRadius 			64		// Bloom Sample Count - Amount of samples taken, directly affects performance
#define		bloomSkipPixels			1		// Bloom Skipped Samples - Adds banding & reduces precision, therefore much higher performance. Resulting radius is bloomSkipPixels*bloomRadius
#define		bloomLumaDamp			0.600	// Bloom LumaDamping - Scales bloomPower with original images's brightness to prevent local overexposing.
#define		bloomContrast			1.550	// Bloom Contrast - The effect's contrast - the higher, the more will bloom be damped in dark areas - and vice versa
#define		bloomSaturation			3.500	// Bloom Saturation - The effect's color saturation, applied after bloomContrast

// ** TONEMAP **
#define		tonemapMode				3		// Tonemap Mode - choose a tonemapping algorithm (1/2/3), I prefer the first one 
#define		tonemapGammaCurve		0.800	// Tonemap gamma-curve - insert your monitors gamma-value, 2.2 for calibrated displays with accurate grey-levels
#define		tonemapAmount			50.000	// Tonemap amount - the amount of tonemapping to be applied to hdr-effects

// ** UNSHARP MASK **
#define		unsharpMaskA			1		// Unsharp Mask A - Small Radius, performs best for edge sharpening
#define		unsharpMaskB			1		// Unsharp Mask B - Radius = bloomRadius - performs best at local contrast enhancement, bloom has to be enabled!
#define		unsharpMaskAIntensity	0.350	// Unsharp Mask A Sharpening Intensity - the effects' intensity, be careful - I wouldn't push it futher than 0.500
#define		unsharpMaskBIntensity	0.575	// Unsharp Mask B Sharpening Intensity - the effects' intensity, be careful - I wouldn't push it futher than 0.500

// ** FILMGRAIN **
#define		grainAmount				0.015	// Grain Amount - Visibility of the filmgrain on your screen
#define		grainPower 				1.000	// Grain Power - the noise's exposure
#define		grainFreq 				0.550	// Grain Frequency - how fast the noise will change. Zero disables the frequency, thus just showing a grain-texture
#define		grainSize 				0.450	// Grain Scale - the scale of the grain-texture. Too low values will let you see the tiled pattern of the noise. Too high one's are not realistic

// ** TECHNICOLOR 3-STRIP **
#define		techniAmount			0.200	// Technicolor Strength - This parameter controls how strong the Technicolor-effect is applied, too high is not realistic
#define		techniPower				1.200	// Technicolor Power - Technically the effect's saturation but good to combine with lower techniAmounts if you like to
#define		techniPunch				0.750	// Technicolor Punch - Normally the effect's only visible on high-chroma pixels - this value helps to punch bright&dark pixels to prevent that 

// ** LENSDIRT **
#define		lensdirtIntensity		1.200	// Lensdirt Intensity - The dirt texture's maximum intensity
#define		lensdirtCurve			1.400	// Lensdirt Curve - The curve which the dirt texture's intensity scales with - try higher values to limit visibility solely to bright/almost-white scenes
#define		lensdirtSmoothmode		1		// Lensdirt Smooth-mode - If enabled, the bloom texture will be used for brightness check, thus scaling the intensity with the local luma instead of the current pixels' one (bloom has to be enabled!)

// ** LETTERBOX **
#define		letterboxSize			0.015	// Letterbox Size - The vertical range of the letterbox, 0.5 = black screen


//+++++++++++++++++++++++++++++
//external parameters, do not modify
//+++++++++++++++++++++++++++++
//keyboard controlled temporary variables (in some versions exists in the config file). Press and hold key 1,2,3...8 together with PageUp or PageDown to modify. By default all set to 1.0
float4	tempF1; //0,1,2,3
float4	tempF2; //5,6,7,8
float4	tempF3; //9,0
//x=generic timer in range 0..1, period of 16777216 ms (4.6 hours), w=frame time elapsed (in seconds)
#if (ENABLE_FILMGRAIN == 1)
float4	Timer;
#endif
//x=Width, y=1/Width, z=ScreenScaleY, w=1/ScreenScaleY
float4	ScreenSize;
static const float2 pxSize = float2(ScreenSize.y,1/(ScreenSize.x * ScreenSize.w));
static const float3 lumaCoeff = float3(0.2126f,0.7152f,0.0722f);

texture2D texColor;
#if (ENABLE_FILMGRAIN == 1)
texture2D texNoise;
#endif
#if (ENABLE_LENSDIRT == 1)
texture2D texDirt < String ResourceName="ppfx/dirt.png"; >;
#endif

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

#if (ENABLE_BLOOM == 1)
sampler2D SamplerSplitRGB = sampler_state
{
	Texture   = <texColor>;
	MinFilter = POINT;
	MagFilter = POINT;
	MipFilter = NONE;
	AddressU  = Border;
	AddressV  = Border;
	SRGBTexture=FALSE;
	MaxMipLevel=0;
	MipMapLodBias=0;
};
#endif

#if (ENABLE_FILMGRAIN == 1)
sampler2D SamplerNoise = sampler_state
{
	Texture = <texNoise>;
	MinFilter = POINT;
	MagFilter = POINT;
	MipFilter = NONE;
	AddressU = Wrap;
	AddressV = Wrap;
	SRGBTexture=FALSE;
	MaxMipLevel=0;
	MipMapLodBias=0;
};
#endif

#if (ENABLE_LENSDIRT == 1)
sampler2D SamplerDirt = sampler_state
{
	Texture = <texDirt>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = NONE;
	AddressU = Wrap;
	AddressV = Wrap;
	SRGBTexture=FALSE;
	MaxMipLevel=0;
	MipMapLodBias=0;
};
#endif

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

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

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Vertex-Shader
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

VS_OUTPUT_POST VS_PostProcess(VS_INPUT_POST IN)
{
	VS_OUTPUT_POST OUT;

	float4 pos=float4(IN.pos.x,IN.pos.y,IN.pos.z,1.0);

	OUT.vpos=pos;
	OUT.txcoord.xy=IN.txcoord.xy;

	return OUT;
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Effects
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

// *** Bloom ***
#if (ENABLE_BLOOM == 1)
	// Bloom - SplitRGB Prepass
	half4 FX_BloomSplitRGB( half4 pxInput, half2 txCoords )
	{
		pxInput.w = tex2D(SamplerSplitRGB, txCoords*2.0h).x;
		pxInput.w += tex2D(SamplerSplitRGB, txCoords*2.0h-half2(1.0h , 0.0h)).y;
		pxInput.w += tex2D(SamplerSplitRGB, txCoords*2.0h-half2(0.0h , 1.0h)).z;
		return pxInput;
	}
	
	// Bloom - Horizontal
	half FX_BloomH( half pxInput, half2 txCoords )
	{
		half2	fetchCoords = txCoords;
		half	weight = 1.0h;
		half	weightDiv = 1.0h+5.0h/(half)bloomRadius;
		half	sampleSum = 0.0h;
		bool	cornerFix = (txCoords.x < 0.5h); 
		
		[unroll (bloomRadius / 4)] for (half hOffs=1.0h; hOffs<(half)bloomRadius; hOffs+=2)
		{				
			fetchCoords = txCoords;
			if (cornerFix == true) fetchCoords.x = clamp(fetchCoords.x + (half)bloomSkipPixels * hOffs * (half)pxSize.x + 0.5h * (half)pxSize.x, 0.0h, 0.499h);
			else fetchCoords.x = clamp(fetchCoords.x + (half)bloomSkipPixels * hOffs * (half)pxSize.x + 0.5h * (half)pxSize.x, 0.5h, 1.0h);
			pxInput+=tex2D(SamplerColor, fetchCoords.xy).w * weight;
			fetchCoords = txCoords;
			if (cornerFix == true) fetchCoords.x = clamp(fetchCoords.x - (half)bloomSkipPixels * hOffs * (half)pxSize.x + 0.5h * (half)pxSize.x, 0.0h, 0.499h);
			else fetchCoords.x = clamp(fetchCoords.x - (half)bloomSkipPixels * hOffs * (half)pxSize.x + 0.5h * (half)pxSize.x, 0.5h, 1.0h);
			pxInput+=tex2D(SamplerColor, fetchCoords.xy).w * weight;
			sampleSum += 2.0h * weight;
			weight /= weightDiv;
		}
		pxInput /= sampleSum;
		
		return pxInput;
	}
	
	// Bloom - Vertical
	half FX_BloomV( half pxInput, half2 txCoords )
	{
		half2	fetchCoords = txCoords;
		half	weight = 1.0h;
		half	weightDiv = 1.0h+5.0h/(half)bloomRadius;
		half	sampleSum = 0.0h;
		bool	cornerFix = (txCoords.y < 0.5h);
		
		[unroll (bloomRadius / 4)] for (half vOffs=1.0h; vOffs<(half)bloomRadius; vOffs+=2)
		{				
			fetchCoords = txCoords;
			if (cornerFix == true) fetchCoords.y = clamp(fetchCoords.y + (half)bloomSkipPixels * vOffs * (half)pxSize.y + 0.5h * (half)pxSize.y, 0.0h, 0.499h);
			else fetchCoords.y = clamp(fetchCoords.y + (half)bloomSkipPixels * vOffs * (half)pxSize.y + 0.5h * (half)pxSize.y, 0.5h, 1.0h);
			pxInput+=tex2D(SamplerColor, fetchCoords.xy).w * weight;
			fetchCoords = txCoords;
			if (cornerFix == true) fetchCoords.y = clamp(fetchCoords.y - (half)bloomSkipPixels * vOffs * (half)pxSize.y + 0.5h * (half)pxSize.y, 0.0h, 0.499h);
			else fetchCoords.y = clamp(fetchCoords.y - (half)bloomSkipPixels * vOffs * (half)pxSize.y + 0.5h * (half)pxSize.y, 0.5h, 1.0h);
			pxInput+=tex2D(SamplerColor, fetchCoords.xy).w * weight;
			sampleSum += 2.0h * weight;
			weight /= weightDiv;
		}
		pxInput /= sampleSum;
		
		return pxInput;
	}
	
	// Bloom - MergeRGB Final Pass
	half3 FX_BloomMergeRGB( half3 pxInput, half2 txCoords )
	{
		half3	origcolor = pxInput;
		half	origLuma = (half)bloomLumaDamp*dot(origcolor.xyz,(half)lumaCoeff.xyz);
		
		pxInput.x = tex2D(SamplerColor, txCoords / 2.0h).w;
		pxInput.y = tex2D(SamplerColor, txCoords / 2.0h + half2(0.5h, 0.0h)).w;
		pxInput.z = tex2D(SamplerColor, txCoords / 2.0h + half2(0.0h, 0.5h)).w;
		
		half3 blurTexture = pxInput;
		
		pxInput = pow(pxInput,(half)bloomContrast);
		pxInput = lerp(dot(pxInput.xyz,(half)lumaCoeff.xyz),pxInput,(half)bloomSaturation);
		pxInput /= clamp(max(pxInput.x,max(pxInput.y,pxInput.z)),1.0h,100.0h);
		#if (unsharpMaskB == 1) && (ENABLE_UNSHARPMASK == 1)
			half3 detailMask = origcolor - blurTexture;
			pxInput = origcolor+pxInput*(half)bloomPower*(1.0h-origLuma)+detailMask*(half)unsharpMaskBIntensity;
		#else
			pxInput = origcolor+pxInput*(half)bloomPower*(1.0h-origLuma);
		#endif
		#if (lensdirtSmoothmode == 1) && (ENABLE_LENSDIRT == 1)
			pxInput += (half)tex2D(SamplerDirt, txCoords).xyz*pow(dot(blurTexture,(half)lumaCoeff),(half)lensdirtCurve)*lensdirtIntensity;
		#endif
		return pxInput;
	}
#endif

// *** Custom Tonemapping ***
#if (ENABLE_TONEMAP == 1)
	half3 FX_Tonemap( half3 pxInput )
	{
		#if (tonemapMode == 1)
			half3 pxHDR = saturate(pxInput);
			pxInput.xyz /= (1.0h+(half)bloomPower);
			return lerp(pxHDR,lerp(pxHDR.xyz,pow(pxInput.xyz,1.0h/(half)tonemapGammaCurve),dot(pxHDR.xyz,(half)lumaCoeff.xyz)),(half)tonemapAmount);
		#endif
		
		#if (tonemapMode == 2)
			half3 pxHDR = pxInput.xyz;
			pxInput.xyz /= (1.0h+(half)bloomPower);
			return lerp(pxHDR.xyz,pow(lerp(pxInput.xyz,pow(pxInput.xyz,((half)bloomPower+1.0h)-pxHDR),dot(saturate(pxHDR),(half)lumaCoeff.xyz)),1.0h/(half)tonemapGammaCurve),(half)tonemapAmount);
		#endif
		
		#if (tonemapMode == 3)
			half maxLuma = 1.0h+(half)bloomPower;
			half exposureDiv = log10(maxLuma+1.0h)/log10(maxLuma*2.0h*(half)tonemapAmount);
			return lerp(pxInput,pow(log10(pxInput+1.0h)/log10(pxInput+maxLuma*(half)tonemapAmount)/exposureDiv,1.0h/(half)tonemapGammaCurve),saturate(pxInput));
		#endif
	}
#endif

// *** Unsharp Mask ***
#if (ENABLE_UNSHARPMASK == 1) && (unsharpMaskA == 1)
	half3 FX_UnsharpMask( half3 pxInput, half2 txCoords )
	{
		half2 blurKernel[2] = { half2(-1.5h , 0.0h), half2( 0.5h ,  1.5h) };
		half3 blurTex = pxInput;
		[unroll (2)] for (half i = 0.0h; i<2.0h; i++) { blurTex += tex2D(SamplerColor,txCoords.xy+blurKernel[i]*pxSize.xy).xyz*(1.0h/(i+1.0h)); }
		blurTex /= 2.5h;
		return pxInput + (pxInput-blurTex) * (half)unsharpMaskAIntensity;
	}
#endif

// *** Technicolor ***
#if (ENABLE_TECHNICOLOR == 1)
	half3 FX_Technicolor( half3 pxInput )
	{
		half3 	origcolor = pxInput;
		
		half	redPower 	= pxInput.x * (max(pxInput.x,max(pxInput.y,pxInput.z))-min(pxInput.x,min(pxInput.y,pxInput.z))+techniPunch);
		half 	greenPower 	= pxInput.y * (max(pxInput.x,max(pxInput.y,pxInput.z))-min(pxInput.x,min(pxInput.y,pxInput.z))+techniPunch);
		half 	bluePower 	= pxInput.z * (max(pxInput.x,max(pxInput.y,pxInput.z))-min(pxInput.x,min(pxInput.y,pxInput.z))+techniPunch);
		
		pxInput.yz = lerp(pxInput.yz,pow(pxInput.yz,2.0h),redPower);
		pxInput.xz = lerp(pxInput.xz,pow(pxInput.xz,1.2h),greenPower);
		pxInput.xy = lerp(pxInput.xy,pow(pxInput.xy,1.75h),bluePower);
		pxInput.z *= 0.95h;
		
		half techniLuma = dot(pxInput.xyz,(half)lumaCoeff.xyz);
		return lerp(origcolor.xyz,lerp(techniLuma,pxInput.xyz,(half)techniPower),(half)techniAmount);
	}
#endif

// *** Filmgrain ***
#if (ENABLE_FILMGRAIN == 1)
	half3 FX_Filmgrain( half3 pxInput, half2 txCoords )
	{
		half	noise1 = tex2D(SamplerNoise,txCoords.xy*(1-grainSize)*16+Timer.x).x;
		half3	noise2 = tex2D(SamplerNoise,txCoords.xy*(1-grainSize)*16+Timer.x*noise1.x*10000*grainFreq);
		return	lerp(pxInput.xyz,noise2.xyz*(half)grainPower,(half)grainAmount);
	}
#endif

// *** Lensdirt ***
#if (ENABLE_LENSDIRT == 1) && (lensdirtSmoothmode == 0)
	half3 FX_Lensdirt( half3 pxInput, half2 txCoords )
	{
		return pxInput+(half)tex2D(SamplerDirt, txCoords).xyz*pow(dot(pxInput.xyz,(half)lumaCoeff),(half)lensdirtCurve)*(half)lensdirtIntensity;
	}
#endif

// *** Letterbox ***
#if (ENABLE_LETTERBOX == 1)
	half FX_Letterbox( half vCoord )
	{
		bool setBlack = (vCoord<=(half)letterboxSize) || (vCoord>=1-(half)letterboxSize);
		if (setBlack == true) return 0.0h;
		else return 1.0h;
	}
#endif

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Pixel-Shader
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

// *** Bloom-passes ***
#if (ENABLE_BLOOM == 1)
	// Bloom - Prepass
	half4 PS_BloomSplitRGB(VS_OUTPUT_POST IN, float2 vPos : VPOS) : COLOR
	{
		return half4(FX_BloomSplitRGB(tex2D(SamplerSplitRGB, IN.txcoord.xy), IN.txcoord.xy));
	}

	// Bloom - Horizontal
	half4 PS_BloomH(VS_OUTPUT_POST IN, float2 vPos : VPOS) : COLOR
	{
		half4 res;
		res.xyz = tex2D(SamplerColor, IN.txcoord.xy);
		res.w = FX_BloomH(0.0h, IN.txcoord.xy);
		return res;
	}

	// Bloom - Vertical
	half4 PS_BloomV(VS_OUTPUT_POST IN, float2 vPos : VPOS) : COLOR
	{
		half4 res;
		res.xyz = tex2D(SamplerColor, IN.txcoord.xy);
		res.w = FX_BloomV(0.0h, IN.txcoord.xy);
		return res;
	}
#endif

// *** More Effects ***
half4 PS_ProcessFX(VS_OUTPUT_POST IN, float2 vPos : VPOS) : COLOR
{
	half2 pxCoord = IN.txcoord.xy;
	half4 res;
	res.xyz = tex2D(SamplerColor,pxCoord.xy).xyz;
	res.w = 1.0h;
	
	#if (ENABLE_BLOOM == 1)
	res.xyz = FX_BloomMergeRGB(res.xyz, pxCoord.xy);
	#endif
	
	#if (ENABLE_TONEMAP == 1)
	res.xyz = FX_Tonemap(res.xyz);
	#endif
	
	#if (ENABLE_UNSHARPMASK == 1) && (unsharpMaskA == 1)
	res.xyz = FX_UnsharpMask(res.xyz, pxCoord.xy);
	#endif
	
	#if (ENABLE_TECHNICOLOR == 1)
	res.xyz = FX_Technicolor(res.xyz);
	#endif
	
	#if (ENABLE_FILMGRAIN == 1)
	res.xyz = FX_Filmgrain(res.xyz, pxCoord.xy);
	#endif
	
	#if (ENABLE_LENSDIRT == 1) && (lensdirtSmoothmode == 0)
	res.xyz = FX_Lensdirt(res.xyz, pxCoord.xy);
	#endif
	
	#if (ENABLE_LETTERBOX == 1)
	res.xyz *= FX_Letterbox(pxCoord.y);
	#endif
	
	return res;
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Techniques
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

technique PostProcess
{
	pass P0
	{	
		VertexShader = compile vs_3_0 VS_PostProcess();
		#if (ENABLE_BLOOM == 1)
			PixelShader  = compile ps_3_0 PS_BloomSplitRGB();
		#else
			PixelShader  = compile ps_3_0 PS_ProcessFX();
		#endif
		DitherEnable=FALSE;
		ZEnable=FALSE;
		CullMode=NONE;
		ALPHATESTENABLE=FALSE;
		SEPARATEALPHABLENDENABLE=FALSE;
		AlphaBlendEnable=FALSE;
		StencilEnable=FALSE;
		FogEnable=FALSE;
		SRGBWRITEENABLE=FALSE;
	}
}

#if (ENABLE_BLOOM == 1)
	technique PostProcess2
	{
		pass P0
		{	
			VertexShader = compile vs_3_0 VS_PostProcess();
			PixelShader  = compile ps_3_0 PS_BloomH();

			DitherEnable=FALSE;
			ZEnable=FALSE;
			CullMode=NONE;
			ALPHATESTENABLE=FALSE;
			SEPARATEALPHABLENDENABLE=FALSE;
			AlphaBlendEnable=FALSE;
			StencilEnable=FALSE;
			FogEnable=FALSE;
			SRGBWRITEENABLE=FALSE;
		}
	}

	technique PostProcess3
	{
		pass P0
		{	
			VertexShader = compile vs_3_0 VS_PostProcess();
			PixelShader  = compile ps_3_0 PS_BloomV();

			DitherEnable=FALSE;
			ZEnable=FALSE;
			CullMode=NONE;
			ALPHATESTENABLE=FALSE;
			SEPARATEALPHABLENDENABLE=FALSE;
			AlphaBlendEnable=FALSE;
			StencilEnable=FALSE;
			FogEnable=FALSE;
			SRGBWRITEENABLE=FALSE;
		}
	}

	technique PostProcess4
	{
		pass P0
		{	
			VertexShader = compile vs_3_0 VS_PostProcess();
			PixelShader  = compile ps_3_0 PS_ProcessFX();

			DitherEnable=FALSE;
			ZEnable=FALSE;
			CullMode=NONE;
			ALPHATESTENABLE=FALSE;
			SEPARATEALPHABLENDENABLE=FALSE;
			AlphaBlendEnable=FALSE;
			StencilEnable=FALSE;
			FogEnable=FALSE;
			SRGBWRITEENABLE=FALSE;
		}
	}
#endif