Start support for multiple point light casters
ci/woodpecker/push/build Pipeline was successful Details

This commit is contained in:
SeanOMik 2023-11-10 14:00:52 -05:00
parent d26e1ccfb4
commit 6510d5a7b9
Signed by: SeanOMik
GPG Key ID: 568F326C7EB33ACB
3 changed files with 222 additions and 76 deletions

View File

@ -1,17 +1,193 @@
pub mod point; pub mod point;
use std::{collections::{VecDeque, HashMap}, num::NonZeroU64};
use edict::query::EpochOf;
pub use point::*; pub use point::*;
use tracing::debug;
use wgpu::util::DeviceExt;
use crate::math::Transform; use std::mem;
#[repr(C)] use crate::{math::Transform, ecs::components::TransformComponent};
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct LightUniform { pub struct LightBuffer {
pub position: glam::Vec3, pub bind_group: wgpu::BindGroup,
// Due to uniforms requiring 16 byte (4 float) spacing, we need to use a padding field here /// The buffer on the gpu storing the lights
pub(crate) _padding: u32, buffer: wgpu::Buffer,
pub color: glam::Vec3, device_limits: wgpu::Limits,
// Due to uniforms requiring 16 byte (4 float) spacing, we need to use a padding field here /// The max amount of light casters that could fit in this buffer.
pub(crate) _padding2: u32, pub max_count: usize,
/// The current amount of light casters in this buffer.
pub current_count: usize,
/// The buffer index for a specific entity/caster.
used_indexes: HashMap<edict::EntityId, usize>,
/// Indexes that were being used but are no longer needed.
dead_indexes: VecDeque<usize>,
}
impl LightBuffer {
pub fn new<U>(device: &wgpu::Device, layout: &wgpu::BindGroupLayout, max_count: usize) -> Self
where
U: Default + bytemuck::Pod + bytemuck::Zeroable
{
let device_limits = device.limits();
let buffer = device.create_buffer(
&wgpu::BufferDescriptor {
label: Some("Light buffer"),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
size: (mem::size_of::<U>() * max_count) as u64,//render_limits.max_uniform_buffer_binding_size as u64,
mapped_at_creation: false,
/* label: Some("Light Buffer"),
contents: bytemuck::cast_slice(&[U::default()]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, */
}
);
/* let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: buffer.as_entire_binding(),
}
],
label: Some("light_bind_group"),
}); */
let stride = device_limits.min_uniform_buffer_offset_alignment as usize + mem::size_of::<U>();
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(
wgpu::BufferBinding {
buffer: &buffer,
offset: 0,
size: Some(NonZeroU64::new(stride as u64).unwrap())
}
)
}
],
label: Some("light_bind_group"),
});
Self {
buffer,
bind_group,
device_limits,
max_count,
current_count: 0,
used_indexes: HashMap::new(),
dead_indexes: VecDeque::new(),
}
}
pub fn has_light(&self, entity: edict::EntityId) -> bool {
self.used_indexes.contains_key(&entity)
}
/// Update an existing light in the light buffer.
pub fn update_light(&mut self, queue: &wgpu::Queue, entity: edict::EntityId, light: &PointLightUniform) {
let buffer_idx = *self.used_indexes.get(&entity)
.expect("Entity for Light is not in buffer!");
let offset = buffer_idx as u64 * self.device_limits.min_uniform_buffer_offset_alignment as u64;
queue.write_buffer(&self.buffer, offset, bytemuck::bytes_of(light));
}
/// Add a new light to the light buffer.
pub fn add_light(&mut self, queue: &wgpu::Queue, entity: edict::EntityId, light: &PointLightUniform) {
let buffer_idx = match self.dead_indexes.pop_front() {
Some(i) => i,
None => {
let i = self.current_count;
self.current_count += 1;
i
},
};
self.used_indexes.insert(entity, buffer_idx);
self.update_light(queue, entity, light);
}
/// Update, or add a new caster, to the light buffer.
pub fn update_or_add(&mut self, queue: &wgpu::Queue, entity: edict::EntityId, light: &PointLightUniform) {
if self.used_indexes.contains_key(&entity) {
self.update_light(queue, entity, light);
} else {
self.add_light(queue, entity, light);
}
}
pub fn remove_light(&mut self, queue: &wgpu::Queue, entity: edict::EntityId) {
todo!() // TODO
}
pub fn prune_dead_lights(&mut self, queue: &wgpu::Queue, entity: edict::EntityId) {
todo!() // TODO
}
}
pub struct LightUniformBuffers {
pub bindgroup_layout: wgpu::BindGroupLayout,
pub point_lights: LightBuffer,
//spotlights: LightBuffer,
//directional_light
}
impl LightUniformBuffers {
pub fn new(device: &wgpu::Device) -> Self {
let bindgroup_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}
],
label: Some("light_bind_group_layout"),
});
let point_lights = LightBuffer::new::<PointLightUniform>(device, &bindgroup_layout, 10);
Self {
bindgroup_layout,
point_lights,
}
}
pub fn update_lights(&mut self, queue: &wgpu::Queue, world: &edict::World) {
let now_epoch = world.epoch();
for (entity, point_light, transform, light_epoch, transform_epoch)
in world.query::<(edict::Entities, &PointLight, &TransformComponent, EpochOf<PointLight>, EpochOf<TransformComponent>)>().iter() {
/* if !self.point_lights.has_light(entity) || light_epoch == now_epoch || transform_epoch == now_epoch {
debug!("Updated light after update!");
let uniform = PointLightUniform::from_bundle(point_light, &transform.transform);
self.point_lights.update_or_add(queue, entity, &uniform);
} */
let uniform = PointLightUniform::from_bundle(point_light, &transform.transform);
self.point_lights.update_or_add(queue, entity, &uniform);
}
}
// Binds the light buffer to the render pass.
//
// Parameters:
// * `render_pass` - The render pass to bind the buffers to.
// * `point_bind_index` - The bind group index that the point light buffers will be bound to.
/* pub fn bind_lights<'a, 'b: 'a>(&'a mut self, render_pass: &'b mut wgpu::RenderPass<'b>, point_bind_index: u32) {
render_pass.set_bind_group(point_bind_index, &self.point_lights.bind_group, &[]);
//render_pass.set_bind_group(3, &self.point_light_bind_group, &[]);
} */
} }
#[repr(C)] #[repr(C)]

