Tiled Forward Rendering #5
|
@ -127,7 +127,7 @@ async fn main() {
|
||||||
light_tran.rotate_y(math::Angle::Degrees(25.0));
|
light_tran.rotate_y(math::Angle::Degrees(25.0));
|
||||||
world.spawn((
|
world.spawn((
|
||||||
DirectionalLight {
|
DirectionalLight {
|
||||||
enabled: true,
|
enabled: false,
|
||||||
color: Vec3::ONE,
|
color: Vec3::ONE,
|
||||||
intensity: 0.35
|
intensity: 0.35
|
||||||
//..Default::default()
|
//..Default::default()
|
||||||
|
@ -174,6 +174,25 @@ async fn main() {
|
||||||
),
|
),
|
||||||
cube_mesh.clone(),
|
cube_mesh.clone(),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
/* world.spawn((
|
||||||
|
SpotLight {
|
||||||
|
enabled: true,
|
||||||
|
color: Vec3::new(1.0, 0.0, 0.0),
|
||||||
|
intensity: 1.0,
|
||||||
|
range: 1.5,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
Transform::new(
|
||||||
|
Vec3::new(0.0, 0.2, -1.5),
|
||||||
|
//Vec3::new(-5.0, 1.0, -0.28),
|
||||||
|
//Vec3::new(-10.0, 0.94, -0.28),
|
||||||
|
|
||||||
|
Quat::IDENTITY,
|
||||||
|
Vec3::new(0.15, 0.15, 0.15),
|
||||||
|
),
|
||||||
|
cube_mesh.clone(),
|
||||||
|
)); */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* {
|
/* {
|
||||||
|
|
|
@ -6,7 +6,7 @@ use lyra_ecs::{Entity, Tick, World, query::{Entities, TickOf}};
|
||||||
pub use point::*;
|
pub use point::*;
|
||||||
pub use spotlight::*;
|
pub use spotlight::*;
|
||||||
|
|
||||||
use std::{collections::{HashMap, VecDeque}, marker::PhantomData};
|
use std::{collections::{HashMap, VecDeque}, marker::PhantomData, mem};
|
||||||
|
|
||||||
use crate::math::Transform;
|
use crate::math::Transform;
|
||||||
|
|
||||||
|
@ -103,13 +103,14 @@ pub(crate) struct LightUniformBuffers {
|
||||||
pub buffer: wgpu::Buffer,
|
pub buffer: wgpu::Buffer,
|
||||||
pub bind_group_pair: BindGroupPair,
|
pub bind_group_pair: BindGroupPair,
|
||||||
pub light_indexes: HashMap<Entity, u32>,
|
pub light_indexes: HashMap<Entity, u32>,
|
||||||
|
dead_indices: VecDeque<u32>,
|
||||||
pub current_light_idx: u32,
|
pub current_light_idx: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LightUniformBuffers {
|
impl LightUniformBuffers {
|
||||||
pub fn new(device: &wgpu::Device) -> Self {
|
pub fn new(device: &wgpu::Device) -> Self {
|
||||||
let limits = device.limits();
|
let limits = device.limits();
|
||||||
// TODO: check this limit somehow
|
// TODO: ensure we dont write over this limit
|
||||||
let max_buffer_sizes = (limits.max_uniform_buffer_binding_size as u64) / 2;
|
let max_buffer_sizes = (limits.max_uniform_buffer_binding_size as u64) / 2;
|
||||||
|
|
||||||
let buffer = device.create_buffer(
|
let buffer = device.create_buffer(
|
||||||
|
@ -161,45 +162,88 @@ impl LightUniformBuffers {
|
||||||
bind_group_pair: BindGroupPair::new(bindgroup, bindgroup_layout),
|
bind_group_pair: BindGroupPair::new(bindgroup, bindgroup_layout),
|
||||||
light_indexes: Default::default(),
|
light_indexes: Default::default(),
|
||||||
current_light_idx: 0,
|
current_light_idx: 0,
|
||||||
|
dead_indices: VecDeque::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the index for the entity, and if this index is new
|
||||||
|
fn get_index_for(&mut self, missed: &mut HashMap<Entity, u32>, entity: Entity) -> (bool, u32) {
|
||||||
|
let idx = missed.remove(&entity)
|
||||||
|
.map(|v| (false, v))
|
||||||
|
.or_else(||
|
||||||
|
self.dead_indices.pop_front()
|
||||||
|
.map(|v| (true, v))
|
||||||
|
)
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
let t = self.current_light_idx;
|
||||||
|
self.current_light_idx += 1;
|
||||||
|
(true, t)
|
||||||
|
});
|
||||||
|
idx
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update_lights(&mut self, queue: &wgpu::Queue, world_tick: Tick, world: &World) {
|
pub fn update_lights(&mut self, queue: &wgpu::Queue, world_tick: Tick, world: &World) {
|
||||||
let mut lights = LightsUniform::default();
|
// used to detect what lights were removed
|
||||||
|
let mut missed_lights: HashMap<Entity, u32> = self.light_indexes.drain().collect();
|
||||||
|
|
||||||
for (entity, point_light, transform, light_epoch, transform_epoch)
|
for (entity, point_light, transform, light_epoch, transform_epoch)
|
||||||
in world.view_iter::<(Entities, &PointLight, &Transform, TickOf<PointLight>, TickOf<Transform>)>() {
|
in world.view_iter::<(Entities, &PointLight, &Transform, TickOf<PointLight>, TickOf<Transform>)>() {
|
||||||
|
|
||||||
// TODO: dont update light every frame
|
let (new, idx) = self.get_index_for(&mut missed_lights, entity);
|
||||||
let idx = *self.light_indexes.entry(entity)
|
self.light_indexes.insert(entity, idx);
|
||||||
.or_insert_with(|| {
|
|
||||||
let t = self.current_light_idx;
|
|
||||||
self.current_light_idx += 1;
|
|
||||||
t
|
|
||||||
}) as usize;
|
|
||||||
|
|
||||||
let uniform = LightUniform::from_point_light_bundle(&point_light, &transform);
|
if new || light_epoch == world_tick || transform_epoch == world_tick {
|
||||||
lights.data[idx] = uniform;
|
let uniform = LightUniform::from_point_light_bundle(&point_light, &transform);
|
||||||
|
|
||||||
|
let offset = mem::size_of::<u32>() * 4 + mem::size_of::<LightUniform>() * idx as usize;
|
||||||
|
queue.write_buffer(&self.buffer, offset as _, bytemuck::cast_slice(&[uniform]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((entity, dir_light, transform)) =
|
for (entity, spot_light, transform, light_epoch, transform_epoch)
|
||||||
world.view_iter::<(Entities, &DirectionalLight, &Transform)>().next() {
|
in world.view_iter::<(Entities, &SpotLight, &Transform, TickOf<SpotLight>, TickOf<Transform>)>() {
|
||||||
|
|
||||||
|
let (new, idx) = self.get_index_for(&mut missed_lights, entity);
|
||||||
|
self.light_indexes.insert(entity, idx);
|
||||||
|
|
||||||
|
if new || light_epoch == world_tick || transform_epoch == world_tick {
|
||||||
|
let uniform = LightUniform::from_spot_light_bundle(&spot_light, &transform);
|
||||||
|
|
||||||
|
let offset = mem::size_of::<u32>() * 4 + mem::size_of::<LightUniform>() * idx as usize;
|
||||||
|
queue.write_buffer(&self.buffer, offset as _, bytemuck::cast_slice(&[uniform]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (entity, dir_light, transform, light_epoch, transform_epoch)
|
||||||
|
in world.view_iter::<(Entities, &DirectionalLight, &Transform, TickOf<DirectionalLight>, TickOf<Transform>)>() {
|
||||||
|
|
||||||
let idx = *self.light_indexes.entry(entity)
|
let (new, idx) = self.get_index_for(&mut missed_lights, entity);
|
||||||
.or_insert_with(|| {
|
self.light_indexes.insert(entity, idx);
|
||||||
let t = self.current_light_idx;
|
|
||||||
self.current_light_idx += 1;
|
if new || light_epoch == world_tick || transform_epoch == world_tick {
|
||||||
t
|
let uniform = LightUniform::from_directional_bundle(&dir_light, &transform);
|
||||||
}) as usize;
|
let offset = mem::size_of::<u32>() * 4 + mem::size_of::<LightUniform>() * idx as usize;
|
||||||
|
queue.write_buffer(&self.buffer, offset as _, bytemuck::cast_slice(&[uniform]));
|
||||||
let uniform = LightUniform::from_directional_bundle(&dir_light, &transform);
|
}
|
||||||
lights.data[idx] = uniform;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lights.light_count = self.light_indexes.len() as u32;
|
// anything left in missed_lights were lights that were deleted
|
||||||
|
let len = missed_lights.len();
|
||||||
|
self.dead_indices.reserve(len);
|
||||||
|
|
||||||
// update the light count in the struct
|
for (_, v) in missed_lights.drain() {
|
||||||
queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(&[lights]));
|
// write zeros in place of this now dead light, the enabled boolean will be set to false
|
||||||
|
let mut zeros = Vec::new();
|
||||||
|
zeros.resize(mem::size_of::<LightUniform>(), 0u32);
|
||||||
|
|
||||||
|
let offset = mem::size_of::<u32>() * 4 + mem::size_of::<LightUniform>() * v as usize;
|
||||||
|
queue.write_buffer(&self.buffer, offset as _, bytemuck::cast_slice(zeros.as_slice()));
|
||||||
|
|
||||||
|
self.dead_indices.push_back(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the amount of lights, then the array of lights
|
||||||
|
queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(&[self.current_light_idx as u32]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,8 +271,8 @@ pub(crate) struct LightUniform {
|
||||||
pub intensity: f32,
|
pub intensity: f32,
|
||||||
pub smoothness: f32,
|
pub smoothness: f32,
|
||||||
|
|
||||||
pub spot_cutoff: f32,
|
pub spot_cutoff_rad: f32,
|
||||||
pub spot_outer_cutoff: f32,
|
pub spot_outer_cutoff_rad: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LightUniform {
|
impl LightUniform {
|
||||||
|
@ -244,8 +288,8 @@ impl LightUniform {
|
||||||
intensity: light.intensity,
|
intensity: light.intensity,
|
||||||
smoothness: light.smoothness,
|
smoothness: light.smoothness,
|
||||||
|
|
||||||
spot_cutoff: 0.0,
|
spot_cutoff_rad: 0.0,
|
||||||
spot_outer_cutoff: 0.0,
|
spot_outer_cutoff_rad: 0.0,
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -262,190 +306,27 @@ impl LightUniform {
|
||||||
intensity: light.intensity,
|
intensity: light.intensity,
|
||||||
smoothness: 0.0,
|
smoothness: 0.0,
|
||||||
|
|
||||||
spot_cutoff: 0.0,
|
spot_cutoff_rad: 0.0,
|
||||||
spot_outer_cutoff: 0.0,
|
spot_outer_cutoff_rad: 0.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the SpotLightUniform from an ECS bundle
|
// Create the SpotLightUniform from an ECS bundle
|
||||||
/* pub fn from_bundle(light: &SpotLight, transform: &Transform) -> Self {
|
pub fn from_spot_light_bundle(light: &SpotLight, transform: &Transform) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
light_type: LightType::Spotlight as u32,
|
||||||
|
enabled: light.enabled as u32,
|
||||||
position: transform.translation,
|
position: transform.translation,
|
||||||
_padding: 0,
|
|
||||||
direction: transform.forward(),
|
direction: transform.forward(),
|
||||||
_padding2: 0,
|
|
||||||
color: light.color,
|
color: light.color,
|
||||||
cutoff: light.cutoff.to_radians().cos(),
|
|
||||||
outer_cutoff: light.outer_cutoff.to_radians().cos(),
|
|
||||||
constant: light.constant,
|
|
||||||
linear: light.linear,
|
|
||||||
quadratic: light.quadratic,
|
|
||||||
ambient: light.ambient,
|
|
||||||
diffuse: light.diffuse,
|
|
||||||
specular: light.specular,
|
|
||||||
_padding3: 0,
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
range: light.range,
|
||||||
#[derive(Default, Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
|
||||||
pub struct LightsUniform {
|
|
||||||
light_count: u32,
|
|
||||||
_padding: [u32; 3],
|
|
||||||
data: [LightUniform; 10], // TODO: make this a dynamic length
|
|
||||||
}
|
|
||||||
|
|
||||||
/* #[repr(C)]
|
|
||||||
#[derive(Default, Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
|
||||||
pub struct LightsUniform {
|
|
||||||
light_count: u32,
|
|
||||||
_padding: [u32; 3],
|
|
||||||
spot_lights: [SpotLightUniform; MAX_LIGHT_COUNT],
|
|
||||||
spot_light_count: u32,
|
|
||||||
_padding2: [u32; 3],
|
|
||||||
directional_light: DirectionalLightUniform,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Default, Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
|
||||||
pub struct PointLightUniform {
|
|
||||||
/// The position of the light
|
|
||||||
/// vec4 is used here for gpu padding, w is ignored in the shader
|
|
||||||
pub position: glam::Vec4,
|
|
||||||
|
|
||||||
/// The color of the light
|
|
||||||
/// vec4 is used here for gpu padding, w is ignored in the shader
|
|
||||||
pub color: glam::Vec4,
|
|
||||||
|
|
||||||
/// The intensity of the light
|
|
||||||
/// This works by just multiplying the result of the lighting
|
|
||||||
/// calculations by this scalar
|
|
||||||
pub intensity: f32,
|
|
||||||
|
|
||||||
/// The constant used in the quadratic attenuation calculation. Its best to leave this at 1.0
|
|
||||||
pub constant: f32,
|
|
||||||
|
|
||||||
/// The linear factor used in the quadratic attenuation calculation.
|
|
||||||
pub linear: f32,
|
|
||||||
|
|
||||||
/// The quadratic factor used in the quadratic attenuation calculation.
|
|
||||||
pub quadratic: f32,
|
|
||||||
|
|
||||||
pub ambient: f32,
|
|
||||||
pub diffuse: f32,
|
|
||||||
pub specular: f32,
|
|
||||||
|
|
||||||
pub _padding: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PointLightUniform {
|
|
||||||
/// Create the PointLightUniform from an ECS bundle
|
|
||||||
pub fn from_bundle(light: &PointLight, transform: &Transform) -> Self {
|
|
||||||
Self {
|
|
||||||
position: glam::Vec4::new(transform.translation.x, transform.translation.y, transform.translation.z, 0.0),
|
|
||||||
//_padding: 0,
|
|
||||||
color: glam::Vec4::new(light.color.x, light.color.y, light.color.z, 0.0),
|
|
||||||
//_padding2: 0,
|
|
||||||
intensity: light.intensity,
|
intensity: light.intensity,
|
||||||
constant: light.constant,
|
smoothness: light.smoothness,
|
||||||
linear: light.linear,
|
|
||||||
quadratic: light.quadratic,
|
|
||||||
|
|
||||||
ambient: light.ambient,
|
spot_cutoff_rad: light.cutoff.to_radians(),
|
||||||
diffuse: light.diffuse,
|
spot_outer_cutoff_rad: light.outer_cutoff.to_radians(),
|
||||||
specular: light.specular,
|
|
||||||
|
|
||||||
_padding: 0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Default, Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
|
||||||
pub struct DirectionalLightUniform {
|
|
||||||
/// The direction of the light
|
|
||||||
pub direction: glam::Vec3,
|
|
||||||
// gpu padding
|
|
||||||
pub _padding: u32,
|
|
||||||
/// The color of the light
|
|
||||||
pub color: glam::Vec3,
|
|
||||||
// no padding is needed here since ambient acts as the padding
|
|
||||||
// that would usually be needed for the vec3
|
|
||||||
|
|
||||||
/// The scalar of the ambient light created by this caster.
|
|
||||||
pub ambient: f32,
|
|
||||||
/// The scalar of the diffuse light created by this caster.
|
|
||||||
pub diffuse: f32,
|
|
||||||
/// The scalar of the specular reflections created by this caster.
|
|
||||||
pub specular: f32,
|
|
||||||
|
|
||||||
pub _padding2: [u32; 2],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DirectionalLightUniform {
|
|
||||||
/// Create the DirectionalLightUniform from an ECS bundle
|
|
||||||
pub fn from_bundle(light: &DirectionalLight, transform: &Transform) -> Self {
|
|
||||||
//transform.forward()
|
|
||||||
Self {
|
|
||||||
direction: transform.forward(),
|
|
||||||
_padding: 0,
|
|
||||||
color: light.color,
|
|
||||||
ambient: light.ambient,
|
|
||||||
diffuse: light.diffuse,
|
|
||||||
specular: light.specular,
|
|
||||||
_padding2: [0; 2],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Default, Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
|
||||||
pub(crate) struct SpotLightUniform {
|
|
||||||
pub position: glam::Vec3,
|
|
||||||
pub _padding: u32,
|
|
||||||
pub direction: glam::Vec3,
|
|
||||||
pub _padding2: u32,
|
|
||||||
pub color: glam::Vec3,
|
|
||||||
// no padding is needed here since cutoff acts as the padding
|
|
||||||
// that would usually be needed for the vec3
|
|
||||||
|
|
||||||
pub cutoff: f32,
|
|
||||||
pub outer_cutoff: f32,
|
|
||||||
|
|
||||||
/// The constant used in the quadratic attenuation calculation. Its best to leave this at 1.0
|
|
||||||
pub constant: f32,
|
|
||||||
|
|
||||||
/// The linear factor used in the quadratic attenuation calculation.
|
|
||||||
pub linear: f32,
|
|
||||||
|
|
||||||
/// The quadratic factor used in the quadratic attenuation calculation.
|
|
||||||
pub quadratic: f32,
|
|
||||||
|
|
||||||
pub ambient: f32,
|
|
||||||
pub diffuse: f32,
|
|
||||||
pub specular: f32,
|
|
||||||
pub _padding3: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpotLightUniform {
|
|
||||||
/// Create the SpotLightUniform from an ECS bundle
|
|
||||||
pub fn from_bundle(light: &SpotLight, transform: &Transform) -> Self {
|
|
||||||
Self {
|
|
||||||
position: transform.translation,
|
|
||||||
_padding: 0,
|
|
||||||
direction: transform.forward(),
|
|
||||||
_padding2: 0,
|
|
||||||
color: light.color,
|
|
||||||
cutoff: light.cutoff.to_radians().cos(),
|
|
||||||
outer_cutoff: light.outer_cutoff.to_radians().cos(),
|
|
||||||
constant: light.constant,
|
|
||||||
linear: light.linear,
|
|
||||||
quadratic: light.quadratic,
|
|
||||||
ambient: light.ambient,
|
|
||||||
diffuse: light.diffuse,
|
|
||||||
specular: light.specular,
|
|
||||||
_padding3: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} */
|
|
|
@ -4,15 +4,26 @@ use crate::math;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Component)]
|
#[derive(Debug, Clone, Component)]
|
||||||
pub struct SpotLight {
|
pub struct SpotLight {
|
||||||
|
pub enabled: bool,
|
||||||
pub color: glam::Vec3,
|
pub color: glam::Vec3,
|
||||||
|
pub range: f32,
|
||||||
|
pub intensity: f32,
|
||||||
|
pub smoothness: f32,
|
||||||
pub cutoff: math::Angle,
|
pub cutoff: math::Angle,
|
||||||
pub outer_cutoff: math::Angle,
|
pub outer_cutoff: math::Angle,
|
||||||
|
}
|
||||||
|
|
||||||
pub constant: f32,
|
impl Default for SpotLight {
|
||||||
pub linear: f32,
|
fn default() -> Self {
|
||||||
pub quadratic: f32,
|
Self {
|
||||||
|
enabled: true,
|
||||||
|
color: glam::Vec3::new(1.0, 1.0, 1.0),
|
||||||
|
range: 1.0,
|
||||||
|
intensity: 1.0,
|
||||||
|
smoothness: 0.75,
|
||||||
|
|
||||||
pub ambient: f32,
|
cutoff: math::Angle::Degrees(45.0),
|
||||||
pub diffuse: f32,
|
outer_cutoff: math::Angle::Degrees(45.0),
|
||||||
pub specular: f32,
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -83,7 +83,6 @@ impl LightCullCompute {
|
||||||
label: Some("BGL_LightIndicesGrid"),
|
label: Some("BGL_LightIndicesGrid"),
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: resize texture when screen is resized
|
|
||||||
let size = wgpu::Extent3d {
|
let size = wgpu::Extent3d {
|
||||||
width: workgroup_size.x,
|
width: workgroup_size.x,
|
||||||
height: workgroup_size.y,
|
height: workgroup_size.y,
|
||||||
|
@ -168,7 +167,8 @@ impl LightCullCompute {
|
||||||
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(shader_src)),
|
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(shader_src)),
|
||||||
});
|
});
|
||||||
|
|
||||||
let workgroup_size = glam::UVec2::new((screen_size.width as f32 / 16.0).ceil() as u32, (screen_size.height as f32 / 16.0).ceil() as u32);
|
let workgroup_size = glam::UVec2::new((screen_size.width as f32 / 16.0).ceil() as u32,
|
||||||
|
(screen_size.height as f32 / 16.0).ceil() as u32);
|
||||||
let light_grid = Self::create_grid(&device, workgroup_size);
|
let light_grid = Self::create_grid(&device, workgroup_size);
|
||||||
|
|
||||||
let depth_tex_pair = depth_texture.create_bind_group(&device);
|
let depth_tex_pair = depth_texture.create_bind_group(&device);
|
||||||
|
@ -205,7 +205,11 @@ impl LightCullCompute {
|
||||||
pub fn update_screen_size(&mut self, size: PhysicalSize<u32>) {
|
pub fn update_screen_size(&mut self, size: PhysicalSize<u32>) {
|
||||||
self.screen_size_buffer.write_buffer(&self.queue, 0,
|
self.screen_size_buffer.write_buffer(&self.queue, 0,
|
||||||
&[UVec2::new(size.width, size.height)]);
|
&[UVec2::new(size.width, size.height)]);
|
||||||
self.workgroup_size = glam::UVec2::new((size.width as f32 / 16.0).ceil() as u32, (size.height as f32 / 16.0).ceil() as u32);
|
self.workgroup_size = glam::UVec2::new((size.width as f32 / 16.0).ceil() as u32,
|
||||||
|
(size.height as f32 / 16.0).ceil() as u32);
|
||||||
|
|
||||||
|
// I hate that the entire bind group is recreated on a resize but its the only way :(
|
||||||
|
self.light_indices_grid = Self::create_grid(&self.device, self.workgroup_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compute(&mut self, camera_buffers: &BufferWrapper, lights_buffers: &LightUniformBuffers, depth_texture: &RenderTexture) {
|
pub fn compute(&mut self, camera_buffers: &BufferWrapper, lights_buffers: &LightUniformBuffers, depth_texture: &RenderTexture) {
|
||||||
|
@ -228,6 +232,7 @@ impl LightCullCompute {
|
||||||
|
|
||||||
pass.dispatch_workgroups(self.workgroup_size.x, self.workgroup_size.y, 1);
|
pass.dispatch_workgroups(self.workgroup_size.x, self.workgroup_size.y, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.queue.submit(std::iter::once(encoder.finish()));
|
self.queue.submit(std::iter::once(encoder.finish()));
|
||||||
self.device.poll(wgpu::Maintain::Wait);
|
self.device.poll(wgpu::Maintain::Wait);
|
||||||
self.cleanup();
|
self.cleanup();
|
||||||
|
|
|
@ -238,5 +238,50 @@ fn blinn_phong_point_light(world_pos: vec3<f32>, world_norm: vec3<f32>, point_li
|
||||||
}
|
}
|
||||||
|
|
||||||
fn blinn_phong_spot_light(world_pos: vec3<f32>, world_norm: vec3<f32>, spot_light: Light, material: Material, specular_factor: vec3<f32>) -> vec3<f32> {
|
fn blinn_phong_spot_light(world_pos: vec3<f32>, world_norm: vec3<f32>, spot_light: Light, material: Material, specular_factor: vec3<f32>) -> vec3<f32> {
|
||||||
return vec3<f32>(0.0); // TODO
|
let light_color = spot_light.color;
|
||||||
|
let light_pos = spot_light.position;
|
||||||
|
let camera_view_pos = u_camera.position;
|
||||||
|
|
||||||
|
let light_dir = normalize(spot_light.position - world_pos);
|
||||||
|
|
||||||
|
var ambient_color = light_color * material.ambient.xyz * material.diffuse.xyz;
|
||||||
|
|
||||||
|
//// diffuse ////
|
||||||
|
//let light_dir = normalize(light_pos - world_pos);
|
||||||
|
|
||||||
|
let diffuse_strength = max(dot(world_norm, light_dir), 0.0);
|
||||||
|
var diffuse_color = light_color * (diffuse_strength * material.diffuse.xyz);
|
||||||
|
//// end of diffuse ////
|
||||||
|
|
||||||
|
//// specular ////
|
||||||
|
let view_dir = normalize(camera_view_pos - world_pos);
|
||||||
|
let half_dir = normalize(view_dir + light_dir);
|
||||||
|
|
||||||
|
let specular_strength = pow(max(dot(world_norm, half_dir), 0.0), material.shininess);
|
||||||
|
var specular_color = specular_strength * (light_color * specular_factor);
|
||||||
|
//// end of specular ////
|
||||||
|
|
||||||
|
//// spot light soft edges ////
|
||||||
|
let theta = dot(light_dir, normalize(-spot_light.direction));
|
||||||
|
let epsilon = spot_light.spot_cutoff - spot_light.spot_outer_cutoff;
|
||||||
|
let intensity = clamp((theta - spot_light.spot_outer_cutoff) / epsilon, 0.0, 1.0);
|
||||||
|
//diffuse_color *= intensity;
|
||||||
|
//specular_color *= intensity;
|
||||||
|
//// end of spot light soft edges ////
|
||||||
|
|
||||||
|
//// spot light attenuation ////
|
||||||
|
let distance = length(light_pos - world_pos);
|
||||||
|
let attenuation = calc_attenuation(spot_light, distance);
|
||||||
|
|
||||||
|
ambient_color *= attenuation * intensity;
|
||||||
|
diffuse_color *= attenuation * intensity;
|
||||||
|
specular_color *= attenuation * intensity;
|
||||||
|
//// end of spot light attenuation ////
|
||||||
|
|
||||||
|
|
||||||
|
return /*ambient_color +*/ diffuse_color + specular_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_attenuation(light: Light, distance: f32) -> f32 {
|
||||||
|
return 1.0 - smoothstep(light.range * light.smoothness, light.range, distance);
|
||||||
}
|
}
|
|
@ -33,6 +33,13 @@ struct Lights {
|
||||||
data: array<Light>,
|
data: array<Light>,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Cone {
|
||||||
|
tip: vec3<f32>,
|
||||||
|
height: f32,
|
||||||
|
direction: vec3<f32>,
|
||||||
|
radius: f32,
|
||||||
|
}
|
||||||
|
|
||||||
var<workgroup> wg_min_depth: atomic<u32>;
|
var<workgroup> wg_min_depth: atomic<u32>;
|
||||||
var<workgroup> wg_max_depth: atomic<u32>;
|
var<workgroup> wg_max_depth: atomic<u32>;
|
||||||
var<workgroup> wg_light_index_start: atomic<u32>;
|
var<workgroup> wg_light_index_start: atomic<u32>;
|
||||||
|
@ -160,13 +167,22 @@ fn cs_main(
|
||||||
&& sphere_inside_frustrum(wg_frustum_planes, position, radius)) {
|
&& sphere_inside_frustrum(wg_frustum_planes, position, radius)) {
|
||||||
// TODO: add the light to the transparent geometry list
|
// TODO: add the light to the transparent geometry list
|
||||||
|
|
||||||
add_light(light_index);
|
|
||||||
|
|
||||||
if (!sphere_inside_plane(position, radius, wg_frustum_planes[4])) {
|
if (!sphere_inside_plane(position, radius, wg_frustum_planes[4])) {
|
||||||
|
add_light(light_index);
|
||||||
|
}
|
||||||
|
} else if (light.light_ty == LIGHT_TY_SPOT) {
|
||||||
|
let dir_vs = u_camera.view * vec4<f32>(light.direction, 1.0);
|
||||||
|
let cone_radius = tan(light.spot_cutoff) * light.range;
|
||||||
|
let cone = Cone(position, radius, dir_vs.xyz, cone_radius);
|
||||||
|
|
||||||
|
if (cone_inside_frustum(cone, wg_frustum_planes)) {
|
||||||
|
// TODO: add the light to the transparent geometry list
|
||||||
|
|
||||||
|
if (!cone_inside_plane(cone, wg_frustum_planes[4])) {
|
||||||
|
add_light(light_index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: spotlights
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,4 +274,45 @@ fn compute_plane(p0: vec3<f32>, p1: vec3<f32>, p2: vec3<f32>) -> vec4<f32> {
|
||||||
plane.w = dot(plane.xyz, p0);
|
plane.w = dot(plane.xyz, p0);
|
||||||
|
|
||||||
return plane;
|
return plane;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn point_inside_plane(point: vec3<f32>, plane: vec4<f32>) -> bool {
|
||||||
|
return dot(plane.xyz, point) - plane.w < 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check to see if a cone if fully behind (inside the negative halfspace of) a plane.
|
||||||
|
///
|
||||||
|
/// Source: Real-time collision detection, Christer Ericson (2005)
|
||||||
|
/// (https://www.3dgep.com/forward-plus/#light-culling-compute-shader)
|
||||||
|
fn cone_inside_plane(cone: Cone, plane: vec4<f32>) -> bool {
|
||||||
|
// Compute the farthest point on the end of the cone to the positive space of the plane.
|
||||||
|
let m = cross(cross(plane.xyz, cone.direction), cone.direction);
|
||||||
|
let farthest = cone.tip + cone.direction * cone.height - m * cone.radius;
|
||||||
|
|
||||||
|
// The cone is in the negative halfspace of the plane if the tip of the cone,
|
||||||
|
// and the farthest point on the end of the cone are inside the negative halfspace
|
||||||
|
// of the plane.
|
||||||
|
return point_inside_plane(cone.tip, plane) && point_inside_plane(farthest, plane);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cone_inside_frustum(cone: Cone, frustum: array<vec4<f32>, 6>) -> bool {
|
||||||
|
//let near_plane = frustum[4];
|
||||||
|
//let far_plane = frustum[5];
|
||||||
|
|
||||||
|
// check near and far clipping planes first
|
||||||
|
//if (cone_inside_plane(cone, near_plane) || cone_inside_plane(cone, far_plane)) {
|
||||||
|
// return false;
|
||||||
|
//}
|
||||||
|
|
||||||
|
// to be able to index this array with a non-const value,
|
||||||
|
// it must be defined as a var
|
||||||
|
var frustum_v = frustum;
|
||||||
|
|
||||||
|
for (var i = 0u; i < 4u; i++) {
|
||||||
|
if (cone_inside_plane(cone, frustum_v[i])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
|
@ -15,13 +15,19 @@ in
|
||||||
mold
|
mold
|
||||||
udev
|
udev
|
||||||
lua5_4_compat
|
lua5_4_compat
|
||||||
(nixpkgs.rustChannelOf { rustToolchain = ./rust-toolchain.toml; }).rust
|
((nixpkgs.rustChannelOf { rustToolchain = ./rust-toolchain.toml; }).rust.override {
|
||||||
|
extensions = [
|
||||||
|
"rust-src"
|
||||||
|
"rust-analysis"
|
||||||
|
];
|
||||||
|
})
|
||||||
];
|
];
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
udev alsa-lib libGL gcc
|
udev alsa-lib libGL gcc
|
||||||
vulkan-loader vulkan-headers vulkan-tools
|
vulkan-loader vulkan-headers vulkan-tools
|
||||||
xorg.libX11 xorg.libXcursor xorg.libXi xorg.libXrandr # To use the x11 feature
|
xorg.libX11 xorg.libXcursor xorg.libXi xorg.libXrandr # To use the x11 feature
|
||||||
libxkbcommon wayland # To use the wayland feature
|
libxkbcommon wayland # To use the wayland feature
|
||||||
|
|
||||||
];
|
];
|
||||||
LD_LIBRARY_PATH = lib.makeLibraryPath buildInputs;
|
LD_LIBRARY_PATH = lib.makeLibraryPath buildInputs;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue