//This shader provides an automatic adaptive bloom effect based on screen brightness to scale the bloom effect
//released under creative commons license by attribution: http://creativecommons.org/licenses/by-sa/3.0/
//Shader code by Mark Blosser email: mjblosser@gmail.com website: www.mjblosser.com
string Description = "Adaptive Bloom by bond1 & TGC";
string Thumbnail = "Blur.png";
float2 ViewSize : ViewSize;
float deltatime : deltatime;

//very hacky way to get special matrices from DBP
float4x4 ViewProjection : ViewProjection;
float4x4 g_ViewProjectionMatrix : WorldViewTranspose;
float4x4 g_ViewProjectionInverseMatrix : ViewProjectionTranspose; // it passes the inverse of WorldViewProj in
float4x4 g_previousViewProjectionMatrix : WorldViewProjectionTranspose; // it passes the previous ViewProjectionMatrix in

// For Poisson Disk Gather Technique for Depth Of Field (crude but effective)
static const float2 PoissonSamples[64] =
{
    float2(-0.5119625f, -0.4827938f),
    float2(-0.2171264f, -0.4768726f),
    float2(-0.7552931f, -0.2426507f),
    float2(-0.7136765f, -0.4496614f),
    float2(-0.5938849f, -0.6895654f),
    float2(-0.3148003f, -0.7047654f),
    float2(-0.42215f, -0.2024607f),
    float2(-0.9466816f, -0.2014508f),
    float2(-0.8409063f, -0.03465778f),
    float2(-0.6517572f, -0.07476326f),
    float2(-0.1041822f, -0.02521214f),
    float2(-0.3042712f, -0.02195431f),
    float2(-0.5082307f, 0.1079806f),
    float2(-0.08429877f, -0.2316298f),
    float2(-0.9879128f, 0.1113683f),
    float2(-0.3859636f, 0.3363545f),
    float2(-0.1925334f, 0.1787288f),
    float2(0.003256182f, 0.138135f),
    float2(-0.8706837f, 0.3010679f),
    float2(-0.6982038f, 0.1904326f),
    float2(0.1975043f, 0.2221317f),
    float2(0.1507788f, 0.4204168f),
    float2(0.3514056f, 0.09865579f),
    float2(0.1558783f, -0.08460935f),
    float2(-0.0684978f, 0.4461993f),
    float2(0.3780522f, 0.3478679f),
    float2(0.3956799f, -0.1469177f),
    float2(0.5838975f, 0.1054943f),
    float2(0.6155105f, 0.3245716f),
    float2(0.3928624f, -0.4417621f),
    float2(0.1749884f, -0.4202175f),
    float2(0.6813727f, -0.2424808f),
    float2(-0.6707711f, 0.4912741f),
    float2(0.0005130528f, -0.8058334f),
    float2(0.02703013f, -0.6010728f),
    float2(-0.1658188f, -0.9695674f),
    float2(0.4060591f, -0.7100726f),
    float2(0.7713396f, -0.4713659f),
    float2(0.573212f, -0.51544f),
    float2(-0.3448896f, -0.9046497f),
    float2(0.1268544f, -0.9874692f),
    float2(0.7418533f, -0.6667366f),
    float2(0.3492522f, 0.5924662f),
    float2(0.5679897f, 0.5343465f),
    float2(0.5663417f, 0.7708698f),
    float2(0.7375497f, 0.6691415f),
    float2(0.2271994f, -0.6163502f),
    float2(0.2312844f, 0.8725659f),
    float2(0.4216993f, 0.9002838f),
    float2(0.4262091f, -0.9013284f),
    float2(0.2001408f, -0.808381f),
    float2(0.149394f, 0.6650763f),
    float2(-0.09640376f, 0.9843736f),
    float2(0.7682328f, -0.07273844f),
    float2(0.04146584f, 0.8313184f),
    float2(0.9705266f, -0.1143304f),
    float2(0.9670017f, 0.1293385f),
    float2(0.9015037f, -0.3306949f),
    float2(-0.5085648f, 0.7534177f),
    float2(0.9055501f, 0.3758393f),
    float2(0.7599946f, 0.1809109f),
    float2(-0.2483695f, 0.7942952f),
    float2(-0.4241052f, 0.5581087f),
    float2(-0.1020106f, 0.6724468f),
};

