Added polar shadowmaps for lights

master
hheik 2023-08-31 00:29:00 +03:00
parent 4cec5f45fe
commit 9056e8350b
5 changed files with 86 additions and 13 deletions

View File

@ -35,6 +35,12 @@ var<uniform> spot_light_count: i32;
@group(1) @binding(4)
var<uniform> spot_lights: array<SpotLight, 64>;
@group(1) @binding(5)
var shadowmap_texture: texture_2d<f32>;
@group(1) @binding(6)
var shadowmap_sampler: sampler;
@fragment
fn fragment(
mesh: MeshVertexOutput,
@ -54,13 +60,19 @@ fn fragment(
}
let radius = point_lights[i].radius - (sin(t * 5.0) * 0.5 + 0.5) * 0.2;
let diff = pos - point_lights[i].position;
let dist = distance(pos, point_lights[i].position);
let edge_treshold = radius * light_edge_mult - light_edge;
let edge_dist = max(dist - edge_treshold, 0.0);
bright_light = bright_light + step(dist, edge_treshold);
bright_light = bright_light + 1.0 / edge_dist;
dim_light = dim_light + step(dist, radius);
dim_light = dim_light + 1.0 / max(dist - radius, 0.0);
let uv = vec2<f32>(
(atan2(diff.y, diff.x) + radians(180.0)) / radians(360.0),
f32(i) / 128.0
);
let max_range = textureSample(shadowmap_texture, shadowmap_sampler, uv).r;
bright_light = bright_light + step(dist, edge_treshold) * step(dist, max_range);
dim_light = dim_light + step(dist, radius) * step(dist, max_range);
}
for (var i: i32 = 0; i < spot_light_count; i++) {
@ -74,11 +86,17 @@ fn fragment(
let spot_dir = vec2<f32>(cos(spot.rotation), sin(spot.rotation));
let angle_diff = acos(dot(spot_dir, normalize(diff)));
let uv = vec2<f32>(
(atan2(diff.y, diff.x) + radians(180.0)) / radians(360.0),
f32(i + 64) / 128.0
);
let max_range = textureSample(shadowmap_texture, shadowmap_sampler, uv).r;
let edge_treshold = radius * light_edge_mult - light_edge;
let edge_dist = max(dist - edge_treshold, 0.0);
let angle_mult = step(angle_diff, spot.angle / 2.0);
bright_light = bright_light + step(dist, edge_treshold) * angle_mult;
dim_light = dim_light + step(dist, radius) * angle_mult;
bright_light = bright_light + step(dist, edge_treshold) * angle_mult * step(dist, max_range);
dim_light = dim_light + step(dist, radius) * angle_mult * step(dist, max_range);
}
let edge1 = step(bright_light, 0.75);

View File

@ -2,7 +2,7 @@ use crate::{debug, game_setup};
use bevy::prelude::*;
use bevy_ecs_ldtk::{LdtkWorldBundle, LevelSelection};
use self::darkness::{PointLight2D, SpotLight2D};
use self::darkness::SpotLight2D;
pub mod camera;
pub mod darkness;
@ -36,6 +36,5 @@ fn setup(mut commands: Commands, assets: Res<AssetServer>) {
radius: 100.0,
angle: 1.0,
},
PointLight2D { radius: 20.0 },
));
}

View File

@ -87,7 +87,7 @@ fn camera_setup(mut commands: Commands) {
Color::rgb(0.0, 0.0, 0.0),
),
},
transform: Transform::from_xyz(0.0, 0.0, 999.9),
transform: Transform::from_xyz(128.0, -73.0, 999.9),
..default()
},
GameCamera::default(),

View File

