Compare commits
2 Commits
f345f065c1
...
cfd5cabfbb
Author | SHA1 | Date |
---|---|---|
SeanOMik | cfd5cabfbb | |
SeanOMik | 22c08ba66e |
|
@ -38,16 +38,34 @@ impl Projection {
|
|||
#[repr(C)]
|
||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct CameraUniform {
|
||||
/// The view matrix of the camera
|
||||
pub view_mat: glam::Mat4,
|
||||
/// The view projection matrix
|
||||
pub view_proj: glam::Mat4,
|
||||
// vec4 is used because of the uniforms 16 byte spacing requirement
|
||||
pub view_pos: glam::Vec4,
|
||||
/// The position of the camera
|
||||
pub position: glam::Vec3,
|
||||
_padding: u32,
|
||||
|
||||
}
|
||||
|
||||
impl Default for CameraUniform {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
view_mat: glam::Mat4::IDENTITY,
|
||||
view_proj: glam::Mat4::IDENTITY,
|
||||
view_pos: Default::default()
|
||||
position: Default::default(),
|
||||
_padding: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CameraUniform {
|
||||
pub fn new(view_mat: glam::Mat4, view_proj: glam::Mat4, position: glam::Vec3) -> Self {
|
||||
Self {
|
||||
view_mat,
|
||||
view_proj,
|
||||
position,
|
||||
_padding: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,9 +97,11 @@ impl RenderCamera {
|
|||
self.aspect = size.width as f32 / size.height as f32;
|
||||
}
|
||||
|
||||
pub fn update_view_projection(&mut self, camera: &CameraComponent) -> &glam::Mat4 {
|
||||
match camera.mode {
|
||||
CameraProjectionMode::Perspective => {
|
||||
/// Calculates the view projection, and the view
|
||||
///
|
||||
/// Returns: A tuple with the view projection as the first element, and the
|
||||
/// view matrix as the second.
|
||||
pub fn calc_view_projection(&mut self, camera: &CameraComponent) -> (&glam::Mat4, glam::Mat4) {
|
||||
let position = camera.transform.translation;
|
||||
let forward = camera.transform.forward();
|
||||
let up = camera.transform.up();
|
||||
|
@ -92,13 +112,14 @@ impl RenderCamera {
|
|||
up
|
||||
);
|
||||
|
||||
match camera.mode {
|
||||
CameraProjectionMode::Perspective => {
|
||||
let proj = glam::Mat4::perspective_rh_gl(camera.fov.to_radians(), self.aspect, self.znear, self.zfar);
|
||||
|
||||
self.view_proj = OPENGL_TO_WGPU_MATRIX * proj * view;
|
||||
&self.view_proj
|
||||
(&self.view_proj, view)
|
||||
},
|
||||
CameraProjectionMode::Orthographic => {
|
||||
let position = camera.transform.translation;
|
||||
let target = camera.transform.rotation * glam::Vec3::new(0.0, 0.0, -1.0);
|
||||
let target = target.normalize();
|
||||
|
||||
|
@ -111,7 +132,7 @@ impl RenderCamera {
|
|||
let proj = glam::Mat4::orthographic_rh_gl(-size_x, size_x, -size_y, size_y, self.znear, self.zfar);
|
||||
|
||||
self.view_proj = OPENGL_TO_WGPU_MATRIX * proj;
|
||||
&self.view_proj
|
||||
(&self.view_proj, view)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,18 +103,21 @@ pub(crate) struct LightUniformBuffers {
|
|||
pub buffer: wgpu::Buffer,
|
||||
pub bindgroup_layout: wgpu::BindGroupLayout,
|
||||
pub bindgroup: wgpu::BindGroup,
|
||||
pub lights_uniform: LightsUniform,
|
||||
pub point_lights: LightBuffer<PointLightUniform>,
|
||||
pub spot_lights: LightBuffer<SpotLightUniform>,
|
||||
pub light_indexes: HashMap<Entity, u32>,
|
||||
pub current_light_idx: u32,
|
||||
}
|
||||
|
||||
impl LightUniformBuffers {
|
||||
pub fn new(device: &wgpu::Device) -> Self {
|
||||
let limits = device.limits();
|
||||
// TODO: check this limit somehow
|
||||
let max_buffer_sizes = (limits.max_uniform_buffer_binding_size as u64) / 2;
|
||||
|
||||
let buffer = device.create_buffer(
|
||||
&wgpu::BufferDescriptor {
|
||||
label: Some("UBO_Lights"),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
size: mem::size_of::<LightsUniform>() as u64,
|
||||
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
|
||||
size: max_buffer_sizes,
|
||||
mapped_at_creation: false,
|
||||
}
|
||||
);
|
||||
|
@ -123,14 +126,16 @@ impl LightUniformBuffers {
|
|||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT | wgpu::ShaderStages::COMPUTE,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
ty: wgpu::BufferBindingType::Storage {
|
||||
read_only: true
|
||||
},
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
}
|
||||
},
|
||||
],
|
||||
label: Some("BGL_Lights"),
|
||||
});
|
||||
|
@ -147,63 +152,167 @@ impl LightUniformBuffers {
|
|||
size: None, // use the full buffer
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
],
|
||||
label: Some("BG_Lights"),
|
||||
});
|
||||
|
||||
let point_lights = LightBuffer::new(MAX_LIGHT_COUNT);
|
||||
let spot_lights = LightBuffer::new(MAX_LIGHT_COUNT);
|
||||
|
||||
Self {
|
||||
buffer,
|
||||
bindgroup_layout,
|
||||
bindgroup,
|
||||
lights_uniform: LightsUniform::default(),
|
||||
point_lights,
|
||||
spot_lights,
|
||||
light_indexes: Default::default(),
|
||||
current_light_idx: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_lights(&mut self, queue: &wgpu::Queue, world_tick: Tick, world: &World) {
|
||||
let mut lights = LightsUniform::default();
|
||||
|
||||
for (entity, point_light, transform, light_epoch, transform_epoch)
|
||||
in world.view_iter::<(Entities, &PointLight, &Transform, TickOf<PointLight>, TickOf<Transform>)>() {
|
||||
|
||||
if !self.point_lights.has_light(entity) || light_epoch == world_tick || transform_epoch == world_tick {
|
||||
let uniform = PointLightUniform::from_bundle(&point_light, &transform);
|
||||
self.point_lights.update_or_add(&mut self.lights_uniform.point_lights, entity, uniform);
|
||||
//debug!("Updated point light");
|
||||
// TODO: dont update light every frame
|
||||
let idx = *self.light_indexes.entry(entity)
|
||||
.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);
|
||||
lights.data[idx] = uniform;
|
||||
}
|
||||
|
||||
if let Some((entity, dir_light, transform)) =
|
||||
world.view_iter::<(Entities, &DirectionalLight, &Transform)>().next() {
|
||||
|
||||
let idx = *self.light_indexes.entry(entity)
|
||||
.or_insert_with(|| {
|
||||
let t = self.current_light_idx;
|
||||
self.current_light_idx += 1;
|
||||
t
|
||||
}) as usize;
|
||||
|
||||
let uniform = LightUniform::from_directional_bundle(&dir_light, &transform);
|
||||
lights.data[idx] = uniform;
|
||||
}
|
||||
|
||||
lights.light_count = self.light_indexes.len() as u32;
|
||||
|
||||
// update the light count in the struct
|
||||
queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(&[lights]));
|
||||
}
|
||||
}
|
||||
|
||||
for (entity, spot_light, transform, light_epoch, transform_epoch)
|
||||
in world.view_iter::<(Entities, &SpotLight, &Transform, TickOf<SpotLight>, TickOf<Transform>)>() {
|
||||
#[repr(C)]
|
||||
#[derive(Default, Debug, Copy, Clone)]
|
||||
pub(crate) enum LightType {
|
||||
#[default]
|
||||
Directional = 0,
|
||||
Point = 1,
|
||||
Spotlight = 2,
|
||||
}
|
||||
|
||||
if !self.spot_lights.has_light(entity) || light_epoch == world_tick || transform_epoch == world_tick {
|
||||
let uniform = SpotLightUniform::from_bundle(&spot_light, &transform);
|
||||
self.spot_lights.update_or_add(&mut self.lights_uniform.spot_lights, entity, uniform);
|
||||
//debug!("Updated spot light");
|
||||
#[repr(C)]
|
||||
#[derive(Default, Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub(crate) struct LightUniform {
|
||||
pub light_type: u32, // LightType
|
||||
pub enabled: u32, // bool
|
||||
pub _padding: [u32; 2],
|
||||
pub position: glam::Vec3,
|
||||
pub _padding2: u32,
|
||||
pub direction: glam::Vec3,
|
||||
pub _padding3: 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 range: f32,
|
||||
pub intensity: f32,
|
||||
|
||||
pub spot_cutoff: f32,
|
||||
pub spot_outer_cutoff: f32,
|
||||
|
||||
pub _padding4: u32,
|
||||
}
|
||||
|
||||
impl LightUniform {
|
||||
pub fn from_point_light_bundle(light: &PointLight, transform: &Transform) -> Self {
|
||||
Self {
|
||||
light_type: LightType::Point as u32,
|
||||
enabled: true as u32, // TODO
|
||||
_padding: [0; 2],
|
||||
position: transform.translation,
|
||||
_padding2: 0,
|
||||
direction: transform.forward(),
|
||||
_padding3: 0,
|
||||
color: light.color,
|
||||
|
||||
range: 2.0,
|
||||
intensity: 1.0,
|
||||
|
||||
spot_cutoff: 0.0,
|
||||
spot_outer_cutoff: 0.0,
|
||||
|
||||
_padding4: 0,
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((dir_light, transform)) =
|
||||
world.view_iter::<(&DirectionalLight, &Transform)>().next() {
|
||||
pub fn from_directional_bundle(light: &DirectionalLight, transform: &Transform) -> Self {
|
||||
Self {
|
||||
light_type: LightType::Directional as u32,
|
||||
enabled: true as u32, // TODO: take from component
|
||||
_padding: [0; 2],
|
||||
position: transform.translation,
|
||||
_padding2: 0,
|
||||
direction: transform.forward(),
|
||||
_padding3: 0,
|
||||
color: light.color,
|
||||
|
||||
let uniform = DirectionalLightUniform::from_bundle(&dir_light, &transform);
|
||||
self.lights_uniform.directional_light = uniform;
|
||||
range: 0.0,
|
||||
intensity: 0.0,
|
||||
|
||||
spot_cutoff: 0.0,
|
||||
spot_outer_cutoff: 0.0,
|
||||
|
||||
_padding4: 0,
|
||||
}
|
||||
}
|
||||
|
||||
self.lights_uniform.point_light_count = self.point_lights.buffer_count as u32;
|
||||
self.lights_uniform.spot_light_count = self.spot_lights.buffer_count as u32;
|
||||
queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(&[self.lights_uniform]));
|
||||
// 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,
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct LightsUniform {
|
||||
point_lights: [PointLightUniform; MAX_LIGHT_COUNT],
|
||||
point_light_count: u32,
|
||||
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,
|
||||
|
@ -352,4 +461,4 @@ impl SpotLightUniform {
|
|||
_padding3: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
} */
|
|
@ -0,0 +1,223 @@
|
|||
use std::{borrow::Cow, mem, num::NonZeroU32, ptr::NonNull, rc::Rc};
|
||||
|
||||
use glam::UVec2;
|
||||
use wgpu::{util::DeviceExt, ComputePipeline};
|
||||
use winit::dpi::PhysicalSize;
|
||||
|
||||
use super::{light::LightUniformBuffers, render_buffer::BufferWrapper, renderer::RenderPass, texture::RenderTexture};
|
||||
|
||||
struct LightIndicesGridBuffer {
|
||||
indices_buffer: wgpu::Buffer,
|
||||
grid_texture: wgpu::Texture,
|
||||
grid_texture_view: wgpu::TextureView,
|
||||
bind_group_layout: wgpu::BindGroupLayout,
|
||||
bind_group: wgpu::BindGroup,
|
||||
}
|
||||
|
||||
pub(crate) struct LightCullCompute {
|
||||
device: Rc<wgpu::Device>,
|
||||
queue: Rc<wgpu::Queue>,
|
||||
pipeline: ComputePipeline,
|
||||
lights: NonNull<LightUniformBuffers>,
|
||||
camera: NonNull<BufferWrapper>,
|
||||
light_indices_grid: LightIndicesGridBuffer,
|
||||
screen_size_buffer: BufferWrapper,
|
||||
depth_tex: NonNull<RenderTexture>,
|
||||
}
|
||||
|
||||
impl LightCullCompute {
|
||||
fn create_grid(device: &wgpu::Device, screen_size: PhysicalSize<u32>) -> LightIndicesGridBuffer {
|
||||
let limits = device.limits();
|
||||
//let max_buffer_sizes = (limits.max_uniform_buffer_binding_size as u64) / 2;
|
||||
|
||||
/* let light_indices_buffer = device.create_buffer(
|
||||
&wgpu::BufferDescriptor {
|
||||
label: Some("B_LightIndices"),
|
||||
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
|
||||
size: (mem::size_of::<u32>() * 16 * 16) as u64,
|
||||
mapped_at_creation: false,
|
||||
}
|
||||
); */
|
||||
|
||||
let light_indices_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("B_LightIndices"),
|
||||
contents: &[0; mem::size_of::<u32>() * 16 * 16],
|
||||
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
|
||||
});
|
||||
|
||||
let light_indices_bg_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::COMPUTE | wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Storage {
|
||||
read_only: false
|
||||
},
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStages::COMPUTE | wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::StorageTexture {
|
||||
access: wgpu::StorageTextureAccess::ReadWrite,
|
||||
format: wgpu::TextureFormat::Rg32Uint, // vec2<uint>
|
||||
view_dimension: wgpu::TextureViewDimension::D2
|
||||
},
|
||||
count: None,
|
||||
}
|
||||
],
|
||||
label: Some("BGL_LightIndicesGrid"),
|
||||
});
|
||||
|
||||
// TODO: shrink the texture to match the amount of grid cells that the shader actually uses
|
||||
let size = wgpu::Extent3d {
|
||||
width: screen_size.width,
|
||||
height: screen_size.height,
|
||||
depth_or_array_layers: 1,
|
||||
};
|
||||
let grid_texture = device.create_texture(
|
||||
&wgpu::TextureDescriptor {
|
||||
label: Some("Tex_LightGrid"),
|
||||
size,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::Rg32Uint, // vec2<uint>
|
||||
usage: wgpu::TextureUsages::STORAGE_BINDING,
|
||||
view_formats: &[],
|
||||
}
|
||||
);
|
||||
|
||||
let grid_texture_view = grid_texture.create_view(&wgpu::TextureViewDescriptor {
|
||||
label: Some("TexV_LightGrid"),
|
||||
format: Some(wgpu::TextureFormat::Rg32Uint), // vec2<uint>
|
||||
dimension: Some(wgpu::TextureViewDimension::D2),
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
base_mip_level: 0,
|
||||
mip_level_count: None,
|
||||
base_array_layer: 0,
|
||||
array_layer_count: None,
|
||||
});
|
||||
|
||||
let light_indices_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &light_indices_bg_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::Buffer(
|
||||
wgpu::BufferBinding {
|
||||
buffer: &light_indices_buffer,
|
||||
offset: 0,
|
||||
size: None, // the entire light buffer is needed
|
||||
}
|
||||
)
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::TextureView(&grid_texture_view)
|
||||
}
|
||||
],
|
||||
label: Some("BG_LightIndicesGrid"),
|
||||
});
|
||||
|
||||
LightIndicesGridBuffer {
|
||||
indices_buffer: light_indices_buffer,
|
||||
grid_texture,
|
||||
grid_texture_view,
|
||||
bind_group_layout: light_indices_bg_layout,
|
||||
bind_group: light_indices_bg,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(device: Rc<wgpu::Device>, queue: Rc<wgpu::Queue>, screen_size: PhysicalSize<u32>, lights: &LightUniformBuffers, camera_buffers: &BufferWrapper, depth_texture: &mut RenderTexture) -> Self {
|
||||
let screen_size_buffer = BufferWrapper::builder()
|
||||
.buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST)
|
||||
.label_prefix("ScreenSize")
|
||||
.visibility(wgpu::ShaderStages::COMPUTE)
|
||||
.buffer_dynamic_offset(false)
|
||||
.contents(&[UVec2::new(screen_size.width, screen_size.height)])
|
||||
.finish(&device);
|
||||
|
||||
let shader_src = include_str!("shaders/light_cull.comp.wgsl");
|
||||
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("LightCullCompute"),
|
||||
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(shader_src)),
|
||||
});
|
||||
|
||||
let light_grid = Self::create_grid(&device, screen_size);
|
||||
|
||||
let depth_tex_pair = depth_texture.create_bind_group(&device);
|
||||
|
||||
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("PipeLay_LightCull"),
|
||||
bind_group_layouts: &[
|
||||
//&depth_texture.bindgroup_pair.as_ref().unwrap().layout,
|
||||
&depth_tex_pair.layout,
|
||||
camera_buffers.bindgroup_layout().unwrap(),
|
||||
&lights.bindgroup_layout,
|
||||
&light_grid.bind_group_layout,
|
||||
screen_size_buffer.bindgroup_layout().unwrap(),
|
||||
],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
|
||||
label: Some("Pipe_LightCull"),
|
||||
layout: Some(&layout),
|
||||
module: &shader,
|
||||
entry_point: "cs_main",
|
||||
});
|
||||
|
||||
Self {
|
||||
device,
|
||||
queue,
|
||||
pipeline,
|
||||
lights: NonNull::from(lights),
|
||||
camera: NonNull::from(camera_buffers),
|
||||
light_indices_grid: light_grid,
|
||||
screen_size_buffer,
|
||||
depth_tex: NonNull::from(depth_texture),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_screen_size(&self, size: PhysicalSize<u32>) {
|
||||
self.screen_size_buffer.write_buffer(&self.queue, 0,
|
||||
&[UVec2::new(size.width, size.height)]);
|
||||
}
|
||||
|
||||
pub fn compute(&mut self) {
|
||||
//self.queue.write_buffer(&self.light_indices_grid.indices_buffer, 0, &[0; mem::size_of::<u32>() * 16 * 16]);
|
||||
|
||||
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("LightCullCompute"),
|
||||
});
|
||||
|
||||
{
|
||||
let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
|
||||
label: Some("Pass_LightCull"),
|
||||
});
|
||||
|
||||
pass.set_pipeline(&self.pipeline);
|
||||
|
||||
let depth = unsafe { self.depth_tex.as_ref() };
|
||||
pass.set_bind_group(0, depth.bind_group(), &[]);
|
||||
|
||||
let cam = unsafe { self.camera.as_ref() };
|
||||
pass.set_bind_group(1, cam.bindgroup(), &[]);
|
||||
|
||||
let lights = unsafe { self.lights.as_ref() };
|
||||
pass.set_bind_group(2, &lights.bindgroup, &[]);
|
||||
|
||||
pass.set_bind_group(3, &self.light_indices_grid.bind_group, &[]);
|
||||
pass.set_bind_group(4, self.screen_size_buffer.bindgroup(), &[]);
|
||||
|
||||
pass.dispatch_workgroups(16, 16, 1);
|
||||
}
|
||||
self.queue.submit(std::iter::once(encoder.finish()));
|
||||
self.device.poll(wgpu::Maintain::Wait);
|
||||
}
|
||||
}
|
|
@ -12,3 +12,4 @@ pub mod camera;
|
|||
pub mod window;
|
||||
pub mod transform_buffer_storage;
|
||||
pub mod light;
|
||||
pub mod light_cull_compute;
|
|
@ -1,4 +1,4 @@
|
|||
use std::{sync::Arc, num::NonZeroU32};
|
||||
use std::{num::NonZeroU32, ops::Deref, sync::Arc};
|
||||
|
||||
use wgpu::util::DeviceExt;
|
||||
|
||||
|
@ -39,12 +39,19 @@ impl BindGroupPair {
|
|||
layout,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(bindgroup: wgpu::BindGroup, layout: wgpu::BindGroupLayout) -> Self {
|
||||
Self {
|
||||
bindgroup,
|
||||
layout: Arc::new(layout),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BufferWrapper {
|
||||
pub bindgroup_pair: Option<BindGroupPair>,
|
||||
pub inner_buf: wgpu::Buffer,
|
||||
pub len: usize,
|
||||
pub len: Option<usize>,
|
||||
}
|
||||
|
||||
impl BufferWrapper {
|
||||
|
@ -54,7 +61,7 @@ impl BufferWrapper {
|
|||
Self {
|
||||
bindgroup_pair: bind_group,
|
||||
inner_buf: buffer,
|
||||
len: 0,
|
||||
len: Some(0),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,15 +71,82 @@ impl BufferWrapper {
|
|||
Self {
|
||||
bindgroup_pair: bind_group,
|
||||
inner_buf: buffer,
|
||||
len: 0,
|
||||
len: Some(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_parts(bind_group: wgpu::BindGroup, bind_group_layout: wgpu::BindGroupLayout, buffer: wgpu::Buffer) -> Self {
|
||||
Self {
|
||||
bindgroup_pair: Some(BindGroupPair::new(bind_group, bind_group_layout)),
|
||||
inner_buf: buffer,
|
||||
len: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a builder for a BufferWrapper
|
||||
pub fn builder() -> BufferWrapperBuilder {
|
||||
BufferWrapperBuilder::new()
|
||||
}
|
||||
|
||||
/// Retrieve the layout of the bindgroup associated with this buffer.
|
||||
///
|
||||
/// Returns None if this buffer object was not provided a bindgroup.
|
||||
pub fn bindgroup_layout(&self) -> Option<&wgpu::BindGroupLayout> {
|
||||
self.bindgroup_pair.as_ref().map(|bg| bg.layout.deref())
|
||||
}
|
||||
|
||||
/// Queue's the data to be written to `buffer` starting at `offset`.
|
||||
///
|
||||
/// The write is not immediately submitted, and instead enqueued
|
||||
/// internally to happen at the start of the next submit() call.
|
||||
///
|
||||
/// This method fails if data overruns the size of buffer starting at offset.
|
||||
///
|
||||
/// See [`wgpu::Queue::write_buffer`](https://docs.rs/wgpu/latest/wgpu/struct.Queue.html#method.write_buffer).
|
||||
pub fn write_buffer<T>(&self, queue: &wgpu::Queue, offset: u64, data: &[T])
|
||||
where
|
||||
T: bytemuck::NoUninit
|
||||
{
|
||||
queue.write_buffer(&self.inner_buf, offset, bytemuck::cast_slice(data));
|
||||
}
|
||||
|
||||
/// Sets the buffer's bind group to `index` in the `pass`.
|
||||
///
|
||||
/// The bind group layout in the active pipeline when any `draw()` function is called must
|
||||
/// match the layout of this bind group.
|
||||
///
|
||||
/// See [`wgpu::RenderPass::set_bind_group`](https://docs.rs/wgpu/latest/wgpu/struct.RenderPass.html#method.set_bind_group).
|
||||
pub fn render_pass_bind_at<'a, 'b>(
|
||||
&'a self,
|
||||
pass: &'b mut wgpu::RenderPass<'a>,
|
||||
index: u32,
|
||||
offsets: &[wgpu::DynamicOffset],
|
||||
) {
|
||||
let pair = self.bindgroup_pair.as_ref().expect(
|
||||
"BufferWrapper is missing bindgroup pair! Cannot set bind group on RenderPass!",
|
||||
);
|
||||
pass.set_bind_group(index, &pair.bindgroup, offsets);
|
||||
}
|
||||
|
||||
/// Returns the bindgroup of this buffer, panics if the buffer does not have a bindgroup.
|
||||
pub fn bindgroup(&self) -> &wgpu::BindGroup {
|
||||
&self.bindgroup_pair.as_ref().expect(
|
||||
"BufferWrapper is missing bindgroup pair! Cannot set bind group on RenderPass!",
|
||||
).bindgroup
|
||||
}
|
||||
}
|
||||
|
||||
/// Struct used for building a BufferWrapper
|
||||
///
|
||||
/// ```nobuild
|
||||
/// let camera_buffer = BufferWrapper::builder()
|
||||
/// .buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST)
|
||||
/// .contents(&[CameraUniform::default()])
|
||||
/// .label_prefix("Camera")
|
||||
/// .visibility(wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT)
|
||||
/// .buffer_dynamic_offset(false)
|
||||
/// .finish(&device);
|
||||
/// ```
|
||||
#[derive(Default)]
|
||||
pub struct BufferWrapperBuilder {
|
||||
buffer_usage: Option<wgpu::BufferUsages>,
|
||||
|
@ -222,7 +296,7 @@ impl BufferWrapperBuilder {
|
|||
BufferWrapper {
|
||||
bindgroup_pair: Some(bg_pair),
|
||||
inner_buf: buffer,
|
||||
len: self.count.unwrap_or_default() as usize,
|
||||
len: Some(self.count.unwrap_or_default() as usize),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use std::collections::{HashMap, VecDeque, HashSet};
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::borrow::Cow;
|
||||
|
||||
|
@ -24,6 +25,7 @@ use crate::scene::CameraComponent;
|
|||
use super::camera::{RenderCamera, CameraUniform};
|
||||
use super::desc_buf_lay::DescVertexBufferLayout;
|
||||
use super::light::LightUniformBuffers;
|
||||
use super::light_cull_compute::LightCullCompute;
|
||||
use super::material::Material;
|
||||
use super::render_buffer::BufferWrapper;
|
||||
use super::texture::RenderTexture;
|
||||
|
@ -45,6 +47,12 @@ pub trait Renderer {
|
|||
fn add_render_pipeline(&mut self, shader_id: u64, pipeline: Arc<FullRenderPipeline>);
|
||||
}
|
||||
|
||||
pub trait RenderPass {
|
||||
fn prepare(&mut self, main_world: &mut World);
|
||||
fn render(&mut self, encoder: &mut wgpu::CommandEncoder) -> Result<(), wgpu::SurfaceError>;
|
||||
fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>);
|
||||
}
|
||||
|
||||
struct MeshBufferStorage {
|
||||
buffer_vertex: BufferStorage,
|
||||
buffer_indices: Option<(wgpu::IndexFormat, BufferStorage)>,
|
||||
|
@ -68,8 +76,8 @@ pub struct CachedTransform {
|
|||
|
||||
pub struct BasicRenderer {
|
||||
pub surface: wgpu::Surface,
|
||||
pub device: wgpu::Device,
|
||||
pub queue: wgpu::Queue,
|
||||
pub device: Rc<wgpu::Device>, // device does not need to be mutable, no need for refcell
|
||||
pub queue: Rc<wgpu::Queue>,
|
||||
pub config: wgpu::SurfaceConfiguration,
|
||||
pub size: winit::dpi::PhysicalSize<u32>,
|
||||
pub window: Arc<Window>,
|
||||
|
@ -88,8 +96,8 @@ pub struct BasicRenderer {
|
|||
render_limits: Limits,
|
||||
|
||||
inuse_camera: RenderCamera,
|
||||
camera_buffer: wgpu::Buffer,
|
||||
camera_bind_group: wgpu::BindGroup,
|
||||
camera_buffer: BufferWrapper,
|
||||
//camera_bind_group: wgpu::BindGroup,
|
||||
|
||||
bgl_texture: Arc<BindGroupLayout>,
|
||||
default_texture: RenderTexture,
|
||||
|
@ -98,6 +106,8 @@ pub struct BasicRenderer {
|
|||
material_buffer: BufferWrapper,
|
||||
|
||||
light_buffers: LightUniformBuffers,
|
||||
|
||||
light_cull_compute: LightCullCompute,
|
||||
}
|
||||
|
||||
impl BasicRenderer {
|
||||
|
@ -123,7 +133,7 @@ impl BasicRenderer {
|
|||
|
||||
let (device, queue) = adapter.request_device(
|
||||
&wgpu::DeviceDescriptor {
|
||||
features: wgpu::Features::empty(),
|
||||
features: wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
|
||||
// WebGL does not support all wgpu features.
|
||||
// Not sure if the engine will ever completely support WASM,
|
||||
// but its here just in case
|
||||
|
@ -143,10 +153,7 @@ impl BasicRenderer {
|
|||
let render_limits = device.limits();
|
||||
let surface_caps = surface.get_capabilities(&adapter);
|
||||
|
||||
let present_mode = surface_caps.present_modes[0]; /* match surface_caps.present_modes.contains(&wgpu::PresentMode::Immediate) {
|
||||
true => wgpu::PresentMode::Immediate,
|
||||
false => surface_caps.present_modes[0]
|
||||
}; */
|
||||
let present_mode = surface_caps.present_modes[0];
|
||||
|
||||
debug!("present mode: {:?}", present_mode);
|
||||
|
||||
|
@ -174,43 +181,15 @@ impl BasicRenderer {
|
|||
});
|
||||
|
||||
let transform_buffers = TransformBuffers::new(&device);
|
||||
let camera_buffer = BufferWrapper::builder()
|
||||
.buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST)
|
||||
.contents(&[CameraUniform::default()])
|
||||
.label_prefix("Camera")
|
||||
.visibility(wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT | wgpu::ShaderStages::COMPUTE)
|
||||
.buffer_dynamic_offset(false)
|
||||
.finish(&device);
|
||||
|
||||
let camera_buffer = device.create_buffer_init(
|
||||
&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Camera Buffer"),
|
||||
contents: bytemuck::cast_slice(&[CameraUniform::default()]),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
}
|
||||
);
|
||||
|
||||
let camera_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("camera_bind_group_layout"),
|
||||
});
|
||||
|
||||
let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &camera_bind_group_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: camera_buffer.as_entire_binding(),
|
||||
}
|
||||
],
|
||||
label: Some("camera_bind_group"),
|
||||
});
|
||||
|
||||
let depth_texture = RenderTexture::create_depth_texture(&device, &config, "Depth Buffer");
|
||||
let mut depth_texture = RenderTexture::create_depth_texture(&device, &config, "Tex_Depth");
|
||||
|
||||
// load the default texture
|
||||
let bytes = include_bytes!("default_texture.png");
|
||||
|
@ -222,9 +201,12 @@ impl BasicRenderer {
|
|||
.buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST)
|
||||
.visibility(wgpu::ShaderStages::FRAGMENT)
|
||||
.contents(&[MaterialUniform::default()])
|
||||
//.size(mem::size_of::<MaterialUniform>())
|
||||
.finish(&device);
|
||||
|
||||
let device = Rc::new(device);
|
||||
let queue = Rc::new(queue);
|
||||
let light_cull_compute = LightCullCompute::new(device.clone(), queue.clone(), size, &light_uniform_buffers, &camera_buffer, &mut depth_texture);
|
||||
|
||||
let mut s = Self {
|
||||
window,
|
||||
surface,
|
||||
|
@ -248,7 +230,6 @@ impl BasicRenderer {
|
|||
|
||||
inuse_camera: RenderCamera::new(size),
|
||||
camera_buffer,
|
||||
camera_bind_group,
|
||||
|
||||
bgl_texture,
|
||||
default_texture,
|
||||
|
@ -257,13 +238,15 @@ impl BasicRenderer {
|
|||
|
||||
light_buffers: light_uniform_buffers,
|
||||
material_buffer: mat_buffer,
|
||||
light_cull_compute,
|
||||
};
|
||||
|
||||
// create the default pipelines
|
||||
let mut pipelines = HashMap::new();
|
||||
pipelines.insert(0, Arc::new(FullRenderPipeline::new(&s.device, &s.config, &shader,
|
||||
vec![super::vertex::Vertex::desc(),],
|
||||
vec![&s.bgl_texture, &s.transform_buffers.bindgroup_layout, &camera_bind_group_layout,
|
||||
vec![&s.bgl_texture, &s.transform_buffers.bindgroup_layout,
|
||||
s.camera_buffer.bindgroup_layout().unwrap(),
|
||||
&s.light_buffers.bindgroup_layout, &s.material_buffer.bindgroup_pair.as_ref().unwrap().layout,
|
||||
&s.bgl_texture])));
|
||||
s.render_pipelines = pipelines;
|
||||
|
@ -511,13 +494,10 @@ impl Renderer for BasicRenderer {
|
|||
}
|
||||
|
||||
if let Some(camera) = main_world.view_iter::<&mut CameraComponent>().next() {
|
||||
let view_proj = self.inuse_camera.update_view_projection(&camera);
|
||||
let (view_proj, view_mat) = self.inuse_camera.calc_view_projection(&camera);
|
||||
let pos = camera.transform.translation;
|
||||
let uniform = CameraUniform {
|
||||
view_proj: *view_proj,
|
||||
view_pos: glam::Vec4::new(pos.x, pos.y, pos.z, 0.0),
|
||||
};
|
||||
self.queue.write_buffer(&self.camera_buffer, 0, bytemuck::cast_slice(&[uniform]));
|
||||
let uniform = CameraUniform::new(view_mat, *view_proj, pos);
|
||||
self.camera_buffer.write_buffer(&self.queue, 0, &[uniform]);
|
||||
} else {
|
||||
warn!("Missing camera!");
|
||||
}
|
||||
|
@ -529,6 +509,8 @@ impl Renderer for BasicRenderer {
|
|||
let output = self.surface.get_current_texture()?;
|
||||
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
||||
//self.light_cull_compute.compute();
|
||||
|
||||
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("Basic Renderer's Encoder")
|
||||
});
|
||||
|
@ -588,7 +570,7 @@ impl Renderer for BasicRenderer {
|
|||
let offset = TransformBuffers::index_offset(&self.render_limits, transform_indices) as u32;
|
||||
render_pass.set_bind_group(1, bindgroup, &[ offset, offset, ]);
|
||||
|
||||
render_pass.set_bind_group(2, &self.camera_bind_group, &[]);
|
||||
render_pass.set_bind_group(2, &self.camera_buffer.bindgroup(), &[]);
|
||||
render_pass.set_bind_group(3, &self.light_buffers.bindgroup, &[]);
|
||||
render_pass.set_bind_group(4, &self.material_buffer.bindgroup_pair.as_ref().unwrap().bindgroup, &[]);
|
||||
|
||||
|
@ -625,6 +607,7 @@ impl Renderer for BasicRenderer {
|
|||
self.surface.configure(&self.device, &self.config);
|
||||
self.depth_buffer_texture = RenderTexture::create_depth_texture(&self.device, &self.config, "Depth Buffer Texture");
|
||||
self.inuse_camera.update_aspect_ratio(self.size);
|
||||
self.light_cull_compute.update_screen_size(new_size);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
const max_light_count: u32 = 16u;
|
||||
|
||||
const light_ty_directional = 0u;
|
||||
const light_ty_point = 1u;
|
||||
const light_ty_spot = 2u;
|
||||
|
||||
struct VertexInput {
|
||||
@location(0) position: vec3<f32>,
|
||||
@location(1) tex_coords: vec2<f32>,
|
||||
|
@ -16,57 +20,30 @@ struct VertexOutput {
|
|||
}
|
||||
|
||||
struct CameraUniform {
|
||||
view_mat: mat4x4<f32>,
|
||||
view_proj: mat4x4<f32>,
|
||||
view_pos: vec4<f32>,
|
||||
view_pos: vec3<f32>,
|
||||
};
|
||||
|
||||
struct PointLight {
|
||||
position: vec4<f32>,
|
||||
color: vec4<f32>,
|
||||
struct Light {
|
||||
light_ty: u32,
|
||||
enabled: u32,
|
||||
|
||||
intensity: f32,
|
||||
constant: f32,
|
||||
linear: f32,
|
||||
quadratic: f32,
|
||||
|
||||
ambient: f32,
|
||||
diffuse: f32,
|
||||
specular: f32,
|
||||
};
|
||||
|
||||
struct DirectionalLight {
|
||||
direction: vec3<f32>,
|
||||
color: vec3<f32>,
|
||||
|
||||
ambient: f32,
|
||||
diffuse: f32,
|
||||
specular: f32,
|
||||
};
|
||||
|
||||
struct SpotLight {
|
||||
position: vec3<f32>,
|
||||
direction: vec3<f32>,
|
||||
color: vec3<f32>,
|
||||
|
||||
cutoff: f32,
|
||||
outer_cutoff: f32,
|
||||
range: f32,
|
||||
intensity: f32,
|
||||
|
||||
constant: f32,
|
||||
linear: f32,
|
||||
quadratic: f32,
|
||||
|
||||
ambient: f32,
|
||||
diffuse: f32,
|
||||
specular: f32,
|
||||
spot_cutoff: f32,
|
||||
spot_outer_cutoff: f32,
|
||||
};
|
||||
|
||||
struct Lights {
|
||||
point_lights: array<PointLight, max_light_count>,
|
||||
point_light_count: u32,
|
||||
spot_lights: array<SpotLight, max_light_count>,
|
||||
spot_light_count: u32,
|
||||
directional_light: DirectionalLight,
|
||||
}
|
||||
light_count: u32,
|
||||
data: array<Light>,
|
||||
};
|
||||
|
||||
@group(1) @binding(0)
|
||||
var<uniform> u_model_transform: mat4x4<f32>;
|
||||
|
@ -77,7 +54,7 @@ var<uniform> u_model_normal_matrix: mat4x4<f32>;
|
|||
var<uniform> u_camera: CameraUniform;
|
||||
|
||||
@group(3) @binding(0)
|
||||
var<uniform> u_lights: Lights;
|
||||
var<storage> u_lights: Lights;
|
||||
|
||||
@vertex
|
||||
fn vs_main(
|
||||
|
@ -128,7 +105,21 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
|||
// this needs to be 0.0 for the math
|
||||
//u_lights.directional_light.direction.w = 0.0;
|
||||
|
||||
var light_res = blinn_phong_dir_light(in.world_position, in.world_normal, u_lights.directional_light, u_material, specular_color);
|
||||
var light_res = vec3<f32>(0.0);
|
||||
|
||||
for (var i = 0u; i < u_lights.light_count; i++) {
|
||||
var light = u_lights.data[i];
|
||||
|
||||
if (light.light_ty == light_ty_directional) {
|
||||
light_res += blinn_phong_dir_light(in.world_position, in.world_normal, light, u_material, specular_color);
|
||||
} else if (light.light_ty == light_ty_point) {
|
||||
light_res += blinn_phong_point_light(in.world_position, in.world_normal, light, u_material, specular_color);
|
||||
} else if (light.light_ty == light_ty_spot) {
|
||||
light_res += blinn_phong_spot_light(in.world_position, in.world_normal, light, u_material, specular_color);
|
||||
}
|
||||
}
|
||||
|
||||
/*var light_res = blinn_phong_dir_light(in.world_position, in.world_normal, u_lights.directional_light, u_material, specular_color);
|
||||
|
||||
for (var i = 0u; i < u_lights.point_light_count; i++) {
|
||||
light_res += blinn_phong_point_light(in.world_position, in.world_normal, u_lights.point_lights[i], u_material, specular_color);
|
||||
|
@ -136,14 +127,14 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
|||
|
||||
for (var i = 0u; i < u_lights.spot_light_count; i++) {
|
||||
light_res += blinn_phong_spot_light(in.world_position, in.world_normal, u_lights.spot_lights[i], u_material, specular_color);
|
||||
}
|
||||
}*/
|
||||
|
||||
let light_object_res = light_res * (object_color.xyz/* * u_material.diffuse.xyz*/);
|
||||
|
||||
return vec4<f32>(light_object_res, object_color.a);
|
||||
}
|
||||
|
||||
fn blinn_phong_dir_light(world_pos: vec3<f32>, world_norm: vec3<f32>, dir_light: DirectionalLight, material: Material, specular_factor: vec3<f32>) -> vec3<f32> {
|
||||
fn blinn_phong_dir_light(world_pos: vec3<f32>, world_norm: vec3<f32>, dir_light: Light, material: Material, specular_factor: vec3<f32>) -> vec3<f32> {
|
||||
let light_color = dir_light.color.xyz;
|
||||
let camera_view_pos = u_camera.view_pos.xyz;
|
||||
|
||||
|
@ -165,14 +156,14 @@ fn blinn_phong_dir_light(world_pos: vec3<f32>, world_norm: vec3<f32>, dir_light:
|
|||
var specular_color = specular_strength * (light_color * specular_factor);
|
||||
//// end of specular ////
|
||||
|
||||
ambient_color *= dir_light.ambient;
|
||||
/*ambient_color *= dir_light.ambient;
|
||||
diffuse_color *= dir_light.diffuse;
|
||||
specular_color *= dir_light.specular;
|
||||
specular_color *= dir_light.specular;*/
|
||||
|
||||
return ambient_color + diffuse_color + specular_color;
|
||||
}
|
||||
|
||||
fn blinn_phong_point_light(world_pos: vec3<f32>, world_norm: vec3<f32>, point_light: PointLight, material: Material, specular_factor: vec3<f32>) -> vec3<f32> {
|
||||
fn blinn_phong_point_light(world_pos: vec3<f32>, world_norm: vec3<f32>, point_light: Light, material: Material, specular_factor: vec3<f32>) -> vec3<f32> {
|
||||
let light_color = point_light.color.xyz;
|
||||
let light_pos = point_light.position.xyz;
|
||||
let camera_view_pos = u_camera.view_pos.xyz;
|
||||
|
@ -195,8 +186,14 @@ fn blinn_phong_point_light(world_pos: vec3<f32>, world_norm: vec3<f32>, point_li
|
|||
var specular_color = specular_strength * (light_color * specular_factor);
|
||||
//// end of specular ////
|
||||
|
||||
//// point light attenuation ////
|
||||
// TODO: Point light range
|
||||
let distance = length(light_pos - world_pos);
|
||||
// TODO: make smoothness in this a configurable value
|
||||
// 0.75 is the smoothness or falloff
|
||||
let attenuation = 1.0 - smoothstep(point_light.range * 0.75, point_light.range, distance);
|
||||
|
||||
//// point light attenuation ////
|
||||
/*let distance = length(light_pos - world_pos);
|
||||
let attenuation = 1.0 / (point_light.constant + point_light.linear * distance +
|
||||
point_light.quadratic * (distance * distance));
|
||||
|
||||
|
@ -204,58 +201,15 @@ fn blinn_phong_point_light(world_pos: vec3<f32>, world_norm: vec3<f32>, point_li
|
|||
|
||||
ambient_color *= point_light.ambient * attenuation;
|
||||
diffuse_color *= point_light.diffuse * attenuation;
|
||||
specular_color *= point_light.specular * attenuation;
|
||||
specular_color *= point_light.specular * attenuation;*/
|
||||
|
||||
ambient_color *= attenuation;
|
||||
diffuse_color *= attenuation;
|
||||
specular_color *= attenuation;
|
||||
|
||||
return (ambient_color + diffuse_color + specular_color) * point_light.intensity;
|
||||
}
|
||||
|
||||
fn blinn_phong_spot_light(world_pos: vec3<f32>, world_norm: vec3<f32>, spot_light: SpotLight, material: Material, specular_factor: vec3<f32>) -> vec3<f32> {
|
||||
let light_color = spot_light.color;//.xyz;
|
||||
let light_pos = spot_light.position.xyz;
|
||||
let camera_view_pos = u_camera.view_pos.xyz;
|
||||
|
||||
let light_dir = normalize(spot_light.position - world_pos);
|
||||
|
||||
|
||||
//if (theta > spot_light.cutoff) {
|
||||
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.cutoff - spot_light.outer_cutoff;
|
||||
let intensity = clamp((theta - spot_light.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 = 1.0 / (spot_light.constant + spot_light.linear * distance +
|
||||
spot_light.quadratic * (distance * distance));
|
||||
|
||||
ambient_color *= attenuation * intensity * spot_light.ambient;
|
||||
diffuse_color *= attenuation * intensity * spot_light.diffuse;
|
||||
specular_color *= attenuation * intensity * spot_light.specular;
|
||||
//// end of spot light attenuation ////
|
||||
|
||||
|
||||
return /*ambient_color +*/ diffuse_color + specular_color;
|
||||
/*} else {
|
||||
return vec3<f32>(0.0);
|
||||
}*/
|
||||
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
|
||||
}
|
|
@ -0,0 +1,248 @@
|
|||
const block_size: i32 = 16;
|
||||
const max_tile_visible_lights: u32 = 1024u;
|
||||
|
||||
const light_ty_directional = 0u;
|
||||
const light_ty_point = 1u;
|
||||
const light_ty_spot = 2u;
|
||||
|
||||
// Possible computer shader inputs:
|
||||
//
|
||||
// local_invocation_id
|
||||
// workgroup_id
|
||||
// global_invocation_id
|
||||
// num_workgroups
|
||||
// local_invocation_index
|
||||
|
||||
struct CameraUniform {
|
||||
view_mat: mat4x4<f32>,
|
||||
view_proj: mat4x4<f32>,
|
||||
view_pos: vec3<f32>,
|
||||
};
|
||||
|
||||
struct Light {
|
||||
light_ty: u32,
|
||||
enabled: u32,
|
||||
|
||||
position: vec3<f32>,
|
||||
direction: vec3<f32>,
|
||||
color: vec3<f32>,
|
||||
|
||||
range: f32,
|
||||
intensity: f32,
|
||||
|
||||
spot_cutoff: f32,
|
||||
spot_outer_cutoff: f32,
|
||||
};
|
||||
|
||||
struct Lights {
|
||||
light_count: u32,
|
||||
data: array<Light>,
|
||||
};
|
||||
|
||||
var<workgroup> wg_min_depth: atomic<u32>;
|
||||
var<workgroup> wg_max_depth: atomic<u32>;
|
||||
var<workgroup> wg_frustum_planes: array<vec4<f32>, 6>;
|
||||
|
||||
// index list of visible light sources for this tile
|
||||
var<workgroup> wg_visible_light_indices: array<u32, max_tile_visible_lights>;
|
||||
var<workgroup> wg_visible_light_count: atomic<u32>;
|
||||
|
||||
//var<workgroup> view_projection: mat4x4;
|
||||
|
||||
@group(0) @binding(0)
|
||||
var t_depthmap: texture_2d<f32>;
|
||||
@group(0) @binding(1)
|
||||
var s_depthmap: sampler;
|
||||
|
||||
@group(1) @binding(0)
|
||||
var<uniform> u_camera: CameraUniform;
|
||||
|
||||
@group(2) @binding(0)
|
||||
var<storage, read> u_lights: Lights;
|
||||
|
||||
@group(3) @binding(0)
|
||||
var<storage, read_write> u_light_indices: array<u32>;
|
||||
/*@group(3) @binding(1)
|
||||
var<uniform> u_light_grid: array<array<vec2<u32>>>;*/
|
||||
|
||||
@group(3) @binding(1)
|
||||
var t_light_grid: texture_storage_2d<rg32uint, read_write>; // rg32uint = vec2<u32> or vec4<u32>(r, g, 0.0, 1.0)
|
||||
|
||||
@group(4) @binding(0)
|
||||
var<uniform> u_screen_size: vec2<u32>;
|
||||
|
||||
@compute
|
||||
@workgroup_size(16, 16, 1)
|
||||
fn cs_main(
|
||||
@builtin(local_invocation_id) local_invocation_id: vec3<u32>,
|
||||
@builtin(workgroup_id) workgroup_id: vec3<u32>,
|
||||
@builtin(global_invocation_id) global_invocation_id: vec3<u32>,
|
||||
@builtin(num_workgroups) num_workgroups: vec3<u32>,
|
||||
@builtin(local_invocation_index) local_invocation_index: u32,
|
||||
) {
|
||||
//var location = vec2<i32>(global_invocation_id.xy);
|
||||
var item_id = vec2<i32>(local_invocation_id.xy);
|
||||
var tile_id = vec2<i32>(workgroup_id.xy);
|
||||
var tile_number = vec2<i32>(num_workgroups.xy);
|
||||
var index = tile_id.y * tile_number.x + tile_id.x;
|
||||
|
||||
// Initialize some shared global values for depth and light count
|
||||
if (local_invocation_index == 0u) {
|
||||
wg_min_depth = 0xFFFFFFFu;
|
||||
wg_max_depth = 0u;
|
||||
wg_visible_light_count = 0u;
|
||||
}
|
||||
|
||||
workgroupBarrier();
|
||||
|
||||
// step 1: calculate the minimum and maximum depth values for this tile (using the depth map)
|
||||
var tex_coord = vec2<f32>(global_invocation_id.xy);
|
||||
//var depth_float: f32 = textureSample(t_depthmap, s_depthmap, tex_coord).r;
|
||||
var depth_float = 0.0;
|
||||
// bitcast the floating depth to u32 for atomic comparisons between threads
|
||||
var depth_uint: u32 = bitcast<u32>(depth_float);
|
||||
|
||||
// step 2: find the minimum and max depth for this tile.
|
||||
// atomically update the workgroup depth
|
||||
atomicMin(&wg_min_depth, depth_uint);
|
||||
atomicMax(&wg_max_depth, depth_uint);
|
||||
|
||||
// convert them back into floats
|
||||
var min_depth: f32 = bitcast<f32>(wg_min_depth);
|
||||
var max_depth: f32 = bitcast<f32>(wg_max_depth);
|
||||
|
||||
workgroupBarrier();
|
||||
|
||||
// Create the frustum planes that will be used for this time
|
||||
if (local_invocation_index == 0u) {
|
||||
var negative_step = (2.0 * vec2<f32>(tile_id)) / vec2<f32>(tile_number);
|
||||
var positive_step = (2.0 * vec2<f32>(tile_id) + vec2<f32>(1.0, 1.0)) / vec2<f32>(tile_number);
|
||||
|
||||
// z in the vec4 is the distance from the center of the tile
|
||||
wg_frustum_planes[0] = vec4<f32>(1.0, 0.0, 0.0, 1.0 - negative_step.x); // left
|
||||
wg_frustum_planes[1] = vec4<f32>(-1.0, 0.0, 0.0, -1.0 + positive_step.x); // right
|
||||
wg_frustum_planes[2] = vec4<f32>(0.0, -1.0, 0.0, 1.0 - negative_step.y); // bottom
|
||||
wg_frustum_planes[3] = vec4<f32>(0.0, -1.0, 0.0, -1.0 + positive_step.y); // top
|
||||
wg_frustum_planes[4] = vec4<f32>(0.0, 0.0, -1.0, -min_depth); // near plane
|
||||
wg_frustum_planes[5] = vec4<f32>(0.0, 0.0, 1.0, max_depth); // far plane
|
||||
|
||||
// convert the side and top planes from clip to view space
|
||||
for (var i = 0u; i < 4u; i++) {
|
||||
wg_frustum_planes[i] *= u_camera.view_proj;
|
||||
wg_frustum_planes[i] /= length(wg_frustum_planes[i].xyz);
|
||||
}
|
||||
|
||||
// convert near and far planes from clip to view space
|
||||
wg_frustum_planes[4] *= u_camera.view_mat;
|
||||
wg_frustum_planes[4] /= length(wg_frustum_planes[4].xyz);
|
||||
wg_frustum_planes[5] *= u_camera.view_mat;
|
||||
wg_frustum_planes[5] /= length(wg_frustum_planes[5].xyz);
|
||||
}
|
||||
|
||||
workgroupBarrier();
|
||||
|
||||
// Step 3: cull lights
|
||||
|
||||
// Process the lights detecting which ones to cull for this tile.
|
||||
// Processes 256 lights simultaniously, each on a thread in the workgroup. Requires multiple
|
||||
// iterations for more lights.
|
||||
var thread_count = u32(block_size * block_size);
|
||||
var pass_count = (u_lights.light_count + thread_count - 1u) / thread_count;
|
||||
for (var i = 0u; i < pass_count; i++) {
|
||||
// find the light index to check on this thread, make sure we're not trying to test
|
||||
// for more lights than we have.
|
||||
var light_index = i * thread_count + local_invocation_index;
|
||||
if (light_index > u_lights.light_count) {
|
||||
break;
|
||||
}
|
||||
|
||||
var light = u_lights.data[light_index];
|
||||
var position = light.position;
|
||||
var radius = light.range;
|
||||
|
||||
if (light.light_ty != light_ty_directional
|
||||
&& sphere_inside_frustrum(wg_frustum_planes, position, radius)) {
|
||||
// TODO: add the light to the transparent geometry list
|
||||
|
||||
// TODO: spotlights
|
||||
if (!sphere_inside_plane(position, radius, wg_frustum_planes[4])) {
|
||||
var offset: u32 = wg_visible_light_count;
|
||||
|
||||
if (offset < max_tile_visible_lights) {
|
||||
atomicAdd(&wg_visible_light_count, 1u);
|
||||
wg_visible_light_indices[offset] = light_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
workgroupBarrier();
|
||||
|
||||
// Update the global memory with the visible light buffer.
|
||||
|
||||
// first update the light grid on the first thread
|
||||
if (local_invocation_index == 0u) {
|
||||
var offset = u32(index) * max_tile_visible_lights; // index in the global light list
|
||||
//t_light_grid[workgroup_id.x][workgroup_id.y] = vec2<f32>(offset, wg_visible_light_count);
|
||||
textureStore(t_light_grid, workgroup_id.xy, vec4<u32>(offset, wg_visible_light_count, 0u, 1u));
|
||||
|
||||
// TODO: update transparent light grid
|
||||
|
||||
/*var offset = index * max_tile_visible_lights; // position in the global light buffer
|
||||
// update the light
|
||||
for (var i = 0u; i < wg_visible_light_count; i++) {
|
||||
//u_visible_light_indices[offset + i] = wg_visible_light_indices[i];
|
||||
}
|
||||
|
||||
if (wg_visible_light_count != 1024) {
|
||||
// Mark the end of the visible lights for this tile
|
||||
u_visible_light_indices[offset + wg_visible_light_count] = -1;
|
||||
}*/
|
||||
}
|
||||
|
||||
workgroupBarrier();
|
||||
|
||||
// now update the light index list on all threads.
|
||||
var indices_offset = u32(index) * max_tile_visible_lights;
|
||||
//var pass_count = (wg_visible_light_count + thread_count - 1) / thread_count;
|
||||
for (var i = 0u; i < pass_count; i++) {
|
||||
// find the light index to check on this thread, make sure we're not trying to test
|
||||
// for more lights than we have.
|
||||
//var light_index: u32 = i * thread_count + local_invocation_index;
|
||||
/*if (light_index > u_lights.light_count) {
|
||||
u_visible_light_indices
|
||||
break;
|
||||
}*/
|
||||
|
||||
var offset = indices_offset + i;
|
||||
if (offset >= wg_visible_light_count) {
|
||||
// stop if we're over the over the amount of lights we saw
|
||||
break;
|
||||
}
|
||||
|
||||
u_light_indices[offset] = wg_visible_light_indices[i];
|
||||
}
|
||||
}
|
||||
|
||||
fn sphere_inside_frustrum(frustum: array<vec4<f32>, 6>, sphere_origin: vec3<f32>, radius: f32) -> bool {
|
||||
// to be able to index this array with a non-const value,
|
||||
// it must be defined as a var
|
||||
var frustum_v = frustum;
|
||||
|
||||
// only check the sides of the frustum
|
||||
for (var i = 0u; i < 4u; i++) {
|
||||
if (!sphere_inside_plane(sphere_origin, radius, frustum_v[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Check if the sphere is fully behind (i.e., inside the negative half-space of) a plane.
|
||||
///
|
||||
/// Source: Real-time collision detection, Christer Ericson (2005)
|
||||
/// (https://www.3dgep.com/forward-plus/#light-culling-compute-shader)
|
||||
fn sphere_inside_plane(sphere_origin: vec3<f32>, radius: f32, plane: vec4<f32>) -> bool {
|
||||
return dot(plane.xyz, sphere_origin) - plane.w < -radius;
|
||||
}
|
|
@ -297,6 +297,66 @@ impl RenderTexture {
|
|||
}
|
||||
}
|
||||
|
||||
/// Creates a bind group for this texture and returns a borrow to the [`BindGroupPair`]
|
||||
///
|
||||
/// This does not create a new bind group if the texture already has one.
|
||||
/// The view dimension will be the same as the texture dimension.
|
||||
pub fn create_bind_group(&mut self, device: &wgpu::Device) -> &BindGroupPair {
|
||||
if self.bindgroup_pair.is_some() {
|
||||
// could not use an if-let here due to the borrow checker thinking
|
||||
// that there was multiple borrows to self.bindgroup_pair
|
||||
return self.bindgroup_pair.as_ref().unwrap();
|
||||
}
|
||||
|
||||
let view_dim = match self.inner_texture.dimension() {
|
||||
wgpu::TextureDimension::D1 => wgpu::TextureViewDimension::D1,
|
||||
wgpu::TextureDimension::D2 => wgpu::TextureViewDimension::D2,
|
||||
wgpu::TextureDimension::D3 => wgpu::TextureViewDimension::D3,
|
||||
};
|
||||
|
||||
let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::COMPUTE | wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Texture {
|
||||
sample_type: wgpu::TextureSampleType::Depth,
|
||||
view_dimension: view_dim,
|
||||
multisampled: false
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStages::COMPUTE | wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Comparison),
|
||||
count: None,
|
||||
}
|
||||
],
|
||||
label: Some("BGL_Texture"),
|
||||
});
|
||||
|
||||
let bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::TextureView(&self.view)
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::Sampler(&self.sampler)
|
||||
}
|
||||
],
|
||||
label: Some("BG_Texture"),
|
||||
});
|
||||
|
||||
let pair = BindGroupPair::new(bg, layout);
|
||||
|
||||
self.bindgroup_pair = Some(pair);
|
||||
self.bindgroup_pair.as_ref().unwrap()
|
||||
}
|
||||
|
||||
/// Returns the bind group stored inside the bind group pair.
|
||||
///
|
||||
/// Panics:
|
||||
|
|
Loading…
Reference in New Issue