float PreBloomBoost 
<
	string UIWidget = "slider";
	float UIMax = 4.0;
	float UIMin = 0.0;
	float UIStep = 0.1;
> = 2.0f;

float BloomThreshold 
<
	string UIWidget = "slider";
	float UIMax = 1.0;
	float UIMin = 0.0;
	float UIStep = 0.05;
> = 0.9;

float PostContrast
<
	string UIWidget = "slider";
	float UIMax = 5.0;
	float UIMin = 0.0;
	float UIStep = 0.001;
> = 2.0f;

float PostBrightness
<
	string UIWidget = "slider";
	float UIMax = 1.0;
	float UIMin = 0.0;
	float UIStep = 0.001;
> = 0.4f;

float4 ScreenColor
<   string UIType = "Screen Color Effect";
> = {0.0f, 0.0f, 0.0f, 0.0f};

float4 OverallColor
<   string UIType = "Overall Color Effect";
> = {1.0f, 1.0f, 1.0f, 1.0f};

float4 Vignette
<   string UIType = "Vignette";
> = {0.0f, 0.0f, 0.0f, 0.0f};

float4 Motion
<   string UIType = "Motion";
> = {0.0f, 0.0f, 0.0f, 0.0f};

float4 DepthOfField
<   string UIType = "DepthOfField";
> = {0.0f, 0.0f, 0.0f, 0.0f};

//9 sample gauss filter, declare in pixel offsets convert to texel offsets in PS
float4 GaussFilter[9] =
{
    { -1,  -1, 0,  0.0625 },
    { -1,   1, 0,  0.0625 },
    {  1,  -1, 0,  0.0625 },
    {  1,   1, 0,  0.0625 },
    { -1,   0, 0,  0.125  },
    {  1,   0, 0,  0.125  },
    {  0,  -1, 0,  0.125 },
    {  0,   1, 0,  0.125 },
    {  0,   0, 0,  0.25 },
};

// starting scene image
// RENDERCOLORTARGET only used to inform that a part of this uses dynamic render targets
texture frame : RENDERCOLORTARGET
< 
	string ResourceName = "";
	float2 ViewportRatio = {1.0,1.0 };
>;
sampler2D frameSamp = sampler_state {
    Texture = < frame >;
    MinFilter = Linear; MagFilter = Linear; MipFilter = Linear;
    AddressU = Clamp; AddressV = Clamp;
};

texture DepthTex : RENDERCOLORTARGET
< 
	string ResourceName = "";
	float2 ViewportRatio = {1.0,1.0 };
>;
sampler2D DepthTexSamp = sampler_state 
{
    Texture = < DepthTex >;
    MinFilter = Point; 
	MagFilter = Point; 
	MipFilter = Point;
    AddressU = Clamp; 
	AddressV = Clamp;
};

//2x2 average luminosity texture
texture AvgLum2x2Img : RENDERCOLORTARGET 
< 
	string ResourceName = ""; 
	int width = 2;
	int height = 2;
>;
sampler2D AvgLum2x2ImgSamp = sampler_state {
    Texture = < AvgLum2x2Img >;
    MinFilter = Point; MagFilter = Point; MipFilter = Point;
    AddressU = Clamp; AddressV = Clamp;
};

//Average scene luminosity stored in 1x1 texture 
texture AvgLumFinal : RENDERCOLORTARGET 
< 
	string ResourceName = ""; 
	int width = 1;
	int height = 1;
>;
sampler2D AvgLumFinalSamp = sampler_state {
    Texture = < AvgLumFinal >;
    MinFilter = Point; MagFilter = Point; MipFilter = Point;
    AddressU = Clamp; AddressV = Clamp;
};

//reduce image to 1/8 size for brightpass
texture BrightpassImg : RENDERCOLORTARGET
< 
	string ResourceName = "";
	//float2 ViewportRatio = {0.125,0.125 };
	int width = 512;
	int height = 384;
	
>;
sampler2D BrightpassImgSamp = sampler_state {
    Texture = < BrightpassImg >;
    MinFilter = Linear; MagFilter = Linear; MipFilter = Linear;
    AddressU = Clamp; AddressV = Clamp;
};

//blur texture 1
texture Blur1Img : RENDERCOLORTARGET
< 
	string ResourceName = "";
	//float2 ViewportRatio = {0.125,0.125 };
	int width = 512;
	int height = 384;
	
>;
sampler2D Blur1ImgSamp = sampler_state {
    Texture = < Blur1Img >;
    MinFilter = Linear; MagFilter = Linear; MipFilter = Linear;
    AddressU = Clamp; AddressV = Clamp;
};

//blur texture 2
texture Blur2Img : RENDERCOLORTARGET
< 
	string ResourceName = "";
	//float2 ViewportRatio = {0.125,0.125 };
	int width = 512;
	int height = 384;
	
>;
sampler2D Blur2ImgSamp = sampler_state {
    Texture = < Blur2Img >;
    MinFilter = Linear; MagFilter = Linear; MipFilter = Linear;
    AddressU = Clamp; AddressV = Clamp;
};

//depth of field texture
texture DepthOfFieldImg : RENDERCOLORTARGET
< 
	string ResourceName = "";
>;
sampler2D DepthOfFieldImgSamp = sampler_state {
    Texture = < DepthOfFieldImg >;
    MinFilter = Linear; MagFilter = Linear; MipFilter = Linear;
    AddressU = Clamp; AddressV = Clamp;
};

struct input 
{
	float4 pos : POSITION;
	float2 uv : TEXCOORD0;
};
 
struct output 
{
	float4 pos: POSITION;
	float2 uv: TEXCOORD0;
};

output VS( input IN ) 
{
	output OUT;

	//quad needs to be shifted by half a pixel.
    //Go here for more info: http://www.sjbrown.co.uk/?article=directx_texels
	float4 oPos = float4( IN.pos.xy + float2( -1.0f/ViewSize.x, 1.0f/ViewSize.y ),0.0,1.0 );
	OUT.pos = oPos;
	float2 uv = (IN.pos.xy + 1.0) / 2.0;
	uv.y = 1 - uv.y; 
	OUT.uv = uv;
	
	return OUT;	
}

//takes original frame image and outputs to 2x2
float4 PSReduce( output IN, uniform sampler2D srcTex ) : COLOR
{
    float4 color = tex2D( srcTex, IN.uv );
    return color;    
}

//-----------------computes average luminosity for scene-----------------------------
float4 PSGlareAmount( output IN, uniform sampler2D srcTex ) : COLOR
{
    float4 GlareAmount = 0;
    
    //sample texture 4 times with offset texture coordinates
    float4 color1= tex2D( srcTex, IN.uv + float2(-0.5, -0.5) );
    float4 color2= tex2D( srcTex, IN.uv + float2(-0.5, 0.5) );
    float4 color3= tex2D( srcTex, IN.uv + float2(0.5, -0.5) );
    float4 color4= tex2D( srcTex, IN.uv + float2(0.5, 0.5) );
    
    //average these samples
    float3 AvgColor = saturate(color1.xyz * 0.25 + color2.xyz * 0.25 + color3.xyz * 0.25 + color4.xyz * 0.25);
    
    //convert to luminance
    AvgColor = dot(float3(0.3,0.59,0.11), AvgColor);
    GlareAmount.xyz = pow(AvgColor,2);
    
    //interpolation value to blend with previous frames
    GlareAmount.w = deltatime * 2;
       
    return GlareAmount;    
}

float4 PSBrightpass( output IN, uniform sampler2D srcTex, uniform sampler2D srcTex2  ) : COLOR
{
    //remove low luminance pixels, keeping only brightest
    float4 screen = tex2D(srcTex, IN.uv);  //original screen texture;
    float4 glaretex = tex2D(srcTex2, IN.uv);  //glareamount from 1x1 in previous pass
    float3 Brightest = saturate(screen.xyz - BloomThreshold);
    Brightest.xyz = pow(Brightest.xyz,2) * (1+glaretex.xyz) * PreBloomBoost;
    float4 color = float4(Brightest.xyz, 1);
    return color;    
}

float4 PSBlur( output IN, uniform sampler2D srcTex ) : COLOR
{
    float4 color = float4(0,0,0,0);
    
    //inverse view for correct pixel to texel mapping
    float2 ViewInv = float2( 1/ViewSize.x,1/ViewSize.y);   
    
    //sample and output average colors using gauss filter samples
    for(int i=0;i<9;i++)
    {
		float4 col = GaussFilter[i].w * tex2D(srcTex,IN.uv + float2(GaussFilter[i].x * ViewInv.x*2.5, GaussFilter[i].y *ViewInv.y*2.5));  
		color+=col;
    }
	
    return color;
}

float4 PSDepthOfField( output IN, uniform sampler2D srcTex ) : COLOR
{
    float3 color = tex2D(srcTex, IN.uv).xyz;
    float2 ViewInv = float2( 1/ViewSize.x,1/ViewSize.y);   	
	float fCircleOfConfusion = max(0,(tex2D(DepthTexSamp, IN.uv).z-DepthOfField.x)*100.0f*DepthOfField.y);
	float fInvTargetSize = 1.0f/1360.0f;
	float fContributions = 1;
	for(int p=0; p<64; p+=2)
	{
		float2 offsetuv = PoissonSamples[p] * ViewInv * fCircleOfConfusion;
		float3 currentColor = tex2D(srcTex, IN.uv+offsetuv).xyz;  
		float weight = max(0,tex2D(DepthTexSamp, IN.uv+offsetuv).y);
		color += (currentColor*weight);
		fContributions += weight;
	}
	color = color / fContributions;
    return float4(color,1);
}

float4 PSPresent( output IN, uniform sampler2D srcTex, uniform sampler2D srcTex2, uniform sampler2D srcTex3 ) : COLOR
{
	// sample screen texture with supersampled UV's
    float4 BloomMap=tex2D( srcTex2, IN.uv );
    float4 AmtMap=tex2D( srcTex3, IN.uv );
	
	// as scene gets brighter, reduce brightness effects of post process
    float ToneLuminance = dot(AmtMap.xyz, float3(0.3, 0.59, 0.11)) * 1.5;
	
	// stores both projected Z and unprojected view Z(0-8000)
	float2 zOverW = tex2D(DepthTexSamp, IN.uv).xy;
	
	// debug shader line to view depth values
	//return float4(zOverW,0,1);
	
	// Motion Blur effect calculation
	// H is the viewport position at this pixel in the range -1 to 1.  
	float4 H = float4(IN.uv.x * 2 - 1, (1 - IN.uv.y) * 2 - 1, zOverW.x, 1);  
	// Transform by the view-projection inverse.  
	float4 D = mul(H, g_ViewProjectionInverseMatrix);  
	// Divide by w to get the world position.  
	float4 worldPos = D / D.w;
	float4 currentPos = H;
	// Use the world position, and transform by the previous view-projection matrix.  
	float4 previousPos = mul(worldPos, g_previousViewProjectionMatrix);  
	// Convert to nonhomogeneous points [-1,1] by dividing by w.  
	previousPos /= previousPos.w;  
	// Use this frame's position and last frame's to compute the pixel velocity
	// and divide by two to get from -1/+1 to 0..1 (travel across UV area)
	float2 velocity = (currentPos - previousPos) / 2.0f;
	// convertion (8 bit depth creates inaccurate worldpos reproduction)
	if ( velocity.x > -0.02 && velocity.x < 0.02 ) velocity.x = 0.0f;
	if ( velocity.y > -0.02 && velocity.y < 0.02 ) velocity.y = 0.0f;
	//return float4(velocity*100,0,1);
	// this corrects the issue of reversed spiral syndrome
	velocity.y = -velocity.y;	
	// blur velocity modulated by real distance
	float fModulatedDepth = zOverW.y-(Motion.x*0.25);
	velocity = velocity * min(max(0,fModulatedDepth)*10*Motion.y,1.0f);
	
	// Motion Blur - collect (10 samples)
	float2 texCoord = IN.uv;
	float4 color = tex2D(srcTex, texCoord);  
	velocity = velocity / 10; 
	texCoord += velocity;
	float fContributions = 1;
	for(int i = 1; i < 10; ++i, texCoord += velocity)  
	{  
		// Sample the color buffer along the velocity vector.  
		float4 currentColor = tex2D(srcTex, texCoord);
		// Ignore backbuffer colors that have a depth of pure zero
		float weight = min(1,tex2D(DepthTexSamp, texCoord).y * 100000);
		// Add the current color to our color sum.  
		color += (currentColor*weight);
		fContributions += weight;
	}  
	
	// Average all of the samples to get the final blur color
	float4 ScreenMap = color / fContributions;
	
    // add results based scene pixel brightness
    float4 MaxAmount = ScreenMap + BloomMap;
    float3 final = lerp(MaxAmount, ScreenMap, ToneLuminance);

    // ***** Remmed by Myke add any screen color effect
    // final.xyz += ScreenColor.xyz;

	// overall modulation control
    final.xyz *= OverallColor.xyz;

	// Apply contrast
	final.rgb = ((final.rgb - 0.5f) * max(PostContrast, 0)) + 0.5f;

	// Apply brightness
	final.rgb += PostBrightness;
	
    // Vignetting	
	float2 xy = ((IN.uv*2)-1) * Vignette.x;
	final *= (1.0-Vignette.y) + (Vignette.y)*pow( (xy.x+1.0)*(xy.y+1.0)*(xy.x-1.0)*(xy.y-1.0), 0.25 );
	
	// final pixel color
    return float4(final,1);
}

technique dx9textured
<
	//specify where we want the original image to be put
	string RenderColorTarget = "frame";
>
{
	//1. first reduce to 2x2 and save in AvgLum2x2Img
	pass Reduce2x2
	<
		string RenderColorTarget = "AvgLum2x2Img";
	>
	{
		ZEnable = False;
		VertexShader = compile vs_3_0 VS();
		PixelShader = compile ps_3_0 PSReduce( frameSamp );
	}
	
	//2. reduce to 1x1 and save in AvgLumFinalImg, using alpha blending to blend with previous frames
	pass Reduce1x1
	<
		string RenderColorTarget = "AvgLumFinal";
	>
	{
		ZEnable = False;
		VertexShader = compile vs_3_0 VS();
		PixelShader = compile ps_3_0 PSGlareAmount( AvgLum2x2ImgSamp );
		AlphaBlendEnable = true;
		SrcBlend = SRCALPHA;
		DestBlend = INVSRCALPHA;
	}
	
	//3. remove low luminance pixels keeping only brightest for blurring in next pass
	pass Brightpass
	<
		string RenderColorTarget = "BrightpassImg";
	>
	{
		ZEnable = False;
		VertexShader = compile vs_3_0 VS();
		PixelShader = compile ps_3_0 PSBrightpass( frameSamp, AvgLumFinalSamp );
	}
	
	//4. blur texture and save in Blur1Img
	pass Blur1
	<
		string RenderColorTarget = "Blur1Img";
	>
	{
		ZEnable = False;
		VertexShader = compile vs_3_0 VS();
		PixelShader = compile ps_3_0 PSBlur( BrightpassImgSamp );
	}
	
	//5. repeat blur texture and save in Blur2Img
	pass Blur2
	<
		string RenderColorTarget = "Blur2Img";
	>
	{
		ZEnable = False;
		VertexShader = compile vs_3_0 VS();
		PixelShader = compile ps_3_0 PSBlur( Blur1ImgSamp );
	}
	
	//6. repeat blur again
	pass Blur3
	<
		string RenderColorTarget = "Blur1Img";
	>
	{
		ZEnable = False;
		VertexShader = compile vs_3_0 VS();
		PixelShader = compile ps_3_0 PSBlur( Blur2ImgSamp );
	}
	
	pass Blur4
	<
		string RenderColorTarget = "Blur2Img";
	>
	{
		ZEnable = False;
		VertexShader = compile vs_3_0 VS();
		PixelShader = compile ps_3_0 PSBlur( Blur1ImgSamp );
	}
	
	pass Blur5
	<
		string RenderColorTarget = "Blur1Img";
	>
	{
		ZEnable = False;
		VertexShader = compile vs_3_0 VS();
		PixelShader = compile ps_3_0 PSBlur( Blur2ImgSamp );
	}
	
	pass DepthOfField
	<
		string RenderColorTarget = "DepthOfFieldImg";
	>
	{
		ZEnable = False;
		VertexShader = compile vs_3_0 VS();
		PixelShader = compile ps_3_0 PSDepthOfField( frameSamp );
	}
	
	//send the combined image to the screen
	pass Present
	<
		string RenderColorTarget = "";
	>
	{
		VertexShader = compile vs_3_0 VS();
		PixelShader = compile ps_3_0 PSPresent( DepthOfFieldImgSamp, Blur1ImgSamp, AvgLumFinalSamp );
	}
}
