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;
use std::{collections::{VecDeque, HashMap}, num::NonZeroU64};
use edict::query::EpochOf;
pub use point::*;
use tracing::debug;
use wgpu::util::DeviceExt;
use crate::math::Transform;
use std::mem;
#[repr(C)]
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct LightUniform {
pub position: glam::Vec3,
// Due to uniforms requiring 16 byte (4 float) spacing, we need to use a padding field here
pub(crate) _padding: u32,
pub color: glam::Vec3,
// Due to uniforms requiring 16 byte (4 float) spacing, we need to use a padding field here
pub(crate) _padding2: u32,
use crate::{math::Transform, ecs::components::TransformComponent};
pub struct LightBuffer {
pub bind_group: wgpu::BindGroup,
/// The buffer on the gpu storing the lights
buffer: wgpu::Buffer,
device_limits: wgpu::Limits,
/// The max amount of light casters that could fit in this buffer.
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)]

View File

@ -22,7 +22,7 @@ use crate::render::light::PointLightUniform;
use super::camera::{RenderCamera, CameraUniform};
use super::desc_buf_lay::DescVertexBufferLayout;
use super::light::{LightUniform, PointLight};
use super::light::{PointLight, LightUniformBuffers};
use super::texture::RenderTexture;
use super::transform_buffer_storage::{TransformBufferIndices, TransformBuffers};
use super::vertex::Vertex;
@ -91,9 +91,7 @@ pub struct BasicRenderer {
default_texture_bind_group: BindGroup,
depth_buffer_texture: RenderTexture,
point_light_buffer: wgpu::Buffer,
point_light_bind_group_layout: BindGroupLayout,
point_light_bind_group: BindGroup,
light_buffers: LightUniformBuffers,
}
impl BasicRenderer {
@ -243,41 +241,6 @@ impl BasicRenderer {
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(
&wgpu::util::BufferInitDescriptor {
label: Some("Camera Buffer"),
@ -335,6 +298,8 @@ impl BasicRenderer {
}
);
let light_uniform_buffers = LightUniformBuffers::new(&device);
let mut s = Self {
window,
surface,
@ -366,9 +331,7 @@ impl BasicRenderer {
depth_buffer_texture: depth_texture,
entity_last_transforms: HashMap::new(),
point_light_buffer,
point_light_bind_group_layout,
point_light_bind_group,
light_buffers: light_uniform_buffers,
};
// create the default pipelines
@ -376,7 +339,7 @@ impl BasicRenderer {
pipelines.insert(0, Arc::new(FullRenderPipeline::new(&s.device, &s.config, &shader,
vec![super::vertex::Vertex::desc(),],
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
@ -647,10 +610,7 @@ impl Renderer for BasicRenderer {
warn!("Missing camera!");
}
for (point_light, transform) in main_world.query::<(&PointLight, &TransformComponent)>().iter() {
let uniform = PointLightUniform::from_bundle(point_light, &transform.transform);
self.queue.write_buffer(&self.point_light_buffer, 0, bytemuck::cast_slice(&[uniform]));
}
self.light_buffers.update_lights(&self.queue, &main_world);
}
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, &[]);
// 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 let Some((idx_type, indices)) = buffers.buffer_indices.as_ref() {

View File

@ -1,5 +1,7 @@
// Vertex shader
const max_light_count: i32 = 10;
struct VertexInput {
@location(0) position: vec3<f32>,
@location(1) tex_coords: vec2<f32>,
@ -31,10 +33,10 @@ struct PointLight {
var<uniform> u_model_transform: mat4x4<f32>;
@group(2) @binding(0)
var<uniform> camera: CameraUniform;
var<uniform> u_camera: CameraUniform;
@group(3) @binding(0)
var<uniform> point_light: PointLight;
var<uniform> u_point_lights: array<PointLight, max_light_count>;
@vertex
fn vs_main(
@ -43,7 +45,7 @@ fn vs_main(
var out: VertexOutput;
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;
@ -61,38 +63,45 @@ var t_diffuse: texture_2d<f32>;
@group(0)@binding(1)
var s_diffuse: sampler;
//@group(3) @binding(0)
//var<uniform> point_light: PointLight;
@fragment
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_pos = point_light.position.xyz;
let camera_view_pos = camera.view_pos.xyz;
let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords);
let camera_view_pos = u_camera.view_pos.xyz;
// We don't need (or want) much ambient light, so 0.1 is fine
let ambient_strength = 0.1;
var ambient_color = light_color * ambient_strength;
//// 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;
//// end of diffuse ////
//// 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 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;
//// end of specular ////
//// 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 +
point_light.quadratic * (distance * distance));
@ -101,7 +110,5 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
specular_color *= attenuation * point_light.intensity;
//// end of point light attenuation ////
let result = (ambient_color + diffuse_color + specular_color) * object_color.xyz;
return vec4<f32>(result, object_color.a);
return (ambient_color + diffuse_color + specular_color);
}