From 15cdcd0757d207d1e77c04034bf7b5eac65420e9 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Sat, 10 Jul 2021 08:23:06 +0200 Subject: [PATCH 01/20] 3d_scene_pipelined: Use a shallower directional light angle to provoke acne --- examples/3d/3d_scene_pipelined.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/3d/3d_scene_pipelined.rs b/examples/3d/3d_scene_pipelined.rs index b3b37efba0271..58cdcedd01fee 100644 --- a/examples/3d/3d_scene_pipelined.rs +++ b/examples/3d/3d_scene_pipelined.rs @@ -199,7 +199,7 @@ fn setup( }, transform: Transform { translation: Vec3::new(0.0, 2.0, 0.0), - rotation: Quat::from_rotation_x(-1.2), + rotation: Quat::from_rotation_x(-std::f32::consts::FRAC_PI_4), ..Default::default() }, ..Default::default() From f7ac34d4fdefee8b78adbc38fc6f14878358e56c Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Sat, 10 Jul 2021 08:23:32 +0200 Subject: [PATCH 02/20] cornell_box_pipelined: Remove bias tweaks --- examples/3d/cornell_box_pipelined.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/examples/3d/cornell_box_pipelined.rs b/examples/3d/cornell_box_pipelined.rs index e25be52a4669c..9dbce9a8b0e57 100644 --- a/examples/3d/cornell_box_pipelined.rs +++ b/examples/3d/cornell_box_pipelined.rs @@ -136,8 +136,6 @@ fn setup( builder.spawn_bundle(PointLightBundle { point_light: PointLight { color: Color::WHITE, - shadow_bias_min: 0.00001, - shadow_bias_max: 0.0001, intensity: 25.0, ..Default::default() }, @@ -150,8 +148,6 @@ fn setup( commands.spawn_bundle(DirectionalLightBundle { directional_light: DirectionalLight { illuminance: 10000.0, - shadow_bias_min: 0.00001, - shadow_bias_max: 0.0001, shadow_projection: OrthographicProjection { left: -HALF_SIZE, right: HALF_SIZE, From eb992866fb1c5a2ae5908897a57ca0d12fed2a99 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Sat, 10 Jul 2021 08:24:15 +0200 Subject: [PATCH 03/20] bevy_pbr2: Simplify shadow biases by moving them to linear depth --- pipelined/bevy_pbr2/src/light.rs | 24 ++++++------- pipelined/bevy_pbr2/src/render/light.rs | 32 +++++++++--------- pipelined/bevy_pbr2/src/render/pbr.wgsl | 45 ++++++++++++++----------- 3 files changed, 53 insertions(+), 48 deletions(-) diff --git a/pipelined/bevy_pbr2/src/light.rs b/pipelined/bevy_pbr2/src/light.rs index 71109c906b4ac..db6d71b9237f2 100644 --- a/pipelined/bevy_pbr2/src/light.rs +++ b/pipelined/bevy_pbr2/src/light.rs @@ -7,8 +7,8 @@ pub struct PointLight { pub intensity: f32, pub range: f32, pub radius: f32, - pub shadow_bias_min: f32, - pub shadow_bias_max: f32, + pub shadow_depth_bias: f32, + pub shadow_normal_bias: f32, } impl Default for PointLight { @@ -18,15 +18,15 @@ impl Default for PointLight { intensity: 200.0, range: 20.0, radius: 0.0, - shadow_bias_min: Self::DEFAULT_SHADOW_BIAS_MIN, - shadow_bias_max: Self::DEFAULT_SHADOW_BIAS_MAX, + shadow_depth_bias: Self::DEFAULT_SHADOW_DEPTH_BIAS, + shadow_normal_bias: Self::DEFAULT_SHADOW_NORMAL_BIAS, } } } impl PointLight { - pub const DEFAULT_SHADOW_BIAS_MIN: f32 = 0.00005; - pub const DEFAULT_SHADOW_BIAS_MAX: f32 = 0.002; + pub const DEFAULT_SHADOW_DEPTH_BIAS: f32 = 0.02; + pub const DEFAULT_SHADOW_NORMAL_BIAS: f32 = 0.02; } /// A Directional light. @@ -60,8 +60,8 @@ pub struct DirectionalLight { pub color: Color, pub illuminance: f32, pub shadow_projection: OrthographicProjection, - pub shadow_bias_min: f32, - pub shadow_bias_max: f32, + pub shadow_depth_bias: f32, + pub shadow_normal_bias: f32, } impl Default for DirectionalLight { @@ -79,15 +79,15 @@ impl Default for DirectionalLight { far: size, ..Default::default() }, - shadow_bias_min: Self::DEFAULT_SHADOW_BIAS_MIN, - shadow_bias_max: Self::DEFAULT_SHADOW_BIAS_MAX, + shadow_depth_bias: Self::DEFAULT_SHADOW_DEPTH_BIAS, + shadow_normal_bias: Self::DEFAULT_SHADOW_NORMAL_BIAS, } } } impl DirectionalLight { - pub const DEFAULT_SHADOW_BIAS_MIN: f32 = 0.00005; - pub const DEFAULT_SHADOW_BIAS_MAX: f32 = 0.002; + pub const DEFAULT_SHADOW_DEPTH_BIAS: f32 = 0.02; + pub const DEFAULT_SHADOW_NORMAL_BIAS: f32 = 0.02; } // Ambient light color. diff --git a/pipelined/bevy_pbr2/src/render/light.rs b/pipelined/bevy_pbr2/src/render/light.rs index 97eb1c2d81544..158655c027e65 100644 --- a/pipelined/bevy_pbr2/src/render/light.rs +++ b/pipelined/bevy_pbr2/src/render/light.rs @@ -29,8 +29,8 @@ pub struct ExtractedPointLight { range: f32, radius: f32, transform: GlobalTransform, - shadow_bias_min: f32, - shadow_bias_max: f32, + shadow_depth_bias: f32, + shadow_normal_bias: f32, } pub struct ExtractedDirectionalLight { @@ -38,8 +38,8 @@ pub struct ExtractedDirectionalLight { illuminance: f32, direction: Vec3, projection: Mat4, - shadow_bias_min: f32, - shadow_bias_max: f32, + shadow_depth_bias: f32, + shadow_normal_bias: f32, } #[repr(C)] @@ -52,8 +52,8 @@ pub struct GpuPointLight { radius: f32, near: f32, far: f32, - shadow_bias_min: f32, - shadow_bias_max: f32, + shadow_depth_bias: f32, + shadow_normal_bias: f32, } #[repr(C)] @@ -62,8 +62,8 @@ pub struct GpuDirectionalLight { view_projection: Mat4, color: Vec4, dir_to_light: Vec3, - shadow_bias_min: f32, - shadow_bias_max: f32, + shadow_depth_bias: f32, + shadow_normal_bias: f32, } #[repr(C)] @@ -235,8 +235,8 @@ pub fn extract_lights( range: point_light.range, radius: point_light.radius, transform: *transform, - shadow_bias_min: point_light.shadow_bias_min, - shadow_bias_max: point_light.shadow_bias_max, + shadow_depth_bias: point_light.shadow_depth_bias, + shadow_normal_bias: point_light.shadow_normal_bias, }); } for (entity, directional_light, transform) in directional_lights.iter() { @@ -247,8 +247,8 @@ pub fn extract_lights( illuminance: directional_light.illuminance, direction: transform.forward(), projection: directional_light.shadow_projection.get_projection_matrix(), - shadow_bias_min: directional_light.shadow_bias_min, - shadow_bias_max: directional_light.shadow_bias_max, + shadow_depth_bias: directional_light.shadow_depth_bias, + shadow_normal_bias: directional_light.shadow_normal_bias, }); } } @@ -443,8 +443,8 @@ pub fn prepare_lights( near: 0.1, far: light.range, // proj: projection, - shadow_bias_min: light.shadow_bias_min, - shadow_bias_max: light.shadow_bias_max, + shadow_depth_bias: light.shadow_depth_bias, + shadow_normal_bias: light.shadow_normal_bias, }; } @@ -482,8 +482,8 @@ pub fn prepare_lights( dir_to_light, // NOTE: * view is correct, it should not be view.inverse() here view_projection: projection * view, - shadow_bias_min: light.shadow_bias_min, - shadow_bias_max: light.shadow_bias_max, + shadow_depth_bias: light.shadow_depth_bias, + shadow_normal_bias: light.shadow_normal_bias, }; let depth_texture_view = diff --git a/pipelined/bevy_pbr2/src/render/pbr.wgsl b/pipelined/bevy_pbr2/src/render/pbr.wgsl index 3bc711fb06a3a..63cf0e7711091 100644 --- a/pipelined/bevy_pbr2/src/render/pbr.wgsl +++ b/pipelined/bevy_pbr2/src/render/pbr.wgsl @@ -95,16 +95,16 @@ struct PointLight { radius: f32; near: f32; far: f32; - shadow_bias_min: f32; - shadow_bias_max: f32; + shadow_depth_bias: f32; + shadow_normal_bias: f32; }; struct DirectionalLight { view_projection: mat4x4; color: vec4; direction_to_light: vec3; - shadow_bias_min: f32; - shadow_bias_max: f32; + shadow_depth_bias: f32; + shadow_normal_bias: f32; }; [[block]] @@ -379,7 +379,7 @@ fn directional_light(light: DirectionalLight, roughness: f32, NdotV: f32, normal return (specular_light + diffuse) * light.color.rgb * NoL; } -fn fetch_point_shadow(light_id: i32, frag_position: vec4, shadow_bias: f32) -> f32 { +fn fetch_point_shadow(light_id: i32, frag_position: vec4) -> f32 { let light = lights.point_lights[light_id]; // because the shadow maps align with the axes and the frustum planes are at 45 degrees @@ -412,11 +412,12 @@ fn fetch_point_shadow(light_id: i32, frag_position: vec4, shadow_bias: f32) // a quad (2x2 fragments) being processed not being sampled, and this messing with // mip-mapping functionality. The shadow maps have no mipmaps so Level just samples // from LOD 0. - let bias = 0.0001; - return textureSampleCompareLevel(point_shadow_textures, point_shadow_textures_sampler, frag_ls, i32(light_id), depth - shadow_bias); + return textureSampleCompareLevel(point_shadow_textures, point_shadow_textures_sampler, frag_ls, i32(light_id), depth); } -fn fetch_directional_shadow(light_id: i32, homogeneous_coords: vec4, shadow_bias: f32) -> f32 { +fn fetch_directional_shadow(light_id: i32, frag_position: vec4) -> f32 { + let light = lights.directional_lights[light_id]; + let homogeneous_coords = light.view_projection * frag_position; if (homogeneous_coords.w <= 0.0) { return 1.0; } @@ -428,7 +429,7 @@ fn fetch_directional_shadow(light_id: i32, homogeneous_coords: vec4, shadow // do the lookup, using HW PCF and comparison // NOTE: Due to non-uniform control flow above, we must use the level variant of the texture // sampler to avoid use of implicit derivatives causing possible undefined behavior. - return textureSampleCompareLevel(directional_shadow_textures, directional_shadow_textures_sampler, light_local, i32(light_id), homogeneous_coords.z * proj_correction - shadow_bias); + return textureSampleCompareLevel(directional_shadow_textures, directional_shadow_textures_sampler, light_local, i32(light_id), homogeneous_coords.z * proj_correction); } struct FragmentInput { @@ -521,23 +522,27 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4 { let n_directional_lights = i32(lights.n_directional_lights); for (var i: i32 = 0; i < n_point_lights; i = i + 1) { let light = lights.point_lights[i]; - let light_contrib = point_light(in.world_position.xyz, light, roughness, NdotV, N, V, R, F0, diffuse_color); + let dir_to_light = normalize(light.position.xyz - in.world_position.xyz); - let shadow_bias = max( - light.shadow_bias_max * (1.0 - dot(in.world_normal, dir_to_light)), - light.shadow_bias_min - ); - let shadow = fetch_point_shadow(i, in.world_position, shadow_bias); + let depth_bias = light.shadow_depth_bias * dir_to_light.xyz; + let NdotL = dot(dir_to_light.xyz, in.world_normal.xyz); + let normal_bias = light.shadow_normal_bias * (1.0 - NdotL) * in.world_normal.xyz; + let biased_position = vec4(in.world_position.xyz + depth_bias + normal_bias, in.world_position.w); + + let shadow = fetch_point_shadow(i, biased_position); + let light_contrib = point_light(in.world_position.xyz, light, roughness, NdotV, N, V, R, F0, diffuse_color); light_accum = light_accum + light_contrib * shadow; } for (var i: i32 = 0; i < n_directional_lights; i = i + 1) { let light = lights.directional_lights[i]; + + let depth_bias = light.shadow_depth_bias * light.direction_to_light.xyz; + let NdotL = dot(light.direction_to_light.xyz, in.world_normal.xyz); + let normal_bias = light.shadow_normal_bias * (1.0 - NdotL) * in.world_normal.xyz; + let biased_position = vec4(in.world_position.xyz + depth_bias + normal_bias, in.world_position.w); + + let shadow = fetch_directional_shadow(i, biased_position); let light_contrib = directional_light(light, roughness, NdotV, N, V, R, F0, diffuse_color); - let shadow_bias = max( - light.shadow_bias_max * (1.0 - dot(in.world_normal, light.direction_to_light.xyz)), - light.shadow_bias_min - ); - let shadow = fetch_directional_shadow(i, light.view_projection * in.world_position, shadow_bias); light_accum = light_accum + light_contrib * shadow; } From f97942e04a5dc0d4cc716d8b0ff83aff632286c8 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Tue, 13 Jul 2021 16:14:30 +0200 Subject: [PATCH 04/20] bevy_pbr2: Do not use DepthBiasState --- pipelined/bevy_pbr2/src/render/light.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pipelined/bevy_pbr2/src/render/light.rs b/pipelined/bevy_pbr2/src/render/light.rs index 158655c027e65..91127323b5f9b 100644 --- a/pipelined/bevy_pbr2/src/render/light.rs +++ b/pipelined/bevy_pbr2/src/render/light.rs @@ -172,8 +172,8 @@ impl FromWorld for ShadowShaders { write_mask: 0, }, bias: DepthBiasState { - constant: 2, - slope_scale: 2.0, + constant: 0, + slope_scale: 0.0, clamp: 0.0, }, }), From f53baab0232ce218866a45cad6902b470f4cf2c4 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Tue, 13 Jul 2021 16:16:25 +0200 Subject: [PATCH 05/20] bevy_pbr2: Do not use bilinear filtering for sampling depth textures --- pipelined/bevy_pbr2/src/render/light.rs | 8 ++++---- pipelined/bevy_pbr2/src/render/mod.rs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pipelined/bevy_pbr2/src/render/light.rs b/pipelined/bevy_pbr2/src/render/light.rs index 91127323b5f9b..ccb7bdc11e357 100644 --- a/pipelined/bevy_pbr2/src/render/light.rs +++ b/pipelined/bevy_pbr2/src/render/light.rs @@ -197,8 +197,8 @@ impl FromWorld for ShadowShaders { address_mode_u: AddressMode::ClampToEdge, address_mode_v: AddressMode::ClampToEdge, address_mode_w: AddressMode::ClampToEdge, - mag_filter: FilterMode::Linear, - min_filter: FilterMode::Linear, + mag_filter: FilterMode::Nearest, + min_filter: FilterMode::Nearest, mipmap_filter: FilterMode::Nearest, compare: Some(CompareFunction::LessEqual), ..Default::default() @@ -207,8 +207,8 @@ impl FromWorld for ShadowShaders { address_mode_u: AddressMode::ClampToEdge, address_mode_v: AddressMode::ClampToEdge, address_mode_w: AddressMode::ClampToEdge, - mag_filter: FilterMode::Linear, - min_filter: FilterMode::Linear, + mag_filter: FilterMode::Nearest, + min_filter: FilterMode::Nearest, mipmap_filter: FilterMode::Nearest, compare: Some(CompareFunction::LessEqual), ..Default::default() diff --git a/pipelined/bevy_pbr2/src/render/mod.rs b/pipelined/bevy_pbr2/src/render/mod.rs index 94d9f1507554d..0c03cc12d4ccf 100644 --- a/pipelined/bevy_pbr2/src/render/mod.rs +++ b/pipelined/bevy_pbr2/src/render/mod.rs @@ -89,7 +89,7 @@ impl FromWorld for PbrShaders { visibility: ShaderStage::FRAGMENT, ty: BindingType::Sampler { comparison: true, - filtering: true, + filtering: false, }, count: None, }, @@ -110,7 +110,7 @@ impl FromWorld for PbrShaders { visibility: ShaderStage::FRAGMENT, ty: BindingType::Sampler { comparison: true, - filtering: true, + filtering: false, }, count: None, }, From f7315ccabea050dece46c0b968c1197f05528727 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Tue, 13 Jul 2021 16:17:00 +0200 Subject: [PATCH 06/20] pbr.wgsl: Remove unnecessary comment --- pipelined/bevy_pbr2/src/render/pbr.wgsl | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pipelined/bevy_pbr2/src/render/pbr.wgsl b/pipelined/bevy_pbr2/src/render/pbr.wgsl index 63cf0e7711091..f99a149969b06 100644 --- a/pipelined/bevy_pbr2/src/render/pbr.wgsl +++ b/pipelined/bevy_pbr2/src/render/pbr.wgsl @@ -398,14 +398,6 @@ fn fetch_point_shadow(light_id: i32, frag_position: vec4) -> f32 { let w = major_axis_magnitude; let depth = z / w; - // let shadow = texture(samplerCubeArrayShadow(t_Shadow, s_Shadow), vec4(frag_ls, i), depth - bias); - - // manual depth testing - // float shadow = texture(samplerCubeArray(t_Shadow, s_Shadow), vec4(-frag_ls, 6 * i)).r; - // shadow = depth > shadow ? 0.0 : 1.0; - // o_Target = vec4(vec3(shadow * 20 - 19, depth * 20 - 19, 0.0), 1.0); - // o_Target = vec4(vec3(shadow * 20 - 19), 1.0); - // do the lookup, using HW PCF and comparison // NOTE: Due to the non-uniform control flow above, we must use the Level variant of // textureSampleCompare to avoid undefined behaviour due to some of the fragments in From 7df1bab38a42d8a33bc50ca583d4be37bd9c9f0d Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Tue, 13 Jul 2021 16:20:05 +0200 Subject: [PATCH 07/20] bevy_pbr2: Do manual shadow map depth comparisons for more flexibility --- pipelined/bevy_pbr2/src/render/light.rs | 4 +-- pipelined/bevy_pbr2/src/render/mod.rs | 4 +-- pipelined/bevy_pbr2/src/render/pbr.wgsl | 33 +++++++++++++++---------- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/pipelined/bevy_pbr2/src/render/light.rs b/pipelined/bevy_pbr2/src/render/light.rs index ccb7bdc11e357..3458265e23dba 100644 --- a/pipelined/bevy_pbr2/src/render/light.rs +++ b/pipelined/bevy_pbr2/src/render/light.rs @@ -200,7 +200,7 @@ impl FromWorld for ShadowShaders { mag_filter: FilterMode::Nearest, min_filter: FilterMode::Nearest, mipmap_filter: FilterMode::Nearest, - compare: Some(CompareFunction::LessEqual), + compare: None, ..Default::default() }), directional_light_sampler: render_device.create_sampler(&SamplerDescriptor { @@ -210,7 +210,7 @@ impl FromWorld for ShadowShaders { mag_filter: FilterMode::Nearest, min_filter: FilterMode::Nearest, mipmap_filter: FilterMode::Nearest, - compare: Some(CompareFunction::LessEqual), + compare: None, ..Default::default() }), } diff --git a/pipelined/bevy_pbr2/src/render/mod.rs b/pipelined/bevy_pbr2/src/render/mod.rs index 0c03cc12d4ccf..e33d4ca878b2b 100644 --- a/pipelined/bevy_pbr2/src/render/mod.rs +++ b/pipelined/bevy_pbr2/src/render/mod.rs @@ -88,7 +88,7 @@ impl FromWorld for PbrShaders { binding: 3, visibility: ShaderStage::FRAGMENT, ty: BindingType::Sampler { - comparison: true, + comparison: false, filtering: false, }, count: None, @@ -109,7 +109,7 @@ impl FromWorld for PbrShaders { binding: 5, visibility: ShaderStage::FRAGMENT, ty: BindingType::Sampler { - comparison: true, + comparison: false, filtering: false, }, count: None, diff --git a/pipelined/bevy_pbr2/src/render/pbr.wgsl b/pipelined/bevy_pbr2/src/render/pbr.wgsl index f99a149969b06..8b80ea725af04 100644 --- a/pipelined/bevy_pbr2/src/render/pbr.wgsl +++ b/pipelined/bevy_pbr2/src/render/pbr.wgsl @@ -131,11 +131,11 @@ var lights: Lights; [[group(0), binding(2)]] var point_shadow_textures: texture_depth_cube_array; [[group(0), binding(3)]] -var point_shadow_textures_sampler: sampler_comparison; +var point_shadow_textures_sampler: sampler; [[group(0), binding(4)]] var directional_shadow_textures: texture_depth_2d_array; [[group(0), binding(5)]] -var directional_shadow_textures_sampler: sampler_comparison; +var directional_shadow_textures_sampler: sampler; [[group(2), binding(0)]] var material: StandardMaterial; @@ -398,13 +398,14 @@ fn fetch_point_shadow(light_id: i32, frag_position: vec4) -> f32 { let w = major_axis_magnitude; let depth = z / w; - // do the lookup, using HW PCF and comparison - // NOTE: Due to the non-uniform control flow above, we must use the Level variant of - // textureSampleCompare to avoid undefined behaviour due to some of the fragments in - // a quad (2x2 fragments) being processed not being sampled, and this messing with - // mip-mapping functionality. The shadow maps have no mipmaps so Level just samples - // from LOD 0. - return textureSampleCompareLevel(point_shadow_textures, point_shadow_textures_sampler, frag_ls, i32(light_id), depth); + let shadow_map_depth = textureSampleLevel(point_shadow_textures, point_shadow_textures_sampler, frag_ls, i32(light_id), 0.0); + var shadow: f32; + if (depth < shadow_map_depth) { + shadow = 1.0; + } else { + shadow = 0.0; + } + return shadow; } fn fetch_directional_shadow(light_id: i32, frag_position: vec4) -> f32 { @@ -418,10 +419,16 @@ fn fetch_directional_shadow(light_id: i32, frag_position: vec4) -> f32 { let proj_correction = 1.0 / homogeneous_coords.w; // compute texture coordinates for shadow lookup let light_local = homogeneous_coords.xy * flip_correction * proj_correction + vec2(0.5, 0.5); - // do the lookup, using HW PCF and comparison - // NOTE: Due to non-uniform control flow above, we must use the level variant of the texture - // sampler to avoid use of implicit derivatives causing possible undefined behavior. - return textureSampleCompareLevel(directional_shadow_textures, directional_shadow_textures_sampler, light_local, i32(light_id), homogeneous_coords.z * proj_correction); + let depth = homogeneous_coords.z * proj_correction; + + let shadow_map_depth = textureSampleLevel(directional_shadow_textures, directional_shadow_textures_sampler, light_local, i32(light_id), 0.0); + var shadow: f32; + if (depth < shadow_map_depth) { + shadow = 1.0; + } else { + shadow = 0.0; + } + return shadow; } struct FragmentInput { From 07795932b5befb08672946f685815411dfe082b4 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Wed, 14 Jul 2021 09:56:44 +0200 Subject: [PATCH 08/20] examples: Add shadow_biases_pipelined example This is useful for stress testing biases. --- Cargo.toml | 4 + examples/3d/shadow_biases_pipelined.rs | 307 +++++++++++++++++++++++++ 2 files changed, 311 insertions(+) create mode 100644 examples/3d/shadow_biases_pipelined.rs diff --git a/Cargo.toml b/Cargo.toml index 4d9cf45a81f20..e5bf0cdb0792c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -175,6 +175,10 @@ path = "examples/3d/pbr_pipelined.rs" name = "render_to_texture" path = "examples/3d/render_to_texture.rs" +[[example]] +name = "shadow_biases_pipelined" +path = "examples/3d/shadow_biases_pipelined.rs" + [[example]] name = "spawner" path = "examples/3d/spawner.rs" diff --git a/examples/3d/shadow_biases_pipelined.rs b/examples/3d/shadow_biases_pipelined.rs new file mode 100644 index 0000000000000..9f80c254de308 --- /dev/null +++ b/examples/3d/shadow_biases_pipelined.rs @@ -0,0 +1,307 @@ +use bevy::{ + core::Time, + diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, + ecs::prelude::*, + input::{ + mouse::{self, MouseMotion}, + Input, + }, + math::{EulerRot, Mat4, Quat, Vec2, Vec3}, + pbr2::{ + AmbientLight, DirectionalLight, DirectionalLightBundle, PbrBundle, PointLight, + PointLightBundle, StandardMaterial, + }, + prelude::{App, Assets, BuildChildren, KeyCode, Transform}, + render2::{ + camera::{camera_system, Camera, OrthographicProjection, PerspectiveCameraBundle}, + color::Color, + mesh::{shape, Mesh}, + }, + PipelinedDefaultPlugins, +}; +use rand::Rng; + +fn main() { + App::new() + .add_plugins(PipelinedDefaultPlugins) + .add_startup_system(setup.system()) + .add_system(adjust_point_light_biases.system()) + .add_system(adjust_directional_light_biases.system()) + .add_system(camera_controller.system()) + .run(); +} + +/// set up a 3D scene to test shadow biases and perspective projections +fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + let capsule_spawn_plane_width = 10.0f32; + let capsule_spawn_plane_depth = 500.0f32; + + let white_handle = materials.add(StandardMaterial { + base_color: Color::WHITE, + perceptual_roughness: 1.0, + ..Default::default() + }); + + let capsule_handle = meshes.add(Mesh::from(shape::Capsule::default())); + let sphere_handle = meshes.add(Mesh::from(shape::Icosphere { + radius: 0.25, + ..Default::default() + })); + + // light + commands.spawn_bundle(PointLightBundle { + transform: Transform::from_xyz(5.0, 5.0, 0.0), + point_light: PointLight { + intensity: 1000000000.0, + range: 500.0, + color: Color::WHITE, + shadow_depth_bias: 0.0, + shadow_normal_bias: 0.0, + ..Default::default() + }, + ..Default::default() + }); + + // // FIXME: Try to fit the projection to the scene + // let light_transform = Mat4::from_euler( + // EulerRot::XYZ, + // -std::f32::consts::FRAC_PI_4, + // std::f32::consts::FRAC_PI_4, + // 0.0, + // ); + // let world_to_light = light_transform.inverse(); + // let lbn = world_to_light * Vec3::new(-10.0, -0.1, 0.1).extend(1.0); + // let rtf = world_to_light * Vec3::new(1.0, 3.0, -capsule_spawn_plane_depth - 0.1).extend(1.0); + + // commands.spawn_bundle(DirectionalLightBundle { + // directional_light: DirectionalLight { + // shadow_projection: OrthographicProjection { + // left: lbn.x, + // right: rtf.x, + // bottom: lbn.y, + // top: rtf.y, + // near: -lbn.z, + // far: -rtf.z, + // ..Default::default() + // }, + // ..Default::default() + // }, + // transform: Transform::from_matrix(light_transform), + // ..Default::default() + // }); + + // camera + commands + .spawn_bundle(PerspectiveCameraBundle { + transform: Transform::from_xyz(-1.0, 1.0, 1.0) + .looking_at(Vec3::new(-1.0, 1.0, 0.0), Vec3::Y), + ..Default::default() + }) + .insert(CameraController::default()); + + let mut rng = rand::thread_rng(); + let half_width = 0.5 * capsule_spawn_plane_width; + for z_i32 in -capsule_spawn_plane_depth as i32..=0 { + commands.spawn_bundle(PbrBundle { + mesh: sphere_handle.clone(), + material: white_handle.clone(), + transform: Transform::from_xyz(0.0, 2.0, z_i32 as f32), + ..Default::default() + }); + } + + // ground plane + commands.spawn_bundle(PbrBundle { + mesh: meshes.add(Mesh::from(shape::Plane { size: 1000.0 })), + material: white_handle.clone(), + ..Default::default() + }); +} + +fn adjust_point_light_biases(input: Res>, mut query: Query<&mut PointLight>) { + let depth_bias_step_size = 0.01; + let normal_bias_step_size = 0.1; + for mut light in query.iter_mut() { + if input.just_pressed(KeyCode::Key1) { + light.shadow_depth_bias -= depth_bias_step_size; + println!("PointLight shadow_depth_bias: {}", light.shadow_depth_bias); + } + if input.just_pressed(KeyCode::Key2) { + light.shadow_depth_bias += depth_bias_step_size; + println!("PointLight shadow_depth_bias: {}", light.shadow_depth_bias); + } + if input.just_pressed(KeyCode::Key3) { + light.shadow_normal_bias -= normal_bias_step_size; + println!( + "PointLight shadow_normal_bias: {}", + light.shadow_normal_bias + ); + } + if input.just_pressed(KeyCode::Key4) { + light.shadow_normal_bias += normal_bias_step_size; + println!( + "PointLight shadow_normal_bias: {}", + light.shadow_normal_bias + ); + } + } +} + +fn adjust_directional_light_biases( + input: Res>, + mut query: Query<&mut DirectionalLight>, +) { + let depth_bias_step_size = 0.01; + let normal_bias_step_size = 0.01; + for mut light in query.iter_mut() { + if input.just_pressed(KeyCode::Key5) { + light.shadow_depth_bias -= depth_bias_step_size; + println!( + "DirectionalLight shadow_depth_bias: {}", + light.shadow_depth_bias + ); + } + if input.just_pressed(KeyCode::Key6) { + light.shadow_depth_bias += depth_bias_step_size; + println!( + "DirectionalLight shadow_depth_bias: {}", + light.shadow_depth_bias + ); + } + if input.just_pressed(KeyCode::Key7) { + light.shadow_normal_bias -= normal_bias_step_size; + println!( + "DirectionalLight shadow_normal_bias: {}", + light.shadow_normal_bias + ); + } + if input.just_pressed(KeyCode::Key8) { + light.shadow_normal_bias += normal_bias_step_size; + println!( + "DirectionalLight shadow_normal_bias: {}", + light.shadow_normal_bias + ); + } + } +} + +struct CameraController { + pub enabled: bool, + pub sensitivity: f32, + pub key_forward: KeyCode, + pub key_back: KeyCode, + pub key_left: KeyCode, + pub key_right: KeyCode, + pub key_up: KeyCode, + pub key_down: KeyCode, + pub key_run: KeyCode, + pub walk_speed: f32, + pub run_speed: f32, + pub friction: f32, + pub pitch: f32, + pub yaw: f32, + pub velocity: Vec3, +} + +impl Default for CameraController { + fn default() -> Self { + Self { + enabled: true, + sensitivity: 0.5, + key_forward: KeyCode::W, + key_back: KeyCode::S, + key_left: KeyCode::A, + key_right: KeyCode::D, + key_up: KeyCode::E, + key_down: KeyCode::Q, + key_run: KeyCode::LShift, + walk_speed: 10.0, + run_speed: 30.0, + friction: 0.5, + pitch: 0.0, + yaw: 0.0, + velocity: Vec3::ZERO, + } + } +} + +fn camera_controller( + time: Res