//-----------------------------------------------------------------------------
// Global variables
//-----------------------------------------------------------------------------

texture g_SourceTexture;
texture g_LuminanceTexture;
texture	g_AddaptedLuminanceTexture;
texture g_HighPassTexture;
texture g_BloomTexture;

texture g_SourceA;
texture g_SourceB;

float3 g_time;	// x = time, y = prevTime, z = dt

static const float g_fExposure = 1.3;

static const float g_fScaleDownDeltaOne = 0.002604167;	// 128x128 - 9 samples
static const float g_fScaleDownDeltaTwo = 0.015625;		// 32x32 - 4 samples
static const float g_fScaleDownDeltaThree = 0.0625;		// 8x8 - 4 samples
static const float g_fScaleDownDeltaFour = 0.25;		// 2x2 - 4 samples

#define HDR_ADAPTION_SPEED	0.0//0.01
#define HDR_MAX_IRIS		5.0	// 3.7 Higher numbers let the eye adjust to bright lights
#define HDR_MIN_IRIS		2.0 // 0.7 Lower numbers let the eye adjust to darkness 

#define HDR_BLOOM_AMOUNT	3.5
#define HDR_BRIGHT_PASS_THRESHOLD 0.6

//-----------------------------------------------------------------------------
// Texture samplers
//-----------------------------------------------------------------------------
sampler SourceSampler = 
sampler_state
{
    Texture = <g_SourceTexture>;
    MinFilter = Linear;  
    MagFilter = Linear;

    AddressU = Clamp;
    AddressV = Clamp;
};

sampler LuminanceSampler = 
sampler_state
{
    Texture = <g_LuminanceTexture>;
    MinFilter = Linear;  
    MagFilter = Linear;

    AddressU = Clamp;
    AddressV = Clamp;
};

sampler AddaptedLuminanceSampler = 
sampler_state
{
    Texture = <g_AddaptedLuminanceTexture>;
    MinFilter = Linear;  
    MagFilter = Linear;

    AddressU = Clamp;
    AddressV = Clamp;
};

sampler HighPassSampler = 
sampler_state
{
    Texture = <g_HighPassTexture>;
    MinFilter = Linear;  
    MagFilter = Linear;

    AddressU = Clamp;
    AddressV = Clamp;
};

sampler BloomSampler = 
sampler_state
{
    Texture = <g_BloomTexture>;
    MinFilter = Linear;  
    MagFilter = Linear;

    AddressU = Clamp;
    AddressV = Clamp;
};

sampler SourceASampler = 
sampler_state
{
    Texture = <g_SourceA>;
    MinFilter = Linear;  
    MagFilter = Linear;

    AddressU = Clamp;
    AddressV = Clamp;
};

sampler SourceBSampler = 
sampler_state
{
    Texture = <g_SourceB>;
    MinFilter = Linear;  
    MagFilter = Linear;

    AddressU = Clamp;
    AddressV = Clamp;
};


//-----------------------------------------------------------------------------
// Name:  
// Type: Pixel shader                                      
// Desc: 
//-----------------------------------------------------------------------------
float4 ScaleDownOne( float2 OriginalUV : TEXCOORD0, uniform float fDelta ) : COLOR
{
	// Sample 9 texels from source image, convert to grayscale then average
	
	float fTotal = 0;
	float fLogSum = 0;
	
	for (float i=-1; i<2; ++i)
	{
		for (float j=-1; j<2; ++j)
		{
			float3 vColour = tex2D(SourceSampler, OriginalUV + float2(i*fDelta, j*fDelta ) );
			
			float3 vLuminance = float3(0.27,0.67,0.06);
			//float3 vLuminance = float3(0.33,0.33,0.33);
			
			float fDot = dot ( vLuminance, vColour);
			fTotal += fDot / 3;
			fLogSum += log( fDot );
		}
	}
	
	fTotal = fTotal / 9;
	
	return float4( fTotal, fTotal, fTotal, fLogSum );
}

//-----------------------------------------------------------------------------
// Name:  
// Type: Pixel shader                                      
// Desc: 
//-----------------------------------------------------------------------------
float4 ScaleDownTwo( float2 OriginalUV : TEXCOORD0, uniform bool bScaleByCenter, uniform float fDelta ) : COLOR
{
	// Sample 4 texels from source image and average them
	
	float fTotal = 0;
	
	for (float i=-1; i<2; ++i)
	{
		for (float j=-1; j<2; ++j)
		{
			float2 fSampleUV = OriginalUV + float2(i*fDelta, j*fDelta );
			
			float fSample = tex2D(SourceSampler, fSampleUV ).r;
			
			if (bScaleByCenter)
			{
				float fScale = 3.38f * (0.75 - length( OriginalUV - float2(0.5,0.5) ));
				fSample *= fScale;
			}
			
			fTotal += fSample;
		}
	}
	
	fTotal /= 4;
	
	return float4( fTotal, fTotal, fTotal, 1 );
}

//-----------------------------------------------------------------------------
// Name:  
// Type: Pixel shader                                      
// Desc: 
//-----------------------------------------------------------------------------
float4 CalculateAdaptiveExposure( float2 OriginalUV : TEXCOORD0 ) : COLOR
{
	float fAdaptedLum = tex2D(AddaptedLuminanceSampler, float2(0.5,0.5) ).r;
	float fCurrentLum = tex2D(LuminanceSampler, float2(0.5,0.5) ).r;
		
	// The user's adapted luminance level is simulated by closing the gap between
    // adapted luminance and current luminance by 2% every frame, based on a
    // 30 fps rate. This is not an accurate model of human adaptation, which can
    // take longer than half an hour.
    float fNewAdaptation = fAdaptedLum + (fCurrentLum - fAdaptedLum) * ( 1 - pow( 0.98f, g_time.z * HDR_ADAPTION_SPEED) );
    
	fNewAdaptation = clamp( fNewAdaptation, HDR_MIN_IRIS, HDR_MAX_IRIS );
    
    return float4(fNewAdaptation, fNewAdaptation, fNewAdaptation, 1.0f);	
}

//-----------------------------------------------------------------------------
// Name:  
// Type: Pixel shader                                      
// Desc: 
//-----------------------------------------------------------------------------
float4 HighPassFilter( float2 OriginalUV : TEXCOORD0 ) : COLOR
{
	float4 vColour = tex2D(SourceSampler, OriginalUV );

	vColour *= g_fExposure / (tex2D(AddaptedLuminanceSampler, float2(0.5,0.5)).r + 0.001);
	//vColour *= (1.0f + vColour / 1.5f);
	//vColour /= (1.0f + vColour);
	
	//vColour -= float4(0.98,0.98,0.98,0);
	vColour -= float4(1.0,1.0,1.0,0) * HDR_BRIGHT_PASS_THRESHOLD;
	vColour = max( vColour, 0.0 );
	//vColour /= (0.2+vColour);
	vColour.x = log(vColour.x+1);
	vColour.y = log(vColour.y+1);
	vColour.z = log(vColour.z+1);
	
	return vColour;
}

//-----------------------------------------------------------------------------
// Name:  
// Type: Pixel shader                                      
// Desc: 
//-----------------------------------------------------------------------------
float4 Blur( float2 OriginalUV : TEXCOORD0, uniform float fSamples, uniform float fdx, uniform float fdy ) : COLOR
{
	float2 StartUV = OriginalUV - float2( fdx*(fSamples*0.5-0.5), fdy*(fSamples*0.5-0.5) );
    
    float3 vBlurred = float3(0,0,0);
    
    float fTotalWeight = 0.0;
    
    for(float i = 0; i < fSamples; i++)
    {        
        float2 lookup = StartUV + float2(fdx*i,fdy*i);
        
        // Lookup the color at this new spot
        float3 vSample = tex2D(HighPassSampler, lookup);
        
        float fWeight = 1.0 - abs( (2.0*i/fSamples)-1.0 );
        //float fWeight = 1.0;
        
        fTotalWeight += fWeight;
        
        // Add it with the other samples
        vBlurred += (vSample * fWeight);
    }
    
    // Return the average color of all the samples
    return float4(vBlurred / fTotalWeight, 1);
}


//-----------------------------------------------------------------------------
// Name:  
// Type: Pixel shader                                      
// Desc: 
//-----------------------------------------------------------------------------
float4 CompositeAndToneMapPS( float2 OriginalUV : TEXCOORD0 ) : COLOR
{
	float fLuminanceMul = g_fExposure / (tex2D(AddaptedLuminanceSampler, float2(0.5,0.5)).r + 0.001);
	float3 vColour = tex2D(SourceSampler, OriginalUV );
	
	vColour *= fLuminanceMul;	
	//vColour *= (1.0f + vColour / 1.5f);
	//vColour /= (1.0f + vColour);

	float3 vBloom = tex2D(BloomSampler, OriginalUV ) * HDR_BLOOM_AMOUNT;
	
	//vBloom *= fLuminanceMul;	
	//vBloom *= (1.0f + vBloom / 1.5f);
	//vBloom /= (1.0f + vBloom);

	return float4( vColour + vBloom, 1 );
}

//-----------------------------------------------------------------------------
// Name:  
// Type: Pixel shader                                      
// Desc: 
//-----------------------------------------------------------------------------
float4 CombinePS( float2 OriginalUV : TEXCOORD0 ) : COLOR
{
	float3 vColour = tex2D(SourceASampler, OriginalUV ) + tex2D(SourceBSampler, OriginalUV );
	
	return float4( vColour, 1 );
}

//-----------------------------------------------------------------------------
// Name: 
// Type: Technique                                     
// Desc: 
//-----------------------------------------------------------------------------
technique ScaleDown
{
    pass P0
    {      
		AlphaBlendEnable = false;  
        PixelShader = compile ps_2_0 ScaleDownOne(g_fScaleDownDeltaOne);
    }
    pass P1
    {      
		AlphaBlendEnable = false;  
        PixelShader = compile ps_2_0 ScaleDownTwo(true,g_fScaleDownDeltaTwo);
    }
    pass P2
    {      
		AlphaBlendEnable = false;  
        PixelShader = compile ps_2_0 ScaleDownTwo(false,g_fScaleDownDeltaThree);
    }
    pass P3
    {      
		AlphaBlendEnable = false;  
        PixelShader = compile ps_2_0 ScaleDownTwo(false,g_fScaleDownDeltaFour);
    }
}

//-----------------------------------------------------------------------------
// Name: 
// Type: Technique                                     
// Desc: 
//-----------------------------------------------------------------------------
technique CreateBloom
{
    pass P0
    {      
		AlphaBlendEnable = false;  
        PixelShader = compile ps_2_0 HighPassFilter();
    }
    pass P1
    {      
		AlphaBlendEnable = false;  
        PixelShader = compile ps_2_0 Blur( 10, 0.0, 0.011 );
    }
    pass P2
    {      
		AlphaBlendEnable = false;  
        PixelShader = compile ps_2_0 Blur( 10, 0.011, 0.0 );
    }
}


//-----------------------------------------------------------------------------
// Name: 
// Type: Technique                                     
// Desc: 
//-----------------------------------------------------------------------------
technique CompositeAndToneMap
{
    pass P0
    {      
		AlphaBlendEnable = false;  
        PixelShader = compile ps_2_0 CalculateAdaptiveExposure();
    }
    pass P1
    {      
		AlphaBlendEnable = false;  
        PixelShader = compile ps_2_0 CompositeAndToneMapPS();
    }
}

//-----------------------------------------------------------------------------
// Name: 
// Type: Combine                                     
// Desc: 
//-----------------------------------------------------------------------------
technique Combine
{
    pass P0
    {      
        PixelShader = compile ps_2_0 CombinePS();
    }
}


