/*=============================================================================
	DOFAndBloomGatherPixelShader.usf: Pixel shader for gathering the combined depth of field and bloom samples for blurring.
	Copyright 1998-2010 Epic Games, Inc. All Rights Reserved.
=============================================================================*/

#include "Common.usf"


/** The amount bloomed colors are scaled by. */
half2 BloomScaleAndThreshold;

// half resolution with depth DeviceZ in alpha
sampler2D SmallSceneColorTexture;

/** Computes a pixel's luminance for bloom */
half ComputeLuminanceForBloom( half3 InSceneColor )
{
	// Compute the luminance for this pixel
	half TotalLuminance;
	if( 1 )
	{
		// Compute luminance as the maximum of RGB.  This is a bit more intuitive for artists as they know
		// that any pixel with either of the RGB channels above 1.0 will begin to bloom.
		TotalLuminance = max( InSceneColor.r, max( InSceneColor.g, InSceneColor.b ) );
	}
	else
	{
		// RGB scale factor to calculated pixel luminance using a weight average
		half3 LuminanceFactor = half3( 0.3, 0.59, 0.11 );

		// Compute true luminance
		TotalLuminance = dot( LuminanceFactor, InSceneColor );
	}

	return TotalLuminance;
}



/** Computes bloomed amount for the specified scene color */
half ComputeBloomAmount( half3 InSceneColor, half InLuminance )
{
	// Size of the bloom "ramp".  This value specifies the amount of light beyond the bloom threshold required
	// before a pixel's bloom will be 100% of the original color.
	// NOTE: Any value above 0.8 looks pretty good here (and 1.0 is often fastest), but a value of 2.0 here
	//	     minimizes artifacts: the bloom ramp-up will closely match the linear ascent of additive color
	half BloomRampSize = 2.0f;

	// Figure out how much luminance is beyond the bloom threshold.  Note that this value could be negative but
	// we handle that in the next step.
	half BloomLuminance = InLuminance - BloomScaleAndThreshold.y;

	// Note that we clamp the bloom amount between 0.0 and 1.0, but pixels beyond our bloom ramp will still 
	// bloom brighter because we'll use 100% of the original scene color as bloom
	half BloomAmount = saturate( BloomLuminance / BloomRampSize );
	
	return BloomAmount;
}



/** Computes bloomed amount for the specified scene color */
half ComputeBloomAmount( half3 InSceneColor )
{
	// Compute the luminance for this pixel
	half TotalLuminance = ComputeLuminanceForBloom( InSceneColor );

	return ComputeBloomAmount( InSceneColor, TotalLuminance );
}



/** Computes bloomed color for the specified scene color */
half3 ComputeBloomColor( half3 InSceneColor, half InLuminance )
{
	// Multiply with the scene color to get the final bloom amount
	return InSceneColor * ComputeBloomAmount( InSceneColor, InLuminance );
}


/** Computes bloomed color for the specified scene color */
half3 ComputeBloomColor( half3 InSceneColor )
{
	// Multiply with the scene color to get the final bloom amount
	return InSceneColor * ComputeBloomAmount( InSceneColor );
}


#include "DepthOfFieldCommon.usf"

/** The number of float4s the 2D sample offsets are packed into. */
#define NUM_CHUNKS ((NUM_SAMPLES + 1) / 2)

/**
 * Combines bloom color and weighted unfocused DOF color, with unfocused percent in alpha.
 */
half4 ComputeWeightedSample(half3 BloomColor, half3 SceneColor, half SceneDepth, in half2 DOFBloomMask )
{
	half UnfocusedPercent = CalcUnfocusedPercent(SceneDepth);

	UnfocusedPercent *= DOFBloomMask.x;

	// The unfocused color is the scene color scaled by the unfocused percent.
	half3 UnfocusedColor = UnfocusedPercent * SceneColor;

	BloomColor *= DOFBloomMask.y;

	return half4(
		UnfocusedColor + BloomColor,
		UnfocusedPercent
		);
}

