Expose max_mip_dimension and uv_offset in BloomSettings. (#14512)

# Objective

By default, Bevy's bloom effect shows square artifacts on small bright
particles due to a low max mip resolution. This PR makes this
configurable via BloomSettings so users can customize these parameters
instead of having them in private module constants.

## Solution

Expose max_mip_dimension and uv_offset in BloomSettings.

## Testing

I tested these changes by running the Bloom 2D / 3D examples.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
Josiah Putman 2024-08-13 08:01:42 -07:00 committed by GitHub
parent 6183b56b5d
commit 882973a528
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 27 additions and 11 deletions

View file

@ -10,6 +10,7 @@ struct BloomUniforms {
threshold_precomputations: vec4<f32>, threshold_precomputations: vec4<f32>,
viewport: vec4<f32>, viewport: vec4<f32>,
aspect: f32, aspect: f32,
uv_offset: f32
}; };
@group(0) @binding(0) var input_texture: texture_2d<f32>; @group(0) @binding(0) var input_texture: texture_2d<f32>;
@ -94,9 +95,9 @@ fn sample_input_13_tap(uv: vec2<f32>) -> vec3<f32> {
// [COD] slide 162 // [COD] slide 162
fn sample_input_3x3_tent(uv: vec2<f32>) -> vec3<f32> { fn sample_input_3x3_tent(uv: vec2<f32>) -> vec3<f32> {
// Radius. Empirically chosen by and tweaked from the LearnOpenGL article. // UV offsets configured from uniforms.
let x = 0.004 / uniforms.aspect; let x = uniforms.uv_offset / uniforms.aspect;
let y = 0.004; let y = uniforms.uv_offset;
let a = textureSample(input_texture, s, vec2<f32>(uv.x - x, uv.y + y)).rgb; let a = textureSample(input_texture, s, vec2<f32>(uv.x - x, uv.y + y)).rgb;
let b = textureSample(input_texture, s, vec2<f32>(uv.x, uv.y + y)).rgb; let b = textureSample(input_texture, s, vec2<f32>(uv.x, uv.y + y)).rgb;

View file

@ -41,6 +41,7 @@ pub struct BloomUniforms {
pub threshold_precomputations: Vec4, pub threshold_precomputations: Vec4,
pub viewport: Vec4, pub viewport: Vec4,
pub aspect: f32, pub aspect: f32,
pub uv_offset: f32,
} }
impl FromWorld for BloomDownsamplingPipeline { impl FromWorld for BloomDownsamplingPipeline {

View file

@ -38,10 +38,6 @@ const BLOOM_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(9295994769239
const BLOOM_TEXTURE_FORMAT: TextureFormat = TextureFormat::Rg11b10Float; const BLOOM_TEXTURE_FORMAT: TextureFormat = TextureFormat::Rg11b10Float;
// Maximum size of each dimension for the largest mipchain texture used in downscaling/upscaling.
// 512 behaves well with the UV offset of 0.004 used in bloom.wgsl
const MAX_MIP_DIMENSION: u32 = 512;
pub struct BloomPlugin; pub struct BloomPlugin;
impl Plugin for BloomPlugin { impl Plugin for BloomPlugin {
@ -328,18 +324,18 @@ fn prepare_bloom_textures(
mut commands: Commands, mut commands: Commands,
mut texture_cache: ResMut<TextureCache>, mut texture_cache: ResMut<TextureCache>,
render_device: Res<RenderDevice>, render_device: Res<RenderDevice>,
views: Query<(Entity, &ExtractedCamera), With<BloomSettings>>, views: Query<(Entity, &ExtractedCamera, &BloomSettings)>,
) { ) {
for (entity, camera) in &views { for (entity, camera, settings) in &views {
if let Some(UVec2 { if let Some(UVec2 {
x: width, x: width,
y: height, y: height,
}) = camera.physical_viewport_size }) = camera.physical_viewport_size
{ {
// How many times we can halve the resolution minus one so we don't go unnecessarily low // How many times we can halve the resolution minus one so we don't go unnecessarily low
let mip_count = MAX_MIP_DIMENSION.ilog2().max(2) - 1; let mip_count = settings.max_mip_dimension.ilog2().max(2) - 1;
let mip_height_ratio = if height != 0 { let mip_height_ratio = if height != 0 {
MAX_MIP_DIMENSION as f32 / height as f32 settings.max_mip_dimension as f32 / height as f32
} else { } else {
0. 0.
}; };

View file

@ -102,9 +102,20 @@ pub struct BloomSettings {
/// configured in a non-energy-conserving way, /// configured in a non-energy-conserving way,
/// otherwise set to [`BloomCompositeMode::EnergyConserving`]. /// otherwise set to [`BloomCompositeMode::EnergyConserving`].
pub composite_mode: BloomCompositeMode, pub composite_mode: BloomCompositeMode,
/// Maximum size of each dimension for the largest mipchain texture used in downscaling/upscaling.
/// Only tweak if you are seeing visual artifacts.
pub max_mip_dimension: u32,
/// UV offset for bloom shader. Ideally close to 2.0 / `max_mip_dimension`.
/// Only tweak if you are seeing visual artifacts.
pub uv_offset: f32,
} }
impl BloomSettings { impl BloomSettings {
const DEFAULT_MAX_MIP_DIMENSION: u32 = 512;
const DEFAULT_UV_OFFSET: f32 = 0.004;
/// The default bloom preset. /// The default bloom preset.
/// ///
/// This uses the [`EnergyConserving`](BloomCompositeMode::EnergyConserving) composite mode. /// This uses the [`EnergyConserving`](BloomCompositeMode::EnergyConserving) composite mode.
@ -118,6 +129,8 @@ impl BloomSettings {
threshold_softness: 0.0, threshold_softness: 0.0,
}, },
composite_mode: BloomCompositeMode::EnergyConserving, composite_mode: BloomCompositeMode::EnergyConserving,
max_mip_dimension: Self::DEFAULT_MAX_MIP_DIMENSION,
uv_offset: Self::DEFAULT_UV_OFFSET,
}; };
/// A preset that's similar to how older games did bloom. /// A preset that's similar to how older games did bloom.
@ -131,6 +144,8 @@ impl BloomSettings {
threshold_softness: 0.2, threshold_softness: 0.2,
}, },
composite_mode: BloomCompositeMode::Additive, composite_mode: BloomCompositeMode::Additive,
max_mip_dimension: Self::DEFAULT_MAX_MIP_DIMENSION,
uv_offset: Self::DEFAULT_UV_OFFSET,
}; };
/// A preset that applies a very strong bloom, and blurs the whole screen. /// A preset that applies a very strong bloom, and blurs the whole screen.
@ -144,6 +159,8 @@ impl BloomSettings {
threshold_softness: 0.0, threshold_softness: 0.0,
}, },
composite_mode: BloomCompositeMode::EnergyConserving, composite_mode: BloomCompositeMode::EnergyConserving,
max_mip_dimension: Self::DEFAULT_MAX_MIP_DIMENSION,
uv_offset: Self::DEFAULT_UV_OFFSET,
}; };
} }
@ -213,6 +230,7 @@ impl ExtractComponent for BloomSettings {
/ UVec4::new(target_size.x, target_size.y, target_size.x, target_size.y) / UVec4::new(target_size.x, target_size.y, target_size.x, target_size.y)
.as_vec4(), .as_vec4(),
aspect: AspectRatio::from_pixels(size.x, size.y).into(), aspect: AspectRatio::from_pixels(size.x, size.y).into(),
uv_offset: settings.uv_offset,
}; };
Some((settings.clone(), uniform)) Some((settings.clone(), uniform))