WebGPU Voxel Game

More tweaks to shadowmap & web fixes

j0.lol 7d5230da 410e0e52

Changed files
+67 -205
.nova
Publishing
src
+7 -1
.nova/Publishing/J0.lol.json
··· 9 9 "Cargo.toml", 10 10 "justfile", 11 11 "cube.blend", 12 - "cube.blend1" 12 + "cube.blend1", 13 + ".first-init", 14 + ".idea", 15 + "gen", 16 + "mobile.toml", 17 + "save.bl0ck", 18 + ".gitignore" 13 19 ], 14 20 "remotePath" : "\/var\/www\/vps.j0.lol\/fyptest", 15 21 "server" : "J0.lol",
+4 -1
Cargo.toml
··· 22 22 egui = "0.31" 23 23 egui-wgpu = "0.31" 24 24 egui-winit = { version = "0.31", default-features = false } 25 - rand = "0.9.0" 26 25 bincode = "2.0.0-RC.3" 27 26 28 27 [build-dependencies] ··· 30 29 31 30 [lib] 32 31 crate-type = ["cdylib", "rlib"] 32 + 33 + [target.'cfg(not(target_arch = "wasm32"))'.dependencies] 34 + rand = "0.9.0" 35 + 33 36 34 37 [target.'cfg(target_arch = "wasm32")'.dependencies] 35 38 console_error_panic_hook = "0.1.6"
+17 -198
src/gfx.rs
··· 42 42 pub clear_color: wgpu::Color, 43 43 pub wireframe: bool, 44 44 pub shadows: bool, 45 + pub sun_speed: f32, 45 46 } 46 47 pub struct ObjectState { 47 48 pub model: model::Model, ··· 214 215 pub async fn new(window: std::sync::Arc<winit::window::Window>) -> Self { 215 216 // 0 size can cause web-app to crash, better to enforce this than not. 216 217 let size = if cfg!(target_arch = "wasm32") { 217 - winit::dpi::PhysicalSize { 218 + PhysicalSize { 218 219 width: WASM_WIN_SIZE.0, 219 220 height: WASM_WIN_SIZE.1, 220 221 } ··· 331 332 let mut camera_uniform = camera::CameraUniform::new(); 332 333 camera_uniform.update_view_proj(&camera); 333 334 334 - // let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { 335 - // layout: &camera_bind_group_layout, 336 - // entries: &[wgpu::BindGroupEntry { 337 - // binding: 0, 338 - // resource: camera_buffer.as_entire_binding(), 339 - // }], 340 - // label: Some("camera_bind_group"), 341 - // }); 342 335 let camera_state = CameraState { 343 336 object: camera, 344 337 controller: camera_controller, ··· 380 373 let mut light_uniform = camera::CameraUniform::new(); 381 374 light_uniform.update_view_proj(&light); 382 375 383 - //let light_uniform = LightUniform::new(Vec3::splat(90.0).with_y(40.0), vec3(1.0, 1.0, 1.0)); 384 - 385 - log::info!("Shadowmap setup!"); 386 - let shadow_sampler = device.create_sampler(&wgpu::SamplerDescriptor { 387 - label: Some("Shadow Sampler"), 388 - address_mode_u: wgpu::AddressMode::ClampToEdge, 389 - address_mode_v: wgpu::AddressMode::ClampToEdge, 390 - address_mode_w: wgpu::AddressMode::ClampToEdge, 391 - mag_filter: wgpu::FilterMode::Linear, 392 - min_filter: wgpu::FilterMode::Linear, 393 - mipmap_filter: wgpu::FilterMode::Nearest, 394 - compare: Some(wgpu::CompareFunction::LessEqual), 395 - ..Default::default() 396 - }); 397 - 398 - log::info!("asdfasdgf"); 399 - 400 376 let shadow_map = texture::Texture::create_depth_texture( 401 377 &device, 402 378 &surface_config, 403 - Some((1000, 1000)), 379 + Some({ 380 + if cfg!(target_arch = "wasm32") { 381 + (2048, 2048) 382 + } else { 383 + (5000, 5000) 384 + } 385 + }), 404 386 "Shadow Map", 405 387 ); 406 - // let shadow_texture = device.create_texture(&wgpu::TextureDescriptor { 407 - // size: wgpu::Extent3d { 408 - // width: 1024, 409 - // height: 1024, 410 - // depth_or_array_layers: 10, 411 - // }, 412 - // mip_level_count: 1, 413 - // sample_count: 1, 414 - // dimension: wgpu::TextureDimension::D2, 415 - // format: wgpu::TextureFormat::Depth32Float, 416 - // usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, 417 - // label: Some("Shadow texture"), 418 - // view_formats: &[], 419 - // }); 420 - // For forward pass 421 - // let shadow_view = shadow_texture.create_view(&wgpu::TextureViewDescriptor::default()); 422 - 423 - log::info!("alskjgsdl;kfgjs;dkjl"); 424 - // For light in shadow pass 425 - // let light_shadow_map = shadow_texture.create_view(&wgpu::TextureViewDescriptor { 426 - // label: Some("Shadow Map"), 427 - // format: Some(wgpu::TextureFormat::Depth32Float), 428 - // dimension: Some(wgpu::TextureViewDimension::D2), 429 - // usage: None, 430 - // aspect: wgpu::TextureAspect::All, 431 - // base_mip_level: 0, 432 - // mip_level_count: None, 433 - // base_array_layer: 0 as u32, 434 - // array_layer_count: Some(1), 435 - // }); 436 - 437 - // let shadow_map_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { 438 - // label: Some("Shadow Map Buffer"), 439 - // contents: bytemuck::cast_slice(&[camera_uniform]), 440 - // usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, 441 - // }); 442 - 443 - // let shadow_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { 444 - // entries: &[wgpu::BindGroupLayoutEntry { 445 - // binding: 0, 446 - // visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT, 447 - // ty: wgpu::BindingType::Buffer { 448 - // ty: wgpu::BufferBindingType::Uniform, 449 - // has_dynamic_offset: false, 450 - // min_binding_size: None, 451 - // }, 452 - // count: None, 453 - // }], 454 - // label: None, 455 - // }); 456 - // let shadow_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { 457 - // layout: &shadow_bgl, 458 - // entries: &[wgpu::BindGroupEntry { 459 - // binding: 0, 460 - // resource: light_buffer.as_entire_binding(), 461 - // }], 462 - // label: None, 463 - // }); 464 - 465 - log::info!("Pipeline setup!"); 466 - // New stuff goes above here! 467 - 468 - log::info!("Pipeline/Globals setup!"); 469 - // let render_pipeline = { 470 - // let shader = wgpu::ShaderModuleDescriptor { 471 - // label: Some("Normal Shader"), 472 - // source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()), 473 - // }; 474 - // create_render_pipeline( 475 - // &device, 476 - // &render_pipeline_layout, 477 - // surface_config.format, 478 - // Some(texture::Texture::DEPTH_FORMAT), 479 - // &[model::ModelVertex::desc(), InstanceRaw::desc()], 480 - // shader, 481 - // wgpu::PolygonMode::Fill, 482 - // Some("Normal Render Pipeline"), 483 - // ) 484 - // }; 485 - // 486 - // //let fill_pipeline = render_pipeline(wgpu::PolygonMode::Fill); 487 - // let wireframe_render_pipeline = if (device.features() & wgpu::Features::POLYGON_MODE_LINE) 488 - // == wgpu::Features::POLYGON_MODE_LINE 489 - // { 490 - // Some({ 491 - // let shader = wgpu::ShaderModuleDescriptor { 492 - // label: Some("Normal Shader"), 493 - // source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()), 494 - // }; 495 - // create_render_pipeline( 496 - // &device, 497 - // &render_pipeline_layout, 498 - // surface_config.format, 499 - // Some(texture::Texture::DEPTH_FORMAT), 500 - // &[model::ModelVertex::desc(), InstanceRaw::desc()], 501 - // shader, 502 - // wgpu::PolygonMode::Line, 503 - // Some("Normal Render Pipeline"), 504 - // ) 505 - // }) 506 - // } else { 507 - // None 508 - // }; 509 388 510 389 fn bgl_t(binding: u32) -> wgpu::BindGroupLayoutEntry { 511 390 wgpu::BindGroupLayoutEntry { ··· 736 615 a: 1.0, 737 616 }, 738 617 shadows: true, 618 + sun_speed: 0.001, 739 619 }, 740 620 object: ObjectState { 741 621 model: obj_model, ··· 751 631 } 752 632 } 753 633 754 - pub(crate) fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) { 634 + pub(crate) fn resize(&mut self, new_size: PhysicalSize<u32>) { 755 635 if new_size.width == 0 || new_size.height == 0 { 756 636 return; 757 637 } ··· 888 768 0..self.object.instances.len() as u32, 889 769 &[bind_group], 890 770 ); 891 - 892 - drop(render_pass); 893 771 } 894 772 895 773 encoder.pop_debug_group(); 896 774 897 775 encoder.push_debug_group("Forward pass"); 898 - 899 776 { 900 777 let Pass { 901 778 pipeline, ··· 928 805 render_pass.set_vertex_buffer(1, self.object.instance_buffer.slice(..)); 929 806 930 807 use crate::gfx::model::DrawLight; 931 - // render_pass.set_pipeline(pipeline.two().1); 932 - // render_pass.draw_light_model( 933 - // &self.object.model, 934 - // &self.camera.bind_group, 935 - // &self.light.bind_group, 936 - // ); 937 - 938 - // render_pass.set_pipeline(&self.render_pipelines.cam); 939 - // render_pass.draw_model_instanced( 940 - // &self.object.model, 941 - // 0..self.object.instances.len() as u32, 942 - // &self.camera.bind_group, 943 - // &self.light.bind_group, 944 - // ); 945 - 946 - /* render_pass.set_pipeline( 947 - self.interact 948 - .wireframe 949 - .then_some(self.render_pipelines.camera_wireframe.as_ref()) 950 - .flatten() 951 - .unwrap_or(&self.render_pipelines.camera), 952 - );*/ 953 808 954 809 render_pass.set_pipeline(pipeline.two().1); 955 - 956 810 render_pass.draw_light_model(&self.object.model, &[bind_group]); 957 811 958 812 render_pass.set_pipeline(pipeline.two().0); 959 - 960 813 render_pass.draw_model_instanced( 961 814 &self.object.model, 962 815 0..self.object.instances.len() as u32, 963 816 &[bind_group], 964 817 ); 965 - 966 - drop(render_pass); 967 818 } 968 - 969 - // drop render pass before we submit to drop the mut borrow on encoder 970 819 971 820 encoder.pop_debug_group(); 972 821 ··· 1003 852 } 1004 853 1005 854 pub fn update(&mut self, world: &mut World) { 855 + // Camera update 1006 856 self.camera 1007 857 .controller 1008 858 .update_camera(&mut self.camera.object); 1009 859 1010 - // dbg 1011 - //self.camera.object.eye = self.light.uniform.view_pos; 1012 - 1013 860 self.camera.uniform.update_view_proj(&self.camera.object); 1014 861 self.queue.write_buffer( 1015 862 &self.forward_pass.uniform_bufs[0], ··· 1017 864 bytemuck::cast_slice(&[self.camera.uniform]), 1018 865 ); 1019 866 1020 - self.light.object.eye = 1021 - Quat::from_axis_angle(vec3(0.0, 0.0, 1.0), 0.01) * self.light.object.eye; 867 + // Light update 868 + self.light.object.eye = Quat::from_axis_angle(vec3(0.0, 0.0, 1.0), self.interact.sun_speed) 869 + * self.light.object.eye; 1022 870 self.light.uniform.update_view_proj(&self.light.object); 1023 - 1024 - // let old_position: Vec3 = self.light.uniform.view_pos; 1025 - // self.light.uniform.view_pos = 1026 - // Quat::from_axis_angle(vec3(0.0, 1.0, 0.0), 0.01) * old_position; 1027 - // self.light 1028 - // .uniform 1029 - // .update_view_proj(self.surface_config.width as f32 / self.surface_config.height as f32); 1030 871 1031 872 self.queue.write_buffer( 1032 873 &self.forward_pass.uniform_bufs[1], 1033 874 0, 1034 875 bytemuck::cast_slice(&[self.light.uniform]), 1035 876 ); 877 + 878 + // Object update 1036 879 if self.object.remake { 1037 880 self.update_instance_buf(&world.map); 1038 881 } ··· 1040 883 1041 884 pub fn input(&mut self, event: &WindowEvent, window_size: PhysicalSize<u32>) -> bool { 1042 885 self.camera.controller.process_events(event); 1043 - 1044 - // Deprecated! Replaced with EGUI debug ui. 1045 - // match event { 1046 - // WindowEvent::CursorMoved { 1047 - // device_id: _, 1048 - // position, 1049 - // } => {} 1050 - // WindowEvent::KeyboardInput { 1051 - // event: 1052 - // KeyEvent { 1053 - // state, 1054 - // physical_key: PhysicalKey::Code(keycode), 1055 - // .. 1056 - // }, 1057 - // .. 1058 - // } => { 1059 - // let is_pressed = *state == ElementState::Pressed; 1060 - // if *keycode == KeyCode::KeyL && is_pressed { 1061 - // self.interact.wireframe = !self.interact.wireframe; 1062 - // return true; 1063 - // } 1064 - // } 1065 - // _ => {} 1066 - // } 1067 886 1068 887 false 1069 888 }
+7 -3
src/gui.rs
··· 193 193 ); 194 194 ui.separator(); 195 195 196 - ui.checkbox( 197 - &mut gfx.interact.shadows, 198 - "Light shadowing (via shadow-maps)", 196 + // ui.checkbox( 197 + // &mut gfx.interact.shadows, 198 + // "Light shadowing (via shadow-maps)", 199 + // ); 200 + 201 + ui.add( 202 + egui::Slider::new(&mut gfx.interact.sun_speed, 0.000001..=0.01).text("Sun rotational speed (radians per frame)").logarithmic(true), 199 203 ); 200 204 201 205 ui.separator();
+25 -1
src/shader.wgsl
··· 105 105 let view_dir = normalize(camera.view_pos.xyz - in.world_position.xyz); 106 106 let half_dir = normalize(view_dir + light_dir); 107 107 108 + // https://github.com/mcclure/webgpu-tutorial-rs/blob/webgpu-tutorial/src/shader.wgsl 109 + // This one-dimensional separable blur filter samples five points and averages them by different amounts. 110 + // If we do it on two separate axes, we get a 2d blur. 111 + // Weights and offsets taken from http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/ 112 + 113 + // The weights for the center, one-point-out, and two-point-out samples 114 + const WEIGHT0 = 0.2270270270; 115 + const WEIGHT1 = 0.3162162162; 116 + const WEIGHT2 = 0.0702702703; 117 + 118 + // The distances-from-center for the samples 119 + const OFFSET1 = 1.3846153846; 120 + const OFFSET2 = 3.2307692308; 121 + 122 + let blur_resolution = vec2<f32>(60.0, 60.0); 123 + 124 + var shadow_guassian = 0.0; 125 + shadow_guassian += fetch_shadow(light.view_proj* vec4<f32>(in.world_position, 1.0)) * WEIGHT0; 126 + shadow_guassian += fetch_shadow(light.view_proj* vec4<f32>(in.world_position.xy + blur_resolution * OFFSET1, in.world_position.z, 1.0)) * WEIGHT1; 127 + shadow_guassian += fetch_shadow(light.view_proj* vec4<f32>(in.world_position.xy - blur_resolution * OFFSET1, in.world_position.z, 1.0)) * WEIGHT1; 128 + shadow_guassian += fetch_shadow(light.view_proj* vec4<f32>(in.world_position.xy + blur_resolution * OFFSET2, in.world_position.z, 1.0)) * WEIGHT2; 129 + shadow_guassian += fetch_shadow(light.view_proj* vec4<f32>(in.world_position.xy - blur_resolution * OFFSET2, in.world_position.z, 1.0)) * WEIGHT2; 130 + 131 + let shadow_strength = 0.7; 108 132 let shadow = fetch_shadow(light.view_proj * vec4<f32>(in.world_position, 1.0)); 109 133 110 134 ··· 114 138 let specular_color = specular_strength * light_color * shadow; 115 139 116 140 // let result = (ambient_color + diffuse_color + specular_color) * shadow * object_color.xyz; 117 - let result = (ambient_color + diffuse_color + specular_color) * (shadow * 0.5 + 0.5); 141 + let result = (ambient_color + diffuse_color + specular_color) * (shadow_guassian * 0.7 + (1.0 - 0.7)) * object_color.xyz; 118 142 119 143 return vec4<f32>(result, object_color.a); 120 144 }
+7 -1
src/world/map.rs
··· 1 1 use bincode::{Decode, Encode}; 2 2 use glam::{ivec2, ivec3, IVec2, IVec3}; 3 3 use itertools::Itertools; 4 + #[cfg(not(target_arch = "wasm32"))] 4 5 use rand::{ 5 6 distr::{Distribution, StandardUniform}, 6 - Rng, 7 + rngs::OsRng, 8 + Rng, TryRngCore, 7 9 }; 8 10 use std::collections::HashMap; 9 11 ··· 39 41 Brick, 40 42 } 41 43 44 + #[cfg(not(target_arch = "wasm32"))] 42 45 impl Distribution<Block> for StandardUniform { 43 46 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Block { 44 47 match rng.random_range(0..=1) { ··· 110 113 .unwrap(), 111 114 }, 112 115 C::Random => |_| Chunk { 116 + #[cfg(not(target_arch = "wasm32"))] 113 117 blocks: rand::rng().random(), 118 + #[cfg(target_arch = "wasm32")] 119 + blocks: { panic!("i hate the web") }, 114 120 }, 115 121 } 116 122 }