From 2c576189258b057ba2b34528610d080d9e85a7e8 Mon Sep 17 00:00:00 2001 From: Ben Hansen Date: Mon, 30 Dec 2019 17:04:38 -0700 Subject: [PATCH] created examples for loop and array --- Cargo.lock | 11 + code/beginner/tutorial7-instancing/Cargo.toml | 23 + .../tutorial7-instancing/src/arrays.rs | 617 +++++++++++++++++ .../tutorial7-instancing/src/arrays.vert | 17 + .../tutorial7-instancing/src/challenge.rs | 609 +++++++++++++++++ .../tutorial7-instancing/src/happy-tree.png | Bin 0 -> 28134 bytes .../beginner/tutorial7-instancing/src/main.rs | 624 +++++++++++++++++ .../tutorial7-instancing/src/naive.rs | 644 ++++++++++++++++++ .../tutorial7-instancing/src/naive.vert | 17 + .../tutorial7-instancing/src/shader.frag | 11 + .../tutorial7-instancing/src/shader.vert | 17 + docs/.vuepress/config.js | 1 + docs/beginner/tutorial7-instancing/README.md | 252 +++++++ docs/beginner/tutorial7-instancing/forest.png | Bin 0 -> 127062 bytes 14 files changed, 2843 insertions(+) create mode 100644 code/beginner/tutorial7-instancing/Cargo.toml create mode 100644 code/beginner/tutorial7-instancing/src/arrays.rs create mode 100644 code/beginner/tutorial7-instancing/src/arrays.vert create mode 100644 code/beginner/tutorial7-instancing/src/challenge.rs create mode 100644 code/beginner/tutorial7-instancing/src/happy-tree.png create mode 100644 code/beginner/tutorial7-instancing/src/main.rs create mode 100644 code/beginner/tutorial7-instancing/src/naive.rs create mode 100644 code/beginner/tutorial7-instancing/src/naive.vert create mode 100644 code/beginner/tutorial7-instancing/src/shader.frag create mode 100644 code/beginner/tutorial7-instancing/src/shader.vert create mode 100644 docs/beginner/tutorial7-instancing/README.md create mode 100644 docs/beginner/tutorial7-instancing/forest.png diff --git a/Cargo.lock b/Cargo.lock index 50f6e993..6b765ace 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1393,6 +1393,17 @@ dependencies = [ "winit 0.20.0-alpha4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tutorial7-instancing" +version = "0.1.0" +dependencies = [ + "cgmath 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glsl-to-spirv 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "image 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)", + "wgpu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winit 0.20.0-alpha4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "typenum" version = "1.11.2" diff --git a/code/beginner/tutorial7-instancing/Cargo.toml b/code/beginner/tutorial7-instancing/Cargo.toml new file mode 100644 index 00000000..5005f21a --- /dev/null +++ b/code/beginner/tutorial7-instancing/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "tutorial7-instancing" +version = "0.1.0" +authors = ["Ben Hansen "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +image = "0.22" +winit = "0.20.0-alpha4" +glsl-to-spirv = "0.1" +wgpu = "0.4" +cgmath = "0.17" + +[[bin]] +name = "tutorial7-instancing" +path = "src/main.rs" + + +[[bin]] +name = "tutorial7-challenge" +path = "src/challenge.rs" \ No newline at end of file diff --git a/code/beginner/tutorial7-instancing/src/arrays.rs b/code/beginner/tutorial7-instancing/src/arrays.rs new file mode 100644 index 00000000..659634ca --- /dev/null +++ b/code/beginner/tutorial7-instancing/src/arrays.rs @@ -0,0 +1,617 @@ +use winit::{ + event::*, + event_loop::{EventLoop, ControlFlow}, + window::{Window, WindowBuilder}, +}; +use cgmath::prelude::*; + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +struct Vertex { + position: [f32; 3], + tex_coords: [f32; 2], +} + +impl Vertex { + fn desc<'a>() -> wgpu::VertexBufferDescriptor<'a> { + use std::mem; + wgpu::VertexBufferDescriptor { + stride: mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::InputStepMode::Vertex, + attributes: &[ + wgpu::VertexAttributeDescriptor { + offset: 0, + shader_location: 0, + format: wgpu::VertexFormat::Float3, + }, + wgpu::VertexAttributeDescriptor { + offset: mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, + shader_location: 1, + format: wgpu::VertexFormat::Float2, + }, + ] + } + } +} + +const VERTICES: &[Vertex] = &[ + Vertex { position: [-0.0868241, -0.49240386, 0.0], tex_coords: [1.0 - 0.4131759, 1.0 - 0.00759614], }, // A + Vertex { position: [-0.49513406, -0.06958647, 0.0], tex_coords: [1.0 - 0.0048659444, 1.0 - 0.43041354], }, // B + Vertex { position: [-0.21918549, 0.44939706, 0.0], tex_coords: [1.0 - 0.28081453, 1.0 - 0.949397057], }, // C + Vertex { position: [0.35966998, 0.3473291, 0.0], tex_coords: [1.0 - 0.85967, 1.0 - 0.84732911], }, // D + Vertex { position: [0.44147372, -0.2347359, 0.0], tex_coords: [1.0 - 0.9414737, 1.0 - 0.2652641], }, // E +]; + +const INDICES: &[u16] = &[ + 0, 1, 4, + 1, 2, 4, + 2, 3, 4, +]; + +#[cfg_attr(rustfmt, rustfmt_skip)] +pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4 = cgmath::Matrix4::new( + 1.0, 0.0, 0.0, 0.0, + 0.0, -1.0, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.0, + 0.0, 0.0, 0.5, 1.0, +); + +const NUM_INSTANCES_PER_ROW: u32 = 10; +const NUM_INSTANCES: u32 = NUM_INSTANCES_PER_ROW * NUM_INSTANCES_PER_ROW; +const INSTANCE_DISPLACEMENT: cgmath::Vector3 = cgmath::Vector3::new(NUM_INSTANCES_PER_ROW as f32 * 0.5, 0.0, NUM_INSTANCES_PER_ROW as f32 * 0.5); + + +struct Camera { + eye: cgmath::Point3, + target: cgmath::Point3, + up: cgmath::Vector3, + aspect: f32, + fovy: f32, + znear: f32, + zfar: f32, +} + +impl Camera { + fn build_view_projection_matrix(&self) -> cgmath::Matrix4 { + let view = cgmath::Matrix4::look_at(self.eye, self.target, self.up); + let proj = cgmath::perspective(cgmath::Deg(self.fovy), self.aspect, self.znear, self.zfar); + return proj * view; + } +} + +#[repr(C)] +#[derive(Copy, Clone)] +struct Uniforms { + view_proj: cgmath::Matrix4, + model: [cgmath::Matrix4; NUM_INSTANCES as usize], +} + +impl Uniforms { + fn new() -> Self { + Self { + view_proj: cgmath::Matrix4::identity(), + model: [cgmath::Matrix4::identity(); NUM_INSTANCES as usize], + } + } + + fn update_view_proj(&mut self, camera: &Camera) { + self.view_proj = OPENGL_TO_WGPU_MATRIX * camera.build_view_projection_matrix(); + } +} + +struct CameraController { + speed: f32, + is_up_pressed: bool, + is_down_pressed: bool, + is_forward_pressed: bool, + is_backward_pressed: bool, + is_left_pressed: bool, + is_right_pressed: bool, +} + +impl CameraController { + fn new(speed: f32) -> Self { + Self { + speed, + is_up_pressed: false, + is_down_pressed: false, + is_forward_pressed: false, + is_backward_pressed: false, + is_left_pressed: false, + is_right_pressed: false, + } + } + + fn process_events(&mut self, event: &WindowEvent) -> bool { + match event { + WindowEvent::KeyboardInput { + input: KeyboardInput { + state, + virtual_keycode: Some(keycode), + .. + }, + .. + } => { + let is_pressed = *state == ElementState::Pressed; + match keycode { + VirtualKeyCode::Space => { + self.is_up_pressed = is_pressed; + true + } + VirtualKeyCode::LShift => { + self.is_down_pressed = is_pressed; + true + } + VirtualKeyCode::W | VirtualKeyCode::Up => { + self.is_forward_pressed = is_pressed; + true + } + VirtualKeyCode::A | VirtualKeyCode::Left => { + self.is_left_pressed = is_pressed; + true + } + VirtualKeyCode::S | VirtualKeyCode::Down => { + self.is_backward_pressed = is_pressed; + true + } + VirtualKeyCode::D | VirtualKeyCode::Right => { + self.is_right_pressed = is_pressed; + true + } + _ => false, + } + } + _ => false, + } + } + + fn update_camera(&self, camera: &mut Camera) { + let forward = (camera.target - camera.eye).normalize(); + + if self.is_forward_pressed { + camera.eye += forward * self.speed; + } + if self.is_backward_pressed { + camera.eye -= forward * self.speed; + } + + let right = forward.cross(camera.up); + + if self.is_right_pressed { + camera.eye += right * self.speed; + } + if self.is_left_pressed { + camera.eye -= right * self.speed; + } + } +} + +struct Instance { + position: cgmath::Vector3, + rotation: cgmath::Quaternion, +} + +impl Instance { + fn to_matrix(&self) -> cgmath::Matrix4 { + cgmath::Matrix4::from_translation(self.position) * cgmath::Matrix4::from(self.rotation) + } +} + +struct State { + surface: wgpu::Surface, + device: wgpu::Device, + queue: wgpu::Queue, + sc_desc: wgpu::SwapChainDescriptor, + swap_chain: wgpu::SwapChain, + + render_pipeline: wgpu::RenderPipeline, + + vertex_buffer: wgpu::Buffer, + index_buffer: wgpu::Buffer, + num_indices: u32, + + diffuse_texture: wgpu::Texture, + diffuse_texture_view: wgpu::TextureView, + diffuse_sampler: wgpu::Sampler, + diffuse_bind_group: wgpu::BindGroup, + + camera: Camera, + camera_controller: CameraController, + uniforms: Uniforms, + uniform_buffer: wgpu::Buffer, + uniform_bind_group: wgpu::BindGroup, + + hidpi_factor: f64, + size: winit::dpi::LogicalSize, + + instances: Vec, +} + +impl State { + fn new(window: &Window) -> Self { + let hidpi_factor = window.hidpi_factor(); + let size = window.inner_size(); + let physical_size = size.to_physical(hidpi_factor); + + let surface = wgpu::Surface::create(window); + + let adapter = wgpu::Adapter::request(&Default::default()).unwrap(); + + let (device, mut queue) = adapter.request_device(&Default::default()); + + let sc_desc = wgpu::SwapChainDescriptor { + usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, + format: wgpu::TextureFormat::Bgra8UnormSrgb, + width: physical_size.width.round() as u32, + height: physical_size.height.round() as u32, + present_mode: wgpu::PresentMode::Vsync, + }; + let swap_chain = device.create_swap_chain(&surface, &sc_desc); + + let diffuse_bytes = include_bytes!("happy-tree.png"); + let diffuse_image = image::load_from_memory(diffuse_bytes).unwrap(); + let diffuse_rgba = diffuse_image.as_rgba8().unwrap(); + + use image::GenericImageView; + let dimensions = diffuse_image.dimensions(); + + let size3d = wgpu::Extent3d { + width: dimensions.0, + height: dimensions.1, + depth: 1, + }; + let diffuse_texture = device.create_texture(&wgpu::TextureDescriptor { + size: size3d, + array_layer_count: 1, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, + }); + + let diffuse_buffer = device + .create_buffer_mapped(diffuse_rgba.len(), wgpu::BufferUsage::COPY_SRC) + .fill_from_slice(&diffuse_rgba); + + let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + todo: 0, + }); + + encoder.copy_buffer_to_texture( + wgpu::BufferCopyView { + buffer: &diffuse_buffer, + offset: 0, + row_pitch: 4 * dimensions.0, + image_height: dimensions.1, + }, + wgpu::TextureCopyView { + texture: &diffuse_texture, + mip_level: 0, + array_layer: 0, + origin: wgpu::Origin3d::ZERO, + }, + size3d, + ); + + queue.submit(&[encoder.finish()]); + + let diffuse_texture_view = diffuse_texture.create_default_view(); + let diffuse_sampler = device.create_sampler(&wgpu::SamplerDescriptor { + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Nearest, + mipmap_filter: wgpu::FilterMode::Nearest, + lod_min_clamp: -100.0, + lod_max_clamp: 100.0, + compare_function: wgpu::CompareFunction::Always, + }); + + let texture_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + bindings: &[ + wgpu::BindGroupLayoutBinding { + binding: 0, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::SampledTexture { + multisampled: false, + dimension: wgpu::TextureViewDimension::D2, + }, + }, + wgpu::BindGroupLayoutBinding { + binding: 1, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Sampler, + }, + ], + }); + + let diffuse_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &texture_bind_group_layout, + bindings: &[ + wgpu::Binding { + binding: 0, + resource: wgpu::BindingResource::TextureView(&diffuse_texture_view), + }, + wgpu::Binding { + binding: 1, + resource: wgpu::BindingResource::Sampler(&diffuse_sampler), + } + ], + }); + + let camera = Camera { + eye: (0.0, 5.0, -10.0).into(), + target: (0.0, 0.0, 0.0).into(), + up: cgmath::Vector3::unit_y(), + aspect: sc_desc.width as f32 / sc_desc.height as f32, + fovy: 45.0, + znear: 0.1, + zfar: 100.0, + }; + let camera_controller = CameraController::new(0.2); + + let mut uniforms = Uniforms::new(); + uniforms.update_view_proj(&camera); + + let uniform_buffer = device + .create_buffer_mapped(1, wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST) + .fill_from_slice(&[uniforms]); + + let uniform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + bindings: &[ + wgpu::BindGroupLayoutBinding { + binding: 0, + visibility: wgpu::ShaderStage::VERTEX, + ty: wgpu::BindingType::UniformBuffer { + dynamic: false, + }, + } + ] + }); + + let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &uniform_bind_group_layout, + bindings: &[ + wgpu::Binding { + binding: 0, + resource: wgpu::BindingResource::Buffer { + buffer: &uniform_buffer, + range: 0..std::mem::size_of_val(&uniforms) as wgpu::BufferAddress, + } + } + ], + }); + + let vs_src = include_str!("arrays.vert"); + let fs_src = include_str!("shader.frag"); + let vs_spirv = glsl_to_spirv::compile(vs_src, glsl_to_spirv::ShaderType::Vertex).unwrap(); + let fs_spirv = glsl_to_spirv::compile(fs_src, glsl_to_spirv::ShaderType::Fragment).unwrap(); + let vs_data = wgpu::read_spirv(vs_spirv).unwrap(); + let fs_data = wgpu::read_spirv(fs_spirv).unwrap(); + let vs_module = device.create_shader_module(&vs_data); + let fs_module = device.create_shader_module(&fs_data); + + let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + bind_group_layouts: &[&texture_bind_group_layout, &uniform_bind_group_layout], + }); + + let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + layout: &render_pipeline_layout, + vertex_stage: wgpu::ProgrammableStageDescriptor { + module: &vs_module, + entry_point: "main", + }, + fragment_stage: Some(wgpu::ProgrammableStageDescriptor { + module: &fs_module, + entry_point: "main", + }), + rasterization_state: Some(wgpu::RasterizationStateDescriptor { + front_face: wgpu::FrontFace::Ccw, + cull_mode: wgpu::CullMode::Back, + depth_bias: 0, + depth_bias_slope_scale: 0.0, + depth_bias_clamp: 0.0, + }), + primitive_topology: wgpu::PrimitiveTopology::TriangleList, + color_states: &[ + wgpu::ColorStateDescriptor { + format: sc_desc.format, + color_blend: wgpu::BlendDescriptor::REPLACE, + alpha_blend: wgpu::BlendDescriptor::REPLACE, + write_mask: wgpu::ColorWrite::ALL, + }, + ], + depth_stencil_state: None, + index_format: wgpu::IndexFormat::Uint16, + vertex_buffers: &[ + Vertex::desc(), + ], + sample_count: 1, + sample_mask: !0, + alpha_to_coverage_enabled: false, + }); + + let vertex_buffer = device + .create_buffer_mapped(VERTICES.len(), wgpu::BufferUsage::VERTEX) + .fill_from_slice(VERTICES); + let index_buffer = device + .create_buffer_mapped(INDICES.len(), wgpu::BufferUsage::INDEX) + .fill_from_slice(INDICES); + let num_indices = INDICES.len() as u32; + + let instances = (0..NUM_INSTANCES_PER_ROW).flat_map(|z| { + (0..NUM_INSTANCES_PER_ROW).map(move |x| { + let position = cgmath::Vector3 { x: x as f32, y: 0.0, z: z as f32 } - INSTANCE_DISPLACEMENT; + + let rotation = if position.is_zero() { + // this is needed so an object at (0, 0, 0) won't get scaled to zero + // as Quaternions can effect scale if they're not create correctly + cgmath::Quaternion::from_axis_angle(cgmath::Vector3::unit_z(), cgmath::Deg(0.0)) + } else { + cgmath::Quaternion::from_axis_angle(position.clone().normalize(), cgmath::Deg(45.0)) + }; + + Instance { + position, rotation, + } + }) + }).collect(); + + Self { + surface, + device, + queue, + sc_desc, + swap_chain, + render_pipeline, + vertex_buffer, + index_buffer, + num_indices, + diffuse_texture, + diffuse_texture_view, + diffuse_sampler, + diffuse_bind_group, + camera, + camera_controller, + uniform_buffer, + uniform_bind_group, + uniforms, + hidpi_factor, + size, + instances, + } + } + + fn update_hidpi_and_resize(&mut self, new_hidpi_factor: f64) { + self.hidpi_factor = new_hidpi_factor; + self.resize(self.size); + } + + fn resize(&mut self, new_size: winit::dpi::LogicalSize) { + let physical_size = new_size.to_physical(self.hidpi_factor); + self.size = new_size; + self.sc_desc.width = physical_size.width.round() as u32; + self.sc_desc.height = physical_size.height.round() as u32; + self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc); + + self.camera.aspect = self.sc_desc.width as f32 / self.sc_desc.height as f32; + } + + fn input(&mut self, event: &WindowEvent) -> bool { + self.camera_controller.process_events(event) + } + + fn update(&mut self) { + self.camera_controller.update_camera(&mut self.camera); + self.uniforms.update_view_proj(&self.camera); + + for (i, instance) in self.instances.iter().enumerate() { + self.uniforms.model[i] = instance.to_matrix(); + } + + let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + todo: 0, + }); + + let staging_buffer = self.device + .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC) + .fill_from_slice(&[self.uniforms]); + + encoder.copy_buffer_to_buffer(&staging_buffer, 0, &self.uniform_buffer, 0, std::mem::size_of::() as wgpu::BufferAddress); + + self.queue.submit(&[encoder.finish()]); + } + + fn render(&mut self) { + let frame = self.swap_chain.get_next_texture(); + + let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + todo: 0, + }); + + { + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[ + wgpu::RenderPassColorAttachmentDescriptor { + attachment: &frame.view, + resolve_target: None, + load_op: wgpu::LoadOp::Clear, + store_op: wgpu::StoreOp::Store, + clear_color: wgpu::Color { + r: 0.1, + g: 0.2, + b: 0.3, + a: 1.0, + }, + } + ], + depth_stencil_attachment: None, + }); + + render_pass.set_pipeline(&self.render_pipeline); + render_pass.set_bind_group(0, &self.diffuse_bind_group, &[]); + render_pass.set_bind_group(1, &self.uniform_bind_group, &[]); + render_pass.set_vertex_buffers(0, &[(&self.vertex_buffer, 0)]); + render_pass.set_index_buffer(&self.index_buffer, 0); + render_pass.draw_indexed(0..self.num_indices, 0, 0..NUM_INSTANCES); + } + + self.queue.submit(&[ + encoder.finish() + ]); + } +} + +fn main() { + let event_loop = EventLoop::new(); + let window = WindowBuilder::new() + .build(&event_loop) + .unwrap(); + + let mut state = State::new(&window); + + event_loop.run(move |event, _, control_flow| { + match event { + Event::WindowEvent { + ref event, + window_id, + } if window_id == window.id() => if state.input(event) { + *control_flow = ControlFlow::Wait; + } else { + match event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::KeyboardInput { + input, + .. + } => { + match input { + KeyboardInput { + state: ElementState::Pressed, + virtual_keycode: Some(VirtualKeyCode::Escape), + .. + } => *control_flow = ControlFlow::Exit, + _ => *control_flow = ControlFlow::Wait, + } + } + WindowEvent::Resized(logical_size) => { + state.resize(*logical_size); + *control_flow = ControlFlow::Wait; + } + WindowEvent::HiDpiFactorChanged(new_hidpi_factor) => { + state.update_hidpi_and_resize(*new_hidpi_factor); + *control_flow = ControlFlow::Wait; + } + _ => *control_flow = ControlFlow::Wait, + } + } + Event::EventsCleared => { + state.update(); + state.render(); + *control_flow = ControlFlow::Wait; + } + _ => *control_flow = ControlFlow::Wait, + } + }); +} \ No newline at end of file diff --git a/code/beginner/tutorial7-instancing/src/arrays.vert b/code/beginner/tutorial7-instancing/src/arrays.vert new file mode 100644 index 00000000..881796a3 --- /dev/null +++ b/code/beginner/tutorial7-instancing/src/arrays.vert @@ -0,0 +1,17 @@ +#version 450 + +layout(location=0) in vec3 a_position; +layout(location=1) in vec2 a_tex_coords; + +layout(location=0) out vec2 v_tex_coords; + +layout(set=1, binding=0) +uniform Uniforms { + mat4 u_view_proj; + mat4 u_model[100]; +}; + +void main() { + v_tex_coords = a_tex_coords; + gl_Position = u_view_proj * u_model[gl_InstanceIndex] * vec4(a_position, 1.0); +} \ No newline at end of file diff --git a/code/beginner/tutorial7-instancing/src/challenge.rs b/code/beginner/tutorial7-instancing/src/challenge.rs new file mode 100644 index 00000000..cfbd8cbb --- /dev/null +++ b/code/beginner/tutorial7-instancing/src/challenge.rs @@ -0,0 +1,609 @@ +use winit::{ + event::*, + event_loop::{EventLoop, ControlFlow}, + window::{Window, WindowBuilder}, +}; + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +struct Vertex { + position: [f32; 3], + tex_coords: [f32; 2], +} + +impl Vertex { + fn desc<'a>() -> wgpu::VertexBufferDescriptor<'a> { + use std::mem; + wgpu::VertexBufferDescriptor { + stride: mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::InputStepMode::Vertex, + attributes: &[ + wgpu::VertexAttributeDescriptor { + offset: 0, + shader_location: 0, + format: wgpu::VertexFormat::Float3, + }, + wgpu::VertexAttributeDescriptor { + offset: mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, + shader_location: 1, + format: wgpu::VertexFormat::Float2, + }, + ] + } + } +} + +const VERTICES: &[Vertex] = &[ + Vertex { position: [-0.0868241, -0.49240386, 0.0], tex_coords: [1.0 - 0.4131759, 1.0 - 0.00759614], }, // A + Vertex { position: [-0.49513406, -0.06958647, 0.0], tex_coords: [1.0 - 0.0048659444, 1.0 - 0.43041354], }, // B + Vertex { position: [-0.21918549, 0.44939706, 0.0], tex_coords: [1.0 - 0.28081453, 1.0 - 0.949397057], }, // C + Vertex { position: [0.35966998, 0.3473291, 0.0], tex_coords: [1.0 - 0.85967, 1.0 - 0.84732911], }, // D + Vertex { position: [0.44147372, -0.2347359, 0.0], tex_coords: [1.0 - 0.9414737, 1.0 - 0.2652641], }, // E +]; + +const INDICES: &[u16] = &[ + 0, 1, 4, + 1, 2, 4, + 2, 3, 4, +]; + +#[cfg_attr(rustfmt, rustfmt_skip)] +pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4 = cgmath::Matrix4::new( + 1.0, 0.0, 0.0, 0.0, + 0.0, -1.0, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.0, + 0.0, 0.0, 0.5, 1.0, +); + +struct Camera { + eye: cgmath::Point3, + target: cgmath::Point3, + up: cgmath::Vector3, + aspect: f32, + fovy: f32, + znear: f32, + zfar: f32, +} + +impl Camera { + fn build_view_projection_matrix(&self) -> cgmath::Matrix4 { + let view = cgmath::Matrix4::look_at(self.eye, self.target, self.up); + let proj = cgmath::perspective(cgmath::Deg(self.fovy), self.aspect, self.znear, self.zfar); + return proj * view; + } +} + +struct UniformStaging { + camera: Camera, + model_rotation: cgmath::Deg, +} + +impl UniformStaging { + fn new(camera: Camera) -> Self { + Self { camera, model_rotation: cgmath::Deg(0.0) } + } + fn update_uniforms(&self, uniforms: &mut Uniforms) { + uniforms.model_view_proj = + OPENGL_TO_WGPU_MATRIX + * self.camera.build_view_projection_matrix() + * cgmath::Matrix4::from_angle_z(self.model_rotation); + } +} + + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +struct Uniforms { + model_view_proj: cgmath::Matrix4, +} + +impl Uniforms { + fn new() -> Self { + use cgmath::SquareMatrix; + Self { + model_view_proj: cgmath::Matrix4::identity(), + } + } +} + +struct CameraController { + speed: f32, + is_up_pressed: bool, + is_down_pressed: bool, + is_forward_pressed: bool, + is_backward_pressed: bool, + is_left_pressed: bool, + is_right_pressed: bool, +} + +impl CameraController { + fn new(speed: f32) -> Self { + Self { + speed, + is_up_pressed: false, + is_down_pressed: false, + is_forward_pressed: false, + is_backward_pressed: false, + is_left_pressed: false, + is_right_pressed: false, + } + } + + fn process_events(&mut self, event: &WindowEvent) -> bool { + match event { + WindowEvent::KeyboardInput { + input: KeyboardInput { + state, + virtual_keycode: Some(keycode), + .. + }, + .. + } => { + let is_pressed = *state == ElementState::Pressed; + match keycode { + VirtualKeyCode::Space => { + self.is_up_pressed = is_pressed; + true + } + VirtualKeyCode::LShift => { + self.is_down_pressed = is_pressed; + true + } + VirtualKeyCode::W | VirtualKeyCode::Up => { + self.is_forward_pressed = is_pressed; + true + } + VirtualKeyCode::A | VirtualKeyCode::Left => { + self.is_left_pressed = is_pressed; + true + } + VirtualKeyCode::S | VirtualKeyCode::Down => { + self.is_backward_pressed = is_pressed; + true + } + VirtualKeyCode::D | VirtualKeyCode::Right => { + self.is_right_pressed = is_pressed; + true + } + _ => false, + } + } + _ => false, + } + } + + fn update_camera(&self, camera: &mut Camera) { + use cgmath::InnerSpace; + let forward = (camera.target - camera.eye).normalize(); + + if self.is_forward_pressed { + camera.eye += forward * self.speed; + } + if self.is_backward_pressed { + camera.eye -= forward * self.speed; + } + + let right = forward.cross(camera.up); + + if self.is_right_pressed { + camera.eye += right * self.speed; + } + if self.is_left_pressed { + camera.eye -= right * self.speed; + } + } +} + +struct State { + surface: wgpu::Surface, + device: wgpu::Device, + queue: wgpu::Queue, + sc_desc: wgpu::SwapChainDescriptor, + swap_chain: wgpu::SwapChain, + + render_pipeline: wgpu::RenderPipeline, + + vertex_buffer: wgpu::Buffer, + index_buffer: wgpu::Buffer, + num_indices: u32, + + diffuse_texture: wgpu::Texture, + diffuse_texture_view: wgpu::TextureView, + diffuse_sampler: wgpu::Sampler, + diffuse_bind_group: wgpu::BindGroup, + + camera_controller: CameraController, + uniforms: Uniforms, + uniform_staging: UniformStaging, + uniform_buffer: wgpu::Buffer, + uniform_bind_group: wgpu::BindGroup, + + hidpi_factor: f64, + size: winit::dpi::LogicalSize, +} + +impl State { + fn new(window: &Window) -> Self { + let hidpi_factor = window.hidpi_factor(); + let size = window.inner_size(); + let physical_size = size.to_physical(hidpi_factor); + + let surface = wgpu::Surface::create(window); + + let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions { + ..Default::default() + }).unwrap(); + + let (device, mut queue) = adapter.request_device(&wgpu::DeviceDescriptor { + extensions: wgpu::Extensions { + anisotropic_filtering: false, + }, + limits: Default::default(), + }); + + let sc_desc = wgpu::SwapChainDescriptor { + usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, + format: wgpu::TextureFormat::Bgra8UnormSrgb, + width: physical_size.width.round() as u32, + height: physical_size.height.round() as u32, + present_mode: wgpu::PresentMode::Vsync, + }; + let swap_chain = device.create_swap_chain(&surface, &sc_desc); + + let diffuse_bytes = include_bytes!("happy-tree.png"); + let diffuse_image = image::load_from_memory(diffuse_bytes).unwrap(); + let diffuse_rgba = diffuse_image.as_rgba8().unwrap(); + + use image::GenericImageView; + let dimensions = diffuse_image.dimensions(); + + let size3d = wgpu::Extent3d { + width: dimensions.0, + height: dimensions.1, + depth: 1, + }; + let diffuse_texture = device.create_texture(&wgpu::TextureDescriptor { + size: size3d, + array_layer_count: 1, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, + }); + + let diffuse_buffer = device + .create_buffer_mapped(diffuse_rgba.len(), wgpu::BufferUsage::COPY_SRC) + .fill_from_slice(&diffuse_rgba); + + let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + todo: 0, + }); + + encoder.copy_buffer_to_texture( + wgpu::BufferCopyView { + buffer: &diffuse_buffer, + offset: 0, + row_pitch: 4 * dimensions.0, + image_height: dimensions.1, + }, + wgpu::TextureCopyView { + texture: &diffuse_texture, + mip_level: 0, + array_layer: 0, + origin: wgpu::Origin3d::ZERO, + }, + size3d, + ); + + queue.submit(&[encoder.finish()]); + + let diffuse_texture_view = diffuse_texture.create_default_view(); + let diffuse_sampler = device.create_sampler(&wgpu::SamplerDescriptor { + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Nearest, + mipmap_filter: wgpu::FilterMode::Nearest, + lod_min_clamp: -100.0, + lod_max_clamp: 100.0, + compare_function: wgpu::CompareFunction::Always, + }); + + let texture_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + bindings: &[ + wgpu::BindGroupLayoutBinding { + binding: 0, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::SampledTexture { + multisampled: false, + dimension: wgpu::TextureViewDimension::D2, + }, + }, + wgpu::BindGroupLayoutBinding { + binding: 1, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Sampler, + }, + ], + }); + + let diffuse_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &texture_bind_group_layout, + bindings: &[ + wgpu::Binding { + binding: 0, + resource: wgpu::BindingResource::TextureView(&diffuse_texture_view), + }, + wgpu::Binding { + binding: 1, + resource: wgpu::BindingResource::Sampler(&diffuse_sampler), + } + ], + }); + + let camera = Camera { + eye: (0.0, 1.0, -2.0).into(), + target: (0.0, 0.0, 0.0).into(), + up: cgmath::Vector3::unit_y(), + aspect: sc_desc.width as f32 / sc_desc.height as f32, + fovy: 45.0, + znear: 0.1, + zfar: 100.0, + }; + let camera_controller = CameraController::new(0.2); + + let mut uniforms = Uniforms::new(); + let uniform_staging = UniformStaging::new(camera); + uniform_staging.update_uniforms(&mut uniforms); + + let uniform_buffer = device + .create_buffer_mapped(1, wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST) + .fill_from_slice(&[uniforms]); + + let uniform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + bindings: &[ + wgpu::BindGroupLayoutBinding { + binding: 0, + visibility: wgpu::ShaderStage::VERTEX, + ty: wgpu::BindingType::UniformBuffer { + dynamic: false, + }, + } + ] + }); + + let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &uniform_bind_group_layout, + bindings: &[ + wgpu::Binding { + binding: 0, + resource: wgpu::BindingResource::Buffer { + buffer: &uniform_buffer, + range: 0..std::mem::size_of_val(&uniforms) as wgpu::BufferAddress, + } + } + ], + }); + + let vs_src = include_str!("shader.vert"); + let fs_src = include_str!("shader.frag"); + let vs_spirv = glsl_to_spirv::compile(vs_src, glsl_to_spirv::ShaderType::Vertex).unwrap(); + let fs_spirv = glsl_to_spirv::compile(fs_src, glsl_to_spirv::ShaderType::Fragment).unwrap(); + let vs_data = wgpu::read_spirv(vs_spirv).unwrap(); + let fs_data = wgpu::read_spirv(fs_spirv).unwrap(); + let vs_module = device.create_shader_module(&vs_data); + let fs_module = device.create_shader_module(&fs_data); + + let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + bind_group_layouts: &[&texture_bind_group_layout, &uniform_bind_group_layout], + }); + + let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + layout: &render_pipeline_layout, + vertex_stage: wgpu::ProgrammableStageDescriptor { + module: &vs_module, + entry_point: "main", + }, + fragment_stage: Some(wgpu::ProgrammableStageDescriptor { + module: &fs_module, + entry_point: "main", + }), + rasterization_state: Some(wgpu::RasterizationStateDescriptor { + front_face: wgpu::FrontFace::Ccw, + cull_mode: wgpu::CullMode::Back, + depth_bias: 0, + depth_bias_slope_scale: 0.0, + depth_bias_clamp: 0.0, + }), + primitive_topology: wgpu::PrimitiveTopology::TriangleList, + color_states: &[ + wgpu::ColorStateDescriptor { + format: sc_desc.format, + color_blend: wgpu::BlendDescriptor::REPLACE, + alpha_blend: wgpu::BlendDescriptor::REPLACE, + write_mask: wgpu::ColorWrite::ALL, + }, + ], + depth_stencil_state: None, + index_format: wgpu::IndexFormat::Uint16, + vertex_buffers: &[ + Vertex::desc(), + ], + sample_count: 1, + sample_mask: !0, + alpha_to_coverage_enabled: false, + }); + + let vertex_buffer = device + .create_buffer_mapped(VERTICES.len(), wgpu::BufferUsage::VERTEX) + .fill_from_slice(VERTICES); + let index_buffer = device + .create_buffer_mapped(INDICES.len(), wgpu::BufferUsage::INDEX) + .fill_from_slice(INDICES); + let num_indices = INDICES.len() as u32; + + Self { + surface, + device, + queue, + sc_desc, + swap_chain, + render_pipeline, + vertex_buffer, + index_buffer, + num_indices, + diffuse_texture, + diffuse_texture_view, + diffuse_sampler, + diffuse_bind_group, + camera_controller, + uniform_staging, + uniform_buffer, + uniform_bind_group, + uniforms, + hidpi_factor, + size, + } + } + + fn update_hidpi_and_resize(&mut self, new_hidpi_factor: f64) { + self.hidpi_factor = new_hidpi_factor; + self.resize(self.size); + } + + fn resize(&mut self, new_size: winit::dpi::LogicalSize) { + let physical_size = new_size.to_physical(self.hidpi_factor); + self.size = new_size; + self.sc_desc.width = physical_size.width.round() as u32; + self.sc_desc.height = physical_size.height.round() as u32; + self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc); + + self.uniform_staging.camera.aspect = self.sc_desc.width as f32 / self.sc_desc.height as f32; + } + + fn input(&mut self, event: &WindowEvent) -> bool { + self.camera_controller.process_events(event) + } + + fn update(&mut self) { + self.camera_controller.update_camera(&mut self.uniform_staging.camera); + self.uniform_staging.model_rotation += cgmath::Deg(2.0); + self.uniform_staging.update_uniforms(&mut self.uniforms); + + let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + todo: 0, + }); + + let staging_buffer = self.device + .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC) + .fill_from_slice(&[self.uniforms]); + + encoder.copy_buffer_to_buffer(&staging_buffer, 0, &self.uniform_buffer, 0, std::mem::size_of::() as wgpu::BufferAddress); + + self.queue.submit(&[encoder.finish()]); + } + + fn render(&mut self) { + let frame = self.swap_chain.get_next_texture(); + + let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + todo: 0, + }); + + { + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[ + wgpu::RenderPassColorAttachmentDescriptor { + attachment: &frame.view, + resolve_target: None, + load_op: wgpu::LoadOp::Clear, + store_op: wgpu::StoreOp::Store, + clear_color: wgpu::Color { + r: 0.1, + g: 0.2, + b: 0.3, + a: 1.0, + }, + } + ], + depth_stencil_attachment: None, + }); + + render_pass.set_pipeline(&self.render_pipeline); + render_pass.set_bind_group(0, &self.diffuse_bind_group, &[]); + render_pass.set_bind_group(1, &self.uniform_bind_group, &[]); + render_pass.set_vertex_buffers(0, &[(&self.vertex_buffer, 0)]); + render_pass.set_index_buffer(&self.index_buffer, 0); + render_pass.draw_indexed(0..self.num_indices, 0, 0..1); + } + + self.queue.submit(&[ + encoder.finish() + ]); + } +} + +fn main() { + let event_loop = EventLoop::new(); + let window = WindowBuilder::new() + .build(&event_loop) + .unwrap(); + + let mut state = State::new(&window); + + let mut old_time = std::time::Instant::now(); + const MSPT: std::time::Duration = std::time::Duration::from_millis(20); + + event_loop.run(move |event, _, control_flow| { + match event { + Event::WindowEvent { + ref event, + window_id, + } if window_id == window.id() => if state.input(event) { + () + } else { + match event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::KeyboardInput { + input, + .. + } => { + match input { + KeyboardInput { + state: ElementState::Pressed, + virtual_keycode: Some(VirtualKeyCode::Escape), + .. + } => *control_flow = ControlFlow::Exit, + _ => (), + } + } + WindowEvent::Resized(logical_size) => { + state.resize(*logical_size); + () + } + WindowEvent::HiDpiFactorChanged(new_hidpi_factor) => { + state.update_hidpi_and_resize(*new_hidpi_factor); + () + } + _ => (), + } + } + Event::EventsCleared => { + state.update(); + state.render(); + + let new_time = std::time::Instant::now(); + let delta_time = new_time - old_time; + *control_flow = if delta_time > MSPT { + ControlFlow::Poll + } else { + ControlFlow::WaitUntil(old_time + MSPT) + }; + old_time = new_time; + } + _ => (), + } + }); +} \ No newline at end of file diff --git a/code/beginner/tutorial7-instancing/src/happy-tree.png b/code/beginner/tutorial7-instancing/src/happy-tree.png new file mode 100644 index 0000000000000000000000000000000000000000..fc86db345c14b95728250b8914fa284f34193834 GIT binary patch literal 28134 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 D;T5;R literal 0 HcmV?d00001 diff --git a/code/beginner/tutorial7-instancing/src/main.rs b/code/beginner/tutorial7-instancing/src/main.rs new file mode 100644 index 00000000..d61ce7cb --- /dev/null +++ b/code/beginner/tutorial7-instancing/src/main.rs @@ -0,0 +1,624 @@ +use winit::{ + event::*, + event_loop::{EventLoop, ControlFlow}, + window::{Window, WindowBuilder}, +}; +use cgmath::prelude::*; + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +struct Vertex { + position: [f32; 3], + tex_coords: [f32; 2], +} + +impl Vertex { + fn desc<'a>() -> wgpu::VertexBufferDescriptor<'a> { + use std::mem; + wgpu::VertexBufferDescriptor { + stride: mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::InputStepMode::Vertex, + attributes: &[ + wgpu::VertexAttributeDescriptor { + offset: 0, + shader_location: 0, + format: wgpu::VertexFormat::Float3, + }, + wgpu::VertexAttributeDescriptor { + offset: mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, + shader_location: 1, + format: wgpu::VertexFormat::Float2, + }, + ] + } + } +} + +const VERTICES: &[Vertex] = &[ + Vertex { position: [-0.0868241, -0.49240386, 0.0], tex_coords: [1.0 - 0.4131759, 1.0 - 0.00759614], }, // A + Vertex { position: [-0.49513406, -0.06958647, 0.0], tex_coords: [1.0 - 0.0048659444, 1.0 - 0.43041354], }, // B + Vertex { position: [-0.21918549, 0.44939706, 0.0], tex_coords: [1.0 - 0.28081453, 1.0 - 0.949397057], }, // C + Vertex { position: [0.35966998, 0.3473291, 0.0], tex_coords: [1.0 - 0.85967, 1.0 - 0.84732911], }, // D + Vertex { position: [0.44147372, -0.2347359, 0.0], tex_coords: [1.0 - 0.9414737, 1.0 - 0.2652641], }, // E +]; + +const INDICES: &[u16] = &[ + 0, 1, 4, + 1, 2, 4, + 2, 3, 4, +]; + +#[cfg_attr(rustfmt, rustfmt_skip)] +pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4 = cgmath::Matrix4::new( + 1.0, 0.0, 0.0, 0.0, + 0.0, -1.0, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.0, + 0.0, 0.0, 0.5, 1.0, +); + +const NUM_INSTANCES_PER_ROW: u32 = 10; +const NUM_INSTANCES: u32 = NUM_INSTANCES_PER_ROW * NUM_INSTANCES_PER_ROW; +const INSTANCE_DISPLACEMENT: cgmath::Vector3 = cgmath::Vector3::new(NUM_INSTANCES_PER_ROW as f32 * 0.5, 0.0, NUM_INSTANCES_PER_ROW as f32 * 0.5); + + +struct Camera { + eye: cgmath::Point3, + target: cgmath::Point3, + up: cgmath::Vector3, + aspect: f32, + fovy: f32, + znear: f32, + zfar: f32, +} + +impl Camera { + fn build_view_projection_matrix(&self) -> cgmath::Matrix4 { + let view = cgmath::Matrix4::look_at(self.eye, self.target, self.up); + let proj = cgmath::perspective(cgmath::Deg(self.fovy), self.aspect, self.znear, self.zfar); + return proj * view; + } +} + +#[repr(C)] +#[derive(Copy, Clone)] +struct Uniforms { + view_proj: cgmath::Matrix4, + model: [cgmath::Matrix4; NUM_INSTANCES as usize], +} + +impl Uniforms { + fn new() -> Self { + Self { + view_proj: cgmath::Matrix4::identity(), + model: [cgmath::Matrix4::identity(); NUM_INSTANCES as usize], + } + } + + fn update_view_proj(&mut self, camera: &Camera) { + self.view_proj = OPENGL_TO_WGPU_MATRIX * camera.build_view_projection_matrix(); + } +} + +struct CameraController { + speed: f32, + is_up_pressed: bool, + is_down_pressed: bool, + is_forward_pressed: bool, + is_backward_pressed: bool, + is_left_pressed: bool, + is_right_pressed: bool, +} + +impl CameraController { + fn new(speed: f32) -> Self { + Self { + speed, + is_up_pressed: false, + is_down_pressed: false, + is_forward_pressed: false, + is_backward_pressed: false, + is_left_pressed: false, + is_right_pressed: false, + } + } + + fn process_events(&mut self, event: &WindowEvent) -> bool { + match event { + WindowEvent::KeyboardInput { + input: KeyboardInput { + state, + virtual_keycode: Some(keycode), + .. + }, + .. + } => { + let is_pressed = *state == ElementState::Pressed; + match keycode { + VirtualKeyCode::Space => { + self.is_up_pressed = is_pressed; + true + } + VirtualKeyCode::LShift => { + self.is_down_pressed = is_pressed; + true + } + VirtualKeyCode::W | VirtualKeyCode::Up => { + self.is_forward_pressed = is_pressed; + true + } + VirtualKeyCode::A | VirtualKeyCode::Left => { + self.is_left_pressed = is_pressed; + true + } + VirtualKeyCode::S | VirtualKeyCode::Down => { + self.is_backward_pressed = is_pressed; + true + } + VirtualKeyCode::D | VirtualKeyCode::Right => { + self.is_right_pressed = is_pressed; + true + } + _ => false, + } + } + _ => false, + } + } + + fn update_camera(&self, camera: &mut Camera) { + let forward = (camera.target - camera.eye).normalize(); + + if self.is_forward_pressed { + camera.eye += forward * self.speed; + } + if self.is_backward_pressed { + camera.eye -= forward * self.speed; + } + + let right = forward.cross(camera.up); + + if self.is_right_pressed { + camera.eye += right * self.speed; + } + if self.is_left_pressed { + camera.eye -= right * self.speed; + } + } +} + +struct Instance { + position: cgmath::Vector3, + rotation: cgmath::Quaternion, +} + +impl Instance { + fn to_matrix(&self) -> cgmath::Matrix4 { + cgmath::Matrix4::from_translation(self.position) * cgmath::Matrix4::from(self.rotation) + } +} + +struct State { + surface: wgpu::Surface, + device: wgpu::Device, + queue: wgpu::Queue, + sc_desc: wgpu::SwapChainDescriptor, + swap_chain: wgpu::SwapChain, + + render_pipeline: wgpu::RenderPipeline, + + vertex_buffer: wgpu::Buffer, + index_buffer: wgpu::Buffer, + num_indices: u32, + + diffuse_texture: wgpu::Texture, + diffuse_texture_view: wgpu::TextureView, + diffuse_sampler: wgpu::Sampler, + diffuse_bind_group: wgpu::BindGroup, + + camera: Camera, + camera_controller: CameraController, + uniforms: Uniforms, + uniform_buffer: wgpu::Buffer, + uniform_bind_group: wgpu::BindGroup, + + hidpi_factor: f64, + size: winit::dpi::LogicalSize, + + instances: Vec, +} + +impl State { + fn new(window: &Window) -> Self { + let hidpi_factor = window.hidpi_factor(); + let size = window.inner_size(); + let physical_size = size.to_physical(hidpi_factor); + + let surface = wgpu::Surface::create(window); + + let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions { + ..Default::default() + }).unwrap(); + + let (device, mut queue) = adapter.request_device(&wgpu::DeviceDescriptor { + extensions: wgpu::Extensions { + anisotropic_filtering: false, + }, + limits: Default::default(), + }); + + let sc_desc = wgpu::SwapChainDescriptor { + usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, + format: wgpu::TextureFormat::Bgra8UnormSrgb, + width: physical_size.width.round() as u32, + height: physical_size.height.round() as u32, + present_mode: wgpu::PresentMode::Vsync, + }; + let swap_chain = device.create_swap_chain(&surface, &sc_desc); + + let diffuse_bytes = include_bytes!("happy-tree.png"); + let diffuse_image = image::load_from_memory(diffuse_bytes).unwrap(); + let diffuse_rgba = diffuse_image.as_rgba8().unwrap(); + + use image::GenericImageView; + let dimensions = diffuse_image.dimensions(); + + let size3d = wgpu::Extent3d { + width: dimensions.0, + height: dimensions.1, + depth: 1, + }; + let diffuse_texture = device.create_texture(&wgpu::TextureDescriptor { + size: size3d, + array_layer_count: 1, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, + }); + + let diffuse_buffer = device + .create_buffer_mapped(diffuse_rgba.len(), wgpu::BufferUsage::COPY_SRC) + .fill_from_slice(&diffuse_rgba); + + let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + todo: 0, + }); + + encoder.copy_buffer_to_texture( + wgpu::BufferCopyView { + buffer: &diffuse_buffer, + offset: 0, + row_pitch: 4 * dimensions.0, + image_height: dimensions.1, + }, + wgpu::TextureCopyView { + texture: &diffuse_texture, + mip_level: 0, + array_layer: 0, + origin: wgpu::Origin3d::ZERO, + }, + size3d, + ); + + queue.submit(&[encoder.finish()]); + + let diffuse_texture_view = diffuse_texture.create_default_view(); + let diffuse_sampler = device.create_sampler(&wgpu::SamplerDescriptor { + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Nearest, + mipmap_filter: wgpu::FilterMode::Nearest, + lod_min_clamp: -100.0, + lod_max_clamp: 100.0, + compare_function: wgpu::CompareFunction::Always, + }); + + let texture_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + bindings: &[ + wgpu::BindGroupLayoutBinding { + binding: 0, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::SampledTexture { + multisampled: false, + dimension: wgpu::TextureViewDimension::D2, + }, + }, + wgpu::BindGroupLayoutBinding { + binding: 1, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Sampler, + }, + ], + }); + + let diffuse_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &texture_bind_group_layout, + bindings: &[ + wgpu::Binding { + binding: 0, + resource: wgpu::BindingResource::TextureView(&diffuse_texture_view), + }, + wgpu::Binding { + binding: 1, + resource: wgpu::BindingResource::Sampler(&diffuse_sampler), + } + ], + }); + + let camera = Camera { + eye: (0.0, 5.0, -10.0).into(), + target: (0.0, 0.0, 0.0).into(), + up: cgmath::Vector3::unit_y(), + aspect: sc_desc.width as f32 / sc_desc.height as f32, + fovy: 45.0, + znear: 0.1, + zfar: 100.0, + }; + let camera_controller = CameraController::new(0.2); + + let mut uniforms = Uniforms::new(); + uniforms.update_view_proj(&camera); + + let uniform_buffer = device + .create_buffer_mapped(1, wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST) + .fill_from_slice(&[uniforms]); + + let uniform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + bindings: &[ + wgpu::BindGroupLayoutBinding { + binding: 0, + visibility: wgpu::ShaderStage::VERTEX, + ty: wgpu::BindingType::UniformBuffer { + dynamic: false, + }, + } + ] + }); + + let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &uniform_bind_group_layout, + bindings: &[ + wgpu::Binding { + binding: 0, + resource: wgpu::BindingResource::Buffer { + buffer: &uniform_buffer, + range: 0..std::mem::size_of_val(&uniforms) as wgpu::BufferAddress, + } + } + ], + }); + + let vs_src = include_str!("shader.vert"); + let fs_src = include_str!("shader.frag"); + let vs_spirv = glsl_to_spirv::compile(vs_src, glsl_to_spirv::ShaderType::Vertex).unwrap(); + let fs_spirv = glsl_to_spirv::compile(fs_src, glsl_to_spirv::ShaderType::Fragment).unwrap(); + let vs_data = wgpu::read_spirv(vs_spirv).unwrap(); + let fs_data = wgpu::read_spirv(fs_spirv).unwrap(); + let vs_module = device.create_shader_module(&vs_data); + let fs_module = device.create_shader_module(&fs_data); + + let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + bind_group_layouts: &[&texture_bind_group_layout, &uniform_bind_group_layout], + }); + + let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + layout: &render_pipeline_layout, + vertex_stage: wgpu::ProgrammableStageDescriptor { + module: &vs_module, + entry_point: "main", + }, + fragment_stage: Some(wgpu::ProgrammableStageDescriptor { + module: &fs_module, + entry_point: "main", + }), + rasterization_state: Some(wgpu::RasterizationStateDescriptor { + front_face: wgpu::FrontFace::Ccw, + cull_mode: wgpu::CullMode::Back, + depth_bias: 0, + depth_bias_slope_scale: 0.0, + depth_bias_clamp: 0.0, + }), + primitive_topology: wgpu::PrimitiveTopology::TriangleList, + color_states: &[ + wgpu::ColorStateDescriptor { + format: sc_desc.format, + color_blend: wgpu::BlendDescriptor::REPLACE, + alpha_blend: wgpu::BlendDescriptor::REPLACE, + write_mask: wgpu::ColorWrite::ALL, + }, + ], + depth_stencil_state: None, + index_format: wgpu::IndexFormat::Uint16, + vertex_buffers: &[ + Vertex::desc(), + ], + sample_count: 1, + sample_mask: !0, + alpha_to_coverage_enabled: false, + }); + + let vertex_buffer = device + .create_buffer_mapped(VERTICES.len(), wgpu::BufferUsage::VERTEX) + .fill_from_slice(VERTICES); + let index_buffer = device + .create_buffer_mapped(INDICES.len(), wgpu::BufferUsage::INDEX) + .fill_from_slice(INDICES); + let num_indices = INDICES.len() as u32; + + let instances = (0..NUM_INSTANCES_PER_ROW).flat_map(|z| { + (0..NUM_INSTANCES_PER_ROW).map(move |x| { + let position = cgmath::Vector3 { x: x as f32, y: 0.0, z: z as f32 } - INSTANCE_DISPLACEMENT; + + let rotation = if position.is_zero() { + // this is needed so an object at (0, 0, 0) won't get scaled to zero + // as Quaternions can effect scale if they're not create correctly + cgmath::Quaternion::from_axis_angle(cgmath::Vector3::unit_z(), cgmath::Deg(0.0)) + } else { + cgmath::Quaternion::from_axis_angle(position.clone().normalize(), cgmath::Deg(45.0)) + }; + + Instance { + position, rotation, + } + }) + }).collect(); + + Self { + surface, + device, + queue, + sc_desc, + swap_chain, + render_pipeline, + vertex_buffer, + index_buffer, + num_indices, + diffuse_texture, + diffuse_texture_view, + diffuse_sampler, + diffuse_bind_group, + camera, + camera_controller, + uniform_buffer, + uniform_bind_group, + uniforms, + hidpi_factor, + size, + instances, + } + } + + fn update_hidpi_and_resize(&mut self, new_hidpi_factor: f64) { + self.hidpi_factor = new_hidpi_factor; + self.resize(self.size); + } + + fn resize(&mut self, new_size: winit::dpi::LogicalSize) { + let physical_size = new_size.to_physical(self.hidpi_factor); + self.size = new_size; + self.sc_desc.width = physical_size.width.round() as u32; + self.sc_desc.height = physical_size.height.round() as u32; + self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc); + + self.camera.aspect = self.sc_desc.width as f32 / self.sc_desc.height as f32; + } + + fn input(&mut self, event: &WindowEvent) -> bool { + self.camera_controller.process_events(event) + } + + fn update(&mut self) { + self.camera_controller.update_camera(&mut self.camera); + self.uniforms.update_view_proj(&self.camera); + + for (i, instance) in self.instances.iter().enumerate() { + self.uniforms.model[i] = instance.to_matrix(); + } + + let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + todo: 0, + }); + + let staging_buffer = self.device + .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC) + .fill_from_slice(&[self.uniforms]); + + encoder.copy_buffer_to_buffer(&staging_buffer, 0, &self.uniform_buffer, 0, std::mem::size_of::() as wgpu::BufferAddress); + + self.queue.submit(&[encoder.finish()]); + } + + fn render(&mut self) { + let frame = self.swap_chain.get_next_texture(); + + let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + todo: 0, + }); + + { + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[ + wgpu::RenderPassColorAttachmentDescriptor { + attachment: &frame.view, + resolve_target: None, + load_op: wgpu::LoadOp::Clear, + store_op: wgpu::StoreOp::Store, + clear_color: wgpu::Color { + r: 0.1, + g: 0.2, + b: 0.3, + a: 1.0, + }, + } + ], + depth_stencil_attachment: None, + }); + + render_pass.set_pipeline(&self.render_pipeline); + render_pass.set_bind_group(0, &self.diffuse_bind_group, &[]); + render_pass.set_bind_group(1, &self.uniform_bind_group, &[]); + render_pass.set_vertex_buffers(0, &[(&self.vertex_buffer, 0)]); + render_pass.set_index_buffer(&self.index_buffer, 0); + render_pass.draw_indexed(0..self.num_indices, 0, 0..NUM_INSTANCES); + } + + self.queue.submit(&[ + encoder.finish() + ]); + } +} + +fn main() { + let event_loop = EventLoop::new(); + let window = WindowBuilder::new() + .build(&event_loop) + .unwrap(); + + let mut state = State::new(&window); + + event_loop.run(move |event, _, control_flow| { + match event { + Event::WindowEvent { + ref event, + window_id, + } if window_id == window.id() => if state.input(event) { + *control_flow = ControlFlow::Wait; + } else { + match event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::KeyboardInput { + input, + .. + } => { + match input { + KeyboardInput { + state: ElementState::Pressed, + virtual_keycode: Some(VirtualKeyCode::Escape), + .. + } => *control_flow = ControlFlow::Exit, + _ => *control_flow = ControlFlow::Wait, + } + } + WindowEvent::Resized(logical_size) => { + state.resize(*logical_size); + *control_flow = ControlFlow::Wait; + } + WindowEvent::HiDpiFactorChanged(new_hidpi_factor) => { + state.update_hidpi_and_resize(*new_hidpi_factor); + *control_flow = ControlFlow::Wait; + } + _ => *control_flow = ControlFlow::Wait, + } + } + Event::EventsCleared => { + state.update(); + state.render(); + *control_flow = ControlFlow::Wait; + } + _ => *control_flow = ControlFlow::Wait, + } + }); +} \ No newline at end of file diff --git a/code/beginner/tutorial7-instancing/src/naive.rs b/code/beginner/tutorial7-instancing/src/naive.rs new file mode 100644 index 00000000..20a420c6 --- /dev/null +++ b/code/beginner/tutorial7-instancing/src/naive.rs @@ -0,0 +1,644 @@ +use winit::{ + event::*, + event_loop::{EventLoop, ControlFlow}, + window::{Window, WindowBuilder}, +}; +use cgmath::prelude::*; + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +struct Vertex { + position: [f32; 3], + tex_coords: [f32; 2], +} + +impl Vertex { + fn desc<'a>() -> wgpu::VertexBufferDescriptor<'a> { + use std::mem; + wgpu::VertexBufferDescriptor { + stride: mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::InputStepMode::Vertex, + attributes: &[ + wgpu::VertexAttributeDescriptor { + offset: 0, + shader_location: 0, + format: wgpu::VertexFormat::Float3, + }, + wgpu::VertexAttributeDescriptor { + offset: mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, + shader_location: 1, + format: wgpu::VertexFormat::Float2, + }, + ] + } + } +} + +const VERTICES: &[Vertex] = &[ + Vertex { position: [-0.0868241, -0.49240386, 0.0], tex_coords: [1.0 - 0.4131759, 1.0 - 0.00759614], }, // A + Vertex { position: [-0.49513406, -0.06958647, 0.0], tex_coords: [1.0 - 0.0048659444, 1.0 - 0.43041354], }, // B + Vertex { position: [-0.21918549, 0.44939706, 0.0], tex_coords: [1.0 - 0.28081453, 1.0 - 0.949397057], }, // C + Vertex { position: [0.35966998, 0.3473291, 0.0], tex_coords: [1.0 - 0.85967, 1.0 - 0.84732911], }, // D + Vertex { position: [0.44147372, -0.2347359, 0.0], tex_coords: [1.0 - 0.9414737, 1.0 - 0.2652641], }, // E +]; + +const INDICES: &[u16] = &[ + 0, 1, 4, + 1, 2, 4, + 2, 3, 4, +]; + +#[cfg_attr(rustfmt, rustfmt_skip)] +pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4 = cgmath::Matrix4::new( + 1.0, 0.0, 0.0, 0.0, + 0.0, -1.0, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.0, + 0.0, 0.0, 0.5, 1.0, +); + +struct Camera { + eye: cgmath::Point3, + target: cgmath::Point3, + up: cgmath::Vector3, + aspect: f32, + fovy: f32, + znear: f32, + zfar: f32, +} + +impl Camera { + fn build_view_projection_matrix(&self) -> cgmath::Matrix4 { + let view = cgmath::Matrix4::look_at(self.eye, self.target, self.up); + let proj = cgmath::perspective(cgmath::Deg(self.fovy), self.aspect, self.znear, self.zfar); + return proj * view; + } +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +struct Uniforms { + view_proj: cgmath::Matrix4, + model: cgmath::Matrix4, +} + +impl Uniforms { + fn new() -> Self { + Self { + view_proj: cgmath::Matrix4::identity(), + model: cgmath::Matrix4::identity(), + } + } + + fn update_view_proj(&mut self, camera: &Camera) { + self.view_proj = OPENGL_TO_WGPU_MATRIX * camera.build_view_projection_matrix(); + } +} + +struct CameraController { + speed: f32, + is_up_pressed: bool, + is_down_pressed: bool, + is_forward_pressed: bool, + is_backward_pressed: bool, + is_left_pressed: bool, + is_right_pressed: bool, +} + +impl CameraController { + fn new(speed: f32) -> Self { + Self { + speed, + is_up_pressed: false, + is_down_pressed: false, + is_forward_pressed: false, + is_backward_pressed: false, + is_left_pressed: false, + is_right_pressed: false, + } + } + + fn process_events(&mut self, event: &WindowEvent) -> bool { + match event { + WindowEvent::KeyboardInput { + input: KeyboardInput { + state, + virtual_keycode: Some(keycode), + .. + }, + .. + } => { + let is_pressed = *state == ElementState::Pressed; + match keycode { + VirtualKeyCode::Space => { + self.is_up_pressed = is_pressed; + true + } + VirtualKeyCode::LShift => { + self.is_down_pressed = is_pressed; + true + } + VirtualKeyCode::W | VirtualKeyCode::Up => { + self.is_forward_pressed = is_pressed; + true + } + VirtualKeyCode::A | VirtualKeyCode::Left => { + self.is_left_pressed = is_pressed; + true + } + VirtualKeyCode::S | VirtualKeyCode::Down => { + self.is_backward_pressed = is_pressed; + true + } + VirtualKeyCode::D | VirtualKeyCode::Right => { + self.is_right_pressed = is_pressed; + true + } + _ => false, + } + } + _ => false, + } + } + + fn update_camera(&self, camera: &mut Camera) { + let forward = (camera.target - camera.eye).normalize(); + + if self.is_forward_pressed { + camera.eye += forward * self.speed; + } + if self.is_backward_pressed { + camera.eye -= forward * self.speed; + } + + let right = forward.cross(camera.up); + + if self.is_right_pressed { + camera.eye += right * self.speed; + } + if self.is_left_pressed { + camera.eye -= right * self.speed; + } + } +} + +struct Instance { + position: cgmath::Vector3, + rotation: cgmath::Quaternion, +} + +impl Instance { + fn to_matrix(&self) -> cgmath::Matrix4 { + cgmath::Matrix4::from_translation(self.position) * cgmath::Matrix4::from(self.rotation) + } +} + +struct State { + surface: wgpu::Surface, + device: wgpu::Device, + queue: wgpu::Queue, + sc_desc: wgpu::SwapChainDescriptor, + swap_chain: wgpu::SwapChain, + + render_pipeline: wgpu::RenderPipeline, + + vertex_buffer: wgpu::Buffer, + index_buffer: wgpu::Buffer, + num_indices: u32, + + diffuse_texture: wgpu::Texture, + diffuse_texture_view: wgpu::TextureView, + diffuse_sampler: wgpu::Sampler, + diffuse_bind_group: wgpu::BindGroup, + + camera: Camera, + camera_controller: CameraController, + uniforms: Uniforms, + uniform_buffer: wgpu::Buffer, + uniform_bind_group: wgpu::BindGroup, + + hidpi_factor: f64, + size: winit::dpi::LogicalSize, + + instances: Vec, +} + +impl State { + fn new(window: &Window) -> Self { + let hidpi_factor = window.hidpi_factor(); + let size = window.inner_size(); + let physical_size = size.to_physical(hidpi_factor); + + let surface = wgpu::Surface::create(window); + + let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions { + ..Default::default() + }).unwrap(); + + let (device, mut queue) = adapter.request_device(&wgpu::DeviceDescriptor { + extensions: wgpu::Extensions { + anisotropic_filtering: false, + }, + limits: Default::default(), + }); + + let sc_desc = wgpu::SwapChainDescriptor { + usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, + format: wgpu::TextureFormat::Bgra8UnormSrgb, + width: physical_size.width.round() as u32, + height: physical_size.height.round() as u32, + present_mode: wgpu::PresentMode::Vsync, + }; + let swap_chain = device.create_swap_chain(&surface, &sc_desc); + + let diffuse_bytes = include_bytes!("happy-tree.png"); + let diffuse_image = image::load_from_memory(diffuse_bytes).unwrap(); + let diffuse_rgba = diffuse_image.as_rgba8().unwrap(); + + use image::GenericImageView; + let dimensions = diffuse_image.dimensions(); + + let size3d = wgpu::Extent3d { + width: dimensions.0, + height: dimensions.1, + depth: 1, + }; + let diffuse_texture = device.create_texture(&wgpu::TextureDescriptor { + size: size3d, + array_layer_count: 1, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, + }); + + let diffuse_buffer = device + .create_buffer_mapped(diffuse_rgba.len(), wgpu::BufferUsage::COPY_SRC) + .fill_from_slice(&diffuse_rgba); + + let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + todo: 0, + }); + + encoder.copy_buffer_to_texture( + wgpu::BufferCopyView { + buffer: &diffuse_buffer, + offset: 0, + row_pitch: 4 * dimensions.0, + image_height: dimensions.1, + }, + wgpu::TextureCopyView { + texture: &diffuse_texture, + mip_level: 0, + array_layer: 0, + origin: wgpu::Origin3d::ZERO, + }, + size3d, + ); + + queue.submit(&[encoder.finish()]); + + let diffuse_texture_view = diffuse_texture.create_default_view(); + let diffuse_sampler = device.create_sampler(&wgpu::SamplerDescriptor { + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Nearest, + mipmap_filter: wgpu::FilterMode::Nearest, + lod_min_clamp: -100.0, + lod_max_clamp: 100.0, + compare_function: wgpu::CompareFunction::Always, + }); + + let texture_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + bindings: &[ + wgpu::BindGroupLayoutBinding { + binding: 0, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::SampledTexture { + multisampled: false, + dimension: wgpu::TextureViewDimension::D2, + }, + }, + wgpu::BindGroupLayoutBinding { + binding: 1, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Sampler, + }, + ], + }); + + let diffuse_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &texture_bind_group_layout, + bindings: &[ + wgpu::Binding { + binding: 0, + resource: wgpu::BindingResource::TextureView(&diffuse_texture_view), + }, + wgpu::Binding { + binding: 1, + resource: wgpu::BindingResource::Sampler(&diffuse_sampler), + } + ], + }); + + let camera = Camera { + eye: (0.0, 5.0, -10.0).into(), + target: (0.0, 0.0, 0.0).into(), + up: cgmath::Vector3::unit_y(), + aspect: sc_desc.width as f32 / sc_desc.height as f32, + fovy: 45.0, + znear: 0.1, + zfar: 100.0, + }; + let camera_controller = CameraController::new(0.2); + + let mut uniforms = Uniforms::new(); + uniforms.update_view_proj(&camera); + + let uniform_buffer = device + .create_buffer_mapped(1, wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST) + .fill_from_slice(&[uniforms]); + + let uniform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + bindings: &[ + wgpu::BindGroupLayoutBinding { + binding: 0, + visibility: wgpu::ShaderStage::VERTEX, + ty: wgpu::BindingType::UniformBuffer { + dynamic: false, + }, + } + ] + }); + + let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &uniform_bind_group_layout, + bindings: &[ + wgpu::Binding { + binding: 0, + resource: wgpu::BindingResource::Buffer { + buffer: &uniform_buffer, + range: 0..std::mem::size_of_val(&uniforms) as wgpu::BufferAddress, + } + } + ], + }); + + let vs_src = include_str!("naive.vert"); + let fs_src = include_str!("shader.frag"); + let vs_spirv = glsl_to_spirv::compile(vs_src, glsl_to_spirv::ShaderType::Vertex).unwrap(); + let fs_spirv = glsl_to_spirv::compile(fs_src, glsl_to_spirv::ShaderType::Fragment).unwrap(); + let vs_data = wgpu::read_spirv(vs_spirv).unwrap(); + let fs_data = wgpu::read_spirv(fs_spirv).unwrap(); + let vs_module = device.create_shader_module(&vs_data); + let fs_module = device.create_shader_module(&fs_data); + + let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + bind_group_layouts: &[&texture_bind_group_layout, &uniform_bind_group_layout], + }); + + let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + layout: &render_pipeline_layout, + vertex_stage: wgpu::ProgrammableStageDescriptor { + module: &vs_module, + entry_point: "main", + }, + fragment_stage: Some(wgpu::ProgrammableStageDescriptor { + module: &fs_module, + entry_point: "main", + }), + rasterization_state: Some(wgpu::RasterizationStateDescriptor { + front_face: wgpu::FrontFace::Ccw, + cull_mode: wgpu::CullMode::Back, + depth_bias: 0, + depth_bias_slope_scale: 0.0, + depth_bias_clamp: 0.0, + }), + primitive_topology: wgpu::PrimitiveTopology::TriangleList, + color_states: &[ + wgpu::ColorStateDescriptor { + format: sc_desc.format, + color_blend: wgpu::BlendDescriptor::REPLACE, + alpha_blend: wgpu::BlendDescriptor::REPLACE, + write_mask: wgpu::ColorWrite::ALL, + }, + ], + depth_stencil_state: None, + index_format: wgpu::IndexFormat::Uint16, + vertex_buffers: &[ + Vertex::desc(), + ], + sample_count: 1, + sample_mask: !0, + alpha_to_coverage_enabled: false, + }); + + let vertex_buffer = device + .create_buffer_mapped(VERTICES.len(), wgpu::BufferUsage::VERTEX) + .fill_from_slice(VERTICES); + let index_buffer = device + .create_buffer_mapped(INDICES.len(), wgpu::BufferUsage::INDEX) + .fill_from_slice(INDICES); + let num_indices = INDICES.len() as u32; + + const NUM_INSTANCES_PER_ROW: u32 = 10; + const INSTANCE_DISPLACEMENT: cgmath::Vector3 = cgmath::Vector3::new(NUM_INSTANCES_PER_ROW as f32 * 0.5, 0.0, NUM_INSTANCES_PER_ROW as f32 * 0.5); + + let instances = (0..NUM_INSTANCES_PER_ROW).flat_map(|z| { + (0..NUM_INSTANCES_PER_ROW).map(move |x| { + let position = cgmath::Vector3 { x: x as f32, y: 0.0, z: z as f32 } - INSTANCE_DISPLACEMENT; + + let rotation = if position.is_zero() { + // this is needed so an object at (0, 0, 0) won't get scaled to zero + // as Quaternions can effect scale if they're not create correctly + cgmath::Quaternion::from_axis_angle(cgmath::Vector3::unit_z(), cgmath::Deg(0.0)) + } else { + cgmath::Quaternion::from_axis_angle(position.clone().normalize(), cgmath::Deg(45.0)) + }; + + Instance { + position, rotation, + } + }) + }).collect(); + + Self { + surface, + device, + queue, + sc_desc, + swap_chain, + render_pipeline, + vertex_buffer, + index_buffer, + num_indices, + diffuse_texture, + diffuse_texture_view, + diffuse_sampler, + diffuse_bind_group, + camera, + camera_controller, + uniform_buffer, + uniform_bind_group, + uniforms, + hidpi_factor, + size, + instances, + } + } + + fn update_hidpi_and_resize(&mut self, new_hidpi_factor: f64) { + self.hidpi_factor = new_hidpi_factor; + self.resize(self.size); + } + + fn resize(&mut self, new_size: winit::dpi::LogicalSize) { + let physical_size = new_size.to_physical(self.hidpi_factor); + self.size = new_size; + self.sc_desc.width = physical_size.width.round() as u32; + self.sc_desc.height = physical_size.height.round() as u32; + self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc); + + self.camera.aspect = self.sc_desc.width as f32 / self.sc_desc.height as f32; + } + + fn input(&mut self, event: &WindowEvent) -> bool { + self.camera_controller.process_events(event) + } + + fn update(&mut self) { + self.camera_controller.update_camera(&mut self.camera); + self.uniforms.update_view_proj(&self.camera); + + let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + todo: 0, + }); + + let staging_buffer = self.device + .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC) + .fill_from_slice(&[self.uniforms]); + + encoder.copy_buffer_to_buffer(&staging_buffer, 0, &self.uniform_buffer, 0, std::mem::size_of::() as wgpu::BufferAddress); + + self.queue.submit(&[encoder.finish()]); + } + + fn render(&mut self) { + let frame = self.swap_chain.get_next_texture(); + + let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + todo: 0, + }); + + { + encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[ + wgpu::RenderPassColorAttachmentDescriptor { + attachment: &frame.view, + resolve_target: None, + load_op: wgpu::LoadOp::Clear, + store_op: wgpu::StoreOp::Store, + clear_color: wgpu::Color { + r: 0.1, + g: 0.2, + b: 0.3, + a: 1.0, + }, + } + ], + depth_stencil_attachment: None, + }); + } + + for instance in &self.instances { + self.uniforms.model = instance.to_matrix(); + let staging_buffer = self.device + .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC) + .fill_from_slice(&[self.uniforms]); + encoder.copy_buffer_to_buffer(&staging_buffer, 0, &self.uniform_buffer, 0, std::mem::size_of::() as wgpu::BufferAddress); + + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[ + wgpu::RenderPassColorAttachmentDescriptor { + attachment: &frame.view, + resolve_target: None, + load_op: wgpu::LoadOp::Load, + store_op: wgpu::StoreOp::Store, + clear_color: wgpu::Color { + r: 0.1, + g: 0.2, + b: 0.3, + a: 1.0, + }, + } + ], + depth_stencil_attachment: None, + }); + + render_pass.set_pipeline(&self.render_pipeline); + render_pass.set_bind_group(0, &self.diffuse_bind_group, &[]); + render_pass.set_bind_group(1, &self.uniform_bind_group, &[]); + render_pass.set_vertex_buffers(0, &[(&self.vertex_buffer, 0)]); + render_pass.set_index_buffer(&self.index_buffer, 0); + render_pass.draw_indexed(0..self.num_indices, 0, 0..1); + } + + self.queue.submit(&[ + encoder.finish() + ]); + } +} + +fn main() { + let event_loop = EventLoop::new(); + let window = WindowBuilder::new() + .build(&event_loop) + .unwrap(); + + let mut state = State::new(&window); + + event_loop.run(move |event, _, control_flow| { + match event { + Event::WindowEvent { + ref event, + window_id, + } if window_id == window.id() => if state.input(event) { + *control_flow = ControlFlow::Wait; + } else { + match event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::KeyboardInput { + input, + .. + } => { + match input { + KeyboardInput { + state: ElementState::Pressed, + virtual_keycode: Some(VirtualKeyCode::Escape), + .. + } => *control_flow = ControlFlow::Exit, + _ => *control_flow = ControlFlow::Wait, + } + } + WindowEvent::Resized(logical_size) => { + state.resize(*logical_size); + *control_flow = ControlFlow::Wait; + } + WindowEvent::HiDpiFactorChanged(new_hidpi_factor) => { + state.update_hidpi_and_resize(*new_hidpi_factor); + *control_flow = ControlFlow::Wait; + } + _ => *control_flow = ControlFlow::Wait, + } + } + Event::EventsCleared => { + state.update(); + state.render(); + *control_flow = ControlFlow::Wait; + } + _ => *control_flow = ControlFlow::Wait, + } + }); +} \ No newline at end of file diff --git a/code/beginner/tutorial7-instancing/src/naive.vert b/code/beginner/tutorial7-instancing/src/naive.vert new file mode 100644 index 00000000..da0db4d1 --- /dev/null +++ b/code/beginner/tutorial7-instancing/src/naive.vert @@ -0,0 +1,17 @@ +#version 450 + +layout(location=0) in vec3 a_position; +layout(location=1) in vec2 a_tex_coords; + +layout(location=0) out vec2 v_tex_coords; + +layout(set=1, binding=0) +uniform Uniforms { + mat4 u_view_proj; + mat4 u_model; +}; + +void main() { + v_tex_coords = a_tex_coords; + gl_Position = u_view_proj * u_model * vec4(a_position, 1.0); +} \ No newline at end of file diff --git a/code/beginner/tutorial7-instancing/src/shader.frag b/code/beginner/tutorial7-instancing/src/shader.frag new file mode 100644 index 00000000..9b033114 --- /dev/null +++ b/code/beginner/tutorial7-instancing/src/shader.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(location=0) in vec2 v_tex_coords; +layout(location=0) out vec4 f_color; + +layout(set = 0, binding = 0) uniform texture2D t_diffuse; +layout(set = 0, binding = 1) uniform sampler s_diffuse; + +void main() { + f_color = texture(sampler2D(t_diffuse, s_diffuse), v_tex_coords); +} \ No newline at end of file diff --git a/code/beginner/tutorial7-instancing/src/shader.vert b/code/beginner/tutorial7-instancing/src/shader.vert new file mode 100644 index 00000000..881796a3 --- /dev/null +++ b/code/beginner/tutorial7-instancing/src/shader.vert @@ -0,0 +1,17 @@ +#version 450 + +layout(location=0) in vec3 a_position; +layout(location=1) in vec2 a_tex_coords; + +layout(location=0) out vec2 v_tex_coords; + +layout(set=1, binding=0) +uniform Uniforms { + mat4 u_view_proj; + mat4 u_model[100]; +}; + +void main() { + v_tex_coords = a_tex_coords; + gl_Position = u_view_proj * u_model[gl_InstanceIndex] * vec4(a_position, 1.0); +} \ No newline at end of file diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 6e06f2ad..c6f52531 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -27,6 +27,7 @@ module.exports = { '/beginner/tutorial4-buffer/', '/beginner/tutorial5-textures/', '/beginner/tutorial6-uniforms/', + '/beginner/tutorial7-instancing/', ], }, { diff --git a/docs/beginner/tutorial7-instancing/README.md b/docs/beginner/tutorial7-instancing/README.md new file mode 100644 index 00000000..d5502e43 --- /dev/null +++ b/docs/beginner/tutorial7-instancing/README.md @@ -0,0 +1,252 @@ +# Instancing + +Up to this point we've been drawing just one object. Most games have hundreds of objects on screen at the same time. If we wanted to draw multiple instances of our model, we could copy the vertex buffer and modify it's vertices to be in the right place, but this would be hilariously inefficient. We have our model, and we now how to position it in 3d space with a matrix, like we did the camera, so all we have to do is change the matrix we're using when we draw. + +## The naive method + +First let's modify `Uniforms` to include a `model` property. + +```rust +#[repr(C)] +#[derive(Debug, Copy, Clone)] +struct Uniforms { + view_proj: cgmath::Matrix4, + model: cgmath::Matrix4, // NEW! +} + +impl Uniforms { + fn new() -> Self { + use cgmath::SquareMatrix; + Self { + view_proj: cgmath::Matrix4::identity(), + model: cgmath::Matrix4::identity(), // NEW! + } + } + + fn update_view_proj(&mut self, camera: &Camera) { + self.view_proj = OPENGL_TO_WGPU_MATRIX * camera.build_view_projection_matrix(); + } +} +``` + +With that let's introduce another struct for our instances. We'll use it to store the position and rotation of our instances. We'll also have a method to convert our instance data into a matrix that we can give to `Uniforms`. + +```rust +struct Instance { + position: cgmath::Vector3, + rotation: cgmath::Quaternion, +} + +impl Instance { + fn to_matrix(&self) -> cgmath::Matrix4 { + cgmath::Matrix4::from_translation(self.position) + * cgmath::Matrix4::from(self.rotation) + } +} +``` + +Next we'll add `instances: Vec,` to `State` and create our instances in new with the following in `new()`. + +```rust +// ... + +// add these at the top of the file +const NUM_INSTANCES_PER_ROW: u32 = 10; +const NUM_INSTANCES: u32 = NUM_INSTANCES_PER_ROW * NUM_INSTANCES_PER_ROW; +const INSTANCE_DISPLACEMENT: cgmath::Vector3 = cgmath::Vector3::new(NUM_INSTANCES_PER_ROW as f32 * 0.5, 0.0, NUM_INSTANCES_PER_ROW as f32 * 0.5); + +// make a 10 by 10 grid of objects +let instances = (0..NUM_INSTANCES_PER_ROW).flat_map(|z| { + (0..NUM_INSTANCES_PER_ROW).map(move |x| { + let position = cgmath::Vector3 { x: x as f32, y: 0.0, z: z as f32 } - INSTANCE_DISPLACEMENT; + + let rotation = if position.is_zero() { + // this is needed so an object at (0, 0, 0) won't get scaled to zero + // as Quaternions can effect scale if they're not create correctly + cgmath::Quaternion::from_axis_angle(cgmath::Vector3::unit_z(), cgmath::Deg(0.0)) + } else { + cgmath::Quaternion::from_axis_angle(position.clone().normalize(), cgmath::Deg(45.0)) + }; + + Instance { + position, rotation, + } + }) +}).collect(); + +// ... + +Self { + // ... + instances, +} +``` + +Now that that's done, we need to update `shader.vert` to use the model matrix passed in through `Uniforms`. + +```glsl +#version 450 + +layout(location=0) in vec3 a_position; +layout(location=1) in vec2 a_tex_coords; + +layout(location=0) out vec2 v_tex_coords; + +layout(set=1, binding=0) +uniform Uniforms { + mat4 u_view_proj; + mat4 u_model; // NEW! +}; + +void main() { + v_tex_coords = a_tex_coords; + gl_Position = u_view_proj * u_model * vec4(a_position, 1.0); // UPDATED! +} +``` + +If you run the program now, you won't see anything different. That's because we aren't actually updating the uniform buffer at all. Using our current method, we need to update the uniform buffer for every instance we draw. We'll do this in `render()` with something like the following. + +```rust +for instance in &self.instances { + // 1. + self.uniforms.model = instance.to_matrix(); + let staging_buffer = self.device + .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC) + .fill_from_slice(&[self.uniforms]); + encoder.copy_buffer_to_buffer(&staging_buffer, 0, &self.uniform_buffer, 0, std::mem::size_of::() as wgpu::BufferAddress); + + // 2. + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[ + wgpu::RenderPassColorAttachmentDescriptor { + attachment: &frame.view, + resolve_target: None, + load_op: wgpu::LoadOp::Load, // 3. + store_op: wgpu::StoreOp::Store, + clear_color: wgpu::Color { + r: 0.1, + g: 0.2, + b: 0.3, + a: 1.0, + }, + } + ], + depth_stencil_attachment: None, + }); + + render_pass.set_pipeline(&self.render_pipeline); + render_pass.set_bind_group(0, &self.diffuse_bind_group, &[]); + render_pass.set_bind_group(1, &self.uniform_bind_group, &[]); + render_pass.set_vertex_buffers(0, &[(&self.vertex_buffer, 0)]); + render_pass.set_index_buffer(&self.index_buffer, 0); + render_pass.draw_indexed(0..self.num_indices, 0, 0..1); +} +``` + +Some things to note: +1. We're creating a hundred buffers a frame. This is inefficent, but we'll cover better ways of doing this later in this tutorial. +2. We have to create a new render pass per instance, as we can't modify the uniform buffer while we have one active. +3. We use `LoadOp::Load` here to prevent the render pass from clearing the entire screen after each draw. This means we lose our clear color. This makes the background black on my machine, but it may be filled with garbage data on yours. We can fix this by added another render pass before the loop. + +```rust +{ + encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[ + wgpu::RenderPassColorAttachmentDescriptor { + attachment: &frame.view, + resolve_target: None, + load_op: wgpu::LoadOp::Clear, + store_op: wgpu::StoreOp::Store, + clear_color: wgpu::Color { + r: 0.1, + g: 0.2, + b: 0.3, + a: 1.0, + }, + } + ], + depth_stencil_attachment: None, + }); +} +``` + +We should get something that looks like this when we're done. + +![A beautiful forest](./forest.png) + +If you haven't guessed already, this way of instancing is not the best. It requires hundreds of render passes, hundereds of staging buffers, and an extra render pass just to get the clear color working again. Cleary there must be a better way. + +## A better way - uniform arrays + +Since GLSL is based on C, it supports arrays. We can leverage this by store *all* of the instance matrices in the `Uniforms` struct. We need to make this change on the Rust side, as well as in our shader. + +```rust +#[repr(C)] +#[derive(Copy, Clone)] +struct Uniforms { + view_proj: cgmath::Matrix4, + model: [cgmath::Matrix4; NUM_INSTANCES as usize], +} +``` + +```glsl +#version 450 + +layout(location=0) in vec3 a_position; +layout(location=1) in vec2 a_tex_coords; + +layout(location=0) out vec2 v_tex_coords; + +layout(set=1, binding=0) +uniform Uniforms { + mat4 u_view_proj; + mat4 u_model[100]; +}; + +void main() { + v_tex_coords = a_tex_coords; + // gl_InstanceIndex what index we're currently on + gl_Position = u_view_proj * u_model[gl_InstanceIndex] * vec4(a_position, 1.0); +} +``` + +Note that we're using an array, *not a `Vec`*. `Vec`s are basically pointers to an array on the heap. Our graphics card doesn't know how follow a pointer to the heap, so our data needs to be stored inline. + +`Uniforms::new()` will change slightly as well. + +```rust +fn new() -> Self { + Self { + view_proj: cgmath::Matrix4::identity(), + model: [cgmath::Matrix4::identity(); NUM_INSTANCES as usize], + } +} +``` + +We need to update our model matrices in `State::update()` before we create the `staging_buffer`. + +```rust +for (i, instance) in self.instances.iter().enumerate() { + self.uniforms.model[i] = instance.to_matrix(); +} +``` + +Lastly we need to change our render code. Fortunately, it's a lot simpler than the before. In fact we can use the code from last tutorial and just change our draw call. + +```rust +render_pass.draw_indexed(0..self.num_indices, 0, 0..NUM_INSTANCES); +``` + +You'll remember that the 3rd parameter in `draw_indexed` is the instance range. This controls how many times our object will be drawn. This is where our shader gets the value for `gl_InstanceIndex`. + +Running the program now won't change anything visually from our last example, but the framerate will be better. + +This technique has its drawbacks. +1. We can't use a `Vec` like we've mentioned before +2. We're limited in the number of instances we can process at a time requiring use to cap it at some abitrary number, or render things in "batches" + +## Another better way - instance buffers + +When we created the `VertexBufferDescriptor` for our model, it required a `step_mode` field. We specified + + \ No newline at end of file diff --git a/docs/beginner/tutorial7-instancing/forest.png b/docs/beginner/tutorial7-instancing/forest.png new file mode 100644 index 0000000000000000000000000000000000000000..e74a9e741684eaef2d08ea2ba44726352619e305 GIT binary patch literal 127062 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

sDi4fD>*=m;mr^)pW3ug}i!k)lZJ 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