Skip to content

Commit

Permalink
Added post-process effects (#330)
Browse files Browse the repository at this point in the history
* Added post-process effects (FXAA, Bloom, Auto-Exposure, Tonemapping)
* Added `PostProcessStack` to manage effect settings and a new component (`CPostProcessStack`) to expose them
* Updated Lua bindings
* Added `Blit` method to the base renderer
* Changed default framebuffer type to HDR (`RGBA32F`)
* Added an option to render the whole scene to the stencil buffer (required for the grid to avoid drawing on top of an already rendered pixel)
* Removed built-in tonemapping + gamma correction in PBR shader (since it's already performed as a post-processing effect)
  • Loading branch information
adriengivry authored Feb 7, 2025
1 parent 3eb4632 commit a4f6bd7
Show file tree
Hide file tree
Showing 45 changed files with 1,868 additions and 27 deletions.
3 changes: 0 additions & 3 deletions Resources/Engine/Shaders/Lighting/PBR.ovfxh
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,5 @@ vec4 ComputePBRLighting(vec2 texCoords, vec3 normal, vec3 viewPos, vec3 fragPos,
vec3 ambient = ambientSum * albedo * ao;
vec3 color = ambient + Lo;

color = color / (color + vec3(1.0));
color = pow(color, vec3(1.0/2.2));

return vec4(color, albedoRGBA.a);
}
29 changes: 29 additions & 0 deletions Resources/Engine/Shaders/PostProcess/ApplyExposure.ovfx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#shader vertex
#version 430 core

layout(location = 0) in vec2 geo_Pos;
layout(location = 1) in vec2 geo_TexCoords;

out vec2 TexCoords;

void main()
{
TexCoords = geo_TexCoords;
gl_Position = vec4(geo_Pos, 0.0, 1.0);
}

#shader fragment
#version 430 core

in vec2 TexCoords;
out vec4 FRAGMENT_COLOR;

uniform sampler2D _InputTexture;
uniform sampler2D _ExposureTexture;

void main()
{
const float exposure = texture(_ExposureTexture, vec2(0.5)).r;
vec3 color = texture(_InputTexture, TexCoords).rgb * exposure;
FRAGMENT_COLOR = vec4(color, 1.0);
}
94 changes: 94 additions & 0 deletions Resources/Engine/Shaders/PostProcess/AutoExposure.ovfx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#shader vertex
#version 430 core

layout(location = 0) in vec2 geo_Pos;
layout(location = 1) in vec2 geo_TexCoords;

out vec2 TexCoords;

void main()
{
TexCoords = geo_TexCoords;
gl_Position = vec4(geo_Pos, 0.0, 1.0);
}

#shader fragment
#version 430 core

in vec2 TexCoords;
out vec4 FRAGMENT_COLOR;

uniform sampler2D _InputTexture;
uniform sampler2D _LuminanceTexture;
uniform float _MinLuminanceEV;
uniform float _MaxLuminanceEV;
uniform float _ExposureCompensationEV;
uniform float _ElapsedTime;
uniform float _SpeedUp;
uniform float _SpeedDown;
uniform int _Progressive;

// Photographic Middle Gray Reference
const float MIDDLE_GRAY = 0.18f;

// To avoid division by zero and log2 of zero
const float EPSILON = 0.0001f;

float SafeLog2(float x)
{
return log2(max(x, EPSILON));
}

// Convert Luminance to Exposure Value (EV100)
float LuminanceToEV100(float luminance)
{
return SafeLog2(luminance / MIDDLE_GRAY) * 2.0f;
}

// Convert EV100 back to Luminance
float EV100ToLuminance(float ev100)
{
return exp2(ev100 * 0.5f) * MIDDLE_GRAY;
}

// Advanced Exposure Calculation
float CalculateExposureMultiplier(float luminance, float minLuminanceEV, float maxLuminanceEV, float exposureCompensation)
{
// Convert average luminance to EV100
float luminanceEV = LuminanceToEV100(luminance);

// Clamp EV within specified range
float clampedLuminanceEV = clamp(luminanceEV, minLuminanceEV, maxLuminanceEV);

// Apply exposure compensation
float compensatedEV = clampedLuminanceEV - exposureCompensation;

// Calculate exposure multiplier
// Using middle gray as reference, with protection against extreme values
float exposureMultiplier = MIDDLE_GRAY / max(EV100ToLuminance(compensatedEV), EPSILON);

return exposureMultiplier;
}

float InterpolateExposure(float newExposure, float oldExposure)
{
if (_Progressive != 0)
{
const float delta = newExposure - oldExposure;
const float speed = delta > 0.0 ? _SpeedUp : _SpeedDown;
return oldExposure + delta * (1.0 - exp2(-_ElapsedTime * speed));
}
else
{
return newExposure;
}
}

void main()
{
const float averageLuminance = textureLod(_LuminanceTexture, vec2(0.5), textureQueryLevels(_LuminanceTexture) - 1).r;
const float newExposure = CalculateExposureMultiplier(averageLuminance, _MinLuminanceEV, _MaxLuminanceEV, _ExposureCompensationEV);
const float previousExposure = max(texture(_InputTexture, vec2(0.5)).r, 0.0001);
const float interpolatedExposure = InterpolateExposure(newExposure, previousExposure);
FRAGMENT_COLOR = vec4(vec3(interpolatedExposure), 1.0);
}
26 changes: 26 additions & 0 deletions Resources/Engine/Shaders/PostProcess/Blit.ovfx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#shader vertex
#version 430 core

layout(location = 0) in vec2 geo_Pos;
layout(location = 1) in vec2 geo_TexCoords;

out vec2 TexCoords;

void main()
{
TexCoords = geo_TexCoords;
gl_Position = vec4(geo_Pos, 0.0, 1.0);
}

#shader fragment
#version 430 core

in vec2 TexCoords;
out vec4 FRAGMENT_COLOR;

uniform sampler2D _InputTexture;

void main()
{
FRAGMENT_COLOR = texture(_InputTexture, TexCoords);
}
30 changes: 30 additions & 0 deletions Resources/Engine/Shaders/PostProcess/Bloom.ovfx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#shader vertex
#version 430 core

layout(location = 0) in vec2 geo_Pos;
layout(location = 1) in vec2 geo_TexCoords;

out vec2 TexCoords;

void main()
{
TexCoords = geo_TexCoords;
gl_Position = vec4(geo_Pos, 0.0, 1.0);
}

#shader fragment
#version 430 core

in vec2 TexCoords;
out vec4 FRAGMENT_COLOR;

uniform sampler2D _InputTexture;
uniform sampler2D _BloomTexture;
uniform float _BloomIntensity;

void main()
{
const vec3 sceneColor = texture(_InputTexture, TexCoords).rgb;
const vec3 bloomColor = texture(_BloomTexture, TexCoords).rgb;
FRAGMENT_COLOR = vec4(sceneColor + _BloomIntensity * bloomColor, 1.0);
}
43 changes: 43 additions & 0 deletions Resources/Engine/Shaders/PostProcess/Blur.ovfx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#shader vertex
#version 430 core

layout(location = 0) in vec2 geo_Pos;
layout(location = 1) in vec2 geo_TexCoords;

out vec2 TexCoords;

void main()
{
TexCoords = geo_TexCoords;
gl_Position = vec4(geo_Pos, 0.0, 1.0);
}

#shader fragment
#version 430 core

in vec2 TexCoords;
out vec4 FRAGMENT_COLOR;

uniform sampler2D _InputTexture;
uniform bool _Horizontal;
uniform float _BlurSize;
uniform int _KernelSize;

void main()
{
const vec2 texelSize = 1.0 / textureSize(_InputTexture, 0);
const vec2 direction = _Horizontal ? vec2(texelSize.x, 0.0) : vec2(0.0, texelSize.y);

vec4 color = vec4(0.0);
float totalWeight = 0.0;

for (int i = -_KernelSize; i <= _KernelSize; i++)
{
float weight = exp(-0.5 * (i * i) / (_BlurSize * _BlurSize));
vec2 offset = float(i) * direction;
color += texture(_InputTexture, TexCoords + offset) * weight;
totalWeight += weight;
}

FRAGMENT_COLOR = max(color / totalWeight, 0.0001f);
}
35 changes: 35 additions & 0 deletions Resources/Engine/Shaders/PostProcess/Brightness.ovfx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#shader vertex
#version 430 core

layout(location = 0) in vec2 geo_Pos;
layout(location = 1) in vec2 geo_TexCoords;

out vec2 TexCoords;

void main()
{
TexCoords = geo_TexCoords;
gl_Position = vec4(geo_Pos, 0.0, 1.0);
}

#shader fragment
#version 430 core

in vec2 TexCoords;
out vec4 FRAGMENT_COLOR;

uniform sampler2D _InputTexture;
uniform float _Threshold;

float luminance(vec3 color)
{
return dot(color, vec3(0.2126, 0.7152, 0.0722));
}

void main()
{
const vec3 color = texture(_InputTexture, TexCoords).rgb;
float brightness = luminance(color);
brightness = max(0.0, brightness - _Threshold);
FRAGMENT_COLOR = vec4(color * sign(brightness), 1.0);
}
82 changes: 82 additions & 0 deletions Resources/Engine/Shaders/PostProcess/FXAA.ovfx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#shader vertex
#version 430 core

layout(location = 0) in vec2 geo_Pos;
layout(location = 1) in vec2 geo_TexCoords;

out vec2 TexCoords;

void main()
{
TexCoords = geo_TexCoords;
gl_Position = vec4(geo_Pos, 0.0, 1.0);
}

#shader fragment
#version 430 core

in vec2 TexCoords;
out vec4 FRAGMENT_COLOR;

uniform sampler2D _InputTexture;

vec3 applyFXAA(sampler2D tex, vec2 uv)
{
vec2 resolution = vec2(textureSize(tex, 0));
vec2 pixelSize = 1.0 / resolution;

// Sample the neighborhood pixels
vec3 rgbNW = texture(tex, uv + vec2(-1.0, -1.0) * pixelSize).rgb;
vec3 rgbNE = texture(tex, uv + vec2(1.0, -1.0) * pixelSize).rgb;
vec3 rgbSW = texture(tex, uv + vec2(-1.0, 1.0) * pixelSize).rgb;
vec3 rgbSE = texture(tex, uv + vec2(1.0, 1.0) * pixelSize).rgb;
vec3 rgbM = texture(tex, uv).rgb;

// Luminance (perceived brightness)
float lumaNW = dot(rgbNW, vec3(0.299, 0.587, 0.114));
float lumaNE = dot(rgbNE, vec3(0.299, 0.587, 0.114));
float lumaSW = dot(rgbSW, vec3(0.299, 0.587, 0.114));
float lumaSE = dot(rgbSE, vec3(0.299, 0.587, 0.114));
float lumaM = dot(rgbM, vec3(0.299, 0.587, 0.114));

// Edge detection
float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));
float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));
float lumaRange = lumaMax - lumaMin;

// Threshold for edge detection
if (lumaRange < 0.1)
{
return rgbM; // No significant edge, return original
}

// Direction of the edge
vec2 dir;
dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));
dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));

// Normalize the direction
float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * 0.25, 0.0001);
float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);
dir = clamp(dir * rcpDirMin, -pixelSize, pixelSize);

// Final FXAA sampling
vec3 rgbA = 0.5 * (
texture(tex, uv + dir * (1.0 / 3.0 - 0.5)).rgb +
texture(tex, uv + dir * (2.0 / 3.0 - 0.5)).rgb
);

vec3 rgbB = rgbA * 0.5 + 0.25 * (
texture(tex, uv + dir * -0.5).rgb +
texture(tex, uv + dir * 0.5).rgb
);

// Choose between original and anti-aliased
float lumaB = dot(rgbB, vec3(0.299, 0.587, 0.114));
return (lumaB < lumaMin || lumaB > lumaMax) ? rgbA : rgbB;
}

void main()
{
FRAGMENT_COLOR = vec4(applyFXAA(_InputTexture, TexCoords), 1.0);
}
Loading

0 comments on commit a4f6bd7

Please sign in to comment.