diff --git a/crates/bevy_core_pipeline/src/core_3d/camera_3d.rs b/crates/bevy_core_pipeline/src/core_3d/camera_3d.rs index 01bf8e8899..e9607962b5 100644 --- a/crates/bevy_core_pipeline/src/core_3d/camera_3d.rs +++ b/crates/bevy_core_pipeline/src/core_3d/camera_3d.rs @@ -41,6 +41,9 @@ pub struct Camera3d { /// Setting this to `0` disables the screen-space refraction effect entirely, and falls /// back to refracting the environment map light's texture. pub transmissive_steps: usize, + + /// The quality of the transmissive blur effect. + pub transmissive_quality: TransmissiveQuality, } impl Default for Camera3d { @@ -50,6 +53,7 @@ impl Default for Camera3d { depth_load_op: Default::default(), depth_texture_usages: TextureUsages::RENDER_ATTACHMENT.into(), transmissive_steps: 1, + transmissive_quality: Default::default(), } } } @@ -94,6 +98,34 @@ impl From for LoadOp { } } +/// The quality of the transmissive blur effect. Higher qualities are more GPU-intensive. +/// +/// **Note:** You can get better-looking results at any quality level by enabling TAA. See: [`TemporalAntiAliasPlugin`](crate::experimental::taa::TemporalAntiAliasPlugin). +#[derive(Resource, Default, Clone, Copy, Reflect, PartialEq, PartialOrd, Debug)] +#[reflect(Resource)] +pub enum TransmissiveQuality { + /// Best performance at the cost of quality. Suitable for lower end GPUs. (e.g. Mobile) + /// + /// `num_taps` = 4 + Low, + + /// A balanced option between quality and performance. + /// + /// `num_taps` = 8 + #[default] + Medium, + + /// Best quality. Suitable for high end GPUs. (e.g. Desktop) + /// + /// `num_taps` = 16 + High, + + /// The highest quality, suitable for non-realtime rendering. (e.g. Pre-rendered cinematics and photo mode) + /// + /// `num_taps` = 32 + Ultra, +} + #[derive(Bundle)] pub struct Camera3dBundle { pub camera: Camera, diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 17ef0d7b15..e150a681e5 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -6,7 +6,9 @@ use crate::{ use bevy_app::{App, Plugin}; use bevy_asset::{Asset, AssetApp, AssetEvent, AssetId, AssetServer, Assets, Handle}; use bevy_core_pipeline::{ - core_3d::{AlphaMask3d, Opaque3d, Transmissive3d, Transparent3d}, + core_3d::{ + AlphaMask3d, Camera3d, Opaque3d, Transmissive3d, TransmissiveQuality, Transparent3d, + }, prepass::NormalPrepass, tonemapping::{DebandDither, Tonemapping}, }; @@ -423,6 +425,17 @@ const fn tonemapping_pipeline_key(tonemapping: Tonemapping) -> MeshPipelineKey { } } +const fn transmissive_quality_pipeline_key( + transmissive_quality: TransmissiveQuality, +) -> MeshPipelineKey { + match transmissive_quality { + TransmissiveQuality::Low => MeshPipelineKey::TRANSMISSIVE_QUALITY_LOW, + TransmissiveQuality::Medium => MeshPipelineKey::TRANSMISSIVE_QUALITY_MEDIUM, + TransmissiveQuality::High => MeshPipelineKey::TRANSMISSIVE_QUALITY_HIGH, + TransmissiveQuality::Ultra => MeshPipelineKey::TRANSMISSIVE_QUALITY_ULTRA, + } +} + #[allow(clippy::too_many_arguments)] pub fn queue_material_meshes( opaque_draw_functions: Res>, @@ -451,6 +464,7 @@ pub fn queue_material_meshes( Option<&ScreenSpaceAmbientOcclusionSettings>, Option<&NormalPrepass>, Option<&TemporalJitter>, + Option<&Camera3d>, &mut RenderPhase, &mut RenderPhase, &mut RenderPhase, @@ -468,6 +482,7 @@ pub fn queue_material_meshes( ssao, normal_prepass, temporal_jitter, + camera_3d, mut opaque_phase, mut alpha_mask_phase, mut transmissive_phase, @@ -502,6 +517,9 @@ pub fn queue_material_meshes( if ssao.is_some() { view_key |= MeshPipelineKey::SCREEN_SPACE_AMBIENT_OCCLUSION; } + if let Some(camera_3d) = camera_3d { + view_key |= transmissive_quality_pipeline_key(camera_3d.transmissive_quality); + } let rangefinder = view.rangefinder3d(); for visible_entity in &visible_entities.entities { let Ok((material_handle, mut material_bind_group_id, mesh_handle, mesh_transforms)) = diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 3b3df1dc03..8919a367d6 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -729,6 +729,11 @@ bitflags::bitflags! { const TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM = 5 << Self::TONEMAP_METHOD_SHIFT_BITS; const TONEMAP_METHOD_TONY_MC_MAPFACE = 6 << Self::TONEMAP_METHOD_SHIFT_BITS; const TONEMAP_METHOD_BLENDER_FILMIC = 7 << Self::TONEMAP_METHOD_SHIFT_BITS; + const TRANSMISSIVE_QUALITY_RESERVED_BITS = Self::TRANSMISSIVE_QUALITY_MASK_BITS << Self::TRANSMISSIVE_QUALITY_SHIFT_BITS; + const TRANSMISSIVE_QUALITY_LOW = 0 << Self::TRANSMISSIVE_QUALITY_SHIFT_BITS; + const TRANSMISSIVE_QUALITY_MEDIUM = 1 << Self::TRANSMISSIVE_QUALITY_SHIFT_BITS; + const TRANSMISSIVE_QUALITY_HIGH = 2 << Self::TRANSMISSIVE_QUALITY_SHIFT_BITS; + const TRANSMISSIVE_QUALITY_ULTRA = 3 << Self::TRANSMISSIVE_QUALITY_SHIFT_BITS; } } @@ -744,6 +749,9 @@ impl MeshPipelineKey { const TONEMAP_METHOD_MASK_BITS: u32 = 0b111; const TONEMAP_METHOD_SHIFT_BITS: u32 = Self::BLEND_SHIFT_BITS - Self::TONEMAP_METHOD_MASK_BITS.count_ones(); + const TRANSMISSIVE_QUALITY_MASK_BITS: u32 = 0b11; + const TRANSMISSIVE_QUALITY_SHIFT_BITS: u32 = + Self::TONEMAP_METHOD_SHIFT_BITS - Self::TRANSMISSIVE_QUALITY_MASK_BITS.count_ones(); pub fn from_msaa_samples(msaa_samples: u32) -> Self { let msaa_bits = @@ -975,6 +983,19 @@ impl SpecializedMeshPipeline for MeshPipeline { shader_defs.push("TEMPORAL_JITTER".into()); } + let transmissive_quality = + key.intersection(MeshPipelineKey::TRANSMISSIVE_QUALITY_RESERVED_BITS); + + if transmissive_quality == MeshPipelineKey::TRANSMISSIVE_QUALITY_LOW { + shader_defs.push(ShaderDefVal::Int("TRANSMISSIVE_TAPS".into(), 4)); + } else if transmissive_quality == MeshPipelineKey::TRANSMISSIVE_QUALITY_MEDIUM { + shader_defs.push(ShaderDefVal::Int("TRANSMISSIVE_TAPS".into(), 8)); + } else if transmissive_quality == MeshPipelineKey::TRANSMISSIVE_QUALITY_HIGH { + shader_defs.push(ShaderDefVal::Int("TRANSMISSIVE_TAPS".into(), 16)); + } else if transmissive_quality == MeshPipelineKey::TRANSMISSIVE_QUALITY_ULTRA { + shader_defs.push(ShaderDefVal::Int("TRANSMISSIVE_TAPS".into(), 32)); + } + let format = if key.contains(MeshPipelineKey::HDR) { ViewTarget::TEXTURE_FORMAT_HDR } else { diff --git a/crates/bevy_pbr/src/render/pbr_lighting.wgsl b/crates/bevy_pbr/src/render/pbr_lighting.wgsl index 453752cff5..baa4f660ce 100644 --- a/crates/bevy_pbr/src/render/pbr_lighting.wgsl +++ b/crates/bevy_pbr/src/render/pbr_lighting.wgsl @@ -342,8 +342,11 @@ fn fetch_transmissive_background(offset_position: vec2, frag_coord: vec3