From f7603661c101e3e47ccdd6119b9ad6bec46721a7 Mon Sep 17 00:00:00 2001 From: Matthew Gordon Date: Fri, 29 Nov 2024 20:28:20 -0400 Subject: [PATCH] Add non-working raycasting code Implemented basic raycasting but it doesn't work; I'll check this in and then add GPU unit tests. --- src/app/dem_renderer.rs | 84 +++++++++++++++------------- src/app/dem_renderer.wgsl | 113 +++++++++++++++++++++++++++++++++++--- 2 files changed, 151 insertions(+), 46 deletions(-) diff --git a/src/app/dem_renderer.rs b/src/app/dem_renderer.rs index 2ce5c3c..7cdc364 100644 --- a/src/app/dem_renderer.rs +++ b/src/app/dem_renderer.rs @@ -69,13 +69,16 @@ impl Camera { #[derive(Clone, Copy, Pod, Zeroable)] struct UniformBufferContents { camera_matrix: [f32; 16], - dem_texture_size: [u32; 2], - dembvh_texture_size: [u32; 2], + camera_position: [f32; 4], + dem_min_corner: [f32; 2], + dem_cell_size: [f32; 2], + dem_z_range: [f32; 2], } -// This struct is 72 bytes but WGSL expects the buffer size to be be a multiple -// of the alignment of the member with the largest alignment. The mat4x4 -// type has a 16-byte alignment, so we round up to 16 * 5 = 80. -const UNIFORM_BUFFER_SIZE: u64 = 80; +// WGSL expects the buffer size to be be a multiple of the alignment of the +// member with the largest alignment. (The mat4x4 type has a 16-byte +// alignment.) So this value may be rounded up from the actual size of +// UniformBufferContents. +const UNIFORM_BUFFER_SIZE: u64 = 112; struct UniformBufferManager { cpu_buffer: UniformBufferContents, @@ -102,16 +105,28 @@ impl UniformBufferManager { .clone_from_slice(&camera_matrix.to_cols_array()) } - fn set_dem_texture_size(&mut self, dem_texture_size: glam::UVec2) { + fn set_camera_position(&mut self, camera_position: glam::Vec4) { self.cpu_buffer - .dem_texture_size - .clone_from_slice(&dem_texture_size.to_array()); + .camera_position + .clone_from_slice(&camera_position.to_array()) } - fn set_dembvh_texture_size(&mut self, dembvh_texture_size: glam::UVec2) { + fn set_dem_min_corner(&mut self, value: glam::Vec2) { self.cpu_buffer - .dembvh_texture_size - .clone_from_slice(&dembvh_texture_size.to_array()); + .dem_min_corner + .clone_from_slice(&value.to_array()); + } + + fn set_dem_cell_size(&mut self, value: glam::Vec2) { + self.cpu_buffer + .dem_cell_size + .clone_from_slice(&value.to_array()); + } + + fn set_dem_z_range(&mut self, value: glam::Vec2) { + self.cpu_buffer + .dem_z_range + .clone_from_slice(&value.to_array()); } fn get_binding(&self) -> wgpu::BindingResource<'_> { @@ -150,15 +165,19 @@ impl DemRenderer { let index_count = index_data.len(); - let (dem_texture_view, dem_texture_size) = load_dem_texture(&source, device, queue); - let (dembvh_texture_view, dembvh_texture_size) = - create_dembvh_texture(&source, device, queue); + let dem_texture_view = load_dem_texture(&source, device, queue); + let dembvh_texture_view = create_dembvh_texture(&source, device, queue); let camera = Camera::new(surface_config.width as f32 / surface_config.height as f32); let mut uniforms = UniformBufferManager::new(device); - uniforms.set_dem_texture_size(dem_texture_size); - uniforms.set_dembvh_texture_size(dembvh_texture_size); + uniforms.set_dem_min_corner(glam::Vec2::new(source.x_min, source.y_min)); + uniforms.set_dem_cell_size( + glam::Vec2::new(source.x_max, source.y_max) + - glam::Vec2::new(source.x_min, source.y_min) + / glam::Vec2::new(source.num_cells_x as f32, source.num_cells_y as f32), + ); + uniforms.set_dem_z_range(glam::Vec2::new(source.z_min, source.z_max)); let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: None, @@ -277,14 +296,15 @@ impl DemRenderer { let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("DemRendererCommandEncoder"), }); - self.camera.set_position( - get_animated_camera_position( - self.animation_start.elapsed(), - self.get_max_dem_dimension(), - ), - self.get_dem_centre(), + let camera_position = get_animated_camera_position( + self.animation_start.elapsed(), + self.get_max_dem_dimension(), ); + self.camera + .set_position(camera_position, self.get_dem_centre()); self.uniforms.set_camera_matrix(self.camera.get_matrix()); + self.uniforms + .set_camera_position(camera_position.extend(1.0)); self.uniforms.update_buffer(queue); { let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { @@ -378,11 +398,7 @@ fn create_vertices(dem: &Rc) -> (Vec, Vec) { (vertex_data.to_vec(), index_data.to_vec()) } -fn load_dem_texture( - source: &Dem, - device: &wgpu::Device, - queue: &wgpu::Queue, -) -> (wgpu::TextureView, glam::UVec2) { +fn load_dem_texture(source: &Dem, device: &wgpu::Device, queue: &wgpu::Queue) -> wgpu::TextureView { let texture_size = wgpu::Extent3d { width: source.num_cells_x, height: source.num_cells_y, @@ -415,17 +431,14 @@ fn load_dem_texture( texture_size, ); - ( - texture.create_view(&wgpu::TextureViewDescriptor::default()), - glam::UVec2::new(source.num_cells_x, source.num_cells_y), - ) + texture.create_view(&wgpu::TextureViewDescriptor::default()) } fn create_dembvh_texture( dem: &Dem, device: &wgpu::Device, queue: &wgpu::Queue, -) -> (wgpu::TextureView, glam::UVec2) { +) -> wgpu::TextureView { let source = DemBvh::new(dem); let dembvh_size = source.layers.first().expect("DEM BVH contains layers").size; @@ -466,10 +479,7 @@ fn create_dembvh_texture( ); } - ( - texture.create_view(&wgpu::TextureViewDescriptor::default()), - glam::UVec2::new(dembvh_size, dembvh_size), - ) + texture.create_view(&wgpu::TextureViewDescriptor::default()) } fn get_animated_camera_position(animation_time: std::time::Duration, dem_size: f32) -> glam::Vec3 { diff --git a/src/app/dem_renderer.wgsl b/src/app/dem_renderer.wgsl index 7d25f91..7560e05 100644 --- a/src/app/dem_renderer.wgsl +++ b/src/app/dem_renderer.wgsl @@ -1,12 +1,14 @@ struct VertexOutput { - @location(0) test: vec2, + @location(0) world_space_position: vec4, @builtin(position) position: vec4, }; struct Uniforms { transform: mat4x4, - dem_texture_size: vec2, - dembvh_texture_size: vec2 + camera_position: vec4, + dem_min_corner: vec2, + dem_cell_size: vec2, + dem_z_range: vec2 } @group(0) @binding(0) @@ -18,6 +20,90 @@ var dem_texture: texture_2d; @group(0) @binding(2) var dembvh_texture: texture_2d; +struct BoundingNode { + index: vec2, + level: u32 +} + +struct AABB { + min_corner: vec3, + max_corner: vec3 +} + +struct Ray { origin: vec3, direction: vec3 }; + +fn intersect_ray_with_aabb(ray: Ray, aabb: AABB) -> bool { + let t1 = (aabb.min_corner - ray.origin) / ray.direction; + let t2 = (aabb.max_corner - ray.origin) / ray.direction; + let t_mins = min(t1, t2); + let t_min = min(t_mins.x, min(t_mins.y, t_mins.z)); + let t_maxs = max(t1, t2); + let t_max = max(t_maxs.x, max(t_maxs.y, t_maxs.z)); + return t_min <= t_max; +} + +fn scale_u16(value: u32, range: vec2) -> f32 { + return range.x + (f32(value - 1) / 65534.0) * (range.y - range.x); +} + +fn get_xy_min_for_node(node: BoundingNode) -> vec2 { + return uniforms.dem_min_corner.xy + uniforms.dem_cell_size * vec2(node.index) * exp2(f32(node.level)); +} + +fn get_xy_max_for_node(node: BoundingNode) -> vec2 { + return uniforms.dem_min_corner.xy + uniforms.dem_cell_size * vec2(node.index + 1) * exp2(f32(node.level)); +} + +struct NodeStack { + stack: array, + count: u32 +} + +fn push_node_stack(stack: ptr, node: BoundingNode) { + (*stack).stack[(*stack).count] = node; + (*stack).count++; +} + +fn pop_node_stack(stack: ptr) -> BoundingNode { + (*stack).count--; + return (*stack).stack[(*stack).count]; +} + +fn intersect_ray_with_node(ray: Ray, root_node: BoundingNode) -> bool { + var node_stack: NodeStack; + node_stack.count = 0u; + push_node_stack(&node_stack, root_node); + while node_stack.count > 0 { + let node = pop_node_stack(&node_stack); + let minmax_z = textureLoad(dembvh_texture, node.index, i32(node.level)).rg; + if minmax_z.r == 0 { + return false; + } + let min_z = scale_u16(minmax_z.r, uniforms.dem_z_range); + let max_z = scale_u16(minmax_z.g, uniforms.dem_z_range); + var aabb: AABB ; + aabb.min_corner = vec3(get_xy_min_for_node(node), min_z); + aabb.max_corner = vec3(get_xy_max_for_node(node), min_z); + if intersect_ray_with_aabb(ray, aabb) { + if node.level == 0 { + return true; + } else { + let next_index = node.index * 2; + var next_node: BoundingNode; + next_node.index = next_index; + next_node.level = node.level - 1; + push_node_stack(&node_stack, next_node); + next_node.index = next_index + vec2(1, 0); + push_node_stack(&node_stack, next_node); + next_node.index = next_index + vec2(0, 1); + push_node_stack(&node_stack, next_node); + next_node.index = next_index + vec2(1, 1); + push_node_stack(&node_stack, next_node); + } + } + } + return false; +} @vertex fn vs_main( @@ -25,14 +111,23 @@ fn vs_main( ) -> VertexOutput { var result: VertexOutput; result.position = uniforms.transform * position; - result.test = position.xy; + result.world_space_position = position; return result; } @fragment fn fs_solid(vertex: VertexOutput) -> @location(0) vec4 { - let texture_index = vec2(fract(abs(vertex.test)/500.0)*vec2(uniforms.dem_texture_size)); - let v_i = vec3(textureLoad(dem_texture, texture_index, 0).r, textureLoad(dembvh_texture, texture_index, 0).rg); - let v = vec3(v_i) / 65535.0; - return vec4(v, 1.0); -} \ No newline at end of file + var ray: Ray; + ray.origin = vertex.world_space_position.xyz; + ray.direction = normalize(vertex.world_space_position.xyz - uniforms.camera_position.xyz); + + var root_node: BoundingNode; + root_node.index = vec2(0); + root_node.level = textureNumLevels(dembvh_texture) - 1; + + if intersect_ray_with_node(ray, root_node) { + return vec4(1.0); + } else { + discard; + } +}