Skip to content

Commit

Permalink
Added autoexposure support for cBloom
Browse files Browse the repository at this point in the history
  • Loading branch information
papadanku committed Jun 29, 2024
1 parent 4f1c053 commit dad90be
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 105 deletions.
53 changes: 6 additions & 47 deletions shaders/cAutoExposure.fx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "shared/cMacros.fxh"
#include "shared/cGraphics.fxh"
#include "shared/cCamera.fxh"
#include "shared/cTonemap.fxh"

/*
Expand All @@ -12,23 +13,6 @@

uniform float _Frametime < source = "frametime"; >;

uniform float _SmoothingSpeed <
ui_category = "Exposure";
ui_label = "Smoothing";
ui_type = "drag";
ui_tooltip = "Exposure time smoothing";
ui_min = 0.0;
ui_max = 10.0;
> = 1.0;

uniform float _ManualBias <
ui_category = "Exposure";
ui_label = "Exposure";
ui_type = "drag";
ui_tooltip = "Optional manual bias ";
ui_min = 0.0;
> = 1.0;

uniform float _Scale <
ui_category = "Spot Metering";
ui_label = "Area Scale";
Expand Down Expand Up @@ -67,54 +51,29 @@ CREATE_SAMPLER(SampleLumaTex, LumaTex, LINEAR, CLAMP)

/*
[Pixel Shaders]
---
AutoExposure(): https://john-chapman.github.io/2017/08/23/dynamic-local-exposure.html
*/

float2 Expand(float2 X)
{
return (X * 2.0) - 1.0;
}

float2 Contract(float2 X)
{
return (X * 0.5) + 0.5;
}

float4 PS_Blit(VS2PS_Quad Input) : SV_TARGET0
{
float2 Tex = Input.Tex0;

if (_Meter == 1)
{
Tex = Expand(Tex);
Tex = (Tex * 2.0) - 1.0;
Tex.x /= ASPECT_RATIO;
Tex = (Tex * _Scale) + float2(_Offset.x, -_Offset.y);
Tex = Contract(Tex);
Tex = (Tex * 0.5) + 0.5;
}

float4 Color = tex2D(CShade_SampleColorTex, Tex);
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 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;
return CreateExposureTex(Color.rgb, _Frametime);
}

float3 PS_Exposure(VS2PS_Quad Input) : SV_TARGET0
{
float4 Color = tex2D(CShade_SampleColorTex, Input.Tex0);
float3 ExposedColor = GetAutoExposure(Color.rgb, Input.Tex0);
float Luma = tex2Dlod(SampleLumaTex, float4(Tex, 0.0, 99.0)).r;
float3 ExposedColor = ApplyAutoExposure(Color.rgb, Luma, _ManualBias);

if (_Debug)
{
Expand Down
63 changes: 46 additions & 17 deletions shaders/cBloom.fx
Original file line number Diff line number Diff line change
@@ -1,55 +1,65 @@

#include "shared/cBuffers.fxh"
#include "shared/cGraphics.fxh"
#include "shared/cCamera.fxh"
#include "shared/cTonemap.fxh"

/*
[Shader Options]
*/

uniform float _Frametime < source = "frametime"; >;

uniform float _Threshold <
ui_category = "Bloom";
ui_category = "Input Settings";
ui_label = "Threshold";
ui_type = "slider";
ui_min = 0.0;
ui_max = 1.0;
> = 0.8;

uniform float _Smooth <
ui_category = "Bloom";
ui_category = "Input Settings";
ui_label = "Smoothing";
ui_type = "slider";
ui_min = 0.0;
ui_max = 1.0;
> = 0.5;

uniform float _Saturation <
ui_category = "Bloom";
ui_category = "Input Settings";
ui_label = "Saturation";
ui_type = "slider";
ui_min = 0.0;
ui_max = 10.0;
> = 1.0;

uniform float3 _ColorShift <
ui_category = "Bloom";
ui_category = "Input Settings";
ui_label = "Color Shift (RGB)";
ui_type = "color";
ui_min = 0.0;
ui_max = 1.0;
> = 1.0;

uniform float _Intensity <
ui_category = "Bloom";
ui_category = "Input Settings";
ui_label = "Intensity";
ui_type = "slider";
ui_min = 0.0;
ui_max = 10.0;
> = 5.0;
> = 1.0;


/*
[Textures & Samplers]
*/

#if USE_AUTOEXPOSURE
CREATE_TEXTURE(ExposureTex, int2(1, 1), R16F, 0)
CREATE_SAMPLER(SampleExposureTex, ExposureTex, LINEAR, CLAMP)
#endif

CREATE_SAMPLER(SampleTempTex0, TempTex0_RGB10A2, LINEAR, CLAMP)
CREATE_SAMPLER(SampleTempTex1, TempTex1_RGBA16F, LINEAR, CLAMP)
CREATE_SAMPLER(SampleTempTex2, TempTex2_RGBA16F, LINEAR, CLAMP)
Expand All @@ -60,6 +70,7 @@ CREATE_SAMPLER(SampleTempTex6, TempTex6_RGBA16F, LINEAR, CLAMP)
CREATE_SAMPLER(SampleTempTex7, TempTex7_RGBA16F, LINEAR, CLAMP)
CREATE_SAMPLER(SampleTempTex8, TempTex8_RGBA16F, LINEAR, CLAMP)


/*
[Pixel Shaders]
---
Expand All @@ -82,8 +93,15 @@ float4 PS_Prefilter(VS2PS_Quad Input) : SV_TARGET0
{
const float Knee = mad(_Threshold, _Smooth, 1e-5);
const float3 Curve = float3(_Threshold - Knee, Knee * 2.0, 0.25 / Knee);

float4 Color = tex2D(CShade_SampleColorTex, Input.Tex0);

#if USE_AUTOEXPOSURE
// Apply auto-exposure here
float Luma = tex2D(SampleExposureTex, Input.Tex0).r;
Color = ApplyAutoExposure(Color.rgb, Luma);
#endif

// Under-threshold
float Brightness = Med3(Color.r, Color.g, Color.b);
float Response_Curve = clamp(Brightness - Curve.x, 0.0, Curve.y);
Expand Down Expand Up @@ -220,9 +238,14 @@ CREATE_PS_DOWNSCALE(PS_Downscale6, SampleTempTex5, false)
CREATE_PS_DOWNSCALE(PS_Downscale7, SampleTempTex6, false)
CREATE_PS_DOWNSCALE(PS_Downscale8, SampleTempTex7, false)

float4 GetPixelUpscale(VS2PS_Quad Input, sampler2D SampleSource)
float4 PS_GetExposure(VS2PS_Quad Input) : SV_TARGET0
{
float4 Color = tex2D(SampleTempTex8, Input.Tex0);
return CreateExposureTex(Color.rgb, _Frametime);
}

float4 GetPixelUpscale(VS2PS_Quad Input, sampler2D SampleSource)
{
// A0 B0 C0
// A1 B1 C1
// A2 B2 C2
Expand Down Expand Up @@ -265,16 +288,6 @@ CREATE_PS_UPSCALE(PS_Upscale3, SampleTempTex4)
CREATE_PS_UPSCALE(PS_Upscale2, SampleTempTex3)
CREATE_PS_UPSCALE(PS_Upscale1, SampleTempTex2)

float3 ToneMapACESFilmic(float3 x)
{
float a = 2.51;
float b = 0.03;
float c = 2.43;
float d = 0.59;
float e = 0.14;
return saturate((x * (a * x + b)) / (x * (c * x + d) + e));
}

float4 PS_Composite(VS2PS_Quad Input) : SV_TARGET0
{
float3 BaseColor = tex2D(CShade_SampleColorTex, Input.Tex0).rgb;
Expand Down Expand Up @@ -311,6 +324,22 @@ technique CShade_Bloom
CREATE_PASS(VS_Quad, PS_Downscale7, TempTex7_RGBA16F, FALSE)
CREATE_PASS(VS_Quad, PS_Downscale8, TempTex8_RGBA16F, FALSE)

#if USE_AUTOEXPOSURE
pass CreateExposureTex
{
ClearRenderTargets = FALSE;
BlendEnable = TRUE;
BlendOp = ADD;
SrcBlend = SRCALPHA;
DestBlend = INVSRCALPHA;

VertexShader = VS_Quad;
PixelShader = PS_GetExposure;

RenderTarget0 = ExposureTex;
}
#endif

CREATE_PASS(VS_Quad, PS_Upscale7, TempTex7_RGBA16F, TRUE)
CREATE_PASS(VS_Quad, PS_Upscale6, TempTex6_RGBA16F, TRUE)
CREATE_PASS(VS_Quad, PS_Upscale5, TempTex5_RGBA16F, TRUE)
Expand Down
51 changes: 15 additions & 36 deletions shaders/cMedian.fx
Original file line number Diff line number Diff line change
@@ -1,31 +1,5 @@
#include "shared/cGraphics.fxh"

/*
[Vertex Shaders]
*/

struct VS2PS_Median
{
float4 HPos : SV_POSITION;
float4 Tex0 : TEXCOORD0;
float4 Tex1 : TEXCOORD1;
float4 Tex2 : TEXCOORD2;
};

VS2PS_Median VS_Median(APP2VS Input)
{
float2 PixelSize = 1.0 / (float2(BUFFER_WIDTH, BUFFER_HEIGHT));

VS2PS_Quad FSQuad = VS_Quad(Input);

VS2PS_Median Output;
Output.HPos = FSQuad.HPos;
Output.Tex0 = FSQuad.Tex0.xyyy + (float4(-1.0, 1.0, 0.0, -1.0) * PixelSize.xyyy);
Output.Tex1 = FSQuad.Tex0.xyyy + (float4(0.0, 1.0, 0.0, -1.0) * PixelSize.xyyy);
Output.Tex2 = FSQuad.Tex0.xyyy + (float4(1.0, 1.0, 0.0, -1.0) * PixelSize.xyyy);
return Output;
}

/*
[Pixel Shaders]
---
Expand Down Expand Up @@ -59,20 +33,25 @@ float4 Med9(float4 X0, float4 X1, float4 X2,

float4 PS_Median(VS2PS_Median Input) : SV_TARGET0
{
float2 PixelSize = fwidth(Input.Tex0.xy);
float4 Tex0 = Input.Tex0.xyyy + (float4(-1.0, 1.0, 0.0, -1.0) * PixelSize.xyyy);
float4 Tex1 = Input.Tex0.xyyy + (float4(0.0, 1.0, 0.0, -1.0) * PixelSize.xyyy);
float4 Tex2 = Input.Tex0.xyyy + (float4(1.0, 1.0, 0.0, -1.0) * PixelSize.xyyy);

// Sample locations:
// [0].xy [1].xy [2].xy
// [0].xz [1].xz [2].xz
// [0].xw [1].xw [2].xw
float4 Sample[9];
Sample[0] = tex2D(CShade_SampleColorTex, Input.Tex0.xy);
Sample[1] = tex2D(CShade_SampleColorTex, Input.Tex1.xy);
Sample[2] = tex2D(CShade_SampleColorTex, Input.Tex2.xy);
Sample[3] = tex2D(CShade_SampleColorTex, Input.Tex0.xz);
Sample[4] = tex2D(CShade_SampleColorTex, Input.Tex1.xz);
Sample[5] = tex2D(CShade_SampleColorTex, Input.Tex2.xz);
Sample[6] = tex2D(CShade_SampleColorTex, Input.Tex0.xw);
Sample[7] = tex2D(CShade_SampleColorTex, Input.Tex1.xw);
Sample[8] = tex2D(CShade_SampleColorTex, Input.Tex2.xw);
Sample[0] = tex2D(CShade_SampleColorTex, Tex0.xy);
Sample[1] = tex2D(CShade_SampleColorTex, Tex1.xy);
Sample[2] = tex2D(CShade_SampleColorTex, Tex2.xy);
Sample[3] = tex2D(CShade_SampleColorTex, Tex0.xz);
Sample[4] = tex2D(CShade_SampleColorTex, Tex1.xz);
Sample[5] = tex2D(CShade_SampleColorTex, Tex2.xz);
Sample[6] = tex2D(CShade_SampleColorTex, Tex0.xw);
Sample[7] = tex2D(CShade_SampleColorTex, Tex1.xw);
Sample[8] = tex2D(CShade_SampleColorTex, Tex2.xw);
return Med9(Sample[0], Sample[1], Sample[2],
Sample[3], Sample[4], Sample[5],
Sample[6], Sample[7], Sample[8]);
Expand All @@ -84,7 +63,7 @@ technique CShade_Median
{
SRGBWriteEnable = WRITE_SRGB;

VertexShader = VS_Median;
VertexShader = VS_Quad;
PixelShader = PS_Median;
}
}
45 changes: 45 additions & 0 deletions shaders/shared/cCamera.fxh
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@

#if !defined(INCLUDE_CAMERA)
#define INCLUDE_CAMERA

// AutoExposure(): https://john-chapman.github.io/2017/08/23/dynamic-local-exposure.html

uniform float _CShadeExposureBias <
ui_category = "Output: AutoExposure";
ui_label = "Exposure bias";
ui_tooltip = "Optional manual bias ";
ui_type = "drag";
ui_step = 0.001;
ui_min = 0.0;
ui_max = 10.0;
> = 0.0;

uniform float _CShadeExposureSmoothingSpeed <
ui_category = "Output: AutoExposure";
ui_label = "Smoothing";
ui_type = "drag";
ui_tooltip = "Exposure time smoothing";
ui_min = 0.001;
ui_max = 1.0;
> = 1.0;

float4 CreateExposureTex(float3 Color, float FrameTime)
{
float3 Luma = max(Color.r, max(Color.g, Color.b));

// .rgb = Output the highest brightness out of red/green/blue component
// .a = Output the weight for temporal blending
float Delay = 1e-3 * FrameTime;
return float4(log(max(Luma, 1e-2)), saturate(Delay * _CShadeExposureSmoothingSpeed));
}

float3 ApplyAutoExposure(float3 Color, float Luma)
{
float LumaAverage = exp(Luma);
float Ev100 = log2(LumaAverage * 100.0 / 12.5);
Ev100 -= _CShadeExposureBias; // optional manual bias
float Exposure = 1.0 / (1.2 * exp2(Ev100));
return Color * Exposure;
}

#endif
10 changes: 5 additions & 5 deletions shaders/shared/cTonemap.fxh
Original file line number Diff line number Diff line change
Expand Up @@ -80,17 +80,17 @@
return 0.5 * (D * SDR - sqrt(((D*D - 4.0*C*E) * SDR + 4.0*A*E-2.0*B*D) * SDR + B*B) - B) / (A - C * SDR);
}

uniform int _OutputTonemapOperator <
ui_category = "OUTPUT";
uniform int _CShadeTonemapOperator <
ui_category = "Output: Tonemapping";
ui_label = "Tonemap Operator";
ui_tooltip = "Select a tonemap operator for the output";
ui_type = "combo";
ui_items = "Reinhard\0DirectX Graphics Sample\0ACES Filmic Approximation\0";
> = 0;
ui_items = "Reinhard\0DirectX Graphics Tonemap\0ACES Filmic Approximation\0";
> = 1;

float3 ApplyTonemap(float3 HDR)
{
switch (_OutputTonemapOperator)
switch (_CShadeTonemapOperator)
{
case 0:
return ApplyReinhardTonemap(HDR, 1.0);
Expand Down

0 comments on commit dad90be

Please sign in to comment.