#include "PPIncludes.hlsl"

// Parameters for post processing
cbuffer PPConstants : register(b1)
{
	float BloomThreshold : packoffset(c0.x);
	float BloomMagnitude : packoffset(c0.y);
	float BloomBlurSigma : packoffset(c0.z);
	float Tau : packoffset(c0.w);
	float TimeDelta : packoffset(c1.x);
	float KeyValue : packoffset(c1.y);
};

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

// Approximates luminance from an RGB value
float CalcLuminance(float3 color)
{
	return max(dot(color, float3(0.299f, 0.587f, 0.114f)), 0.0001f);
}

// Retrieves the log-average luminance from the texture
float GetAvgLuminance(Texture2D lumTex, float2 texCoord)
{
	return exp(lumTex.SampleLevel(LinearSampler, texCoord, 10).x);
}

// Applies the filmic curve from John Hable's presentation
float3 ToneMapFilmicALU(float3 color)
{
	color = max(0, color - 0.004f);
	color = (color * (6.2f * color + 0.5f)) / (color * (6.2f * color + 1.7f) + 0.06f);

	// result has 1/2.2 baked in
	return pow(color, 2.2f);
}

// Determines the color based on exposure settings
float3 CalcExposedColor(float3 color, float avgLuminance, float threshold, out float exposure)
{
	// Use geometric mean
	avgLuminance = max(avgLuminance, 0.001f);
	float keyValue = KeyValue;
	float linearExposure = (KeyValue / avgLuminance);
	exposure = log2(max(linearExposure, 0.0001f));
	exposure -= threshold;
	return exp2(exposure) * color;
}

// Applies exposure and tone mapping to the specific color, and applies
// the threshold to the exposure value.
float3 ToneMap(float3 color, float avgLuminance, float threshold, out float exposure)
{
	float pixelLuminance = CalcLuminance(color);
	color = CalcExposedColor(color, avgLuminance, threshold, exposure);
	color = ToneMapFilmicALU(color);
	return color;
}

// ================================================================================================
// Shader Entry Points
// ================================================================================================

// Uses a lower exposure to produce a value suitable for a bloom pass
float4 main(in PSInput input) : SV_Target
{
	float3 color = 0;

	color = InputTexture0.Sample(LinearSampler, input.TexCoord).rgb;

	// Tone map it to threshold
	float avgLuminance = GetAvgLuminance(InputTexture1, input.TexCoord);
	float exposure = 0;
	color = ToneMap(color, avgLuminance, BloomThreshold, exposure);
	return float4(color, 1.0f);
}

