render: improve shadow settings to make it possible to switch between PCF, PCSS, hardware 2x2 PCF, or disable filtering all together
This commit is contained in:
parent
c961568b96
commit
c91ee67961
|
@ -494,27 +494,8 @@ impl Node for ShadowMapsPass {
|
||||||
// use a queue for storing atlas ids to add to entities after the entities are iterated
|
// use a queue for storing atlas ids to add to entities after the entities are iterated
|
||||||
let mut index_components_queue = VecDeque::new();
|
let mut index_components_queue = VecDeque::new();
|
||||||
|
|
||||||
/* for (entity, pos, (has_dir, has_point)) in world.view_iter::<(Entities, &Transform, Or<Has<DirectionalLight>, Has<PointLight>>)>() {
|
|
||||||
if !self.depth_maps.contains_key(&entity) {
|
|
||||||
// TODO: calculate far plane
|
|
||||||
let (light_type, far_plane) = if has_dir.is_some() {
|
|
||||||
(LightType::Directional, 45.0)
|
|
||||||
} else if has_point.is_some() {
|
|
||||||
(LightType::Point, 45.0)
|
|
||||||
} else {
|
|
||||||
todo!("Spot lights")
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: dont pack the textures as they're added
|
|
||||||
let atlas_index =
|
|
||||||
self.create_depth_map(&context.queue, light_type, entity, *pos, far_plane);
|
|
||||||
index_components_queue.push_back((entity, atlas_index));
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
|
|
||||||
for (entity, pos, _) in world.view_iter::<(Entities, &Transform, Has<DirectionalLight>)>() {
|
for (entity, pos, _) in world.view_iter::<(Entities, &Transform, Has<DirectionalLight>)>() {
|
||||||
if !self.depth_maps.contains_key(&entity) {
|
if !self.depth_maps.contains_key(&entity) {
|
||||||
// TODO: dont pack the textures as they're added
|
|
||||||
let atlas_index = self.create_depth_map(
|
let atlas_index = self.create_depth_map(
|
||||||
&context.queue,
|
&context.queue,
|
||||||
LightType::Directional,
|
LightType::Directional,
|
||||||
|
@ -528,7 +509,6 @@ impl Node for ShadowMapsPass {
|
||||||
|
|
||||||
for (entity, pos, _) in world.view_iter::<(Entities, &Transform, Has<PointLight>)>() {
|
for (entity, pos, _) in world.view_iter::<(Entities, &Transform, Has<PointLight>)>() {
|
||||||
if !self.depth_maps.contains_key(&entity) {
|
if !self.depth_maps.contains_key(&entity) {
|
||||||
// TODO: dont pack the textures as they're added
|
|
||||||
let atlas_index =
|
let atlas_index =
|
||||||
self.create_depth_map(&context.queue, LightType::Point, entity, *pos, 30.0);
|
self.create_depth_map(&context.queue, LightType::Point, entity, *pos, 30.0);
|
||||||
index_components_queue.push_back((entity, atlas_index));
|
index_components_queue.push_back((entity, atlas_index));
|
||||||
|
@ -727,7 +707,6 @@ fn light_shadow_pass_impl<'a>(
|
||||||
}
|
}
|
||||||
let buffers = buffers.unwrap();
|
let buffers = buffers.unwrap();
|
||||||
|
|
||||||
//let uniform_index = light_uniforms_buffer.offset_of(light_depth_map.uniform_index[0]) as u32;
|
|
||||||
pass.set_bind_group(0, &uniforms_bind_group, &[]);
|
pass.set_bind_group(0, &uniforms_bind_group, &[]);
|
||||||
|
|
||||||
// Get the bindgroup for job's transform and bind to it using an offset.
|
// Get the bindgroup for job's transform and bind to it using an offset.
|
||||||
|
@ -805,8 +784,19 @@ impl LightShadowMapAtlas {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Copy, Clone)]
|
||||||
|
pub enum ShadowFilteringMode {
|
||||||
|
None,
|
||||||
|
/// Uses hardware features for 2x2 PCF.
|
||||||
|
Pcf2x2,
|
||||||
|
Pcf,
|
||||||
|
#[default]
|
||||||
|
Pcss,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct ShadowSettings {
|
pub struct ShadowSettings {
|
||||||
|
pub filtering_mode: ShadowFilteringMode,
|
||||||
/// How many PCF filtering samples are used per dimension.
|
/// How many PCF filtering samples are used per dimension.
|
||||||
///
|
///
|
||||||
/// A value of 25 is common, this is maxed to 128.
|
/// A value of 25 is common, this is maxed to 128.
|
||||||
|
@ -821,8 +811,9 @@ pub struct ShadowSettings {
|
||||||
impl Default for ShadowSettings {
|
impl Default for ShadowSettings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
pcf_samples_num: 64,
|
filtering_mode: ShadowFilteringMode::default(),
|
||||||
pcss_blocker_search_samples: 36,
|
pcf_samples_num: 25,
|
||||||
|
pcss_blocker_search_samples: 25,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -830,19 +821,34 @@ impl Default for ShadowSettings {
|
||||||
const PCF_SAMPLES_NUM_MAX: u32 = 128;
|
const PCF_SAMPLES_NUM_MAX: u32 = 128;
|
||||||
const PCSS_SAMPLES_NUM_MAX: u32 = 128;
|
const PCSS_SAMPLES_NUM_MAX: u32 = 128;
|
||||||
|
|
||||||
/// Uniform version of [`ShadowSettings`]
|
/// Uniform version of [`ShadowSettings`].
|
||||||
|
///
|
||||||
|
/// If `pcf_samples_num` is set to zero, PCF and PCSS will be disabled.
|
||||||
|
/// If `pcf_samples_num` is set to 2, ONLY hardware 2x2 PCF will be used.
|
||||||
|
/// If `pcss_blocker_search_samples` is set to zero, PCSS will be disabled.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
struct ShadowSettingsUniform {
|
struct ShadowSettingsUniform {
|
||||||
|
//use_pcf_hardware_2x2: u32,
|
||||||
pcf_samples_num: u32,
|
pcf_samples_num: u32,
|
||||||
pcss_blocker_search_samples: u32,
|
pcss_blocker_search_samples: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ShadowSettings> for ShadowSettingsUniform {
|
impl From<ShadowSettings> for ShadowSettingsUniform {
|
||||||
fn from(value: ShadowSettings) -> Self {
|
fn from(value: ShadowSettings) -> Self {
|
||||||
|
let raw_pcf_samples = value.pcf_samples_num.min(PCF_SAMPLES_NUM_MAX);
|
||||||
|
let raw_pcss_samples = value.pcss_blocker_search_samples.min(PCSS_SAMPLES_NUM_MAX);
|
||||||
|
|
||||||
|
let (pcf_samples, pcss_samples) = match value.filtering_mode {
|
||||||
|
ShadowFilteringMode::None => (0, 0),
|
||||||
|
ShadowFilteringMode::Pcf2x2 => (2, 0),
|
||||||
|
ShadowFilteringMode::Pcf => (raw_pcf_samples, 0),
|
||||||
|
ShadowFilteringMode::Pcss => (raw_pcf_samples, raw_pcss_samples),
|
||||||
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
pcf_samples_num: value.pcf_samples_num.min(PCF_SAMPLES_NUM_MAX),
|
pcf_samples_num: pcf_samples,
|
||||||
pcss_blocker_search_samples: value.pcss_blocker_search_samples.min(PCSS_SAMPLES_NUM_MAX),
|
pcss_blocker_search_samples: pcss_samples,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -256,22 +256,30 @@ fn calc_shadow_dir_light(normal: vec3<f32>, light_dir: vec3<f32>, frag_pos_light
|
||||||
|
|
||||||
// Remap xy to [0.0, 1.0]
|
// Remap xy to [0.0, 1.0]
|
||||||
let xy_remapped = proj_coords.xy * 0.5 + 0.5;
|
let xy_remapped = proj_coords.xy * 0.5 + 0.5;
|
||||||
|
|
||||||
// get the atlas frame in [0; 1] in the atlas texture
|
|
||||||
// z is width, w is height
|
|
||||||
var region_rect = vec4<f32>(f32(shadow_u.atlas_frame.x), f32(shadow_u.atlas_frame.y), f32(shadow_u.atlas_frame.width), f32(shadow_u.atlas_frame.height));
|
|
||||||
region_rect /= f32(atlas_dimensions.x);
|
|
||||||
let region_coords = vec2<f32>(
|
|
||||||
mix(region_rect.x, region_rect.x + region_rect.z, xy_remapped.x),
|
|
||||||
mix(region_rect.y, region_rect.y + region_rect.w, xy_remapped.y)
|
|
||||||
);
|
|
||||||
|
|
||||||
// use a bias to avoid shadow acne
|
// use a bias to avoid shadow acne
|
||||||
let bias = 0.005;//max(0.05 * (1.0 - dot(normal, light_dir)), 0.005);
|
let bias = 0.005;//max(0.05 * (1.0 - dot(normal, light_dir)), 0.005);
|
||||||
let current_depth = proj_coords.z - bias;
|
let current_depth = proj_coords.z - bias;
|
||||||
|
|
||||||
//var shadow = pcf_dir_light(region_coords, current_depth, shadow_u, 1.0);
|
var shadow = 0.0;
|
||||||
var shadow = pcss_dir_light(xy_remapped, current_depth, shadow_u);
|
if u_shadow_settings.pcf_samples_num > 0u && u_shadow_settings.pcss_blocker_search_samples > 0u {
|
||||||
|
shadow = pcss_dir_light(xy_remapped, current_depth, shadow_u);
|
||||||
|
}
|
||||||
|
// hardware 2x2 PCF via camparison sampler
|
||||||
|
else if u_shadow_settings.pcf_samples_num == 2u {
|
||||||
|
let region_coords = to_atlas_frame_coords(shadow_u, xy_remapped);
|
||||||
|
shadow = textureSampleCompare(t_shadow_maps_atlas, s_shadow_maps_atlas_compare, region_coords, current_depth);
|
||||||
|
} else if u_shadow_settings.pcf_samples_num > 0u {
|
||||||
|
let atlas_dimensions = textureDimensions(t_shadow_maps_atlas);
|
||||||
|
// TODO: should texel size be using the entire atlas dimensions, or just the frame?
|
||||||
|
let texel_size = 1.0 / f32(atlas_dimensions.x); // f32(shadow_u.atlas_frame.width)
|
||||||
|
|
||||||
|
shadow = pcf_dir_light(xy_remapped, current_depth, shadow_u, texel_size);
|
||||||
|
} else { // pcf_samples_num == 0
|
||||||
|
let region_coords = to_atlas_frame_coords(shadow_u, xy_remapped);
|
||||||
|
let closest_depth = textureSampleLevel(t_shadow_maps_atlas, s_shadow_maps_atlas, region_coords, 0.0);
|
||||||
|
shadow = select(1.0, 0.0, current_depth > closest_depth);
|
||||||
|
}
|
||||||
|
|
||||||
// dont cast shadows outside the light's far plane
|
// dont cast shadows outside the light's far plane
|
||||||
if (proj_coords.z > 1.0) {
|
if (proj_coords.z > 1.0) {
|
||||||
|
|
Loading…
Reference in New Issue