From ad4d32a089198192bcdf1bb6fe16cf8a9778224d Mon Sep 17 00:00:00 2001 From: allenpkt Date: Wed, 8 Oct 2025 12:06:45 +0800 Subject: [PATCH 1/2] refactor shadow settings to use `to_shadow_config` method for improved clarity and maintainability --- crates/common/src/structs.rs | 41 +++++++++++++++++++ .../src/settings/shadow_settings.rs | 33 +++------------ crates/visuals/src/lib.rs | 33 +++------------ 3 files changed, 52 insertions(+), 55 deletions(-) diff --git a/crates/common/src/structs.rs b/crates/common/src/structs.rs index 590db87ca..549ed02bd 100644 --- a/crates/common/src/structs.rs +++ b/crates/common/src/structs.rs @@ -1,6 +1,7 @@ use std::{f32::consts::PI, num::ParseIntError, ops::Range, str::FromStr, sync::Arc}; use bevy::{ + pbr::{CascadeShadowConfig, CascadeShadowConfigBuilder}, platform::collections::{HashMap, HashSet}, prelude::*, render::view::RenderLayers, @@ -456,6 +457,46 @@ pub enum ShadowSetting { High, } +impl ShadowSetting { + /// Creates cascade shadow configuration based on shadow setting and distance. + /// + /// Returns a tuple of (shadows_enabled, cascade_shadow_config). + /// + /// # Arguments + /// * `shadow_distance` - Maximum distance for shadow rendering + /// + /// # Returns + /// * `bool` - Whether shadows are enabled + /// * `CascadeShadowConfig` - The cascade shadow configuration + pub fn to_shadow_config(&self, shadow_distance: f32) -> (bool, CascadeShadowConfig) { + match self { + ShadowSetting::Off => (false, Default::default()), + ShadowSetting::Low => ( + true, + CascadeShadowConfigBuilder { + num_cascades: 1, + minimum_distance: 0.1, + maximum_distance: shadow_distance, + first_cascade_far_bound: shadow_distance, + overlap_proportion: 0.2, + } + .build(), + ), + ShadowSetting::High => ( + true, + CascadeShadowConfigBuilder { + num_cascades: 4, + minimum_distance: 0.1, + maximum_distance: shadow_distance, + first_cascade_far_bound: shadow_distance / 15.0, + overlap_proportion: 0.2, + } + .build(), + ), + } + } +} + #[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug)] pub enum AaSetting { Off, diff --git a/crates/system_bridge/src/settings/shadow_settings.rs b/crates/system_bridge/src/settings/shadow_settings.rs index c48244393..bbd60b896 100644 --- a/crates/system_bridge/src/settings/shadow_settings.rs +++ b/crates/system_bridge/src/settings/shadow_settings.rs @@ -3,7 +3,7 @@ use bevy::{ lifetimeless::{SQuery, SRes, Write}, SystemParamItem, }, - pbr::{CascadeShadowConfig, CascadeShadowConfigBuilder, ShadowFilteringMethod}, + pbr::{CascadeShadowConfig, ShadowFilteringMethod}, platform::collections::HashSet, prelude::*, }; @@ -71,33 +71,10 @@ impl AppSetting for ShadowSetting { }; for (mut light, mut cascades) in lights.iter_mut() { - match value { - ShadowSetting::Off => { - light.shadows_enabled = false; - } - ShadowSetting::Low => { - light.shadows_enabled = true; - *cascades = CascadeShadowConfigBuilder { - num_cascades: 1, - minimum_distance: 0.1, - maximum_distance: config.graphics.shadow_distance, - first_cascade_far_bound: config.graphics.shadow_distance, - overlap_proportion: 0.2, - } - .build() - } - ShadowSetting::High => { - light.shadows_enabled = true; - *cascades = CascadeShadowConfigBuilder { - num_cascades: 4, - minimum_distance: 0.1, - maximum_distance: config.graphics.shadow_distance, - first_cascade_far_bound: config.graphics.shadow_distance / 15.0, - overlap_proportion: 0.2, - } - .build() - } - } + let (shadows_enabled, cascade_config) = + value.to_shadow_config(config.graphics.shadow_distance); + light.shadows_enabled = shadows_enabled; + *cascades = cascade_config; } let res = &(config, cam_res, lights); diff --git a/crates/visuals/src/lib.rs b/crates/visuals/src/lib.rs index 6518005f9..d7e647ceb 100644 --- a/crates/visuals/src/lib.rs +++ b/crates/visuals/src/lib.rs @@ -3,7 +3,7 @@ mod nishita_cloud; use bevy::{ core_pipeline::dof::{DepthOfField, DepthOfFieldMode}, - pbr::{wireframe::WireframePlugin, CascadeShadowConfigBuilder, DirectionalLightShadowMap}, + pbr::{wireframe::WireframePlugin, DirectionalLightShadowMap}, prelude::*, render::{ render_asset::RenderAssetBytesPerFrame, @@ -25,7 +25,7 @@ use common::{ sets::SetupSets, structs::{ AppConfig, DofConfig, FogSetting, PrimaryCamera, PrimaryCameraRes, PrimaryUser, - SceneGlobalLight, SceneLoadDistance, ShadowSetting, TimeOfDay, GROUND_RENDERLAYER, + SceneGlobalLight, SceneLoadDistance, TimeOfDay, GROUND_RENDERLAYER, PRIMARY_AVATAR_LIGHT_LAYER, }, }; @@ -253,31 +253,10 @@ fn apply_global_light( layer = layer.union(&PRIMARY_AVATAR_LIGHT_LAYER); } - let (shadows_enabled, cascade_shadow_config) = match config.graphics.shadow_settings { - ShadowSetting::Off => (false, Default::default()), - ShadowSetting::Low => ( - true, - CascadeShadowConfigBuilder { - num_cascades: 1, - minimum_distance: 0.1, - maximum_distance: config.graphics.shadow_distance, - first_cascade_far_bound: config.graphics.shadow_distance, - overlap_proportion: 0.2, - } - .build(), - ), - ShadowSetting::High => ( - true, - CascadeShadowConfigBuilder { - num_cascades: 4, - minimum_distance: 0.1, - maximum_distance: config.graphics.shadow_distance, - first_cascade_far_bound: config.graphics.shadow_distance / 15.0, - overlap_proportion: 0.2, - } - .build(), - ), - }; + let (shadows_enabled, cascade_shadow_config) = config + .graphics + .shadow_settings + .to_shadow_config(config.graphics.shadow_distance); commands.spawn(( DirectionalLight { From 00751e60f77521290f0eb0b83d5bed87aa0d3ed2 Mon Sep 17 00:00:00 2001 From: allenpkt Date: Wed, 8 Oct 2025 12:57:22 +0800 Subject: [PATCH 2/2] enhance shadow settings to include 'Middle' option and update related configurations for improved quality and performance --- crates/common/src/structs.rs | 24 ++++++++++++--- .../src/settings/shadow_settings.rs | 30 ++++++++++++------- crates/visuals/src/lib.rs | 9 ++++-- 3 files changed, 46 insertions(+), 17 deletions(-) diff --git a/crates/common/src/structs.rs b/crates/common/src/structs.rs index 549ed02bd..bf1cb21c1 100644 --- a/crates/common/src/structs.rs +++ b/crates/common/src/structs.rs @@ -454,13 +454,14 @@ impl AudioSettings { pub enum ShadowSetting { Off, Low, + Middle, High, } impl ShadowSetting { - /// Creates cascade shadow configuration based on shadow setting and distance. + /// Creates complete shadow configuration including cascade settings and map resolution. /// - /// Returns a tuple of (shadows_enabled, cascade_shadow_config). + /// Returns a tuple of (shadows_enabled, cascade_shadow_config, shadow_map_size). /// /// # Arguments /// * `shadow_distance` - Maximum distance for shadow rendering @@ -468,9 +469,10 @@ impl ShadowSetting { /// # Returns /// * `bool` - Whether shadows are enabled /// * `CascadeShadowConfig` - The cascade shadow configuration - pub fn to_shadow_config(&self, shadow_distance: f32) -> (bool, CascadeShadowConfig) { + /// * `usize` - The recommended shadow map resolution + pub fn to_shadow_config(&self, shadow_distance: f32) -> (bool, CascadeShadowConfig, usize) { match self { - ShadowSetting::Off => (false, Default::default()), + ShadowSetting::Off => (false, Default::default(), 512), ShadowSetting::Low => ( true, CascadeShadowConfigBuilder { @@ -481,6 +483,19 @@ impl ShadowSetting { overlap_proportion: 0.2, } .build(), + 512, // Performance-focused resolution + ), + ShadowSetting::Middle => ( + true, + CascadeShadowConfigBuilder { + num_cascades: 2, + minimum_distance: 0.1, + maximum_distance: shadow_distance, + first_cascade_far_bound: shadow_distance / 8.0, // Optimized for better near-distance quality + overlap_proportion: 0.2, + } + .build(), + 1024, // Balanced quality/performance resolution ), ShadowSetting::High => ( true, @@ -492,6 +507,7 @@ impl ShadowSetting { overlap_proportion: 0.2, } .build(), + 4096, // Quality-focused resolution ), } } diff --git a/crates/system_bridge/src/settings/shadow_settings.rs b/crates/system_bridge/src/settings/shadow_settings.rs index bbd60b896..db952f2e0 100644 --- a/crates/system_bridge/src/settings/shadow_settings.rs +++ b/crates/system_bridge/src/settings/shadow_settings.rs @@ -1,9 +1,9 @@ use bevy::{ ecs::system::{ - lifetimeless::{SQuery, SRes, Write}, + lifetimeless::{SQuery, SRes, SResMut, Write}, SystemParamItem, }, - pbr::{CascadeShadowConfig, ShadowFilteringMethod}, + pbr::{CascadeShadowConfig, DirectionalLightShadowMap, ShadowFilteringMethod}, platform::collections::HashSet, prelude::*, }; @@ -13,13 +13,14 @@ use super::{AppSetting, EnumAppSetting, IntAppSetting}; impl EnumAppSetting for ShadowSetting { fn variants() -> Vec { - vec![Self::Off, Self::Low, Self::High] + vec![Self::Off, Self::Low, Self::Middle, Self::High] } fn name(&self) -> String { match self { ShadowSetting::Off => "Off", ShadowSetting::Low => "Low", + ShadowSetting::Middle => "Middle", ShadowSetting::High => "High", } .to_owned() @@ -31,6 +32,7 @@ impl AppSetting for ShadowSetting { SRes, SRes, SQuery<(Write, Write)>, + SResMut, ); fn title() -> String { @@ -38,11 +40,12 @@ impl AppSetting for ShadowSetting { } fn description(&self) -> String { - format!("How shadows are rendered in the world.\n\n{}", + format!("How shadows are rendered in the world.\n\n{}", match self { - ShadowSetting::Off => "Off: No shadows are rendered. Fastest", - ShadowSetting::Low => "Low: Low quality shadows. Uses a single pass shadow map and low quality hardward 2x2 filtering. Gives blocky shadow outlines, particularly with high shadow draw distances, but is pretty fast.", - ShadowSetting::High => "High: Higher quality shadows. Uses a set of cascaded shadow maps and higher quality filtering for softer shadow outlines and better quality at higher shadow draw distances, but is more GPU intensive.", + ShadowSetting::Off => "Off: No shadows are rendered. Fastest performance.", + ShadowSetting::Low => "Low: Basic shadows with 512px resolution. Uses a single cascade shadow map and hardware 2x2 filtering. Fast but blocky shadows at distance.", + ShadowSetting::Middle => "Middle: Balanced shadows with 1024px resolution. Uses 2 cascade shadow maps and Gaussian filtering. Good quality/performance balance.", + ShadowSetting::High => "High: Premium shadows with 4096px resolution. Uses 4 cascade shadow maps and Gaussian filtering. Best quality but GPU intensive.", }) } @@ -60,7 +63,7 @@ impl AppSetting for ShadowSetting { fn apply( &self, - (config, cam_res, mut lights): SystemParamItem, + (config, cam_res, mut lights, mut shadow_map): SystemParamItem, mut commands: Commands, cameras: &HashSet, ) { @@ -71,13 +74,15 @@ impl AppSetting for ShadowSetting { }; for (mut light, mut cascades) in lights.iter_mut() { - let (shadows_enabled, cascade_config) = + let (shadows_enabled, cascade_config, shadow_map_size) = value.to_shadow_config(config.graphics.shadow_distance); light.shadows_enabled = shadows_enabled; *cascades = cascade_config; + // Update shadow map resolution based on quality setting + shadow_map.size = shadow_map_size; } - let res = &(config, cam_res, lights); + let res = &(config, cam_res, lights, shadow_map); for &cam in cameras { self.apply_to_camera(res, commands.reborrow(), cam); } @@ -97,8 +102,11 @@ impl AppSetting for ShadowSetting { ShadowSetting::Low => { cmds.insert(ShadowFilteringMethod::Hardware2x2); } + ShadowSetting::Middle => { + cmds.insert(ShadowFilteringMethod::Gaussian); // Balanced performance + } ShadowSetting::High => { - cmds.insert(ShadowFilteringMethod::Gaussian); + cmds.insert(ShadowFilteringMethod::Gaussian); // Best quality } } } diff --git a/crates/visuals/src/lib.rs b/crates/visuals/src/lib.rs index d7e647ceb..d14555170 100644 --- a/crates/visuals/src/lib.rs +++ b/crates/visuals/src/lib.rs @@ -38,7 +38,8 @@ pub struct VisualsPlugin { impl Plugin for VisualsPlugin { fn build(&self, app: &mut App) { - app.insert_resource(DirectionalLightShadowMap { size: 4096 }) + // Initialize with default shadow map size - will be updated by shadow settings + app.insert_resource(DirectionalLightShadowMap { size: 1024 }) .init_resource::() .insert_resource(TimeOfDay { time: 10.0 * 3600.0, @@ -176,6 +177,7 @@ fn apply_global_light( mut prev: Local<(f32, SceneGlobalLight)>, config: Res, mut cloud_dt: Local, + mut shadow_map: ResMut, ) { let next_light = if prev.0 >= TRANSITION_TIME && prev.1.source == scene_global_light.source { scene_global_light.clone() @@ -253,11 +255,14 @@ fn apply_global_light( layer = layer.union(&PRIMARY_AVATAR_LIGHT_LAYER); } - let (shadows_enabled, cascade_shadow_config) = config + let (shadows_enabled, cascade_shadow_config, shadow_map_size) = config .graphics .shadow_settings .to_shadow_config(config.graphics.shadow_distance); + // Update shadow map resolution based on current shadow settings + shadow_map.size = shadow_map_size; + commands.spawn(( DirectionalLight { color: next_light.dir_color,