From 86135cb2ce48bfd52797223a73d80894e78a4d2a Mon Sep 17 00:00:00 2001 From: zomseffen <steffen@tom.bi> Date: Fri, 17 Jan 2025 11:10:39 +0100 Subject: [PATCH] diffuse raytracing --- shaders/compiled/frag_rt_quad.spv | Bin 24048 -> 27676 bytes shaders/rt_quad.frag | 69 +++++++++++++++++++++--------- src/app_data.rs | 1 + src/main.rs | 3 +- src/scene/mod.rs | 6 ++- src/scene/oct_tree.rs | 2 +- 6 files changed, 57 insertions(+), 24 deletions(-) diff --git a/shaders/compiled/frag_rt_quad.spv b/shaders/compiled/frag_rt_quad.spv index 32ca9f02830832efa29626333a924d7786d65827..70c2c6ae56ac85eb260c6364ec334c1f470bc6fe 100644 GIT binary patch literal 27676 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 M5B}bO*xwuYKYtDv9{>OV literal 24048 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 Bkw^dl diff --git a/shaders/rt_quad.frag b/shaders/rt_quad.frag index b6ce9aa..d075f61 100644 --- a/shaders/rt_quad.frag +++ b/shaders/rt_quad.frag @@ -134,24 +134,19 @@ vec3 get_light_color(uint light_index) { return vec3(float(scene_info.infos[light_index + 3]) / 255.0, float(scene_info.infos[light_index + 4]) / 255.0, float(scene_info.infos[light_index + 5]) / 255.0); } -void main() { - uint max_length = scene_info.infos[0]; - uint last = scene_info.infos[max_length]; - - uvec4 color_roughness = sample_color_from_scene_info(fragVolumeStart, fragRasterPos, facing); - vec4 orig_color_sample = vec4(float(color_roughness.x) / 255.0, float(color_roughness.y) / 255.0, float(color_roughness.z) / 255.0, 1); - +vec3 get_lighting_color(uint volume_start, vec3 starting_pos, vec4 orig_color_sample) { uint max_light_num = scene_info.infos[0]; uint light_num = 0; - uint volume_index = fragVolumeStart; + // setup volume info + uint volume_index = volume_start; uint volume_pos_x = scene_info.infos[volume_index + 0]; uint volume_pos_y = scene_info.infos[volume_index + 1]; uint volume_pos_z = scene_info.infos[volume_index + 2]; // setup light info - uint light_index = scene_info.infos[fragVolumeStart + 6 + light_num]; - vec3 light_direction = get_light_position(light_index) - origPosition; + uint light_index = scene_info.infos[volume_start + 6 + light_num]; + vec3 light_direction = get_light_position(light_index) - starting_pos; vec3 light_color = get_light_color(light_index); bool x_pos = light_direction.x > 0.0; @@ -166,7 +161,7 @@ void main() { // initialize color vec3 color_sum = vec3(0.0, 0.0, 0.0) + (orig_color_sample.xyz * 0.01); - uint max_iterations = max_light_num * 20; + uint max_iterations = max_light_num * scene_info.infos[1]; for (int i = 0; i < max_iterations; i++) { float x_border = float(volume_pos_x + (scene_info.infos[volume_index + 3]) * uint(x_pos)) - 0.5; float y_border = float(volume_pos_y + (scene_info.infos[volume_index + 4]) * uint(y_pos)) - 0.5; @@ -179,18 +174,18 @@ void main() { float y_factor = 2.0; float z_factor = 2.0; if (!x_null) { - x_factor = (x_border - origPosition.x) / light_direction.x; + x_factor = (x_border - starting_pos.x) / light_direction.x; } if (!y_null) { - y_factor = (y_border - origPosition.y) / light_direction.y; + y_factor = (y_border - starting_pos.y) / light_direction.y; } if (!z_null) { - z_factor = (z_border - origPosition.z) / light_direction.z; + z_factor = (z_border - starting_pos.z) / light_direction.z; } if ((x_factor >= 1.0) && (y_factor >= 1.0) && (z_factor >= 1.0)) { // no hit, add light color result - color_sum += (orig_color_sample.xyz * light_color) / ((0.1 * light_direction.length() * light_direction.length()) + 1.0); + color_sum += (orig_color_sample.xyz * light_color) / ((0.01 * length(light_direction) * length(light_direction)) + 1.0); needs_next_light = true; } else { // if there is a border hit before reaching the light @@ -205,7 +200,7 @@ void main() { } else { hit_facing = 2; } - vec3 intersection_pos = origPosition + x_factor * light_direction; + vec3 intersection_pos = starting_pos + x_factor * light_direction; u = uint(round(intersection_pos.y)) - volume_pos_y; v = uint(round(intersection_pos.z)) - volume_pos_z; } @@ -216,7 +211,7 @@ void main() { } else { hit_facing = 4; } - vec3 intersection_pos = origPosition + y_factor * light_direction; + vec3 intersection_pos = starting_pos + y_factor * light_direction; u = uint(round(intersection_pos.x)) - volume_pos_x; v = uint(round(intersection_pos.z)) - volume_pos_z; } @@ -227,7 +222,7 @@ void main() { } else { hit_facing = 1; } - vec3 intersection_pos = origPosition + z_factor * light_direction; + vec3 intersection_pos = starting_pos + z_factor * light_direction; u = uint(round(intersection_pos.x)) - volume_pos_x; v = uint(round(intersection_pos.y)) - volume_pos_y; } @@ -256,12 +251,12 @@ void main() { break; } // set up the new light - light_index = scene_info.infos[fragVolumeStart + 6 + light_num]; + light_index = scene_info.infos[volume_start + 6 + light_num]; if (light_index == 0) { // abort if there is no new light break; } - light_direction = get_light_position(light_index) - origPosition; + light_direction = get_light_position(light_index) - starting_pos; light_color = get_light_color(light_index); x_pos = light_direction.x > 0.0; @@ -273,11 +268,43 @@ void main() { z_pos = light_direction.z > 0.0; z_null = (light_direction.z == 0.0); // reset volume info - volume_index = fragVolumeStart; + volume_index = volume_start; volume_pos_x = scene_info.infos[volume_index + 0]; volume_pos_y = scene_info.infos[volume_index + 1]; volume_pos_z = scene_info.infos[volume_index + 2]; } } + return color_sum; +} + +void main() { + uint max_length = scene_info.infos[0]; + uint last = scene_info.infos[max_length]; + + uvec4 color_roughness = sample_color_from_scene_info(fragVolumeStart, fragRasterPos, facing); + vec4 orig_color_sample = vec4(float(color_roughness.x) / 255.0, float(color_roughness.y) / 255.0, float(color_roughness.z) / 255.0, 1); + + // singular raytracing + //vec3 color_sum = get_lighting_color(fragVolumeStart, origPosition, orig_color_sample); + + // diffuse raytracing using a quadratic raster of rays + int raster_half_steps = 0; + float raster_distance = 0.01; + int raster_points = (2 * raster_half_steps + 1) * (2 * raster_half_steps + 1); + + vec3 color_sum = vec3(0.0, 0.0, 0.0); + for (int u_offset = -raster_half_steps; u_offset <= raster_half_steps; u_offset++) { + for (int v_offset = -raster_half_steps; v_offset <= raster_half_steps; v_offset++) { + float x_offset = raster_distance * float(u_offset) * float(facing == 0 || facing == 1 || facing == 4 || facing == 5); + float y_offset = raster_distance * float(u_offset) * float(facing == 2 || facing == 3); + y_offset += raster_distance * float(v_offset) * float(facing == 0 || facing == 1); + float z_offset = raster_distance * float(v_offset) * float(facing == 4 || facing == 5 || facing == 2 || facing == 3); + + vec3 offset = vec3(x_offset, y_offset, z_offset); + + color_sum += get_lighting_color(fragVolumeStart, origPosition + offset, orig_color_sample) / float(raster_points); + } + } + outColor = vec4(color_sum, 1.0); } \ No newline at end of file diff --git a/src/app_data.rs b/src/app_data.rs index 9a6e5c6..91d507a 100644 --- a/src/app_data.rs +++ b/src/app_data.rs @@ -61,4 +61,5 @@ pub struct AppData { pub scene_rt_memory_size: u64, pub num_lights_per_volume: u32, + pub max_iterations_per_light: u32, } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 5c6dc6b..bda1050 100644 --- a/src/main.rs +++ b/src/main.rs @@ -58,7 +58,7 @@ const DEVICE_EXTENSIONS: &[vk::ExtensionName] = &[ vk::KHR_SWAPCHAIN_EXTENSION.name ]; -const MAX_FRAMES_IN_FLIGHT: usize = 2; +const MAX_FRAMES_IN_FLIGHT: usize = 3; fn main() -> Result<()> { pretty_env_logger::init(); @@ -179,6 +179,7 @@ impl App { let mut data = app_data::AppData::default(); data.use_geometry_shader = false; data.num_lights_per_volume = 2; + data.max_iterations_per_light = 20; let mut scene_handler = scene::Scene::default(); //load_model::load_model(&mut data)?; diff --git a/src/scene/mod.rs b/src/scene/mod.rs index 2006ece..160636a 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -138,7 +138,9 @@ impl Scene { let index = self.sized_vertices.len(); cube.draw(&data.topology, index, self); - let mut memory_index = 1; // zero should be the location for the overall length (also will be the invalid memory allocation for pointing to a nonexistant neighbor) + let mut memory_index = 2; + // 0 - location for the maximum number of lights referenced per chunk (also will be the invalid memory allocation for pointing to a nonexistant neighbor) + // 1 - location for the max iterations per light for light in &mut self.point_lights { light.memory_start = memory_index; memory_index += light.get_buffer_mem_size() as usize; @@ -157,6 +159,8 @@ impl Scene { } println!("Memory size is {} kB, max indes is {}", memory_index * 32 / 8 /1024 + 1, memory_index); let mut volume_vec = vec![data.num_lights_per_volume; memory_index]; + volume_vec[1] = data.max_iterations_per_light; + for volume in &empty_volumes { volume_vec = volume.borrow().insert_into_memory(volume_vec, data.num_lights_per_volume, &self.point_lights); } diff --git a/src/scene/oct_tree.rs b/src/scene/oct_tree.rs index 006d16d..7f6a3ba 100644 --- a/src/scene/oct_tree.rs +++ b/src/scene/oct_tree.rs @@ -9,7 +9,7 @@ extern crate rand; pub const CHUNK_SIZE_EXPONENT: u32 = 4; pub const CHUNK_SIZE: usize = (2 as usize).pow(CHUNK_SIZE_EXPONENT); -pub const MAX_TREE_DEPTH: usize = 2; +pub const MAX_TREE_DEPTH: usize = CHUNK_SIZE_EXPONENT as usize - 2; pub const MIN_CHUNK_SIZE: usize = CHUNK_SIZE / (2 as usize).pow(MAX_TREE_DEPTH as u32); #[derive(Clone, Debug)]