Skip to content

Commit

Permalink
Added: alpha map shadow clipping
Browse files Browse the repository at this point in the history
  • Loading branch information
443eb9 committed Mar 12, 2024
1 parent fd2579f commit 895b98b
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 17 deletions.
4 changes: 3 additions & 1 deletion RELEASE_DRAFT.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
- Moved redundant Bevy features into dev-dependencies.
- Added `alpha_threshold` to `ShadowMap2dConfig`.
- Samples of PCF is no longer limited to 32.
- Added `SpotLight2d`
- Added `SpotLight2d`.
- Added alpha map to clip shadows, which improved shadow accuracy.
- Added `catalinzz` feature. It will be enabled as default. In the future, in order to support more fancy features, there will be more shading approaches like SDF+RayMarching and Ray Tracing, and you can choose according to your needs.

# What's Fixed:

Expand Down
2 changes: 1 addition & 1 deletion src/ecs/catalinzz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ impl Default for ShadowMap2dConfig {
far: 1000.,
size: 512,
offset: Vec2::ZERO,
bias: 0.,
bias: 0.005,
alpha_threshold: 0.9,
pcf: Default::default(),
}
Expand Down
2 changes: 2 additions & 0 deletions src/render/catalinzz/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ impl Node for Shadow2dPrepassNode {
&pipeline.prepass_layout,
&BindGroupEntries::sequential((
&shadow_view.attachment.texture.default_view,
shadow_map_storage.alpha_map_view(),
shadow_map_storage.texture_view_primary(),
gpu_meta_buffers.shadow_map_meta_buffer_binding(),
)),
Expand Down Expand Up @@ -402,6 +403,7 @@ impl Node for Shadow2dMainPass {
&BindGroupEntries::sequential((
post_process.source,
&pipeline.main_texture_sampler,
shadow_map_storage.alpha_map_view(),
shadow_map_storage.final_texture_view(),
view_uniforms.uniforms.binding().unwrap(),
gpu_meta_buffers.shadow_map_meta_buffer_binding(),
Expand Down
17 changes: 13 additions & 4 deletions src/render/catalinzz/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ pub const SHADOW_MAP_FORMAT: TextureFormat = TextureFormat::Rgba32Float;
#[cfg(not(feature = "compatibility"))]
pub const SHADOW_MAP_FORMAT: TextureFormat = TextureFormat::Rg32Float;

pub const ALPHA_MAP_FORMAT: TextureFormat = TextureFormat::R32Float;

pub struct CatalinzzApproachPlugin;

impl Plugin for CatalinzzApproachPlugin {
Expand Down Expand Up @@ -303,6 +305,7 @@ pub struct ShadowMap2dStorage {
meta: ShadowMap2dMeta,
primary_shadow_map: Option<GpuImage>,
secondary_shadow_map: Option<GpuImage>,
alpha_map: Option<GpuImage>,
work_group_count_per_light: UVec3,
work_group_count_total: UVec3,
num_reductions: u32,
Expand All @@ -320,8 +323,9 @@ impl ShadowMap2dStorage {
}

self.meta = meta;
self.primary_shadow_map = Some(self.create_shadow_map(render_device));
self.secondary_shadow_map = Some(self.create_shadow_map(render_device));
self.primary_shadow_map = Some(self.create_shadow_map(render_device, SHADOW_MAP_FORMAT));
self.secondary_shadow_map = Some(self.create_shadow_map(render_device, SHADOW_MAP_FORMAT));
self.alpha_map = Some(self.create_shadow_map(render_device, ALPHA_MAP_FORMAT));
self.work_group_count_per_light = UVec3 {
x: meta.size.div_ceil(SHADOW_PREPASS_WORKGROUP_SIZE.x),
y: meta.size.div_ceil(SHADOW_PREPASS_WORKGROUP_SIZE.y),
Expand Down Expand Up @@ -353,6 +357,11 @@ impl ShadowMap2dStorage {
&self.secondary_shadow_map.as_ref().unwrap().texture_view
}

#[inline]
pub fn alpha_map_view(&self) -> &TextureView {
&self.alpha_map.as_ref().unwrap().texture_view
}

#[inline]
pub fn final_texture_view(&self) -> &TextureView {
if self.num_reductions % 2 == 0 {
Expand All @@ -377,7 +386,7 @@ impl ShadowMap2dStorage {
self.num_reductions
}

fn create_shadow_map(&self, render_device: &RenderDevice) -> GpuImage {
fn create_shadow_map(&self, render_device: &RenderDevice, format: TextureFormat) -> GpuImage {
let meta = self.meta;

let shadow_map = render_device.create_texture(&TextureDescriptor {
Expand All @@ -390,7 +399,7 @@ impl ShadowMap2dStorage {
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: SHADOW_MAP_FORMAT,
format,
usage: TextureUsages::STORAGE_BINDING
| TextureUsages::TEXTURE_BINDING
| TextureUsages::RENDER_ATTACHMENT,
Expand Down
14 changes: 12 additions & 2 deletions src/render/catalinzz/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ use bevy::render::render_resource::binding_types as binding;
use crate::render::light::{GpuAmbientLight2d, GpuPointLight2d};

use super::{
GpuShadowMapMeta, SHADOW_DISTORT_PASS_SHADER, SHADOW_MAIN_PASS_SHADER, SHADOW_MAP_FORMAT,
SHADOW_PREPASS_SHADER, SHADOW_REDUCTION_PASS_SHADER,
GpuShadowMapMeta, ALPHA_MAP_FORMAT, SHADOW_DISTORT_PASS_SHADER, SHADOW_MAIN_PASS_SHADER,
SHADOW_MAP_FORMAT, SHADOW_PREPASS_SHADER, SHADOW_REDUCTION_PASS_SHADER,
};

fn get_shader_defs() -> Vec<ShaderDefVal> {
Expand Down Expand Up @@ -53,6 +53,11 @@ impl FromWorld for Shadow2dPrepassPipeline {
(
// Main texture
binding::texture_2d(TextureSampleType::Float { filterable: true }),
// Alpha map
binding::texture_storage_2d_array(
ALPHA_MAP_FORMAT,
StorageTextureAccess::WriteOnly,
),
// Shadow map
binding::texture_storage_2d_array(
SHADOW_MAP_FORMAT,
Expand Down Expand Up @@ -201,6 +206,11 @@ impl FromWorld for Shadow2dMainPassPipeline {
// Main texture
binding::texture_2d(TextureSampleType::Float { filterable: true }),
binding::sampler(SamplerBindingType::Filtering),
// Alpha map
binding::texture_storage_2d_array(
ALPHA_MAP_FORMAT,
StorageTextureAccess::ReadOnly,
),
// Shadow map
binding::texture_storage_2d_array(
SHADOW_MAP_FORMAT,
Expand Down
21 changes: 15 additions & 6 deletions src/render/catalinzz/shaders/shadow_2d_main_pass.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ var main_tex: texture_2d<f32>;
var main_tex_sampler: sampler;

@group(0) @binding(2)
var alpha_map: texture_storage_2d_array<r32float, read>;

@group(0) @binding(3)
var shadow_map: texture_storage_2d_array<
#ifdef COMPATIBILITY
rgba32float,
Expand All @@ -22,19 +25,19 @@ var shadow_map: texture_storage_2d_array<
read
>;

@group(0) @binding(3)
@group(0) @binding(4)
var<uniform> main_view: View;

@group(0) @binding(4)
@group(0) @binding(5)
var<uniform> shadow_map_meta: ShadowMapMeta;

@group(0) @binding(5)
@group(0) @binding(6)
var<uniform> ambient_light: AmbientLight2d;

@group(0) @binding(6)
@group(0) @binding(7)
var<storage> poisson_disk: array<vec2f>;

@group(0) @binding(7)
@group(0) @binding(8)
var<storage> point_lights: array<PointLight2d>;

fn get_caster_distance_h(rel_ss: vec2f, i_light: u32) -> f32 {
Expand Down Expand Up @@ -63,14 +66,20 @@ fn pcf(rel_ss: vec2f, sample_count: u32, sample_radius: f32, i_light: u32) -> f3
let sample_ss = rel_ss + poisson_disk[i] * sample_radius;
let dist = get_caster_distance(sample_ss, i_light);

if dist > length(sample_ss) + shadow_map_meta.bias {
if dist > length(sample_ss) - shadow_map_meta.bias
&& get_alpha(sample_ss, i_light) < shadow_map_meta.alpha_threshold {
visibility += 1.;
}
}
visibility /= f32(sample_count);
return visibility;
}

fn get_alpha(rel_ss: vec2f, i_light: u32) -> f32 {
let px = (rel_ss / 2. + 1.) / 2. * f32(shadow_map_meta.size);
return textureLoad(alpha_map, vec2i(px), i_light).r;
}

@fragment
fn dbg_output_shadow_map(in: FullscreenVertexOutput) -> @location(0) vec4f {
return textureLoad(shadow_map, vec2u(in.uv * vec2f(shadow_map_meta.size)), 0);
Expand Down
16 changes: 13 additions & 3 deletions src/render/catalinzz/shaders/shadow_2d_prepass.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
var main_tex: texture_2d<f32>;

@group(0) @binding(1)
var alpha_map: texture_storage_2d_array<r32float, write>;

@group(0) @binding(2)
var shadow_map: texture_storage_2d_array<
#ifdef COMPATIBILITY
rgba32float,
Expand All @@ -13,7 +16,7 @@ var shadow_map: texture_storage_2d_array<
write
>;

@group(0) @binding(2)
@group(0) @binding(3)
var<uniform> shadow_map_meta: ShadowMapMeta;

@compute @workgroup_size(16, 16, 1)
Expand All @@ -25,13 +28,20 @@ fn main(@builtin(global_invocation_id) invocation_id: vec3u) {
}

var d = 1.;
if textureLoad(main_tex, px, 0).a > shadow_map_meta.alpha_threshold {
let alpha = textureLoad(main_tex, px, 0).a;
if alpha > shadow_map_meta.alpha_threshold {
d = length(vec2f(px) / vec2f(f32(shadow_map_meta.size)) - vec2f(0.5)) * 2.;
textureStore(
alpha_map,
px,
shadow_map_meta.index,
vec4f(alpha, 0., 0., 0.),
);
}

textureStore(
shadow_map,
vec2u(px.x, px.y),
px,
shadow_map_meta.index,
vec4f(d, 0., 0., 0.),
);
Expand Down

0 comments on commit 895b98b

Please sign in to comment.