From 249ab27777553e2609c5c2d6a9e0b6b8a3bd6420 Mon Sep 17 00:00:00 2001 From: Jarrod Doyle Date: Mon, 24 Apr 2023 13:52:38 +0100 Subject: [PATCH] Make Renderer a trait and implement it for VoxelRenderer --- assets/shaders/voxel_volume.wgsl | 2 +- src/core/app.rs | 10 +- src/main.rs | 1 + src/render/renderer.rs | 201 +------------------------------ src/voxel.rs | 3 + src/voxel/voxel_renderer.rs | 197 ++++++++++++++++++++++++++++++ 6 files changed, 213 insertions(+), 201 deletions(-) create mode 100644 src/voxel.rs create mode 100644 src/voxel/voxel_renderer.rs diff --git a/assets/shaders/voxel_volume.wgsl b/assets/shaders/voxel_volume.wgsl index 4586434..04433a9 100644 --- a/assets/shaders/voxel_volume.wgsl +++ b/assets/shaders/voxel_volume.wgsl @@ -1,7 +1,7 @@ @group(0) @binding(0) var output: texture_storage_2d; +@group(0) @binding(1) var camera: Camera; @group(1) @binding(0) var voxels_t: texture_3d; @group(1) @binding(1) var voxels_s: sampler; -@group(2) @binding(0) var camera: Camera; struct Camera { projection: mat4x4, diff --git a/src/core/app.rs b/src/core/app.rs index b590e29..97e4e95 100644 --- a/src/core/app.rs +++ b/src/core/app.rs @@ -6,7 +6,10 @@ use winit::{ }; use super::camera; -use crate::render; +use crate::{ + render::{self, Renderer}, + voxel, +}; pub struct App { window: winit::window::Window, @@ -75,7 +78,8 @@ impl App { .with_entry(camera_controller.get_buffer().as_entire_binding()) .build(&self.render_ctx); - let renderer = render::Renderer::new(&self.render_ctx, &camera_bind_group_layout); + // let renderer = render::Renderer::new(&self.render_ctx, &camera_bind_group_layout); + let renderer = voxel::VoxelRenderer::new(&self.render_ctx, &camera_controller); let mut last_render_time = Instant::now(); self.event_loop @@ -98,7 +102,7 @@ impl App { last_render_time = now; camera_controller.update(dt); camera_controller.update_buffer(&self.render_ctx); - renderer.render(&self.render_ctx, &camera_bind_group); + renderer.render(&self.render_ctx); } _ => {} }); diff --git a/src/main.rs b/src/main.rs index 9459ea1..5a4ade1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ mod core; mod render; +mod voxel; fn main() { env_logger::init(); diff --git a/src/render/renderer.rs b/src/render/renderer.rs index 0bca012..4f017aa 100644 --- a/src/render/renderer.rs +++ b/src/render/renderer.rs @@ -1,199 +1,6 @@ -use super::{BindGroupBuilder, BindGroupLayoutBuilder, Context, Texture, TextureBuilder}; +use std::time::Duration; -pub struct Renderer { - clear_color: wgpu::Color, - compute_pipeline: wgpu::ComputePipeline, - compute_bind_group: wgpu::BindGroup, - render_pipeline: wgpu::RenderPipeline, - render_texture: Texture, - voxel_texture: Texture, -} - -impl Renderer { - pub fn new(context: &Context, camera_bind_group_layout: &wgpu::BindGroupLayout) -> Self { - log::info!("Creating render shader..."); - let shader_descriptor = wgpu::include_wgsl!("../../assets/shaders/shader.wgsl"); - let shader = context.device.create_shader_module(shader_descriptor); - - log::info!("Creating render texture..."); - let render_texture = TextureBuilder::new() - .with_size(context.size.width, context.size.height, 1) - .with_format(wgpu::TextureFormat::Rgba8Unorm) - .with_usage( - wgpu::TextureUsages::TEXTURE_BINDING - | wgpu::TextureUsages::COPY_DST - | wgpu::TextureUsages::STORAGE_BINDING, - ) - .with_shader_visibility(wgpu::ShaderStages::FRAGMENT | wgpu::ShaderStages::COMPUTE) - .build(&context); - - log::info!("Creating voxel volume texture..."); - let voxel_texture = TextureBuilder::new() - .with_size(8, 8, 8) - .with_dimension(wgpu::TextureDimension::D3) - .with_format(wgpu::TextureFormat::Rgba8Unorm) - .with_usage( - wgpu::TextureUsages::TEXTURE_BINDING - | wgpu::TextureUsages::COPY_DST - | wgpu::TextureUsages::STORAGE_BINDING, - ) - .with_shader_visibility(wgpu::ShaderStages::COMPUTE) - .build(&context); - - let data_len = 8 * 8 * 8 * 4; - let mut data: Vec = Vec::with_capacity(data_len.try_into().unwrap()); - for z in 0..8 { - for y in 0..8 { - for x in 0..8 { - let pos = glam::vec3(x as f32, y as f32, z as f32) - glam::vec3(3.5, 3.5, 3.5); - if pos.length_squared() <= (u32::pow(4, 2) as f32) { - data.push(((x + 1) * 32 - 1) as u8); - data.push(((y + 1) * 32 - 1) as u8); - data.push(((z + 1) * 32 - 1) as u8); - data.push(255u8); - } else { - data.push(0u8); - data.push(0u8); - data.push(0u8); - data.push(0u8); - } - } - } - } - voxel_texture.update(&context, &data); - - log::info!("Creating camera..."); - - log::info!("Creating render pipeline..."); - let render_pipeline = - context - .device - .create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: None, - layout: Some(&context.device.create_pipeline_layout( - &wgpu::PipelineLayoutDescriptor { - label: Some("draw"), - bind_group_layouts: &[&render_texture.bind_group_layout], - push_constant_ranges: &[], - }, - )), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vertex", - buffers: &[], - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "fragment", - targets: &[Some(context.surface_config.format.into())], - }), - primitive: wgpu::PrimitiveState::default(), - depth_stencil: None, - multisample: wgpu::MultisampleState::default(), - multiview: None, - }); - - log::info!("Creating compute pipeline..."); - let cs_descriptor = wgpu::include_wgsl!("../../assets/shaders/voxel_volume.wgsl"); - let cs = context.device.create_shader_module(cs_descriptor); - let compute_layout = BindGroupLayoutBuilder::new() - .with_entry( - wgpu::ShaderStages::COMPUTE, - wgpu::BindingType::StorageTexture { - access: wgpu::StorageTextureAccess::WriteOnly, - format: render_texture.attributes.format, - view_dimension: wgpu::TextureViewDimension::D2, - }, - None, - ) - .build(context); - let compute_bind_group = BindGroupBuilder::new() - .with_layout(&compute_layout) - .with_entry(wgpu::BindingResource::TextureView(&render_texture.view)) - .build(context); - let compute_pipeline = - context - .device - .create_compute_pipeline(&wgpu::ComputePipelineDescriptor { - label: None, - layout: Some(&context.device.create_pipeline_layout( - &wgpu::PipelineLayoutDescriptor { - label: Some("compute"), - bind_group_layouts: &[ - &compute_layout, - &voxel_texture.bind_group_layout, - &camera_bind_group_layout, - ], - push_constant_ranges: &[], - }, - )), - module: &cs, - entry_point: "compute", - }); - - let clear_color = wgpu::Color { - r: 255.0 / 255.0, - g: 216.0 / 255.0, - b: 102.0 / 255.0, - a: 1.0, - }; - - Self { - clear_color, - compute_pipeline, - compute_bind_group, - render_pipeline, - render_texture, - voxel_texture, - } - } - - pub fn render(&self, context: &Context, camera_bind_group: &wgpu::BindGroup) { - let frame = context.surface.get_current_texture().unwrap(); - let view = frame - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); - - let mut encoder = context - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); - - let size = self.render_texture.attributes.size; - let mut compute_pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor::default()); - compute_pass.set_pipeline(&self.compute_pipeline); - compute_pass.set_bind_group(0, &self.compute_bind_group, &[]); - compute_pass.set_bind_group(1, &self.voxel_texture.bind_group, &[]); - compute_pass.set_bind_group(2, camera_bind_group, &[]); - compute_pass.dispatch_workgroups(size.width / 8, size.height / 8, 1); - drop(compute_pass); - - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("Render Pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(self.clear_color), - store: true, - }, - })], - depth_stencil_attachment: None, - }); - render_pass.set_pipeline(&self.render_pipeline); - render_pass.set_bind_group(0, &self.render_texture.bind_group, &[]); - render_pass.draw(0..6, 0..1); - - drop(render_pass); - - context.queue.submit(Some(encoder.finish())); - frame.present(); - - // let mut command_buffers = Vec::with_capacity(encoders.len()); - // for encoder in encoders { - // command_buffers.push(encoder.finish()); - // } - - // context.queue.submit(command_buffers); - // frame.present(); - } +pub trait Renderer { + fn update(&self, dt: &Duration, context: &super::Context); + fn render(&self, context: &super::Context); } diff --git a/src/voxel.rs b/src/voxel.rs new file mode 100644 index 0000000..c4e5799 --- /dev/null +++ b/src/voxel.rs @@ -0,0 +1,3 @@ +mod voxel_renderer; + +pub use voxel_renderer::*; diff --git a/src/voxel/voxel_renderer.rs b/src/voxel/voxel_renderer.rs new file mode 100644 index 0000000..e563290 --- /dev/null +++ b/src/voxel/voxel_renderer.rs @@ -0,0 +1,197 @@ +use std::time::Duration; + +use crate::{core, render}; + +#[derive(Debug)] +pub struct VoxelRenderer { + clear_color: wgpu::Color, + render_texture: render::Texture, + render_pipeline: wgpu::RenderPipeline, + voxel_texture: render::Texture, + compute_pipeline: wgpu::ComputePipeline, + compute_bind_group: wgpu::BindGroup, +} + +impl VoxelRenderer { + pub fn new(context: &render::Context, camera_controller: &core::CameraController) -> Self { + log::info!("Creating render shader..."); + let shader_descriptor = wgpu::include_wgsl!("../../assets/shaders/shader.wgsl"); + let shader = context.device.create_shader_module(shader_descriptor); + + log::info!("Creating render texture..."); + let render_texture = render::TextureBuilder::new() + .with_size(context.size.width, context.size.height, 1) + .with_format(wgpu::TextureFormat::Rgba8Unorm) + .with_usage( + wgpu::TextureUsages::TEXTURE_BINDING + | wgpu::TextureUsages::COPY_DST + | wgpu::TextureUsages::STORAGE_BINDING, + ) + .with_shader_visibility(wgpu::ShaderStages::FRAGMENT | wgpu::ShaderStages::COMPUTE) + .build(&context); + + log::info!("Creating render pipeline..."); + let render_pipeline = + context + .device + .create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: None, + layout: Some(&context.device.create_pipeline_layout( + &wgpu::PipelineLayoutDescriptor { + label: Some("draw"), + bind_group_layouts: &[&render_texture.bind_group_layout], + push_constant_ranges: &[], + }, + )), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vertex", + buffers: &[], + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fragment", + targets: &[Some(context.surface_config.format.into())], + }), + primitive: wgpu::PrimitiveState::default(), + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + multiview: None, + }); + + log::info!("Creating voxel volume texture..."); + let voxel_texture = render::TextureBuilder::new() + .with_size(8, 8, 8) + .with_dimension(wgpu::TextureDimension::D3) + .with_format(wgpu::TextureFormat::Rgba8Unorm) + .with_usage( + wgpu::TextureUsages::TEXTURE_BINDING + | wgpu::TextureUsages::COPY_DST + | wgpu::TextureUsages::STORAGE_BINDING, + ) + .with_shader_visibility(wgpu::ShaderStages::COMPUTE) + .build(&context); + + let data_len = 8 * 8 * 8 * 4; + let mut data: Vec = Vec::with_capacity(data_len.try_into().unwrap()); + for z in 0..8 { + for y in 0..8 { + for x in 0..8 { + let pos = glam::vec3(x as f32, y as f32, z as f32) - glam::vec3(3.5, 3.5, 3.5); + if pos.length_squared() <= (u32::pow(4, 2) as f32) { + data.push(((x + 1) * 32 - 1) as u8); + data.push(((y + 1) * 32 - 1) as u8); + data.push(((z + 1) * 32 - 1) as u8); + data.push(255u8); + } else { + data.push(0u8); + data.push(0u8); + data.push(0u8); + data.push(0u8); + } + } + } + } + voxel_texture.update(&context, &data); + + log::info!("Creating compute pipeline..."); + let cs_descriptor = wgpu::include_wgsl!("../../assets/shaders/voxel_volume.wgsl"); + let cs = context.device.create_shader_module(cs_descriptor); + let compute_layout = render::BindGroupLayoutBuilder::new() + .with_entry( + wgpu::ShaderStages::COMPUTE, + wgpu::BindingType::StorageTexture { + access: wgpu::StorageTextureAccess::WriteOnly, + format: render_texture.attributes.format, + view_dimension: wgpu::TextureViewDimension::D2, + }, + None, + ) + .with_entry( + wgpu::ShaderStages::COMPUTE, + wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + None, + ) + .build(context); + let compute_bind_group = render::BindGroupBuilder::new() + .with_layout(&compute_layout) + .with_entry(wgpu::BindingResource::TextureView(&render_texture.view)) + .with_entry(camera_controller.get_buffer().as_entire_binding()) + .build(context); + let compute_pipeline = + context + .device + .create_compute_pipeline(&wgpu::ComputePipelineDescriptor { + label: None, + layout: Some(&context.device.create_pipeline_layout( + &wgpu::PipelineLayoutDescriptor { + label: Some("compute"), + bind_group_layouts: &[ + &compute_layout, + &voxel_texture.bind_group_layout, + ], + push_constant_ranges: &[], + }, + )), + module: &cs, + entry_point: "compute", + }); + + Self { + clear_color: wgpu::Color::BLACK, + render_texture, + render_pipeline, + voxel_texture, + compute_pipeline, + compute_bind_group, + } + } +} + +impl render::Renderer for VoxelRenderer { + fn render(&self, context: &render::Context) { + let frame = context.surface.get_current_texture().unwrap(); + let view = frame + .texture + .create_view(&wgpu::TextureViewDescriptor::default()); + + let mut encoder = context + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); + + let size = self.render_texture.attributes.size; + let mut compute_pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor::default()); + compute_pass.set_pipeline(&self.compute_pipeline); + compute_pass.set_bind_group(0, &self.compute_bind_group, &[]); + compute_pass.set_bind_group(1, &self.voxel_texture.bind_group, &[]); + compute_pass.dispatch_workgroups(size.width / 8, size.height / 8, 1); + drop(compute_pass); + + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Render Pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(self.clear_color), + store: true, + }, + })], + depth_stencil_attachment: None, + }); + render_pass.set_pipeline(&self.render_pipeline); + render_pass.set_bind_group(0, &self.render_texture.bind_group, &[]); + render_pass.draw(0..6, 0..1); + + drop(render_pass); + + context.queue.submit(Some(encoder.finish())); + frame.present(); + } + + fn update(&self, dt: &Duration, context: &render::Context) {} +}