// RGB is half resolution scene color, A is depth
half4 CalcSceneColorAndDepthDOFBloomInput(float2 ScreenUV)
{
	if(1)
	{
		// scene color and depth lookup in half resolution
		float4 FetchColor = tex2D(SmallSceneColorTexture, ScreenUV);

		// bring from texture usable range to worldspace range
		FetchColor.a = DecompressDeviceZFromHalfResTextureChannel(FetchColor.a);

		return (half4)FetchColor;
	}
	else
	{
		// scene color and depth lookup in full resolution
		return CalcSceneColorAndDepth(ScreenUV);
	}
}


/**
 * Shared code for the gather pass, which downsamples from scene color to the filter buffer. 
 * DOFBloomMask half2(DOF=0/1,Bloom=0/1)
 */
void MainShared(
	in float4 OffsetUVs[NUM_CHUNKS] : TEXCOORD0,
	out float4 OutColor : COLOR0, 
	in half2 DOFBloomMask 
	)
{
	half3 AvgBloomColor = 0; 
	half4 AvgSceneColorAndDepth = 0;

	//Go through each chunk and take samples.  NUM_SAMPLES must be a factor of 2.
	for(int ChunkIndex = 0;ChunkIndex < NUM_SAMPLES / 2;ChunkIndex++)
	{
		// Sample scene color/depth (1) and accumulate average
		half4 SceneColorAndDepth1 = CalcSceneColorAndDepthDOFBloomInput(OffsetUVs[ChunkIndex].xy);
		AvgSceneColorAndDepth += SceneColorAndDepth1;
		
		// Sample scene color/depth (2) and accumulate average
		half4 SceneColorAndDepth2 = CalcSceneColorAndDepthDOFBloomInput(OffsetUVs[ChunkIndex].wz);
		AvgSceneColorAndDepth += SceneColorAndDepth2;


		// The bloom color is the scaled scene color if it has a component outside the displayable range [0,1].
		// Only bloom if (SceneColor > 1), instead of (0 > SceneColor > 1), in order to mimic XBOX behavior due to having unsigned SceneColor values
		// this comparison is done per scene color sample to reduce aliasing on high frequency bright patterns

		// Compute bloom amount (1) and accumulate average
		AvgBloomColor += ComputeBloomColor( SceneColorAndDepth1.rgb );

		// Compute bloom amount (2) and accumulate average
		AvgBloomColor += ComputeBloomColor( SceneColorAndDepth2.rgb );
	}

	//normalize and scale
	AvgBloomColor = AvgBloomColor * BloomScaleAndThreshold.x / NUM_SAMPLES;
	AvgSceneColorAndDepth = AvgSceneColorAndDepth / NUM_SAMPLES;

	// DOF and Bloom
	//combine weighted DOF color with bloom color
	//scale output down to fit in the [0-1] range of the fixed point filter buffer
	OutColor = ComputeWeightedSample(AvgBloomColor, AvgSceneColorAndDepth.rgb, AvgSceneColorAndDepth.a, DOFBloomMask) / MAX_SCENE_COLOR;

#if PS3
	OutColor = isnan(OutColor) ? half4(0,0,0,0) : OutColor;
#endif
}


/**
 * Entry point for the gather pass, which downsamples from scene color to the filter buffer. 
 * Unfocused DOF color is combined with bloom color in OutColor.rgb, and the Unfocused DOF weight is stored in OutColor.a.
 */
void MainDOFAndBloom(
	in float4 OffsetUVs[NUM_CHUNKS] : TEXCOORD0,
	out float4 OutColor : COLOR0
	)
{
	MainShared(OffsetUVs, OutColor, half2(1, 1) );
}
/**
 * Entry point for the gather pass, which downsamples from scene color to the filter buffer. 
 * Unfocused DOF color is stored in OutColor.rgb.
 */
void MainDOFOnly(
	in float4 OffsetUVs[NUM_CHUNKS] : TEXCOORD0,
	out float4 OutColor : COLOR0
	)
{
	MainShared(OffsetUVs, OutColor, half2(1, 0) );
}

/**
 * Entry point for the gather pass, which downsamples from scene color to the filter buffer. 
 * Bloom color is stored in OutColor.rgb.
 */
void MainBloomOnly(
	in float4 OffsetUVs[NUM_CHUNKS] : TEXCOORD0,
	out float4 OutColor : COLOR0
	)
{
	MainShared(OffsetUVs, OutColor, half2(0, 1) );
}
