Skip to content

Commit

Permalink
Improve bloom
Browse files Browse the repository at this point in the history
- add a quality option
- remove the ping-pong code, we'll disable for GPU that don't work
  instead.
- improve quality by doing a better first downscale
  (using a 5x5 gaussian).
- improve performance by using a 9 tap filter instead of 13 for
  in most cases
- fix usages of setMinMaxLevels as it resets the base level to "min"
  • Loading branch information
pixelflinger committed Sep 18, 2023
1 parent 9c0fb67 commit 9f62dc2
Show file tree
Hide file tree
Showing 18 changed files with 397 additions and 245 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1418,6 +1418,17 @@ public enum BlendMode {
* limit highlights to this value before bloom [10, +inf]
*/
public float highlight = 1000.0f;
/**
* Bloom quality level.
* LOW (default): use a more optimized down-sampling filter, however there can be artifacts
* with dynamic resolution, this can be alleviated by using the homogenous mode.
* MEDIUM: Good balance between quality and performance.
* HIGH: In this mode the bloom resolution is automatically increased to avoid artifacts.
* This mode can be significantly slower on mobile, especially at high resolution.
* This mode greatly improves the anamorphic bloom.
*/
@NonNull
public QualityLevel quality = QualityLevel.LOW;
/**
* enable screen-space lens flare
*/
Expand Down
2 changes: 2 additions & 0 deletions filament/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,8 @@ set(MATERIAL_SRCS
src/materials/flare/flare.mat
src/materials/blitLow.mat
src/materials/bloom/bloomDownsample.mat
src/materials/bloom/bloomDownsample2x.mat
src/materials/bloom/bloomDownsample9.mat
src/materials/bloom/bloomUpsample.mat
src/materials/ssao/bilateralBlur.mat
src/materials/ssao/bilateralBlurBentNormals.mat
Expand Down
11 changes: 11 additions & 0 deletions filament/include/filament/Options.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,17 @@ struct BloomOptions {
bool enabled = false; //!< enable or disable bloom
float highlight = 1000.0f; //!< limit highlights to this value before bloom [10, +inf]

/**
* Bloom quality level.
* LOW (default): use a more optimized down-sampling filter, however there can be artifacts
* with dynamic resolution, this can be alleviated by using the homogenous mode.
* MEDIUM: Good balance between quality and performance.
* HIGH: In this mode the bloom resolution is automatically increased to avoid artifacts.
* This mode can be significantly slower on mobile, especially at high resolution.
* This mode greatly improves the anamorphic bloom.
*/
QualityLevel quality = QualityLevel::LOW;

bool lensFlare = false; //!< enable screen-space lens flare
bool starburst = true; //!< enable starburst effect on lens flare
float chromaticAberration = 0.005f; //!< amount of chromatic aberration
Expand Down
268 changes: 141 additions & 127 deletions filament/src/PostProcessManager.cpp

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions filament/src/PostProcessManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,11 @@ class PostProcessManager {
FrameGraphId<FrameGraphTexture> input, backend::TextureFormat outFormat,
BloomOptions& inoutBloomOptions, math::float2 scale) noexcept;

FrameGraphId<FrameGraphTexture> downscalePass(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input,
FrameGraphTexture::Descriptor const& outDesc,
bool threshold, float highlight, bool fireflies) noexcept;

void commitAndRender(FrameGraphResources::RenderPassInfo const& out,
PostProcessMaterial const& material, uint8_t variant,
backend::DriverApi& driver) const noexcept;
Expand Down
5 changes: 3 additions & 2 deletions filament/src/details/View.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1080,8 +1080,9 @@ void FView::setSoftShadowOptions(SoftShadowOptions options) noexcept {

void FView::setBloomOptions(BloomOptions options) noexcept {
options.dirtStrength = math::saturate(options.dirtStrength);
options.levels = math::clamp(options.levels, uint8_t(1), uint8_t(11));
options.resolution = math::clamp(options.resolution, 1u << options.levels, 2048u);
options.resolution = math::clamp(options.resolution, 2u, 2048u);
options.levels = math::clamp(options.levels, uint8_t(1),
FTexture::maxLevelCount(options.resolution));
options.anamorphism = math::clamp(options.anamorphism, 1.0f/32.0f, 32.0f);
options.highlight = std::max(10.0f, options.highlight);
mBloomOptions = options;
Expand Down
92 changes: 19 additions & 73 deletions filament/src/materials/bloom/bloomDownsample.mat
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,6 @@ material {
type : sampler2d,
name : source,
precision: medium
},
{
type : float,
name : level
},
{
type : float,
name : threshold
},
{
type : float,
name : invHighlight
}
],
variables : [
Expand All @@ -37,85 +25,43 @@ fragment {

void dummy(){}

void threshold(inout vec3 c) {
// threshold everything below 1.0
c = max(vec3(0.0), c - 1.0);
// crush everything above 1
highp float f = max3(c);
c *= 1.0 / (1.0 + f * materialParams.invHighlight);
}

vec3 box4x4(vec3 s0, vec3 s1, vec3 s2, vec3 s3) {
return (s0 + s1 + s2 + s3) * 0.25;
}

vec3 box4x4Reinhard(vec3 s0, vec3 s1, vec3 s2, vec3 s3) {
float w0 = 1.0 / (1.0 + max3(s0));
float w1 = 1.0 / (1.0 + max3(s1));
float w2 = 1.0 / (1.0 + max3(s2));
float w3 = 1.0 / (1.0 + max3(s3));
return (s0 * w0 + s1 * w1 + s2 * w2 + s3 * w3) * (1.0 / (w0 + w1 + w2 + w3));
}

void postProcess(inout PostProcessInputs postProcess) {
float lod = materialParams.level;
highp vec2 uv = variable_vertex.xy;

// see SIGGRAPH 2014: Advances in Real-Time Rendering
// "Next Generation Post-Processing in Call of Duty Advanced Warfare"
// Jorge Jimenez
vec3 c = textureLod(materialParams_source, uv, lod).rgb;
vec3 c = textureLod(materialParams_source, uv, 0.0).rgb;

// The offsets below are in "source" texture space
vec3 lt = textureLodOffset(materialParams_source, uv, lod, ivec2(-1, -1)).rgb;
vec3 rt = textureLodOffset(materialParams_source, uv, lod, ivec2( 1, -1)).rgb;
vec3 rb = textureLodOffset(materialParams_source, uv, lod, ivec2( 1, 1)).rgb;
vec3 lb = textureLodOffset(materialParams_source, uv, lod, ivec2(-1, 1)).rgb;
vec3 lt = textureLodOffset(materialParams_source, uv, 0.0, ivec2(-1, -1)).rgb;
vec3 rt = textureLodOffset(materialParams_source, uv, 0.0, ivec2( 1, -1)).rgb;
vec3 rb = textureLodOffset(materialParams_source, uv, 0.0, ivec2( 1, 1)).rgb;
vec3 lb = textureLodOffset(materialParams_source, uv, 0.0, ivec2(-1, 1)).rgb;

vec3 lt2 = textureLodOffset(materialParams_source, uv, lod, ivec2(-2, -2)).rgb;
vec3 rt2 = textureLodOffset(materialParams_source, uv, lod, ivec2( 2, -2)).rgb;
vec3 rb2 = textureLodOffset(materialParams_source, uv, lod, ivec2( 2, 2)).rgb;
vec3 lb2 = textureLodOffset(materialParams_source, uv, lod, ivec2(-2, 2)).rgb;
vec3 lt2 = textureLodOffset(materialParams_source, uv, 0.0, ivec2(-2, -2)).rgb;
vec3 rt2 = textureLodOffset(materialParams_source, uv, 0.0, ivec2( 2, -2)).rgb;
vec3 rb2 = textureLodOffset(materialParams_source, uv, 0.0, ivec2( 2, 2)).rgb;
vec3 lb2 = textureLodOffset(materialParams_source, uv, 0.0, ivec2(-2, 2)).rgb;

vec3 l = textureLodOffset(materialParams_source, uv, lod, ivec2(-2, 0)).rgb;
vec3 t = textureLodOffset(materialParams_source, uv, lod, ivec2( 0, -2)).rgb;
vec3 r = textureLodOffset(materialParams_source, uv, lod, ivec2( 2, 0)).rgb;
vec3 b = textureLodOffset(materialParams_source, uv, lod, ivec2( 0, 2)).rgb;
vec3 l = textureLodOffset(materialParams_source, uv, 0.0, ivec2(-2, 0)).rgb;
vec3 t = textureLodOffset(materialParams_source, uv, 0.0, ivec2( 0, -2)).rgb;
vec3 r = textureLodOffset(materialParams_source, uv, 0.0, ivec2( 2, 0)).rgb;
vec3 b = textureLodOffset(materialParams_source, uv, 0.0, ivec2( 0, 2)).rgb;

// five h4x4 boxes
vec3 c0, c1;

if (materialParams.level <= 0.5) {
if (materialParams.threshold > 0.0) {
// Threshold the first level blur
threshold(c);
threshold(lt);
threshold(rt);
threshold(rb);
threshold(lb);
threshold(lt2);
threshold(rt2);
threshold(rb2);
threshold(lb2);
threshold(l);
threshold(t);
threshold(r);
threshold(b);
}
// Also apply fireflies (flickering) filtering
c0 = box4x4Reinhard(lt, rt, rb, lb);
c1 = box4x4Reinhard(c, l, t, lt2);
c1 += box4x4Reinhard(c, r, t, rt2);
c1 += box4x4Reinhard(c, r, b, rb2);
c1 += box4x4Reinhard(c, l, b, lb2);
} else {
// common case
c0 = box4x4(lt, rt, rb, lb);
c1 = box4x4(c, l, t, lt2);
c1 += box4x4(c, r, t, rt2);
c1 += box4x4(c, r, b, rb2);
c1 += box4x4(c, l, b, lb2);
}
// common case
c0 = box4x4(lt, rt, rb, lb);
c1 = box4x4(c, l, t, lt2);
c1 += box4x4(c, r, t, rt2);
c1 += box4x4(c, r, b, rb2);
c1 += box4x4(c, l, b, lb2);

// weighted average of the five boxes
postProcess.color.rgb = c0 * 0.5 + c1 * 0.125;
Expand Down
98 changes: 98 additions & 0 deletions filament/src/materials/bloom/bloomDownsample2x.mat
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
material {
name : bloomDownsample2x,
parameters : [
{
type : sampler2d,
name : source,
precision: medium
},
{
type : float,
name : level
},
{
type : float,
name : threshold
},
{
type : float,
name : fireflies
},
{
type : float,
name : invHighlight
}
],
variables : [
vertex
],
domain : postprocess,
depthWrite : false,
depthCulling : false
}

vertex {
void postProcessVertex(inout PostProcessVertexInputs postProcess) {
postProcess.vertex.xy = uvToRenderTargetUV(postProcess.normalizedUV);
}
}

fragment {

void dummy(){}

void threshold(inout vec3 c) {
// threshold everything below 1.0
c = max(vec3(0.0), c - 1.0);
// crush everything above 1
highp float f = max3(c);
c *= 1.0 / (1.0 + f * materialParams.invHighlight);
}

void postProcess(inout PostProcessInputs postProcess) {
float lod = materialParams.level;

highp vec2 size = vec2(textureSize(materialParams_source, int(lod)));
highp vec2 texelSize = vec2(1.0) / size;

// Castaño, 2013, "Shadow Mapping Summary Part 1"
// 3x3 gaussian filter with 4 linear samples
vec2 offset = vec2(0.5);
highp vec2 uv = (variable_vertex.xy * size) + offset;
highp vec2 base = (floor(uv) - offset) * texelSize;
highp vec2 st = fract(uv);
vec2 uw = vec2(3.0 - 2.0 * st.x, 1.0 + 2.0 * st.x);
vec2 vw = vec2(3.0 - 2.0 * st.y, 1.0 + 2.0 * st.y);
highp vec2 u = vec2((2.0 - st.x) / uw.x - 1.0, st.x / uw.y + 1.0) * texelSize.x;
highp vec2 v = vec2((2.0 - st.y) / vw.x - 1.0, st.y / vw.y + 1.0) * texelSize.y;
vec3 c0 = textureLod(materialParams_source, base + vec2(u.x, v.x), lod).rgb;
vec3 c1 = textureLod(materialParams_source, base + vec2(u.y, v.x), lod).rgb;
vec3 c2 = textureLod(materialParams_source, base + vec2(u.x, v.y), lod).rgb;
vec3 c3 = textureLod(materialParams_source, base + vec2(u.y, v.y), lod).rgb;

float w0 = uw.x * vw.x * (1.0 / 16.0);
float w1 = uw.y * vw.x * (1.0 / 16.0);
float w2 = uw.x * vw.y * (1.0 / 16.0);
float w3 = uw.y * vw.y * (1.0 / 16.0);

if (materialParams.fireflies > 0.0) {
w0 /= (1.0 + max3(c0));
w1 /= (1.0 + max3(c1));
w2 /= (1.0 + max3(c2));
w3 /= (1.0 + max3(c3));
float w = 1.0 / (w0 + w1 + w2 + w3);
w0 *= w;
w1 *= w;
w2 *= w;
w3 *= w;
}

vec3 c = c0 * w0 + c1 * w1 + c2 * w2 + c3 * w3;

if (materialParams.threshold > 0.0) {
threshold(c);
}

postProcess.color.rgb = c;
}
}
65 changes: 65 additions & 0 deletions filament/src/materials/bloom/bloomDownsample9.mat
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
material {
name : bloomDownsample9,
parameters : [
{
type : sampler2d,
name : source,
precision: medium
}
],
variables : [
vertex
],
domain : postprocess,
depthWrite : false,
depthCulling : false
}

vertex {
void postProcessVertex(inout PostProcessVertexInputs postProcess) {
postProcess.vertex.xy = uvToRenderTargetUV(postProcess.normalizedUV);
}
}

fragment {

void dummy(){}

// see https://www.shadertoy.com/view/cslczj
// 6x6 downsampling kernel implemented via 9 bilinear samples

void postProcess(inout PostProcessInputs postProcess) {
highp vec2 uv = variable_vertex.xy;
highp vec2 size = vec2(1.0) / vec2(textureSize(materialParams_source, 0));

float o = 1.5 + 0.261629;
float wa = 7.46602 / 32.0;
float wb = 1.0 - wa * 2.0;
float wab = wa * wb;
float waa = wa * wa;
float wbb = wb * wb;

size *= o;

vec3 c = textureLod(materialParams_source, uv + vec2(0.0) , 0.0).rgb;
vec3 l = textureLod(materialParams_source, uv + vec2(-size.x, 0.0), 0.0).rgb;
vec3 r = textureLod(materialParams_source, uv + vec2( size.x, 0.0), 0.0).rgb;
vec3 b = textureLod(materialParams_source, uv + vec2( 0.0,-size.y), 0.0).rgb;
vec3 t = textureLod(materialParams_source, uv + vec2( 0.0, size.y), 0.0).rgb;
vec3 lb = textureLod(materialParams_source, uv + vec2(-size.x,-size.y), 0.0).rgb;
vec3 rb = textureLod(materialParams_source, uv + vec2( size.x,-size.y), 0.0).rgb;
vec3 lt = textureLod(materialParams_source, uv + vec2(-size.x, size.y), 0.0).rgb;
vec3 rt = textureLod(materialParams_source, uv + vec2( size.x, size.y), 0.0).rgb;

postProcess.color.rgb =
(c * wbb +
(l * wab +
(r * wab +
(b * wab +
(t * wab +
(lb * waa +
(rb * waa +
(lt * waa +
(rt * waa)))))))));
}
}
Loading

0 comments on commit 9f62dc2

Please sign in to comment.