//SAGE(San Andreas Graphics Enhancement(Улучшение Графики San Andreas-а))

//--------------------------------------------------------------------------------------
// Defines
//--------------------------------------------------------------------------------------
#define DoF
//#define LensFlares
//#define FakeGodRays //need to ajust, can not working

//#define DoF_Vignetting
//#define DoF_Manual
//#define DoF_Auto
#define DoF_PentagonShape

//--------------------------------------------------------------------------------------
// Vectors, floats and etc.
//--------------------------------------------------------------------------------------
static float2 screenRes = {1366,800};//Screen Resolution, for example 1024x768
//Lens Flares params
const float LensDownsampling = 3;

float3 treshold_rgb = {0.75,0.75,0.75};
static int NSAMPLES = 8;
float FLARE_DISPERSAL = 0.35;
float FLARE_HALO_WIDTH = 0.45;
float3 FLARE_CHROMA_DISTORTION = {0.1,0.12,0.14};
float2 image_center = float2(0.5,0.5);
float LFintensity = 2;

float _Intensity = 8;

//Fake God Rays params

float2 ScreenLightPos = {0.5,0.5};
float Density = 1.50;
static int NUM_SAMPLES = 128;
float Weight = 0.3;
float Decay = 1.0;
float SStresh = 0.95;
float SunExposure = 1.0;

//Dof params
float PI = 3.14159265;

float2 texel = {0.0009765625,0.00130208333333333333333333333333};// 1/screen resolution

float focalDepth = 150.5;
float focalLength = 80.5;
float fstop = 90.5;

float vignint =2; //vignetting intensity

float ndofstart = 0.202; //near dof blur start
float ndofdist = 0.704; //near dof blur falloff distance
float fdofstart = 1.001; //far dof blur start
float fdofdist = 3.7; //far dof blur falloff distance

float2 focus = float2(0.5,0.5);//autofocus position

float CoC = 0.100;// table is here http://en.wikipedia.org/wiki/Circle_of_confusion
float namount = 0.00004;//feel the power of noise))
float DOFdownsample = 4.0;
float maxblur = 4.5;//maximum bluring

static const int samples = 6; //samples on the first ring 
static const int rings = 2; //ring count, can't be more than 2

float threshold = 2.5;//dof brightness treshold
float gain = 0.1;

float bias = 0.1;//bokeh bias
float fringe = 0.5;

float znear = 100.0; //camera clipping start
float zfar = 3500.0; //camera clipping end

float feather = 1.8; //pentagon shape feather

//--------------------------------------------------------------------------------------
// Textures
//--------------------------------------------------------------------------------------

texture2D texColor;
texture2D texDepth;
texture2D texNoise;

//--------------------------------------------------------------------------------------
// Samplers
//--------------------------------------------------------------------------------------

sampler2D InputSampler = sampler_state
{
    Texture = (texColor);
    MinFilter = POINT;
    MagFilter = POINT;
    MipFilter = POINT;
    AddressU   = Clamp;
	AddressV   = Clamp;
	SRGBTexture=FALSE;
	MaxMipLevel=0;
	MipMapLodBias=0;
};

sampler2D SamplerDepth = sampler_state
{
	Texture   = <texDepth>;
	MinFilter = POINT;
	MagFilter = POINT;
	MipFilter = NONE;
	AddressU  = Clamp;
	AddressV  = Clamp;
	SRGBTexture=FALSE;
	MaxMipLevel=0;
	MipMapLodBias=0;
};

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

//--------------------------------------------------------------------------------------
// Structures
//--------------------------------------------------------------------------------------

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

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

//--------------------------------------------------------------------------------------
// Helper Functions
//--------------------------------------------------------------------------------------

float Luminance( float3 c )
{
	return dot( c, float3(0.22, 0.707, 0.071) );
}

float3 ComputeDnB (float2 coords)
{
	float3 Color = tex2Dlod(InputSampler,float4(coords,0,LensDownsampling)).rgb - treshold_rgb;
	return Color;
}

float2 flipTexcoords(float2 texcoords) 
{
	return -texcoords + 1.0;
}

float3 textureDistorted(
	float2 sample_center, // where we'd normally sample
	float2 sample_vector,
	float3 distortion // per-channel distortion coeffs
) {
	return float3(
		ComputeDnB(sample_center + sample_vector * distortion.r).r,
		ComputeDnB(sample_center + sample_vector * distortion.g).g,
		ComputeDnB(sample_center + sample_vector * distortion.b).b
	);
}

float vignette(float2 coord, float _int)
{
	float2 coords = coord;
	coords = (coords - 0.5) * 2.0;		
	float coordDot = dot (coords,coords);	
	return 1.0 - coordDot * _int * 0.1;
}

//--------------------------------------------------------------------------------------
// Vertex Shader Input
//--------------------------------------------------------------------------------------

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

//--------------------------------------------------------------------------------------
// Lens Flares
//--------------------------------------------------------------------------------------

float4 PS_ProcessLensFlares(VS_OUTPUT_POST IN, float2 vPos : VPOS) : COLOR 
{	
	float2 sample_vector = (image_center - IN.txcoord) * FLARE_DISPERSAL;
	float2 halo_vector = normalize(sample_vector) * FLARE_HALO_WIDTH;
	
	float3 result = textureDistorted(IN.txcoord + halo_vector,halo_vector,FLARE_CHROMA_DISTORTION).rgb;
	for (int i = 0; i < NSAMPLES; ++i) {
		float2 offset = sample_vector * float(i);
		result += textureDistorted(IN.txcoord + offset,offset,FLARE_CHROMA_DISTORTION).rgb;
	}
	result /= float(NSAMPLES);
		
	float mask = vignette(IN.txcoord,8.0);
	
	return float4(saturate(result),saturate(result).x*clamp(tex2D(SamplerNoise,IN.txcoord*0.2),0.9,1).x*mask*LFintensity);
}

float4 PS_ProcessSunShafts(VS_OUTPUT_POST IN, float2 vPos : VPOS) : COLOR 
{	
	float2 texCoord = IN.txcoord;
	// Calculate vector from pixel to light source in screen space.  
    half2 deltaTexCoord = (texCoord - ScreenLightPos.xy);  
    // Divide by number of samples and scale by control factor.  
    deltaTexCoord *= 1.0f / NUM_SAMPLES * Density;  
    // Store initial sample.  
    half3 color = max(float4(0,0,0,0),tex2D(InputSampler, texCoord)-SStresh);  
	// Set up illumination decay factor.  
	half illuminationDecay = 1.0f;  
	// Evaluate summation from Equation 3 NUM_SAMPLES iterations.  
	for (int i = 0; i < NUM_SAMPLES; i++)  
	{  
		// Step sample location along ray.  
		texCoord -= deltaTexCoord;  
		// Retrieve sample at new location.  
	    half3 sample = max(float4(0,0,0,0),tex2D(InputSampler, texCoord)-SStresh);  
		// Apply sample attenuation scale/decay factors.  
		sample *= illuminationDecay * Weight;  
		// Accumulate combined color.  
		color += sample;  
		// Update exponential decay factor.  
		illuminationDecay *= Decay;  
	}
	// Output final color with a further scale control factor.  
	return float4( saturate(color*SunExposure) , color.x);
}

//--------------------------------------------------------------------------------------
//DoF (READY TO USE)
//--------------------------------------------------------------------------------------

//Bokeh

float penta(float2 coords) //pentagonal shape
{
	float scale = float(rings) - 3.5;
	float4  HS0 = float4( -20.0, -15.0, -15.0, -20.0);
	float4  HS1 = float4( -20.0, -15.0, -15.0, -20.0);
	float4  HS2 = float4( -20.0, -15.0, -15.0, -20.0);
	float4  HS3 = float4( -20.0, -15.0, -15.0, -20.0);
	float4  HS4 = float4( -20.0, -15.0, -15.0, -20.0);
	float4  HS5 = float4( -20.0, -15.0, -15.0, -20.0);
	
	float4  one = float4(8.0, 8.0, 8.0, 8.0);
	
	float4 P = float4(coords,float2(scale, scale)); 
	
	float4 dist = float4(4.0, 4.0, 4.0, 4.0);
	float inorout = 0.0;
	
	dist.x = dot( P, HS0 );
	dist.y = dot( P, HS1 );
	dist.z = dot( P, HS2 );
	dist.w = dot( P, HS3 );
	
	dist = smoothstep( -feather, feather, dist );
	
	inorout += dot( dist, one );
	
	dist.x = dot( P, HS4 );
	dist.y = HS5.w - abs( P.z );
	
	dist = smoothstep( -feather, feather, dist );
	inorout += dist.x;
	
	return saturate( inorout );
}

float linearize(float depth)
{
	return -zfar * znear / (depth * (zfar - znear) - zfar);
}

float2 rand(float2 coord) //generating noise/pattern texture for dithering
{
	float noiseX = ((frac(3.0-coord.x*(screenRes.x/0.2))*3.25)+(frac(coord.y*(screenRes.y/0.2))*3.75))*0.1-0.2;
	float noiseY = ((frac(3.0-coord.x*(screenRes.x/0.2))*3.75)+(frac(coord.y*(screenRes.y/0.2))*3.25))*0.1-0.2;
	
	return float2(noiseX,noiseY);
}

float4 colorDof(float2 coords,float blur) //processing the sample
{
	float4 colDF = float4(1,1,1,1);
	
	colDF.x = tex2D(InputSampler,coords + float2(0.0,1.0)*texel*fringe*blur).x;
	colDF.y = tex2D(InputSampler,coords + float2(-0.866,-0.5)*texel*fringe*blur).y;
	colDF.z = tex2D(InputSampler,coords + float2(0.866,-0.5)*texel*fringe*blur).z;
	
	float3 lumcoeff = float3(0.299,0.587,0.114);
	float lum = dot(colDF.xyz,lumcoeff);
	float thresh = max((lum-threshold)*gain, 0.0);
	float3 nullcol = float3(0,0,0);
	colDF.xyz +=lerp(nullcol,colDF.xyz,thresh*blur);
	return colDF;
}

float4 PS_ProcessDoFBokeh(VS_OUTPUT_POST IN, float2 vPos : VPOS) : COLOR 
{
	float depth = linearize(tex2D(SamplerDepth,IN.txcoord.xy).x);
	
	float fDepth = focalDepth;
	
	#ifdef DoF_Auto
		fDepth = linearize(tex2D(SamplerDepth,focus).x);
	#endif
	
	float blur = 2.0;
	#ifdef DoF_Manual
		float a = depth-fDepth; //focal plane
		float b = (a-fdofstart)/fdofdist; //far DoF
		float c = (-a-ndofstart)/ndofdist; //near Dof
		blur = (a>0.0)?b:c;
	#else
		float f = focalLength; //focal length in mm
		float d = fDepth*1000.0; //focal plane in mm
		float o = depth*1000.0; //depth in mm
		
		float a = (o*f)/(o-f); 
		float b = (d*f)/(d-f); 
		float c = (d-f)/(d*fstop*CoC); 
		
		blur = abs(a-b)*c;
	#endif
	blur = saturate(blur);
	float2 noise = rand(IN.txcoord.xy)*namount*blur;
	
	float w = (1.0/screenRes.x)*blur*maxblur+noise.x;
	float h = (1.0/screenRes.y)*blur*maxblur+noise.y;
	
	float4 col = float4(0,0,0,1);
	
	if(blur < 0.15) //some optimization thingy
	{
		col = tex2D(InputSampler, IN.txcoord.xy);
	}
	else
	{
	col = tex2D(InputSampler, IN.txcoord.xy);
	float s = 1.0;
	int ringsamples;
	for (int i = 1; i <= rings; i += 1)
	{
		ringsamples = i * samples;
		for (int j = 0 ; j < ringsamples ; j += 1)
		{
			float step = PI*2.0 / ringsamples;
			float pw = cos(j*step)*i;
			float ph = sin(j*step)*i;
			float p = 1.0;
			#ifdef DoF_PentagonShape 
				p = penta(float2(pw,ph));
			#endif
			col.xyz += colorDof(IN.txcoord.xy + float2(pw*w,ph*h),blur).xyz;  
			s += 1.0*lerp(1.0,i/rings,bias)*p;
		}
	}
	col = col/s; //divide by sample count
	}
	
	#ifdef DoF_Vignetting
		col *= vignette(IN.txcoord.xy,vignint);
	#endif
	
	return col;
}


//--------------------------------------------------------------------------------------
// Compiler 1
//--------------------------------------------------------------------------------------

technique PostProcess
{	
#ifdef DoF
	pass P0
	{
		VertexShader = compile vs_3_0 VS_PostProcess();
		PixelShader  = compile ps_3_0 PS_ProcessDoFBokeh();
	}
#endif
#ifdef LensFlares
	pass P1
	{
		VertexShader = compile vs_3_0 VS_PostProcess();
		PixelShader  = compile ps_3_0 PS_ProcessLensFlares();
		AlphaBlendEnable=True;
		SrcBlend = One;
		DestBlend = One;
	}
#endif
#ifdef FakeGodRays
	pass P2
	{
		VertexShader = compile vs_3_0 VS_PostProcess();
		PixelShader  = compile ps_3_0 PS_ProcessSunShafts();
		AlphaBlendEnable=True;
		SrcBlend = One;
		DestBlend = One;
	}
#endif
}