View File

@ -22,7 +22,7 @@ use crate::render::light::PointLightUniform;
use super::camera::{RenderCamera, CameraUniform}; use super::camera::{RenderCamera, CameraUniform};
use super::desc_buf_lay::DescVertexBufferLayout; use super::desc_buf_lay::DescVertexBufferLayout;
use super::light::{LightUniform, PointLight}; use super::light::{PointLight, LightUniformBuffers};
use super::texture::RenderTexture; use super::texture::RenderTexture;
use super::transform_buffer_storage::{TransformBufferIndices, TransformBuffers}; use super::transform_buffer_storage::{TransformBufferIndices, TransformBuffers};
use super::vertex::Vertex; use super::vertex::Vertex;
@ -91,9 +91,7 @@ pub struct BasicRenderer {
default_texture_bind_group: BindGroup, default_texture_bind_group: BindGroup,
depth_buffer_texture: RenderTexture, depth_buffer_texture: RenderTexture,
point_light_buffer: wgpu::Buffer, light_buffers: LightUniformBuffers,
point_light_bind_group_layout: BindGroupLayout,
point_light_bind_group: BindGroup,
} }
impl BasicRenderer { impl BasicRenderer {
@ -243,41 +241,6 @@ impl BasicRenderer {
next_indices: TransformBufferIndices { buffer_index: 0, transform_index: 0 }, next_indices: TransformBufferIndices { buffer_index: 0, transform_index: 0 },
}; };
let point_light_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Point Light Buffer"),
contents: bytemuck::cast_slice(&[PointLightUniform::default()]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
}
);
let point_light_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}
],
label: Some("point_light_bind_group_layout"),
});
let point_light_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &point_light_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: point_light_buffer.as_entire_binding(),
}
],
label: Some("point_light_bind_group"),
});
let camera_buffer = device.create_buffer_init( let camera_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor { &wgpu::util::BufferInitDescriptor {
label: Some("Camera Buffer"), label: Some("Camera Buffer"),
@ -335,6 +298,8 @@ impl BasicRenderer {
} }
); );
let light_uniform_buffers = LightUniformBuffers::new(&device);
let mut s = Self { let mut s = Self {
window, window,
surface, surface,
@ -366,9 +331,7 @@ impl BasicRenderer {
depth_buffer_texture: depth_texture, depth_buffer_texture: depth_texture,
entity_last_transforms: HashMap::new(), entity_last_transforms: HashMap::new(),
point_light_buffer, light_buffers: light_uniform_buffers,
point_light_bind_group_layout,
point_light_bind_group,
}; };
// create the default pipelines // create the default pipelines
@ -376,7 +339,7 @@ impl BasicRenderer {
pipelines.insert(0, Arc::new(FullRenderPipeline::new(&s.device, &s.config, &shader, pipelines.insert(0, Arc::new(FullRenderPipeline::new(&s.device, &s.config, &shader,
vec![super::vertex::Vertex::desc(),], vec![super::vertex::Vertex::desc(),],
vec![&s.texture_bind_group_layout, &s.transform_bind_group_layout, &camera_bind_group_layout, vec![&s.texture_bind_group_layout, &s.transform_bind_group_layout, &camera_bind_group_layout,
&s.point_light_bind_group_layout]))); &s.light_buffers.bindgroup_layout])));
s.render_pipelines = pipelines; s.render_pipelines = pipelines;
s s
@ -647,10 +610,7 @@ impl Renderer for BasicRenderer {
warn!("Missing camera!"); warn!("Missing camera!");
} }
for (point_light, transform) in main_world.query::<(&PointLight, &TransformComponent)>().iter() { self.light_buffers.update_lights(&self.queue, &main_world);
let uniform = PointLightUniform::from_bundle(point_light, &transform.transform);
self.queue.write_buffer(&self.point_light_buffer, 0, bytemuck::cast_slice(&[uniform]));
}
} }
fn render(&mut self) -> Result<(), wgpu::SurfaceError> { fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
@ -711,7 +671,10 @@ impl Renderer for BasicRenderer {
render_pass.set_bind_group(2, &self.camera_bind_group, &[]); render_pass.set_bind_group(2, &self.camera_bind_group, &[]);
// bind light // bind light
render_pass.set_bind_group(3, &self.point_light_bind_group, &[]); //render_pass.set_bind_group(3, &self.point_light_bind_group, &[]);
render_pass.set_bind_group(3, &self.light_buffers.point_lights.bind_group, &[]);
////self.light_buffers.bind_lights(&mut render_pass, 3);
// if this mesh uses indices, use them to draw the mesh // if this mesh uses indices, use them to draw the mesh
if let Some((idx_type, indices)) = buffers.buffer_indices.as_ref() { if let Some((idx_type, indices)) = buffers.buffer_indices.as_ref() {

View File

@ -1,5 +1,7 @@
// Vertex shader // Vertex shader
const max_light_count: i32 = 10;
struct VertexInput { struct VertexInput {
@location(0) position: vec3<f32>, @location(0) position: vec3<f32>,
@location(1) tex_coords: vec2<f32>, @location(1) tex_coords: vec2<f32>,
@ -31,10 +33,10 @@ struct PointLight {
var<uniform> u_model_transform: mat4x4<f32>; var<uniform> u_model_transform: mat4x4<f32>;
@group(2) @binding(0) @group(2) @binding(0)
var<uniform> camera: CameraUniform; var<uniform> u_camera: CameraUniform;
@group(3) @binding(0) @group(3) @binding(0)
var<uniform> point_light: PointLight; var<uniform> u_point_lights: array<PointLight, max_light_count>;
@vertex @vertex
fn vs_main( fn vs_main(
@ -43,7 +45,7 @@ fn vs_main(
var out: VertexOutput; var out: VertexOutput;
out.tex_coords = model.tex_coords; out.tex_coords = model.tex_coords;
out.clip_position = camera.view_proj * u_model_transform * vec4<f32>(model.position, 1.0); out.clip_position = u_camera.view_proj * u_model_transform * vec4<f32>(model.position, 1.0);
out.world_normal = (u_model_transform * vec4<f32>(model.normal, 0.0)).xyz; out.world_normal = (u_model_transform * vec4<f32>(model.normal, 0.0)).xyz;
@ -61,38 +63,45 @@ var t_diffuse: texture_2d<f32>;
@group(0)@binding(1) @group(0)@binding(1)
var s_diffuse: sampler; var s_diffuse: sampler;
//@group(3) @binding(0)
//var<uniform> point_light: PointLight;
@fragment @fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> { fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords);
var light_res = vec3<f32>(0.0);
for (var i = 0; i < 1; i++) {
light_res += blinn_phong_point_light(in.world_position, in.world_normal, u_point_lights[i]);
}
let light_object_res = light_res * object_color.xyz;
return vec4<f32>(light_object_res, object_color.a);
}
fn blinn_phong_point_light(world_pos: vec3<f32>, world_norm: vec3<f32>, point_light: PointLight) -> vec3<f32> {
let light_color = point_light.color.xyz; let light_color = point_light.color.xyz;
let light_pos = point_light.position.xyz; let light_pos = point_light.position.xyz;
let camera_view_pos = camera.view_pos.xyz; let camera_view_pos = u_camera.view_pos.xyz;
let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords);
// We don't need (or want) much ambient light, so 0.1 is fine // We don't need (or want) much ambient light, so 0.1 is fine
let ambient_strength = 0.1; let ambient_strength = 0.1;
var ambient_color = light_color * ambient_strength; var ambient_color = light_color * ambient_strength;
//// diffuse //// //// diffuse ////
let light_dir = normalize(light_pos - in.world_position); let light_dir = normalize(light_pos - world_pos);
let diffuse_strength = max(dot(in.world_normal, light_dir), 0.0); let diffuse_strength = max(dot(world_norm, light_dir), 0.0);
var diffuse_color = light_color * diffuse_strength; var diffuse_color = light_color * diffuse_strength;
//// end of diffuse //// //// end of diffuse ////
//// specular //// //// specular ////
let view_dir = normalize(camera_view_pos - in.world_position); let view_dir = normalize(camera_view_pos - world_pos);
let half_dir = normalize(view_dir + light_dir); let half_dir = normalize(view_dir + light_dir);
let specular_strength = pow(max(dot(in.world_normal, half_dir), 0.0), 32.0); let specular_strength = pow(max(dot(world_norm, half_dir), 0.0), 32.0);
var specular_color = specular_strength * light_color; var specular_color = specular_strength * light_color;
//// end of specular //// //// end of specular ////
//// point light attenuation //// //// point light attenuation ////
let distance = length(light_pos - in.world_position); let distance = length(light_pos - world_pos);
let attenuation = 1.0 / (point_light.constant + point_light.linear * distance + let attenuation = 1.0 / (point_light.constant + point_light.linear * distance +
point_light.quadratic * (distance * distance)); point_light.quadratic * (distance * distance));
@ -101,7 +110,5 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
specular_color *= attenuation * point_light.intensity; specular_color *= attenuation * point_light.intensity;
//// end of point light attenuation //// //// end of point light attenuation ////
let result = (ambient_color + diffuse_color + specular_color) * object_color.xyz; return (ambient_color + diffuse_color + specular_color);
}
return vec4<f32>(result, object_color.a);
}