mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
PCF For DirectionalLight/SpotLight Shadows (#8006)
# Objective - Improve antialiasing for non-point light shadow edges. - Very partially addresses https://github.com/bevyengine/bevy/issues/3628. ## Solution - Implements "The Witness"'s shadow map sampling technique. - Ported from @superdump's old branch, all credit to them :) - Implements "Call of Duty: Advanced Warfare"'s stochastic shadow map sampling technique when the velocity prepass is enabled, for use with TAA. - Uses interleaved gradient noise to generate a random angle, and then averages 8 samples in a spiral pattern, rotated by the random angle. - I also tried spatiotemporal blue noise, but it was far too noisy to be filtered by TAA alone. In the future, we should try spatiotemporal blue noise + a specialized shadow denoiser such as https://gpuopen.com/fidelityfx-denoiser/#shadow. This approach would also be useful for hybrid rasterized applications with raytraced shadows. - The COD presentation has an interesting temporal dithering of the noise for use with temporal supersampling that we should revisit when we get DLSS/FSR/other TSR. --- ## Changelog * Added `ShadowFilteringMethod`. Improved directional light and spotlight shadow edges to be less aliased. ## Migration Guide * Shadows cast by directional lights or spotlights now have smoother edges. To revert to the old behavior, add `ShadowFilteringMethod::Hardware2x2` to your cameras. --------- Co-authored-by: IceSentry <c.giguere42@gmail.com> Co-authored-by: Daniel Chia <danstryder@gmail.com> Co-authored-by: robtfm <50659922+robtfm@users.noreply.github.com> Co-authored-by: Brandon Dyer <brandondyer64@gmail.com> Co-authored-by: Edgar Geier <geieredgar@gmail.com> Co-authored-by: Robert Swain <robert.swain@gmail.com> Co-authored-by: Elabajaba <Elabajaba@users.noreply.github.com> Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
This commit is contained in:
parent
154a490445
commit
1f95a484ed
6 changed files with 232 additions and 40 deletions
|
@ -55,10 +55,10 @@ use bevy_app::prelude::*;
|
|||
use bevy_asset::{load_internal_asset, AssetApp, Assets, Handle};
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_render::{
|
||||
camera::CameraUpdateSystem, extract_resource::ExtractResourcePlugin, prelude::Color,
|
||||
render_asset::prepare_assets, render_graph::RenderGraph, render_phase::sort_phase_system,
|
||||
render_resource::Shader, texture::Image, view::VisibilitySystems, ExtractSchedule, Render,
|
||||
RenderApp, RenderSet,
|
||||
camera::CameraUpdateSystem, extract_component::ExtractComponentPlugin,
|
||||
extract_resource::ExtractResourcePlugin, prelude::Color, render_asset::prepare_assets,
|
||||
render_graph::RenderGraph, render_phase::sort_phase_system, render_resource::Shader,
|
||||
texture::Image, view::VisibilitySystems, ExtractSchedule, Render, RenderApp, RenderSet,
|
||||
};
|
||||
use bevy_transform::TransformSystem;
|
||||
use environment_map::EnvironmentMapPlugin;
|
||||
|
@ -69,6 +69,7 @@ pub const UTILS_HANDLE: Handle<Shader> = Handle::weak_from_u128(1900548483293416
|
|||
pub const CLUSTERED_FORWARD_HANDLE: Handle<Shader> = Handle::weak_from_u128(166852093121196815);
|
||||
pub const PBR_LIGHTING_HANDLE: Handle<Shader> = Handle::weak_from_u128(14170772752254856967);
|
||||
pub const SHADOWS_HANDLE: Handle<Shader> = Handle::weak_from_u128(11350275143789590502);
|
||||
pub const SHADOW_SAMPLING_HANDLE: Handle<Shader> = Handle::weak_from_u128(3145627513789590502);
|
||||
pub const PBR_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(4805239651767701046);
|
||||
pub const PBR_PREPASS_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(9407115064344201137);
|
||||
pub const PBR_FUNCTIONS_HANDLE: Handle<Shader> = Handle::weak_from_u128(16550102964439850292);
|
||||
|
@ -124,6 +125,12 @@ impl Plugin for PbrPlugin {
|
|||
"render/shadows.wgsl",
|
||||
Shader::from_wgsl
|
||||
);
|
||||
load_internal_asset!(
|
||||
app,
|
||||
SHADOW_SAMPLING_HANDLE,
|
||||
"render/shadow_sampling.wgsl",
|
||||
Shader::from_wgsl
|
||||
);
|
||||
load_internal_asset!(
|
||||
app,
|
||||
PBR_FUNCTIONS_HANDLE,
|
||||
|
@ -168,6 +175,7 @@ impl Plugin for PbrPlugin {
|
|||
.register_type::<PointLight>()
|
||||
.register_type::<PointLightShadowMap>()
|
||||
.register_type::<SpotLight>()
|
||||
.register_type::<ShadowFilteringMethod>()
|
||||
.init_resource::<AmbientLight>()
|
||||
.init_resource::<GlobalVisiblePointLights>()
|
||||
.init_resource::<DirectionalLightShadowMap>()
|
||||
|
@ -182,6 +190,7 @@ impl Plugin for PbrPlugin {
|
|||
EnvironmentMapPlugin,
|
||||
ExtractResourcePlugin::<AmbientLight>::default(),
|
||||
FogPlugin,
|
||||
ExtractComponentPlugin::<ShadowFilteringMethod>::default(),
|
||||
))
|
||||
.configure_sets(
|
||||
PostUpdate,
|
||||
|
|
|
@ -6,6 +6,7 @@ use bevy_reflect::prelude::*;
|
|||
use bevy_render::{
|
||||
camera::Camera,
|
||||
color::Color,
|
||||
extract_component::ExtractComponent,
|
||||
extract_resource::ExtractResource,
|
||||
prelude::Projection,
|
||||
primitives::{Aabb, CascadesFrusta, CubemapFrusta, Frustum, HalfSpace, Sphere},
|
||||
|
@ -606,6 +607,36 @@ pub struct NotShadowCaster;
|
|||
#[reflect(Component, Default)]
|
||||
pub struct NotShadowReceiver;
|
||||
|
||||
/// Add this component to a [`Camera3d`](bevy_core_pipeline::core_3d::Camera3d)
|
||||
/// to control how to anti-alias shadow edges.
|
||||
///
|
||||
/// The different modes use different approaches to
|
||||
/// [Percentage Closer Filtering](https://developer.nvidia.com/gpugems/gpugems/part-ii-lighting-and-shadows/chapter-11-shadow-map-antialiasing).
|
||||
///
|
||||
/// Currently does not affect point lights.
|
||||
#[derive(Component, ExtractComponent, Reflect, Clone, Copy, PartialEq, Eq, Default)]
|
||||
#[reflect(Component, Default)]
|
||||
pub enum ShadowFilteringMethod {
|
||||
/// Hardware 2x2.
|
||||
///
|
||||
/// Fast but poor quality.
|
||||
Hardware2x2,
|
||||
/// Method by Ignacio Castaño for The Witness using 9 samples and smart
|
||||
/// filtering to achieve the same as a regular 5x5 filter kernel.
|
||||
///
|
||||
/// Good quality, good performance.
|
||||
#[default]
|
||||
Castano13,
|
||||
/// Method by Jorge Jimenez for Call of Duty: Advanced Warfare using 8
|
||||
/// samples in spiral pattern, randomly-rotated by interleaved gradient
|
||||
/// noise with spatial variation.
|
||||
///
|
||||
/// Good quality when used with
|
||||
/// [`TemporalAntiAliasSettings`](bevy_core_pipeline::experimental::taa::TemporalAntiAliasSettings)
|
||||
/// and good performance.
|
||||
Jimenez14,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
|
||||
pub enum SimulationLightSystems {
|
||||
AddClusters,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
render, AlphaMode, DrawMesh, DrawPrepass, EnvironmentMapLight, MeshPipeline, MeshPipelineKey,
|
||||
PrepassPipelinePlugin, PrepassPlugin, RenderMeshInstances, ScreenSpaceAmbientOcclusionSettings,
|
||||
SetMeshBindGroup, SetMeshViewBindGroup, Shadow,
|
||||
SetMeshBindGroup, SetMeshViewBindGroup, Shadow, ShadowFilteringMethod,
|
||||
};
|
||||
use bevy_app::{App, Plugin};
|
||||
use bevy_asset::{Asset, AssetApp, AssetEvent, AssetId, AssetServer, Assets, Handle};
|
||||
|
@ -440,6 +440,7 @@ pub fn queue_material_meshes<M: Material>(
|
|||
Option<&Tonemapping>,
|
||||
Option<&DebandDither>,
|
||||
Option<&EnvironmentMapLight>,
|
||||
Option<&ShadowFilteringMethod>,
|
||||
Option<&ScreenSpaceAmbientOcclusionSettings>,
|
||||
Option<&NormalPrepass>,
|
||||
Option<&TemporalAntiAliasSettings>,
|
||||
|
@ -456,6 +457,7 @@ pub fn queue_material_meshes<M: Material>(
|
|||
tonemapping,
|
||||
dither,
|
||||
environment_map,
|
||||
shadow_filter_method,
|
||||
ssao,
|
||||
normal_prepass,
|
||||
taa_settings,
|
||||
|
@ -482,6 +484,19 @@ pub fn queue_material_meshes<M: Material>(
|
|||
if environment_map_loaded {
|
||||
view_key |= MeshPipelineKey::ENVIRONMENT_MAP;
|
||||
}
|
||||
|
||||
match shadow_filter_method.unwrap_or(&ShadowFilteringMethod::default()) {
|
||||
ShadowFilteringMethod::Hardware2x2 => {
|
||||
view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_HARDWARE_2X2;
|
||||
}
|
||||
ShadowFilteringMethod::Castano13 => {
|
||||
view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_CASTANO_13;
|
||||
}
|
||||
ShadowFilteringMethod::Jimenez14 => {
|
||||
view_key |= MeshPipelineKey::SHADOW_FILTER_METHOD_JIMENEZ_14;
|
||||
}
|
||||
}
|
||||
|
||||
if !view.hdr {
|
||||
if let Some(tonemapping) = tonemapping {
|
||||
view_key |= MeshPipelineKey::TONEMAP_IN_SHADER;
|
||||
|
|
|
@ -659,24 +659,35 @@ bitflags::bitflags! {
|
|||
const TONEMAP_METHOD_ACES_FITTED = 3 << Self::TONEMAP_METHOD_SHIFT_BITS;
|
||||
const TONEMAP_METHOD_AGX = 4 << Self::TONEMAP_METHOD_SHIFT_BITS;
|
||||
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 TONEMAP_METHOD_TONY_MC_MAPFACE = 6 << Self::TONEMAP_METHOD_SHIFT_BITS;
|
||||
const TONEMAP_METHOD_BLENDER_FILMIC = 7 << Self::TONEMAP_METHOD_SHIFT_BITS;
|
||||
const SHADOW_FILTER_METHOD_RESERVED_BITS = Self::SHADOW_FILTER_METHOD_MASK_BITS << Self::SHADOW_FILTER_METHOD_SHIFT_BITS;
|
||||
const SHADOW_FILTER_METHOD_HARDWARE_2X2 = 0 << Self::SHADOW_FILTER_METHOD_SHIFT_BITS;
|
||||
const SHADOW_FILTER_METHOD_CASTANO_13 = 1 << Self::SHADOW_FILTER_METHOD_SHIFT_BITS;
|
||||
const SHADOW_FILTER_METHOD_JIMENEZ_14 = 2 << Self::SHADOW_FILTER_METHOD_SHIFT_BITS;
|
||||
}
|
||||
}
|
||||
|
||||
impl MeshPipelineKey {
|
||||
const MSAA_MASK_BITS: u32 = 0b111;
|
||||
const MSAA_SHIFT_BITS: u32 = 32 - Self::MSAA_MASK_BITS.count_ones();
|
||||
|
||||
const PRIMITIVE_TOPOLOGY_MASK_BITS: u32 = 0b111;
|
||||
const PRIMITIVE_TOPOLOGY_SHIFT_BITS: u32 =
|
||||
Self::MSAA_SHIFT_BITS - Self::PRIMITIVE_TOPOLOGY_MASK_BITS.count_ones();
|
||||
|
||||
const BLEND_MASK_BITS: u32 = 0b11;
|
||||
const BLEND_SHIFT_BITS: u32 =
|
||||
Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS - Self::BLEND_MASK_BITS.count_ones();
|
||||
|
||||
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 SHADOW_FILTER_METHOD_MASK_BITS: u32 = 0b11;
|
||||
const SHADOW_FILTER_METHOD_SHIFT_BITS: u32 =
|
||||
Self::TONEMAP_METHOD_SHIFT_BITS - Self::SHADOW_FILTER_METHOD_MASK_BITS.count_ones();
|
||||
|
||||
pub fn from_msaa_samples(msaa_samples: u32) -> Self {
|
||||
let msaa_bits =
|
||||
(msaa_samples.trailing_zeros() & Self::MSAA_MASK_BITS) << Self::MSAA_SHIFT_BITS;
|
||||
|
@ -904,6 +915,16 @@ impl SpecializedMeshPipeline for MeshPipeline {
|
|||
shader_defs.push("TAA".into());
|
||||
}
|
||||
|
||||
let shadow_filter_method =
|
||||
key.intersection(MeshPipelineKey::SHADOW_FILTER_METHOD_RESERVED_BITS);
|
||||
if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_HARDWARE_2X2 {
|
||||
shader_defs.push("SHADOW_FILTER_METHOD_HARDWARE_2X2".into());
|
||||
} else if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_CASTANO_13 {
|
||||
shader_defs.push("SHADOW_FILTER_METHOD_CASTANO_13".into());
|
||||
} else if shadow_filter_method == MeshPipelineKey::SHADOW_FILTER_METHOD_JIMENEZ_14 {
|
||||
shader_defs.push("SHADOW_FILTER_METHOD_JIMENEZ_14".into());
|
||||
}
|
||||
|
||||
let format = if key.contains(MeshPipelineKey::HDR) {
|
||||
ViewTarget::TEXTURE_FORMAT_HDR
|
||||
} else {
|
||||
|
@ -1069,10 +1090,12 @@ pub fn prepare_mesh_view_bind_groups(
|
|||
Option<&EnvironmentMapLight>,
|
||||
&Tonemapping,
|
||||
)>,
|
||||
images: Res<RenderAssets<Image>>,
|
||||
mut fallback_images: FallbackImagesMsaa,
|
||||
mut fallback_depths: FallbackImagesDepth,
|
||||
fallback_cubemap: Res<FallbackImageCubemap>,
|
||||
(images, mut fallback_images, mut fallback_depths, fallback_cubemap): (
|
||||
Res<RenderAssets<Image>>,
|
||||
FallbackImagesMsaa,
|
||||
FallbackImagesDepth,
|
||||
Res<FallbackImageCubemap>,
|
||||
),
|
||||
msaa: Res<Msaa>,
|
||||
globals_buffer: Res<GlobalsBuffer>,
|
||||
tonemapping_luts: Res<TonemappingLuts>,
|
||||
|
|
132
crates/bevy_pbr/src/render/shadow_sampling.wgsl
Normal file
132
crates/bevy_pbr/src/render/shadow_sampling.wgsl
Normal file
|
@ -0,0 +1,132 @@
|
|||
#define_import_path bevy_pbr::shadow_sampling
|
||||
|
||||
#import bevy_pbr::mesh_view_bindings as view_bindings
|
||||
#import bevy_pbr::utils PI
|
||||
|
||||
// Do the lookup, using HW 2x2 PCF and comparison
|
||||
fn sample_shadow_map_hardware(light_local: vec2<f32>, depth: f32, array_index: i32) -> f32 {
|
||||
#ifdef NO_ARRAY_TEXTURES_SUPPORT
|
||||
return textureSampleCompareLevel(
|
||||
view_bindings::directional_shadow_textures,
|
||||
view_bindings::directional_shadow_textures_sampler,
|
||||
light_local,
|
||||
depth,
|
||||
);
|
||||
#else
|
||||
return textureSampleCompareLevel(
|
||||
view_bindings::directional_shadow_textures,
|
||||
view_bindings::directional_shadow_textures_sampler,
|
||||
light_local,
|
||||
array_index,
|
||||
depth,
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
// https://web.archive.org/web/20230210095515/http://the-witness.net/news/2013/09/shadow-mapping-summary-part-1
|
||||
fn sample_shadow_map_castano_thirteen(light_local: vec2<f32>, depth: f32, array_index: i32) -> f32 {
|
||||
let shadow_map_size = vec2<f32>(textureDimensions(view_bindings::directional_shadow_textures));
|
||||
let inv_shadow_map_size = 1.0 / shadow_map_size;
|
||||
|
||||
let uv = light_local * shadow_map_size;
|
||||
var base_uv = floor(uv + 0.5);
|
||||
let s = (uv.x + 0.5 - base_uv.x);
|
||||
let t = (uv.y + 0.5 - base_uv.y);
|
||||
base_uv -= 0.5;
|
||||
base_uv *= inv_shadow_map_size;
|
||||
|
||||
let uw0 = (4.0 - 3.0 * s);
|
||||
let uw1 = 7.0;
|
||||
let uw2 = (1.0 + 3.0 * s);
|
||||
|
||||
let u0 = (3.0 - 2.0 * s) / uw0 - 2.0;
|
||||
let u1 = (3.0 + s) / uw1;
|
||||
let u2 = s / uw2 + 2.0;
|
||||
|
||||
let vw0 = (4.0 - 3.0 * t);
|
||||
let vw1 = 7.0;
|
||||
let vw2 = (1.0 + 3.0 * t);
|
||||
|
||||
let v0 = (3.0 - 2.0 * t) / vw0 - 2.0;
|
||||
let v1 = (3.0 + t) / vw1;
|
||||
let v2 = t / vw2 + 2.0;
|
||||
|
||||
var sum = 0.0;
|
||||
|
||||
sum += uw0 * vw0 * sample_shadow_map_hardware(base_uv + (vec2(u0, v0) * inv_shadow_map_size), depth, array_index);
|
||||
sum += uw1 * vw0 * sample_shadow_map_hardware(base_uv + (vec2(u1, v0) * inv_shadow_map_size), depth, array_index);
|
||||
sum += uw2 * vw0 * sample_shadow_map_hardware(base_uv + (vec2(u2, v0) * inv_shadow_map_size), depth, array_index);
|
||||
|
||||
sum += uw0 * vw1 * sample_shadow_map_hardware(base_uv + (vec2(u0, v1) * inv_shadow_map_size), depth, array_index);
|
||||
sum += uw1 * vw1 * sample_shadow_map_hardware(base_uv + (vec2(u1, v1) * inv_shadow_map_size), depth, array_index);
|
||||
sum += uw2 * vw1 * sample_shadow_map_hardware(base_uv + (vec2(u2, v1) * inv_shadow_map_size), depth, array_index);
|
||||
|
||||
sum += uw0 * vw2 * sample_shadow_map_hardware(base_uv + (vec2(u0, v2) * inv_shadow_map_size), depth, array_index);
|
||||
sum += uw1 * vw2 * sample_shadow_map_hardware(base_uv + (vec2(u1, v2) * inv_shadow_map_size), depth, array_index);
|
||||
sum += uw2 * vw2 * sample_shadow_map_hardware(base_uv + (vec2(u2, v2) * inv_shadow_map_size), depth, array_index);
|
||||
|
||||
return sum * (1.0 / 144.0);
|
||||
}
|
||||
|
||||
// https://blog.demofox.org/2022/01/01/interleaved-gradient-noise-a-different-kind-of-low-discrepancy-sequence
|
||||
fn interleaved_gradient_noise(pixel_coordinates: vec2<f32>) -> f32 {
|
||||
let frame = f32(view_bindings::globals.frame_count % 64u);
|
||||
let xy = pixel_coordinates + 5.588238 * frame;
|
||||
return fract(52.9829189 * fract(0.06711056 * xy.x + 0.00583715 * xy.y));
|
||||
}
|
||||
|
||||
fn map(min1: f32, max1: f32, min2: f32, max2: f32, value: f32) -> f32 {
|
||||
return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
|
||||
}
|
||||
|
||||
fn sample_shadow_map_jimenez_fourteen(light_local: vec2<f32>, depth: f32, array_index: i32, texel_size: f32) -> f32 {
|
||||
let shadow_map_size = vec2<f32>(textureDimensions(view_bindings::directional_shadow_textures));
|
||||
|
||||
let random_angle = 2.0 * PI * interleaved_gradient_noise(light_local * shadow_map_size);
|
||||
let m = vec2(sin(random_angle), cos(random_angle));
|
||||
let rotation_matrix = mat2x2(
|
||||
m.y, -m.x,
|
||||
m.x, m.y
|
||||
);
|
||||
|
||||
// Empirically chosen fudge factor to make PCF look better across different CSM cascades
|
||||
let f = map(0.00390625, 0.022949219, 0.015, 0.035, texel_size);
|
||||
let uv_offset_scale = f / (texel_size * shadow_map_size);
|
||||
|
||||
// https://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare (slides 120-135)
|
||||
let sample_offset1 = (rotation_matrix * vec2(-0.7071, 0.7071)) * uv_offset_scale;
|
||||
let sample_offset2 = (rotation_matrix * vec2(-0.0000, -0.8750)) * uv_offset_scale;
|
||||
let sample_offset3 = (rotation_matrix * vec2( 0.5303, 0.5303)) * uv_offset_scale;
|
||||
let sample_offset4 = (rotation_matrix * vec2(-0.6250, -0.0000)) * uv_offset_scale;
|
||||
let sample_offset5 = (rotation_matrix * vec2( 0.3536, -0.3536)) * uv_offset_scale;
|
||||
let sample_offset6 = (rotation_matrix * vec2(-0.0000, 0.3750)) * uv_offset_scale;
|
||||
let sample_offset7 = (rotation_matrix * vec2(-0.1768, -0.1768)) * uv_offset_scale;
|
||||
let sample_offset8 = (rotation_matrix * vec2( 0.1250, 0.0000)) * uv_offset_scale;
|
||||
|
||||
var sum = 0.0;
|
||||
sum += sample_shadow_map_hardware(light_local + sample_offset1, depth, array_index);
|
||||
sum += sample_shadow_map_hardware(light_local + sample_offset2, depth, array_index);
|
||||
sum += sample_shadow_map_hardware(light_local + sample_offset3, depth, array_index);
|
||||
sum += sample_shadow_map_hardware(light_local + sample_offset4, depth, array_index);
|
||||
sum += sample_shadow_map_hardware(light_local + sample_offset5, depth, array_index);
|
||||
sum += sample_shadow_map_hardware(light_local + sample_offset6, depth, array_index);
|
||||
sum += sample_shadow_map_hardware(light_local + sample_offset7, depth, array_index);
|
||||
sum += sample_shadow_map_hardware(light_local + sample_offset8, depth, array_index);
|
||||
return sum / 8.0;
|
||||
}
|
||||
|
||||
fn sample_shadow_map(light_local: vec2<f32>, depth: f32, array_index: i32, texel_size: f32) -> f32 {
|
||||
#ifdef SHADOW_FILTER_METHOD_CASTANO_13
|
||||
return sample_shadow_map_castano_thirteen(light_local, depth, array_index);
|
||||
#else ifdef SHADOW_FILTER_METHOD_JIMENEZ_14
|
||||
return sample_shadow_map_jimenez_fourteen(light_local, depth, array_index, texel_size);
|
||||
#else ifdef SHADOW_FILTER_METHOD_HARDWARE_2X2
|
||||
return sample_shadow_map_hardware(light_local, depth, array_index);
|
||||
#else
|
||||
// This needs a default return value to avoid shader compilation errors if it's compiled with no SHADOW_FILTER_METHOD_* defined.
|
||||
// (eg. if the normal prepass is enabled it ends up compiling this due to the normal prepass depending on pbr_functions, which depends on shadows)
|
||||
// This should never actually get used, as anyone using bevy's lighting/shadows should always have a SHADOW_FILTER_METHOD defined.
|
||||
// Set to 0 to make it obvious that something is wrong.
|
||||
return 0.0;
|
||||
#endif
|
||||
}
|
|
@ -3,6 +3,8 @@
|
|||
#import bevy_pbr::mesh_view_types POINT_LIGHT_FLAGS_SPOT_LIGHT_Y_NEGATIVE
|
||||
#import bevy_pbr::mesh_view_bindings as view_bindings
|
||||
#import bevy_pbr::utils hsv2rgb
|
||||
#import bevy_pbr::shadow_sampling sample_shadow_map
|
||||
|
||||
const flip_z: vec3<f32> = vec3<f32>(1.0, 1.0, -1.0);
|
||||
|
||||
fn fetch_point_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>) -> f32 {
|
||||
|
@ -95,13 +97,9 @@ fn fetch_spot_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: ve
|
|||
// 0.1 must match POINT_LIGHT_NEAR_Z
|
||||
let depth = 0.1 / -projected_position.z;
|
||||
|
||||
#ifdef NO_ARRAY_TEXTURES_SUPPORT
|
||||
return textureSampleCompare(view_bindings::directional_shadow_textures, view_bindings::directional_shadow_textures_sampler,
|
||||
shadow_uv, depth);
|
||||
#else
|
||||
return textureSampleCompareLevel(view_bindings::directional_shadow_textures, view_bindings::directional_shadow_textures_sampler,
|
||||
shadow_uv, i32(light_id) + view_bindings::lights.spot_light_shadowmap_offset, depth);
|
||||
#endif
|
||||
// Number determined by trial and error that gave nice results.
|
||||
let texel_size = 0.0134277345;
|
||||
return sample_shadow_map(shadow_uv, depth, i32(light_id) + view_bindings::lights.spot_light_shadowmap_offset, texel_size);
|
||||
}
|
||||
|
||||
fn get_cascade_index(light_id: u32, view_z: f32) -> u32 {
|
||||
|
@ -115,7 +113,7 @@ fn get_cascade_index(light_id: u32, view_z: f32) -> u32 {
|
|||
return (*light).num_cascades;
|
||||
}
|
||||
|
||||
fn sample_cascade(light_id: u32, cascade_index: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>) -> f32 {
|
||||
fn sample_directional_cascade(light_id: u32, cascade_index: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>) -> f32 {
|
||||
let light = &view_bindings::lights.directional_lights[light_id];
|
||||
let cascade = &(*light).cascades[cascade_index];
|
||||
|
||||
|
@ -141,25 +139,9 @@ fn sample_cascade(light_id: u32, cascade_index: u32, frag_position: vec4<f32>, s
|
|||
let light_local = offset_position_ndc.xy * flip_correction + vec2<f32>(0.5, 0.5);
|
||||
|
||||
let depth = offset_position_ndc.z;
|
||||
// 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.
|
||||
#ifdef NO_ARRAY_TEXTURES_SUPPORT
|
||||
return textureSampleCompareLevel(
|
||||
view_bindings::directional_shadow_textures,
|
||||
view_bindings::directional_shadow_textures_sampler,
|
||||
light_local,
|
||||
depth
|
||||
);
|
||||
#else
|
||||
return textureSampleCompareLevel(
|
||||
view_bindings::directional_shadow_textures,
|
||||
view_bindings::directional_shadow_textures_sampler,
|
||||
light_local,
|
||||
i32((*light).depth_texture_base_index + cascade_index),
|
||||
depth
|
||||
);
|
||||
#endif
|
||||
|
||||
let array_index = i32((*light).depth_texture_base_index + cascade_index);
|
||||
return sample_shadow_map(light_local, depth, array_index, (*cascade).texel_size);
|
||||
}
|
||||
|
||||
fn fetch_directional_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>, view_z: f32) -> f32 {
|
||||
|
@ -170,7 +152,7 @@ fn fetch_directional_shadow(light_id: u32, frag_position: vec4<f32>, surface_nor
|
|||
return 1.0;
|
||||
}
|
||||
|
||||
var shadow = sample_cascade(light_id, cascade_index, frag_position, surface_normal);
|
||||
var shadow = sample_directional_cascade(light_id, cascade_index, frag_position, surface_normal);
|
||||
|
||||
// Blend with the next cascade, if there is one.
|
||||
let next_cascade_index = cascade_index + 1u;
|
||||
|
@ -178,7 +160,7 @@ fn fetch_directional_shadow(light_id: u32, frag_position: vec4<f32>, surface_nor
|
|||
let this_far_bound = (*light).cascades[cascade_index].far_bound;
|
||||
let next_near_bound = (1.0 - (*light).cascades_overlap_proportion) * this_far_bound;
|
||||
if (-view_z >= next_near_bound) {
|
||||
let next_shadow = sample_cascade(light_id, next_cascade_index, frag_position, surface_normal);
|
||||
let next_shadow = sample_directional_cascade(light_id, next_cascade_index, frag_position, surface_normal);
|
||||
shadow = mix(shadow, next_shadow, (-view_z - next_near_bound) / (this_far_bound - next_near_bound));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue