WebGPU Voxel Game

Tutorial 6: Uniform buffers and a 3d camera

j0.lol a434961d 64bde72b

+10 -145
Cargo.lock
··· 19 19 checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" 20 20 21 21 [[package]] 22 - name = "addr2line" 23 - version = "0.24.2" 24 - source = "registry+https://github.com/rust-lang/crates.io-index" 25 - checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 26 - dependencies = [ 27 - "gimli", 28 - ] 29 - 30 - [[package]] 31 22 name = "adler2" 32 23 version = "2.0.0" 33 24 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 137 128 checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" 138 129 139 130 [[package]] 140 - name = "backtrace" 141 - version = "0.3.74" 142 - source = "registry+https://github.com/rust-lang/crates.io-index" 143 - checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 144 - dependencies = [ 145 - "addr2line", 146 - "cfg-if", 147 - "libc", 148 - "miniz_oxide", 149 - "object", 150 - "rustc-demangle", 151 - "windows-targets 0.52.6", 152 - ] 153 - 154 - [[package]] 155 - name = "backtrace-ext" 156 - version = "0.2.1" 157 - source = "registry+https://github.com/rust-lang/crates.io-index" 158 - checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50" 159 - dependencies = [ 160 - "backtrace", 161 - ] 162 - 163 - [[package]] 164 131 name = "bit-set" 165 132 version = "0.6.0" 166 133 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 196 163 "console_error_panic_hook", 197 164 "console_log", 198 165 "env_logger", 166 + "glam", 199 167 "image", 200 168 "log", 201 - "miette", 202 169 "pollster", 203 170 "thiserror 2.0.11", 204 171 "wasm-bindgen", ··· 605 572 ] 606 573 607 574 [[package]] 608 - name = "gimli" 609 - version = "0.31.1" 610 - source = "registry+https://github.com/rust-lang/crates.io-index" 611 - checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 612 - 613 - [[package]] 614 575 name = "gl_generator" 615 576 version = "0.14.0" 616 577 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 622 583 ] 623 584 624 585 [[package]] 586 + name = "glam" 587 + version = "0.29.2" 588 + source = "registry+https://github.com/rust-lang/crates.io-index" 589 + checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677" 590 + dependencies = [ 591 + "bytemuck", 592 + ] 593 + 594 + [[package]] 625 595 name = "glow" 626 596 version = "0.13.1" 627 597 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 784 754 ] 785 755 786 756 [[package]] 787 - name = "is_ci" 788 - version = "1.2.0" 789 - source = "registry+https://github.com/rust-lang/crates.io-index" 790 - checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" 791 - 792 - [[package]] 793 757 name = "jni" 794 758 version = "0.21.1" 795 759 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 941 905 ] 942 906 943 907 [[package]] 944 - name = "miette" 945 - version = "7.4.0" 946 - source = "registry+https://github.com/rust-lang/crates.io-index" 947 - checksum = "317f146e2eb7021892722af37cf1b971f0a70c8406f487e24952667616192c64" 948 - dependencies = [ 949 - "backtrace", 950 - "backtrace-ext", 951 - "cfg-if", 952 - "miette-derive", 953 - "owo-colors", 954 - "supports-color", 955 - "supports-hyperlinks", 956 - "supports-unicode", 957 - "terminal_size", 958 - "textwrap", 959 - "thiserror 1.0.64", 960 - "unicode-width", 961 - ] 962 - 963 - [[package]] 964 - name = "miette-derive" 965 - version = "7.4.0" 966 - source = "registry+https://github.com/rust-lang/crates.io-index" 967 - checksum = "23c9b935fbe1d6cbd1dac857b54a688145e2d93f48db36010514d0f612d0ad67" 968 - dependencies = [ 969 - "proc-macro2", 970 - "quote", 971 - "syn 2.0.96", 972 - ] 973 - 974 - [[package]] 975 908 name = "miniz_oxide" 976 909 version = "0.8.3" 977 910 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1095 1028 checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" 1096 1029 1097 1030 [[package]] 1098 - name = "object" 1099 - version = "0.36.5" 1100 - source = "registry+https://github.com/rust-lang/crates.io-index" 1101 - checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" 1102 - dependencies = [ 1103 - "memchr", 1104 - ] 1105 - 1106 - [[package]] 1107 1031 name = "once_cell" 1108 1032 version = "1.19.0" 1109 1033 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1128 1052 ] 1129 1053 1130 1054 [[package]] 1131 - name = "owo-colors" 1132 - version = "4.1.0" 1133 - source = "registry+https://github.com/rust-lang/crates.io-index" 1134 - checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56" 1135 - 1136 - [[package]] 1137 1055 name = "parking_lot" 1138 1056 version = "0.12.3" 1139 1057 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1343 1261 checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" 1344 1262 1345 1263 [[package]] 1346 - name = "rustc-demangle" 1347 - version = "0.1.24" 1348 - source = "registry+https://github.com/rust-lang/crates.io-index" 1349 - checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 1350 - 1351 - [[package]] 1352 1264 name = "rustc-hash" 1353 1265 version = "1.1.0" 1354 1266 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1513 1425 checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" 1514 1426 1515 1427 [[package]] 1516 - name = "supports-color" 1517 - version = "3.0.1" 1518 - source = "registry+https://github.com/rust-lang/crates.io-index" 1519 - checksum = "8775305acf21c96926c900ad056abeef436701108518cf890020387236ac5a77" 1520 - dependencies = [ 1521 - "is_ci", 1522 - ] 1523 - 1524 - [[package]] 1525 - name = "supports-hyperlinks" 1526 - version = "3.0.0" 1527 - source = "registry+https://github.com/rust-lang/crates.io-index" 1528 - checksum = "2c0a1e5168041f5f3ff68ff7d95dcb9c8749df29f6e7e89ada40dd4c9de404ee" 1529 - 1530 - [[package]] 1531 - name = "supports-unicode" 1532 - version = "3.0.0" 1533 - source = "registry+https://github.com/rust-lang/crates.io-index" 1534 - checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" 1535 - 1536 - [[package]] 1537 1428 name = "syn" 1538 1429 version = "1.0.109" 1539 1430 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1562 1453 checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" 1563 1454 dependencies = [ 1564 1455 "winapi-util", 1565 - ] 1566 - 1567 - [[package]] 1568 - name = "terminal_size" 1569 - version = "0.4.0" 1570 - source = "registry+https://github.com/rust-lang/crates.io-index" 1571 - checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" 1572 - dependencies = [ 1573 - "rustix", 1574 - "windows-sys 0.59.0", 1575 - ] 1576 - 1577 - [[package]] 1578 - name = "textwrap" 1579 - version = "0.16.1" 1580 - source = "registry+https://github.com/rust-lang/crates.io-index" 1581 - checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" 1582 - dependencies = [ 1583 - "unicode-linebreak", 1584 - "unicode-width", 1585 1456 ] 1586 1457 1587 1458 [[package]] ··· 1693 1564 version = "1.0.13" 1694 1565 source = "registry+https://github.com/rust-lang/crates.io-index" 1695 1566 checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" 1696 - 1697 - [[package]] 1698 - name = "unicode-linebreak" 1699 - version = "0.1.5" 1700 - source = "registry+https://github.com/rust-lang/crates.io-index" 1701 - checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" 1702 1567 1703 1568 [[package]] 1704 1569 name = "unicode-segmentation"
+1 -1
Cargo.toml
··· 14 14 pollster = "0.3.0" 15 15 bytemuck = { version = "1.18.0", features = ["derive"] } 16 16 image = { version = "0.25.5", default-features = false, features = ["png", "jpeg"] } 17 - miette = { version = "7.4.0", features = ["fancy"] } 18 17 thiserror = "2.0.11" 18 + glam = { version = "0.29.2", features = ["bytemuck"] } 19 19 20 20 [lib] 21 21 crate-type = ["cdylib", "rlib"]
+121
src/camera.rs
··· 1 + use glam::{Mat4, Vec3}; 2 + use winit::{ 3 + event::{ElementState, KeyEvent, WindowEvent}, 4 + keyboard::KeyCode, 5 + keyboard::PhysicalKey, 6 + }; 7 + 8 + pub struct Camera { 9 + pub eye: Vec3, 10 + pub target: Vec3, 11 + pub up: Vec3, 12 + pub aspect: f32, 13 + pub fovy: f32, 14 + pub znear: f32, 15 + pub zfar: f32, 16 + } 17 + 18 + impl Camera { 19 + pub fn build_view_projection_matrix(&self) -> Mat4 { 20 + let view = Mat4::look_at_rh(self.eye, self.target, self.up); 21 + let proj = Mat4::perspective_rh(self.fovy, self.aspect, self.znear, self.zfar); 22 + 23 + return proj * view; 24 + } 25 + } 26 + 27 + #[repr(C)] 28 + #[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] 29 + pub struct CameraUniform { 30 + view_proj: Mat4, 31 + } 32 + 33 + impl CameraUniform { 34 + pub fn new() -> Self { 35 + Self { 36 + view_proj: Mat4::IDENTITY, 37 + } 38 + } 39 + 40 + pub fn update_view_proj(&mut self, camera: &Camera) { 41 + self.view_proj = camera.build_view_projection_matrix(); 42 + } 43 + } 44 + 45 + pub struct CameraController { 46 + speed: f32, 47 + buttons: glam::BVec4, // fwd, bwd, lft, rht 48 + } 49 + 50 + impl CameraController { 51 + pub fn new(speed: f32) -> Self { 52 + Self { 53 + speed, 54 + buttons: glam::BVec4::FALSE, 55 + } 56 + } 57 + 58 + pub fn process_events(&mut self, event: &WindowEvent) -> bool { 59 + match event { 60 + WindowEvent::KeyboardInput { 61 + event: 62 + KeyEvent { 63 + state, 64 + physical_key: PhysicalKey::Code(keycode), 65 + .. 66 + }, 67 + .. 68 + } => { 69 + let is_pressed = *state == ElementState::Pressed; 70 + match keycode { 71 + KeyCode::KeyW | KeyCode::ArrowUp => { 72 + self.buttons.x = is_pressed; 73 + true 74 + } 75 + KeyCode::KeyA | KeyCode::ArrowLeft => { 76 + self.buttons.y = is_pressed; 77 + true 78 + } 79 + KeyCode::KeyS | KeyCode::ArrowDown => { 80 + self.buttons.z = is_pressed; 81 + true 82 + } 83 + KeyCode::KeyD | KeyCode::ArrowRight => { 84 + self.buttons.w = is_pressed; 85 + true 86 + } 87 + _ => false, 88 + } 89 + } 90 + _ => false, 91 + } 92 + } 93 + 94 + pub fn update_camera(&self, camera: &mut Camera) { 95 + let forward = camera.target - camera.eye; 96 + let forward_norm = forward.normalize(); 97 + let forward_mag = forward.length(); 98 + 99 + // Prevents glitching when the camera gets too close to the 100 + // center of the scene. 101 + if self.buttons.x && forward_mag > self.speed { 102 + camera.eye += forward_norm * self.speed; 103 + } 104 + if self.buttons.z { 105 + camera.eye -= forward_norm * self.speed; 106 + } 107 + 108 + let right = forward_norm.cross(camera.up); 109 + 110 + // Redo radius calc in case the forward/backward is pressed. 111 + let forward = camera.target - camera.eye; 112 + let forward_mag = forward.length(); 113 + 114 + if self.buttons.w { 115 + camera.eye = camera.target - (forward + right * self.speed).normalize() * forward_mag; 116 + } 117 + if self.buttons.y { 118 + camera.eye = camera.target - (forward - right * self.speed).normalize() * forward_mag; 119 + } 120 + } 121 + }
+127 -46
src/lib.rs
··· 1 + #![allow(rust_analyzer::inactive_code)] 2 + 3 + mod camera; 1 4 mod texture; 2 5 3 6 use wgpu::util::DeviceExt; 4 7 use winit::{ 8 + dpi::PhysicalSize, 5 9 event::*, 6 10 event_loop::EventLoop, 7 11 keyboard::{KeyCode, PhysicalKey}, ··· 40 44 } 41 45 42 46 const VERTICES: &[Vertex] = &[ 43 - Vertex { position: [-0.0868241, 0.49240386, 0.0], tex_coords: [0.4131759, 0.00759614], }, // A 44 - Vertex { position: [-0.49513406, 0.06958647, 0.0], tex_coords: [0.0048659444, 0.43041354], }, // B 45 - Vertex { position: [-0.21918549, -0.44939706, 0.0], tex_coords: [0.28081453, 0.949397], }, // C 46 - Vertex { position: [0.35966998, -0.3473291, 0.0], tex_coords: [0.85967, 0.84732914], }, // D 47 - Vertex { position: [0.44147372, 0.2347359, 0.0], tex_coords: [0.9414737, 0.2652641], }, // E 47 + Vertex { 48 + position: [-0.0868241, 0.49240386, 0.0], 49 + tex_coords: [0.4131759, 0.00759614], 50 + }, // A 51 + Vertex { 52 + position: [-0.49513406, 0.06958647, 0.0], 53 + tex_coords: [0.0048659444, 0.43041354], 54 + }, // B 55 + Vertex { 56 + position: [-0.21918549, -0.44939706, 0.0], 57 + tex_coords: [0.28081453, 0.949397], 58 + }, // C 59 + Vertex { 60 + position: [0.35966998, -0.3473291, 0.0], 61 + tex_coords: [0.85967, 0.84732914], 62 + }, // D 63 + Vertex { 64 + position: [0.44147372, 0.2347359, 0.0], 65 + tex_coords: [0.9414737, 0.2652641], 66 + }, // E 48 67 ]; 49 68 50 - const INDICES: &[u16] = &[ 51 - 0, 1, 4, 52 - 1, 2, 4, 53 - 2, 3, 4, 54 - ]; 69 + const INDICES: &[u16] = &[0, 1, 4, 1, 2, 4, 2, 3, 4]; 70 + 71 + use glam::{vec3, Vec3}; 55 72 56 73 struct State<'srfc> { 57 74 surface: wgpu::Surface<'srfc>, ··· 65 82 num_indices: u32, 66 83 diffuse_bind_group: wgpu::BindGroup, 67 84 diffuse_texture: texture::Texture, 85 + camera: camera::Camera, 86 + camera_controller: camera::CameraController, 87 + camera_uniform: camera::CameraUniform, 88 + camera_buffer: wgpu::Buffer, 89 + camera_bind_group: wgpu::BindGroup, 68 90 69 91 // Window must be declared after surface 70 92 // so it gets dropped after it ··· 78 100 79 101 impl<'a> State<'a> { 80 102 async fn new(window: &'a Window) -> State<'a> { 103 + #[cfg(not(target_arch = "wasm32"))] 81 104 let size = window.inner_size(); 105 + #[cfg(target_arch = "wasm32")] 106 + let size = PhysicalSize { 107 + width: 100, 108 + height: 100, 109 + }; 82 110 83 111 // 0 size can cause app to crash, better to enforce this maybe. 84 112 // crashes on webgl?? ··· 89 117 #[cfg(not(target_arch = "wasm32"))] 90 118 backends: wgpu::Backends::PRIMARY, 91 119 92 - // WebGPU is sadly not supported in most browsers (yet!) 120 + // WebGPU is sadly not supported in most browsers (yet!) 93 121 #[cfg(target_arch = "wasm32")] 94 - backends: wgpu::Backends::GL, 122 + backends: wgpu::Backends::BROWSER_WEBGPU | wgpu::Backends::GL, 95 123 96 124 ..Default::default() 97 125 }); ··· 151 179 surface.configure(&device, &config); 152 180 153 181 let diffuse_bytes = include_bytes!("happy-tree.png"); 154 - let diffuse_texture = texture::Texture::from_bytes(&device, &queue, diffuse_bytes, "happy_tree.png").unwrap(); 182 + let diffuse_texture = 183 + texture::Texture::from_bytes(&device, &queue, diffuse_bytes, "happy_tree.png").unwrap(); 155 184 156 - let texture_bind_group_layout = device.create_bind_group_layout( 157 - &wgpu::BindGroupLayoutDescriptor { 185 + let texture_bind_group_layout = 186 + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { 158 187 entries: &[ 159 188 wgpu::BindGroupLayoutEntry { 160 189 binding: 0, ··· 162 191 ty: wgpu::BindingType::Texture { 163 192 multisampled: false, 164 193 view_dimension: wgpu::TextureViewDimension::D2, 165 - sample_type: wgpu::TextureSampleType::Float { 166 - filterable: true 167 - }, 194 + sample_type: wgpu::TextureSampleType::Float { filterable: true }, 168 195 }, 169 196 count: None, 170 197 }, ··· 173 200 visibility: wgpu::ShaderStages::FRAGMENT, 174 201 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), 175 202 count: None, 176 - } 203 + }, 177 204 ], 178 - label: Some("texture_bind_group_layout") 179 - } 180 - ); 205 + label: Some("texture_bind_group_layout"), 206 + }); 207 + 208 + let diffuse_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { 209 + layout: &texture_bind_group_layout, 210 + entries: &[ 211 + wgpu::BindGroupEntry { 212 + binding: 0, 213 + resource: wgpu::BindingResource::TextureView(&diffuse_texture.view), 214 + }, 215 + wgpu::BindGroupEntry { 216 + binding: 1, 217 + resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler), 218 + }, 219 + ], 220 + label: Some("diffuse_bind_group"), 221 + }); 222 + 223 + let camera = camera::Camera { 224 + eye: vec3(0., 1., 2.), 225 + target: Vec3::ZERO, 226 + up: Vec3::Y, 227 + aspect: config.width as f32 / config.height as f32, 228 + fovy: 45.0, 229 + znear: 0.1, 230 + zfar: 100.0, 231 + }; 181 232 182 - let diffuse_bind_group = device.create_bind_group( 183 - &wgpu::BindGroupDescriptor { 184 - layout: &texture_bind_group_layout, 185 - entries: &[ 186 - wgpu::BindGroupEntry { 187 - binding: 0, 188 - resource: wgpu::BindingResource::TextureView(&diffuse_texture.view), 233 + let camera_controller = camera::CameraController::new(0.2); 234 + let mut camera_uniform = camera::CameraUniform::new(); 235 + camera_uniform.update_view_proj(&camera); 236 + 237 + let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { 238 + label: Some("Camera Buffer"), 239 + contents: bytemuck::cast_slice(&[camera_uniform]), 240 + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, 241 + }); 242 + 243 + let camera_bind_group_layout = 244 + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { 245 + entries: &[wgpu::BindGroupLayoutEntry { 246 + binding: 0, 247 + visibility: wgpu::ShaderStages::VERTEX, 248 + ty: wgpu::BindingType::Buffer { 249 + ty: wgpu::BufferBindingType::Uniform, 250 + has_dynamic_offset: false, 251 + min_binding_size: None, 189 252 }, 190 - wgpu::BindGroupEntry { 191 - binding: 1, 192 - resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler), 193 - } 194 - ], 195 - label: Some("diffuse_bind_group"), 196 - } 197 - ); 253 + count: None, 254 + }], 255 + label: Some("camera_bind_group_layout"), 256 + }); 198 257 258 + let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { 259 + layout: &camera_bind_group_layout, 260 + entries: &[wgpu::BindGroupEntry { 261 + binding: 0, 262 + resource: camera_buffer.as_entire_binding(), 263 + }], 264 + label: Some("camera_bind_group"), 265 + }); 199 266 200 267 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { 201 268 label: Some("Shader"), ··· 205 272 let render_pipeline_layout = 206 273 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { 207 274 label: Some("Render Pipeline Layout"), 208 - bind_group_layouts: &[&texture_bind_group_layout], 275 + bind_group_layouts: &[&texture_bind_group_layout, &camera_bind_group_layout], 209 276 push_constant_ranges: &[], 210 277 }); 211 278 ··· 253 320 usage: wgpu::BufferUsages::VERTEX, 254 321 }); 255 322 256 - let index_buffer = device.create_buffer_init( 257 - &wgpu::util::BufferInitDescriptor { 258 - label: Some("Index Buffer"), 259 - contents: bytemuck::cast_slice(INDICES), 260 - usage: wgpu::BufferUsages::INDEX, 261 - } 262 - ); 323 + let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { 324 + label: Some("Index Buffer"), 325 + contents: bytemuck::cast_slice(INDICES), 326 + usage: wgpu::BufferUsages::INDEX, 327 + }); 263 328 264 329 let num_indices = INDICES.len() as u32; 265 330 ··· 276 341 num_indices, 277 342 diffuse_bind_group, 278 343 diffuse_texture, 344 + camera, 345 + camera_controller, 346 + camera_uniform, 347 + camera_buffer, 348 + camera_bind_group, 279 349 clear_color: wgpu::Color { 280 350 r: 0.1, 281 351 g: 0.2, ··· 301 371 } 302 372 303 373 fn input(&mut self, event: &WindowEvent) -> bool { 374 + self.camera_controller.process_events(event); 375 + 304 376 match event { 305 377 WindowEvent::CursorMoved { 306 378 device_id, ··· 321 393 false 322 394 } 323 395 324 - fn update(&mut self) {} 396 + fn update(&mut self) { 397 + self.camera_controller.update_camera(&mut self.camera); 398 + self.camera_uniform.update_view_proj(&self.camera); 399 + self.queue.write_buffer( 400 + &self.camera_buffer, 401 + 0, 402 + bytemuck::cast_slice(&[self.camera_uniform]), 403 + ); 404 + } 325 405 326 406 fn render(&mut self) -> Result<(), wgpu::SurfaceError> { 327 407 let output = self.surface.get_current_texture()?; ··· 353 433 354 434 render_pass.set_pipeline(&self.render_pipeline); 355 435 render_pass.set_bind_group(0, &self.diffuse_bind_group, &[]); 436 + render_pass.set_bind_group(1, &self.camera_bind_group, &[]); 356 437 render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); 357 438 render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16); 358 439
+7 -1
src/shader.wgsl
··· 1 1 // Vertex shader 2 2 3 + struct CameraUniform { 4 + view_proj: mat4x4<f32> 5 + }; 6 + @group(1) @binding(0) 7 + var<uniform> camera: CameraUniform; 8 + 3 9 struct VertexInput { 4 10 @location(0) position: vec3<f32>, 5 11 @location(1) tex_coords: vec2<f32>, ··· 16 22 ) -> VertexOutput { 17 23 var out: VertexOutput; 18 24 out.tex_coords = model.tex_coords; 19 - out.clip_position = vec4<f32>(model.position, 1.0); 25 + out.clip_position = camera.view_proj * vec4<f32>(model.position, 1.0); 20 26 return out; 21 27 } 22 28
+5 -6
src/texture.rs
··· 1 1 use image::GenericImageView; 2 - use miette::{IntoDiagnostic, Report, Result}; 3 2 4 3 pub struct Texture { 5 4 #[allow(unused)] ··· 14 13 queue: &wgpu::Queue, 15 14 bytes: &[u8], 16 15 label: &str, 17 - ) -> Result<Self> { 18 - let img = image::load_from_memory(bytes).into_diagnostic()?; 19 - Self::from_image(device, queue, &img, Some(label)) 16 + ) -> Result<Self, image::ImageError> { 17 + let img = image::load_from_memory(bytes)?; 18 + Ok(Self::from_image(device, queue, &img, Some(label))) 20 19 } 21 20 22 21 fn from_image( ··· 24 23 queue: &wgpu::Queue, 25 24 img: &image::DynamicImage, 26 25 label: Option<&str>, 27 - ) -> Result<Self> { 26 + ) -> Self { 28 27 let rgba = img.to_rgba8(); 29 28 let dimensions = img.dimensions(); 30 29 ··· 76 75 } 77 76 ); 78 77 79 - Ok(Self { texture, view, sampler }) 78 + Self { texture, view, sampler } 80 79 } 81 80 }