Temporal Auto-Exposure with Hardware Blending#
- Some graphics pipelines compute auto-exposure like this:
- Textures:
Previous average brightness
Current average brightness
- Passes:
Store previously generated average brightness
Generates current average brightness
Smooth average brightnesses and compute auto-exposure
- You can use hardware blending for auto-exposure:
- Textures:
Average brightnesses (previous + current)
- Passes:
Generate and smooth average brightnesses
Compute auto-exposure
Source Code#
/*
Automatic exposure shader using hardware blending
*/
/*
Vertex shaders
*/
struct APP2VS
{
float4 HPos : POSITION;
float2 Tex0 : TEXCOORD0;
};
struct VS2PS
{
float4 HPos : POSITION;
float2 Tex0 : TEXCOORD0;
};
VS2PS VS_Quad(APP2VS Input)
{
VS2PS Output;
Output.HPos = Input.HPos;
Output.Tex0 = Input.Tex0;
return Output;
}
/*
Pixel shaders
---
AutoExposure(): https://knarkowicz.wordpress.com/2016/01/09/automatic-exposure/
*/
float3 GetAutoExposure(float3 Color, float2 Tex)
{
float LumaAverage = exp(tex2Dlod(SampleLumaTex, float4(Tex, 0.0, 99.0)).r);
float Ev100 = log2(LumaAverage * 100.0 / 12.5);
Ev100 -= _ManualBias; // optional manual bias
float Exposure = 1.0 / (1.2 * exp2(Ev100));
return Color * Exposure;
}
float4 PS_GenerateAverageLuma(VS2PS Input) : COLOR0
{
float4 Color = tex2D(SampleColorTex, Input.Tex0);
float3 Luma = max(Color.r, max(Color.g, Color.b));
// OutputColor0.rgb = Output the highest brightness out of red/green/blue component
// OutputColor0.a = Output the weight for temporal blending
float Delay = 1e-3 * _Frametime;
return float4(log(max(Luma.rgb, 1e-2)), saturate(Delay * _SmoothingSpeed));
}
float3 PS_Exposure(VS2PS Input) : COLOR0
{
float4 Color = tex2D(SampleColorTex, Input.Tex0);
return GetAutoExposure(Color.rgb, Input.Tex0);
}
technique AutoExposure
{
// Pass0: This shader renders to a texture that blends itself
// NOTE: Do not have another shader overwrite the texture
pass GenerateAverageLuma
{
// Use hardware blending
BlendEnable = TRUE;
BlendOp = ADD;
SrcBlend = SRCALPHA;
DestBlend = INVSRCALPHA;
VertexShader = VS_Quad;
PixelShader = PS_GenerateAverageLuma;
}
// Pass1: Get the texture generated from Pass0
// Do autoexposure shading here
pass ApplyAutoExposure
{
VertexShader = VS_Quad;
PixelShader = PS_Exposure;
}
}