@ -1,6 +1,10 @@
use bevy::{
core::bytes_of,
prelude::*,
render::{mesh::Indices, render_resource::ShaderType},
render::{
mesh::Indices,
render_resource::{Extent3d, ShaderType, TextureDimension},
},
sprite::{Material2dPlugin, Mesh2dHandle},
};
use bevy_ecs_ldtk::{LdtkLevel, LevelEvent};
@ -12,6 +16,7 @@ pub use material::*;
// Needs to be the same as in darkness.wgsl
pub const MAX_POINT_LIGHTS: usize = 64;
pub const MAX_SPOT_LIGHTS: usize = 64;
pub const SHADOWMAP_RESOLUTION: usize = 256;
pub struct DarknessPlugin;
@ -82,6 +87,7 @@ fn add_to_level(
mut level_events: EventReader<LevelEvent>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<DarknessMaterial>>,
mut images: ResMut<Assets<Image>>,
level_query: Query<(Entity, &Handle<LdtkLevel>)>,
levels: Res<Assets<LdtkLevel>>,
) {
@ -131,13 +137,28 @@ fn add_to_level(
plane.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals);
plane.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs);
let width = SHADOWMAP_RESOLUTION;
let height = MAX_SPOT_LIGHTS + MAX_POINT_LIGHTS;
commands.entity(entity).with_children(|builder| {
builder.spawn((
Name::new("Darkness plane"),
DarknessMeshBundle {
transform: Transform::from_xyz(0.0, 0.0, 100.0),
mesh: Mesh2dHandle(meshes.add(plane)),
material: materials.add(DarknessMaterial::default()),
material: materials.add(DarknessMaterial::new(
Color::rgba(0.0, 0.0, 0.0, 0.75),
Some(images.add(Image::new(
Extent3d {
width: width as u32,
height: height as u32,
depth_or_array_layers: 1,
},
TextureDimension::D2,
vec![0; width * height * 4],
bevy::render::render_resource::TextureFormat::R32Float,
))),
)),
..default()
},
));
@ -151,6 +172,7 @@ fn add_to_level(
fn prepare_lights(
mut materials: ResMut<Assets<DarknessMaterial>>,
mut images: ResMut<Assets<Image>>,
material_query: Query<&Handle<DarknessMaterial>>,
point_light_query: Query<(&GlobalTransform, &PointLight2D)>,
spot_light_query: Query<(&GlobalTransform, &SpotLight2D)>,
@ -163,6 +185,14 @@ fn prepare_lights(
None => continue,
};
let shadowmap = match &material.shadowmap_texture {
Some(handle) => match images.get_mut(&handle) {
Some(image) => image,
None => continue,
},
None => continue,
};
material.point_light_count = point_lights.len() as i32;
point_lights
.iter()
@ -171,6 +201,15 @@ fn prepare_lights(
material.point_lights[i] = GpuPointLight2D {
position: transform.translation().truncate(),
radius: light.radius,
};
for x in 0..SHADOWMAP_RESOLUTION {
let offset = (i * SHADOWMAP_RESOLUTION + x) * 4;
let distance = x as f32 / SHADOWMAP_RESOLUTION as f32 * light.radius;
let distance_bytes = bytes_of(&distance);
shadowmap.data[offset + 0] = distance_bytes[0];
shadowmap.data[offset + 1] = distance_bytes[1];
shadowmap.data[offset + 2] = distance_bytes[2];
shadowmap.data[offset + 3] = distance_bytes[3];
}
});
@ -188,6 +227,15 @@ fn prepare_lights(
padding2: 0,
padding3: 0,
};
for x in 0..SHADOWMAP_RESOLUTION {
let offset = ((i + MAX_POINT_LIGHTS) * SHADOWMAP_RESOLUTION + x) * 4;
let distance = x as f32 / SHADOWMAP_RESOLUTION as f32 * light.radius;
let distance_bytes = bytes_of(&distance);
shadowmap.data[offset + 0] = distance_bytes[0];
shadowmap.data[offset + 1] = distance_bytes[1];
shadowmap.data[offset + 2] = distance_bytes[2];
shadowmap.data[offset + 3] = distance_bytes[3];
}
});
}
}

View File

@ -23,6 +23,9 @@ pub struct DarknessMaterial {
pub(crate) spot_light_count: i32,
#[uniform(4)]
pub(crate) spot_lights: [GpuSpotLight2D; MAX_SPOT_LIGHTS],
#[texture(5)]
#[sampler(6)]
pub shadowmap_texture: Option<Handle<Image>>,
}
impl Default for DarknessMaterial {
@ -38,13 +41,18 @@ impl Default for DarknessMaterial {
point_lights: [GpuPointLight2D::default(); MAX_POINT_LIGHTS],
spot_light_count: 0,
spot_lights: [GpuSpotLight2D::default(); MAX_SPOT_LIGHTS],
shadowmap_texture: None,
}
}
}
impl DarknessMaterial {
pub fn new(color: Color) -> Self {
Self { color, ..default() }
pub fn new(color: Color, shadowmap_texture: Option<Handle<Image>>) -> Self {
Self {
color,
shadowmap_texture,
..default()
}
}
}