Start support for multiple point light casters
ci/woodpecker/push/build Pipeline was successful
Details
ci/woodpecker/push/build Pipeline was successful
Details
This commit is contained in:
parent
d26e1ccfb4
commit
6510d5a7b9
|
@ -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)]
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue