/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RealVision ENB Post Processing effects
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

DNI separated VIBRANCY, SHARPENING AND FXAA

vibrancy & FXAA code by Soulwynd
edited and adjusted by SkyrimTuner

THIS IS HLSL FILE FORMAT FOR EXECUTING ADDITIONAL
POST PROCESSING EFFECTS. MAKE THE COPY BEFORE CHANGING IT!
*/

// DEFINES
#define ESHARPENING		//enable sharpening
#define ESHARPENINGCOLOR 	//if defined, color sharpen, otherwise sharp by gray
#define VIBRANCE		//enable vibrancy
//#define ENOISE		//enable noise in dark areas
//#define FFXAA			//enable FXAA
//#define FFXAA_Adaptive_Q	//enable adaptive quality for the AA

#ifdef VIBRANCE 
float	VibranceMultDAY		<string UIName="Vibrance - DAY"; string UIWidget="Spinner";>		= {0.00};
float	VibranceMultNIGHT	<string UIName="Vibrance - NIGHT"; string UIWidget="Spinner";>		= {0.00};
float	VibranceMultINT		<string UIName="Vibrance - INTERIOR"; string UIWidget="Spinner";>	= {0.00};
#endif

#ifdef FFXAA
bool	ShowEdge <string UIName="Show Edge"; int UIMin=0; int UIMax=1;> = 0;
float	EdgeMult <string UIName="Edge Detection"; string UIWidget="Spinner"; float UIMin=0.0; float UIMax=1.0;>			= {0.92};
float	AAMult <string UIName="AA Strength"; string UIWidget="Spinner"; float UIMin=0.0; float UIMax=1.0;>			= {0.67};
  #ifdef FFXAA_Adaptive_Q
    int	MaxQuality <string UIName="Maximum Quality"; string UIWidget="Spinner"; int UIMin=1; int UIMax=10;>			= {1};
    float QualityThreshold <string UIName="Border Threshold"; string UIWidget="Spinner"; float UIMin=0.0; float UIMax=1.0;>	= {0.15};
  #endif
#endif

#ifdef ESHARPENING
float	SharpeningAmount	<string UIName="Sharpening Amount";string UIWidget="Spinner";float UIMin=0.0;float UIMax=10.0;>	= {2.0};
float	SamplingRange		<string UIName="Sharpening Range";string UIWidget="Spinner";float UIMin=0.0;float UIMax=4.0;>	= {1.0};
#endif

#ifdef ENOISE
float	NoiseAmount		<string UIName="Noise Amount";string UIWidget="Spinner";float UIMin=0.0;float UIMax=10.0;>	= {0.0};
#endif

// VARS
float ScanLineAmount=0.0;
float ScanLineRepeat=0.0; //0.5, 0.3333, 0.25, 0.125, so on

#define E_SHADER_3_0

//keyboard controled variables
float tempF1;
float tempF2;
float tempF3;
float tempF4;
float tempF5;
float tempF6;
float tempF7;
float tempF8;
float tempF9;
float tempF0;

//global variables, already set before executing this code
float ScreenScaleY; //screen proportions (1.333 for 1920/1080)
float4 ScreenSize;
float EInteriorFactor;
float ENightDayFactor;

//textures
texture2D texColor;
texture2D texNoise;

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

sampler2D SamplerNoise = sampler_state
{
Texture   = <texNoise>;
MinFilter = POINT;
MagFilter = POINT;
MipFilter = NONE;//NONE;
AddressU  = Wrap;
AddressV  = Wrap;
SRGBTexture=FALSE;
MaxMipLevel=0;
MipMapLodBias=0;
};

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

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

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
VS_OUTPUT_POST VS_PostProcess(VS_INPUT_POST IN)
{
VS_OUTPUT_POST OUT;

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;
}


float4 PS_PostProcess(VS_OUTPUT_POST IN, float2 vPos : VPOS) : COLOR
{
float4 res;
float4 coord=0.0;

coord.xy=IN.txcoord.xy;
float4 origcolor;

coord.w=0.0;

origcolor=tex2Dlod(SamplerColor, coord);

float2 offset[8]=
{
 float2(1.0, 1.0),
 float2(-1.0, -1.0),
 float2(-1.0, 1.0),
 float2(1.0, -1.0),

 float2(1.41, 0.0),
 float2(-1.41, 0.0),
 float2(0.0, 1.41),
 float2(0.0, -1.41)
};
int i=0;

float4 tcol=origcolor;
float invscreensize=1.0/ScreenSize;
//for (i=0; i<8; i++) //higher quality
for (i=0; i<4; i++)
{
 float2 tdir=offset[i].xy;
 coord.xy=IN.txcoord.xy+tdir.xy*invscreensize*SamplingRange;//*1.0;
 float4 ct=tex2Dlod(SamplerColor, coord);

 tcol+=ct;
}
tcol*=0.2; // 1.0/(4+1)
//tcol*=0.111; // 1.0/(8+1)  //higher quality

//sharpening
#ifdef ESHARPENING

	#ifdef ESHARPENINGCOLOR 	//color
	  res=origcolor*(1.0+((origcolor-tcol)*SharpeningAmount));
	#else				//non color
	  float difffact=dot((origcolor.xyz-tcol.xyz), 0.333);
	  res=origcolor*(1.0+difffact*SharpeningAmount);
	#endif

	//less sharpening for bright pixels
	float rgray=origcolor.z; //blue fit well
	//float rgray=max(origcolor.x, max(origcolor.y, origcolor.z));
	rgray=pow(rgray, 3.0);
	res=lerp(res, origcolor, saturate(rgray));

#endif

//grain noise
#ifdef ENOISE
	float origgray=max(res.x, res.y);//dot(res.xyz, 0.333);
	origgray=max(origgray, res.z);
	coord.xy=IN.txcoord.xy*16.0 + origgray;
	float4 cnoi=tex2Dlod(SamplerNoise, coord);
	res=lerp(res, (cnoi.x+0.5)*res, NoiseAmount*saturate(1.0-origgray*1.8));
#endif

//FFXAA
#ifdef FFXAA

	const float2 tap[4] = {
		float2(-1.0, -1.0),
		float2( 1.0, -1.0),
		float2( 1.0,  1.0),
		float2(-1.0,  1.0)
	};
	const int2 weight[4] = {
		int2(1, 0),
		int2(0, 1),
		int2(-1, 0),
		int2(0, -1)
	};
	float2 pixelSize=ScreenSize.y;
	pixelSize.y*=ScreenSize.z;
	
	float2 edge;
	float3 blur;
	
	#ifdef FFXAA_Adaptive_Q
	float3 localtap;
	int mq = min(MaxQuality, 10);
	
	for(int q = 1; q <= mq; q++) {
		blur = 0.0;
		edge = 0.0;
		for(int i=0; i < 4; i++) {
			localtap = tex2D(SamplerColor, coord + tap[i] * pixelSize * (3 - 2 / (float)q)).rgb;
			blur += localtap;
			edge += dot(localtap, 0.33333) * weight[i];
		}
	
		blur /= 4;
		edge *= EdgeMult / q;
		edge = sqrt(pow(edge.x, 2) + pow(edge.y, 2));
		res.rgb = lerp(res.rgb, blur, edge.x * AAMult);
		if (edge.x < QualityThreshold) break;
	}
	#else
	for(int i=0; i < 4; i++) {
		res = tex2D(SamplerColor, coord + tap[i] * pixelSize);
		blur += res.rgb;
		edge += dot(res.rgb, 0.33333) * weight[i];
	}
	
	blur /= 4;
	edge *= EdgeMult;
	edge = sqrt(pow(edge.x, 2) + pow(edge.y, 2));
	res.rgb = lerp(res.rgb, blur, edge.x * AAMult);
	#endif
	
	if (ShowEdge) res.rgb = edge.x;
	
#endif

//vibrancy
#ifdef VIBRANCE

	const float VibranceMult = (EInteriorFactor) ? VibranceMultINT : lerp(VibranceMultNIGHT, VibranceMultDAY, ENightDayFactor);
	float4 lumasat; //x = luma, y = max color, z = min color, w = saturation
	lumasat.x = dot(res.rgb, float3(0.2126, 0.7152, 0.0722));
	lumasat.y = max(max(res.r, res.g), res.b);
	lumasat.z = min(min(res.r, res.g), res.b);
	lumasat.w = lumasat.y - lumasat.z;
	res.rgb = lerp(lumasat.x, res.rgb, (1.0 + (VibranceMult * (1.0 - (sign(VibranceMult) * lumasat.w)))));

#endif

res.w = 1.0;
return res;
}



//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
technique PostProcess
{
   pass P0
   {

VertexShader = compile vs_3_0 VS_PostProcess();
PixelShader  = compile ps_3_0 PS_PostProcess();

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