diff --git a/assets/shaders/darkness.wgsl b/assets/shaders/darkness.wgsl index 376026a..a6a298f 100644 --- a/assets/shaders/darkness.wgsl +++ b/assets/shaders/darkness.wgsl @@ -35,6 +35,12 @@ var spot_light_count: i32; @group(1) @binding(4) var spot_lights: array; +@group(1) @binding(5) +var shadowmap_texture: texture_2d; + +@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( + (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(cos(spot.rotation), sin(spot.rotation)); let angle_diff = acos(dot(spot_dir, normalize(diff))); + let uv = vec2( + (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); diff --git a/src/game.rs b/src/game.rs index 85c4f3e..4c5eaeb 100644 --- a/src/game.rs +++ b/src/game.rs @@ -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) { radius: 100.0, angle: 1.0, }, - PointLight2D { radius: 20.0 }, )); } diff --git a/src/game/camera.rs b/src/game/camera.rs index 6543d3d..76b2289 100644 --- a/src/game/camera.rs +++ b/src/game/camera.rs @@ -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(), diff --git a/src/game/darkness.rs b/src/game/darkness.rs index e0827c1..938ccab 100644 --- a/src/game/darkness.rs +++ b/src/game/darkness.rs @@ -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, mut meshes: ResMut>, mut materials: ResMut>, + mut images: ResMut>, level_query: Query<(Entity, &Handle)>, levels: Res>, ) { @@ -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>, + mut images: ResMut>, material_query: Query<&Handle>, 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]; + } }); } } diff --git a/src/game/darkness/material.rs b/src/game/darkness/material.rs index 1ce5b85..07517ee 100644 --- a/src/game/darkness/material.rs +++ b/src/game/darkness/material.rs @@ -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>, } 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>) -> Self { + Self { + color, + shadowmap_texture, + ..default() + } } }