From 3cf70ba4f9d62396240b186ff379d27a312d2aa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C5=A0vejda?= Date: Sat, 24 Aug 2024 01:56:39 +0100 Subject: [PATCH] Fix fog density texture offset seam (#14900) # Objective - There is a flaw in the implementation of `FogVolume`'s `density_texture_offset` from #14868. Because of the way I am wrapping the UVW coordinates in the volumetric fog shader, a seam is visible when the 3d texture is wrapping around from one side to the other: ![density_texture_offset_seam](https://github.com/user-attachments/assets/89527ef2-5e1b-4b90-8e73-7a3e607697d4) ## Solution - This PR fixes the issue by removing the wrapping from the shader and instead leaving it to the user to configure the 3d noise texture to use `ImageAddressMode::Repeat` if they want it to repeat. Using `ImageAddressMode::Repeat` is the proper solution to avoid the obvious seam: ![density_texture_seam_fixed](https://github.com/user-attachments/assets/06e871a6-2db1-4501-b425-4141605f9b26) - The sampler cannot be implicitly configured to use `ImageAddressMode::Repeat` because that's not always desirable. For example, the `fog_volumes` example wouldn't work properly because the texture from the edges of the volume would overflow to the other sides, which would be bad in this instance (but it's good in the case of the `scrolling_fog` example). So leaving it to the user to decide on their own whether they want the density texture to repeat seems to be the best solution. ## Testing - The `scrolling_fog` example still looks the same, it was just changed to explicitly declare that the density texture should be repeating when loading the asset. The `fog_volumes` example is unaffected.
Minimal reproduction example on current main
use bevy::core_pipeline::experimental::taa::{TemporalAntiAliasBundle,
TemporalAntiAliasPlugin};
use bevy::pbr::{FogVolume, VolumetricFogSettings, VolumetricLight};
use bevy::prelude::*;
fn main() { App::new() .add_plugins((DefaultPlugins, TemporalAntiAliasPlugin)) .add_systems(Startup, setup) .run(); }
fn setup(mut commands: Commands, assets: Res<AssetServer>) { commands.spawn(( Camera3dBundle { transform: Transform::from_xyz(3.5, -1.0, 0.4) .looking_at(Vec3::new(0.0, 0.0, 0.4), Vec3::Y), msaa: Msaa::Off, ..default() }, TemporalAntiAliasBundle::default(), VolumetricFogSettings { ambient_intensity: 0.0, jitter: 0.5, ..default() }, ));
commands.spawn(( DirectionalLightBundle { transform: Transform::from_xyz(-6.0, 5.0, -9.0) .looking_at(Vec3::new(0.0, 0.0, 0.0), Vec3::Y), directional_light: DirectionalLight { illuminance: 32_000.0, shadows_enabled: true, ..default() }, ..default() }, VolumetricLight, ));
commands.spawn(( SpatialBundle { visibility: Visibility::Visible, transform: Transform::from_xyz(0.0, 0.0, 0.0).with_scale(Vec3::splat(3.0)), ..default() }, FogVolume { density_texture: Some(assets.load("volumes/fog_noise.ktx2")), density_texture_offset: Vec3::new(0.0, 0.0, 0.4), scattering: 1.0, ..default() }, )); }
--- crates/bevy_pbr/src/volumetric_fog/mod.rs | 6 ++--- .../src/volumetric_fog/volumetric_fog.wgsl | 6 +---- examples/3d/scrolling_fog.rs | 25 +++++++++++++++++-- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/crates/bevy_pbr/src/volumetric_fog/mod.rs b/crates/bevy_pbr/src/volumetric_fog/mod.rs index ddc9b976c9e63..82aedd3d0b277 100644 --- a/crates/bevy_pbr/src/volumetric_fog/mod.rs +++ b/crates/bevy_pbr/src/volumetric_fog/mod.rs @@ -153,11 +153,11 @@ pub struct FogVolume { /// Optional 3D voxel density texture for the fog. pub density_texture: Option>, - /// Configurable offset of the density texture in UVW coordinates. Values 1.0 or higher - /// will be wrapped into the (0.0..1.0) range. + /// Configurable offset of the density texture in UVW coordinates. /// /// This can be used to scroll a repeating density texture in a direction over time - /// to create effects like fog moving in the wind. + /// to create effects like fog moving in the wind. Make sure to configure the texture + /// to use `ImageAddressMode::Repeat` if this is your intention. /// /// Has no effect when no density texture is present. /// diff --git a/crates/bevy_pbr/src/volumetric_fog/volumetric_fog.wgsl b/crates/bevy_pbr/src/volumetric_fog/volumetric_fog.wgsl index 68e929103275f..2c30eddbeaf8b 100644 --- a/crates/bevy_pbr/src/volumetric_fog/volumetric_fog.wgsl +++ b/crates/bevy_pbr/src/volumetric_fog/volumetric_fog.wgsl @@ -238,11 +238,7 @@ fn fragment(@builtin(position) position: vec4) -> @location(0) vec4 { // case. let P_uvw = Ro_uvw + Rd_step_uvw * f32(step); if (all(P_uvw >= vec3(0.0)) && all(P_uvw <= vec3(1.0))) { - // Add density texture offset and wrap values exceeding the (0, 0, 0) to (1, 1, 1) range. - var uvw = P_uvw + density_texture_offset; - uvw -= floor(uvw); - - density *= textureSample(density_texture, density_sampler, uvw).r; + density *= textureSample(density_texture, density_sampler, P_uvw + density_texture_offset).r; } else { density = 0.0; } diff --git a/examples/3d/scrolling_fog.rs b/examples/3d/scrolling_fog.rs index cfec3b255d78f..1813f99920bcd 100644 --- a/examples/3d/scrolling_fog.rs +++ b/examples/3d/scrolling_fog.rs @@ -14,6 +14,9 @@ use bevy::core_pipeline::bloom::BloomSettings; use bevy::core_pipeline::experimental::taa::{TemporalAntiAliasBundle, TemporalAntiAliasPlugin}; use bevy::pbr::{DirectionalLightShadowMap, FogVolume, VolumetricFogSettings, VolumetricLight}; use bevy::prelude::*; +use bevy_render::texture::{ + ImageAddressMode, ImageFilterMode, ImageLoaderSettings, ImageSampler, ImageSamplerDescriptor, +}; /// Initializes the example. fn main() { @@ -94,7 +97,25 @@ fn setup( ..default() }); - // Spawn FogVolume with repeating 3d noise density texture. + // Load a repeating 3d noise texture. Make sure to set ImageAddressMode to Repeat + // so that the texture wraps around as the density texture offset is moved along. + // Also set ImageFilterMode to Linear so that the fog isn't pixelated. + let noise_texture = assets.load_with_settings("volumes/fog_noise.ktx2", |settings: &mut _| { + *settings = ImageLoaderSettings { + sampler: ImageSampler::Descriptor(ImageSamplerDescriptor { + address_mode_u: ImageAddressMode::Repeat, + address_mode_v: ImageAddressMode::Repeat, + address_mode_w: ImageAddressMode::Repeat, + mag_filter: ImageFilterMode::Linear, + min_filter: ImageFilterMode::Linear, + mipmap_filter: ImageFilterMode::Linear, + ..default() + }), + ..default() + } + }); + + // Spawn a FogVolume and use the repeating noise texture as its density texture. commands.spawn(( SpatialBundle { visibility: Visibility::Visible, @@ -102,7 +123,7 @@ fn setup( ..default() }, FogVolume { - density_texture: Some(assets.load("volumes/fog_noise.ktx2")), + density_texture: Some(noise_texture), density_factor: 0.05, ..default() },