Make Renderer a trait and implement it for VoxelRenderer

This commit is contained in:
Jarrod Doyle 2023-04-24 13:52:38 +01:00
parent a0df8d2bfc
commit 249ab27777
Signed by: Jayrude
GPG Key ID: 38B57B16E7C0ADF7
6 changed files with 213 additions and 201 deletions

View File

@ -1,7 +1,7 @@
@group(0) @binding(0) var output: texture_storage_2d<rgba8unorm, write>; @group(0) @binding(0) var output: texture_storage_2d<rgba8unorm, write>;
@group(0) @binding(1) var<uniform> camera: Camera;
@group(1) @binding(0) var voxels_t: texture_3d<f32>; @group(1) @binding(0) var voxels_t: texture_3d<f32>;
@group(1) @binding(1) var voxels_s: sampler; @group(1) @binding(1) var voxels_s: sampler;
@group(2) @binding(0) var<uniform> camera: Camera;
struct Camera { struct Camera {
projection: mat4x4<f32>, projection: mat4x4<f32>,

View File

@ -6,7 +6,10 @@ use winit::{
}; };
use super::camera; use super::camera;
use crate::render; use crate::{
render::{self, Renderer},
voxel,
};
pub struct App { pub struct App {
window: winit::window::Window, window: winit::window::Window,
@ -75,7 +78,8 @@ impl App {
.with_entry(camera_controller.get_buffer().as_entire_binding()) .with_entry(camera_controller.get_buffer().as_entire_binding())
.build(&self.render_ctx); .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(); let mut last_render_time = Instant::now();
self.event_loop self.event_loop
@ -98,7 +102,7 @@ impl App {
last_render_time = now; last_render_time = now;
camera_controller.update(dt); camera_controller.update(dt);
camera_controller.update_buffer(&self.render_ctx); camera_controller.update_buffer(&self.render_ctx);
renderer.render(&self.render_ctx, &camera_bind_group); renderer.render(&self.render_ctx);
} }
_ => {} _ => {}
}); });

View File

@ -1,5 +1,6 @@
mod core; mod core;
mod render; mod render;
mod voxel;
fn main() { fn main() {
env_logger::init(); env_logger::init();

View File

@ -1,199 +1,6 @@
use super::{BindGroupBuilder, BindGroupLayoutBuilder, Context, Texture, TextureBuilder}; use std::time::Duration;
pub struct Renderer { pub trait Renderer {
clear_color: wgpu::Color, fn update(&self, dt: &Duration, context: &super::Context);
compute_pipeline: wgpu::ComputePipeline, fn render(&self, context: &super::Context);
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<u8> = 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();
}
} }

3
src/voxel.rs Normal file
View File

@ -0,0 +1,3 @@
mod voxel_renderer;
pub use voxel_renderer::*;

197
src/voxel/voxel_renderer.rs Normal file
View File

@ -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<u8> = 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) {}
}