/*********************************************************************NVMH3****

Measure Luminance

Two techniques:
-first convert HDR RGB to luminance R32F G32F
-each subsequent pass downsamples luminance

This is based on the DirectX9 example "HDRPipeline"

******************************************************************************/

float4x4 worldViewProj : WorldViewProjection;
float4x4 Proj : Projection;
float3 campos : CAMERAPOS;

//------------------------------------------------------------------
//  GLOBAL VARIABLES
//------------------------------------------------------------------
float4  afRGBToLumOffsets[4] : afRGBToLumOffsets;     // The offsets used by PS_RGB_To_Luminance()
float4  afDSOffsets[9] : afDSOffsets;                 // The offsets used by PS_LuminanceDownsample()


// adjust luminance
float	 ElapsedTime;
float	 FPS;


texture diffuseTexture : DiffuseTexture;
sampler DiffuseTextureSampler = sampler_state 
{
    texture = <diffuseTexture>;
    AddressU  = CLAMP;        
    AddressV  = CLAMP;
    AddressW  = CLAMP;
    MIPFILTER = LINEAR;
    MINFILTER = LINEAR;
    MAGFILTER = LINEAR;
};


texture ThisLuminanceTexture : ThisLuminanceTexture;
sampler ThisLuminanceTextureSampler = sampler_state 
{
    texture = <ThisLuminanceTexture>;
    AddressU  = CLAMP;        
    AddressV  = CLAMP;
    AddressW  = CLAMP;
    MIPFILTER = LINEAR;
    MINFILTER = LINEAR;
    MAGFILTER = LINEAR;
};


texture LastLuminanceTexture : LastLuminanceTexture;
sampler LastLuminanceTextureSampler = sampler_state 
{
    texture = <LastLuminanceTexture>;
    AddressU  = CLAMP;        
    AddressV  = CLAMP;
    AddressW  = CLAMP;
    MIPFILTER = LINEAR;
    MINFILTER = LINEAR;
    MAGFILTER = LINEAR;
};

struct vertexInput {
    float4 position				: POSITION;
    float2 texCoordDiffuse		: TEXCOORD0;
};

struct vertexOutput {
    float4 hPosition		: POSITION;
    float2 texCoordDiffuse	: TEXCOORD0;
};


vertexOutput VS_Screen(vertexInput IN) 
{
    vertexOutput OUT;
    OUT.hPosition = mul( IN.position, Proj );    
    OUT.texCoordDiffuse = IN.texCoordDiffuse;
    return OUT;
}




//------------------------------------------------------------------
//  This entry point performs the basic first-pass when measuring
//  luminance of the HDR render target. It samples the HDR target
//  multiple times so as to compensate for the down-sampling and
//  subsequent loss of data.
//
// This is based on the DirectX9 example "HDRPipeline"
//------------------------------------------------------------------
float4 PS_RGBToLuminance( vertexOutput IN): COLOR
{
	 // Compute the average of the necessary samples
	float4 color = 0.0f;
	float maxLum = 0.0f;
	
	// used to prevent black pixels
    const float Delta = 0.0001f;   
    
    // luminance values            
	const float3 Luminance  = float3(0.2125f, 0.7154f, 0.0721f);            
    
	float fLogLumSum = 0.0f;  
    float fLumSum = 0.0f;
	for( int i=0; i<9; i++ )
	{
		//afRGBToLumOffsets
		color = tex2D( DiffuseTextureSampler, IN.texCoordDiffuse + float2( afDSOffsets[i].x, afDSOffsets[i].y ) );
		
		fLogLumSum += log(dot(color, Luminance)+ Delta);
		//fLumSum += dot(color, Luminance)+ Delta;
		maxLum = max( maxLum, dot(color, Luminance)+ Delta );
   	}

    // Divide the sum to complete the average
    fLogLumSum /= 9.0f;
    //fLumSum /= 9.0f;

	// Output the luminance to the render target
    //return float4( maxLum, fLumSum, 0, 0 );
    return float4( maxLum, fLogLumSum, 0, 0 );
}



//------------------------------------------------------------------
//  This entry point will, using a 3x3 set of reads will downsample
//  from one luminance map to another.
//
//  This is based on the DirectX9 example "HDRPipeline"
//------------------------------------------------------------------
float4 PS_LuminanceDownsample( vertexOutput IN): COLOR
{	
	// Compute the average of the 9 necessary samples
    float4 color = 0.0f;
    float average = 0.0f;
    float averageSimple = 0.0f;
   	float maxLum = 0.0f;

    for( int i=0; i<9; i++ )
    {
        color = tex2D( DiffuseTextureSampler, IN.texCoordDiffuse + float2( afDSOffsets[i].x, afDSOffsets[i].y ) );
        average += color.y;
        //averageSimple += color.y;
   		maxLum = max( maxLum, color.x );
 
    }
    
	// We've just taken 9 samples from the
	// high resolution texture, so compute the
	// actual average that is written to the
	// lower resolution texture (render target).
	average /= 9.0f;
	//averageSimple /= 9.0f;
    
	// Return the new average luminance
    //return float4( maxLum, averageSimple, 0, 0 );
    return float4( maxLum, average, 0, 0 );
}


//------------------------------------------------------------------
//  
//  The last luminance pass: return exp(average)
//  
//------------------------------------------------------------------
float4 PS_LuminanceExponent( vertexOutput IN): COLOR
{	
	// Compute the average of the 10 necessary samples
    float4 color = 0.0f;
    float average = 0.0f;
    float averageSimple = 0.0f;
    float maxLum = 0.0f;

    for( int i=0; i<9; i++ )
    {
        color = tex2D( DiffuseTextureSampler, IN.texCoordDiffuse + float2( afDSOffsets[i].x, afDSOffsets[i].y ) );
        average += color.y;
        //averageSimple += color.y;
   		maxLum = max( maxLum, color.x );
    }
    
	// We've just taken 9 samples from the
	// high resolution texture, so compute the
	// actual average that is written to the
	// lower resolution texture (render target).
	average = exp( average/9.0f );
    //averageSimple = averageSimple/9.0f;
    
	// Return the new average luminance
    //return float4( maxLum, averageSimple, 0, 0 );
    return float4( maxLum, average, 0, 0 );
}

// -------------------------------------------------------------
// Mimics adaption process of human visual system
// This simulates the light adaptation that occurs when moving from a 
// dark area to a bright area, or vice versa. 
// Uses the luminance value of the current path and interpolates it
// with the luminance value of the previous pass. Both luminance values 
// are stored in a 1x1 texture. The AdaptedLuminanceMapSampler holds
// the texture of the previous pass and the ToneMapSampler the texture 
// of the current pass.
// -------------------------------------------------------------
float4 PS_TimeAdaptedLuminance( float2 Tex :TEXCOORD0): COLOR	
{
	float4 AdaptedLum = tex2D(LastLuminanceTextureSampler, float2(0.5f, 0.5f));
    
    float4 CurrentLum = tex2D(ThisLuminanceTextureSampler, float2(0.5f, 0.5f));

	// Stores the result in the 1x1 texture to be used in the next pass. 
	// To decelerate the adaption process, the term with the pow() intrinsic
	// is used. 
    
    AdaptedLum = saturate( AdaptedLum );
    CurrentLum = saturate( CurrentLum );    
 
    return AdaptedLum + (CurrentLum - AdaptedLum) * ( 1 - pow( 0.98f, FPS * ElapsedTime * 2.0f) );

}




technique RGBToLuminance
{
    pass p0 
    {		
		VertexShader = compile vs_2_0 VS_Screen();
		PixelShader  = compile ps_2_0 PS_RGBToLuminance();
		cullmode = none;
    }
}


//-----------------------------------
technique LuminanceDownsample
{
    pass p0 
    {		
		VertexShader = compile vs_2_0 VS_Screen();
		PixelShader  = compile ps_2_0 PS_LuminanceDownsample();
		cullmode = none;
    }
}


//-----------------------------------
technique LuminanceDownsampleExponent
{
    pass p0 
    {		
		VertexShader = compile vs_2_0 VS_Screen();
		PixelShader  = compile ps_2_0 PS_LuminanceExponent();
		cullmode = none;
    }
}


//-----------------------------------
technique TimeAdaptedLuminance
{
    pass p0 
    {		
		VertexShader = compile vs_2_0 VS_Screen();
		PixelShader  = compile ps_2_0 PS_TimeAdaptedLuminance();
		cullmode = none;
    }
}

