diff --git a/Cargo.toml b/Cargo.toml index 8cf8ff1..0e537b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,4 +11,5 @@ clap = { version = "4.4.8", features = ["derive"] } cgmath = "0.18.0" rayon = "1.8.0" image = "0.24.7" -rand = "0.8.5" \ No newline at end of file +rand = "0.8.5" +indicatif = { version = "0.17.7", features = ["rayon"] } \ No newline at end of file diff --git a/first_really_good_image.png b/first_really_good_image.png new file mode 100644 index 0000000..a03ece7 Binary files /dev/null and b/first_really_good_image.png differ diff --git a/result_image.png b/result_image.png deleted file mode 100644 index a3e14a5..0000000 Binary files a/result_image.png and /dev/null differ diff --git a/scenes/emissive_cube.blend b/scenes/emissive_cube.blend index b65055c..63b5865 100644 Binary files a/scenes/emissive_cube.blend and b/scenes/emissive_cube.blend differ diff --git a/scenes/emissive_cube.blend1 b/scenes/emissive_cube.blend1 index 1c50254..49561eb 100644 Binary files a/scenes/emissive_cube.blend1 and b/scenes/emissive_cube.blend1 differ diff --git a/scenes/emissive_cube.glb b/scenes/emissive_cube.glb index 89f2b7d..6d2baca 100644 Binary files a/scenes/emissive_cube.glb and b/scenes/emissive_cube.glb differ diff --git a/src/colors.rs b/src/colors.rs index 0dd7b3b..a417ffc 100644 --- a/src/colors.rs +++ b/src/colors.rs @@ -48,8 +48,7 @@ pub fn normalize_colors_global(radiosity_buffer: &mut Vec>>) -> }); //calculate difference between min and max pixel radiosity - //TODO: drop upper 20 percent of radiosity to illiminate outliers - let range = (maximum_radiosity * 0.8) - minimum_radiosity; + let range = maximum_radiosity - minimum_radiosity; //normalize to range for column in &mut *radiosity_buffer { for radiosity_value in column { diff --git a/src/main.rs b/src/main.rs index b4a243a..d9f572a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -49,6 +49,7 @@ fn main() { &args.gltf_file_path) .expect(&*format!("Failed to load glTF file {}", &args.gltf_file_path)); + println!("Path tracing image. Columns finished:"); let mut radiosity_buffer: Vec>> = match render(scenes, &args).lock() { Ok(buffer) => {buffer.to_vec()} Err(_) => {panic!("Unable to lock radiosity buffer!")} @@ -64,6 +65,6 @@ fn main() { let output_image = store_colors_to_image(as_colors); - output_image.save("result_image.png").expect("Unable to save image!"); + output_image.save("../output_image.png").expect("Unable to save image!"); } diff --git a/src/renderer.rs b/src/renderer.rs index bd5b041..5d3ff1b 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1,11 +1,13 @@ +use indicatif::ProgressIterator; use std::f32::consts::PI; use std::ops::{Add, Mul}; -use std::sync::{Arc, LockResult, Mutex}; +use std::sync::{Arc, Mutex}; use cgmath::{Angle, ElementWise, InnerSpace, Matrix4, Vector2, Vector3, Vector4}; +use cgmath::num_traits::abs; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use easy_gltf::model::{Mode}; use easy_gltf::{Camera, Projection, Scene}; -use rayon::prelude::IntoParallelRefIterator; +use indicatif::ParallelProgressIterator; use crate::Args; use crate::geometry::{Intersectable}; use crate::ray::{construct_primary_rays, Ray}; @@ -51,36 +53,38 @@ pub fn render(scenes: &Vec, }); //iterate over all pixels in the image - (0..cl_args.width).into_par_iter().for_each(|px| { - (0..cl_args.height).into_par_iter().for_each(|py| { - //construct all rays - let rays: Vec = construct_primary_rays( - (cl_args.width, cl_args.height), - (px, py), - &transform_matrix, - z, - cl_args.multiplier, - ); - - //let the initial pixel color be black and opaque - let mut pixel_radiosity: Vector4 = Vector4::new(0.0, 0.0, 0.0, 1.0); - - //cast each ray and get the output luminosity and sum them up - rays.iter().for_each(|ray| { - pixel_radiosity = pixel_radiosity.add( - raytrace(&ray, render_scene, cl_args.recurse) + (0..cl_args.width).into_par_iter() + .progress_count(cl_args.width as u64) + .for_each(|px| { + (0..cl_args.height).into_par_iter().for_each(|py| { + //construct all rays + let rays: Vec = construct_primary_rays( + (cl_args.width, cl_args.height), + (px, py), + &transform_matrix, + z, + cl_args.multiplier, ); - }); - //store radiosity into the buffer - match radiosity_buffer.clone().lock() { - Ok(mut buffer) => { - buffer[px][py] = pixel_radiosity; + //let the initial pixel color be black and opaque + let mut pixel_radiosity: Vector4 = Vector4::new(0.0, 0.0, 0.0, 1.0); + + //cast each ray and get the output luminosity and sum them up + rays.iter().for_each(|ray| { + pixel_radiosity = pixel_radiosity.add( + raytrace(&ray, render_scene, cl_args.recurse) + ); + }); + + //store radiosity into the buffer + match radiosity_buffer.clone().lock() { + Ok(mut buffer) => { + buffer[px][py] = pixel_radiosity; + } + Err(_) => { panic!("Unable to lock pixel buffer!") } } - Err(_) => { panic!("Unable to lock pixel buffer!") } - } + }); }); - }); radiosity_buffer } @@ -121,6 +125,7 @@ fn raytrace(ray: &Ray, scene: &Scene, recursion_depth_left: usize) -> Vector4 = tangent + let mut face_normal: Vector3 = tangent .cross(intersected_triangle[0].position - intersected_triangle[2].position).normalize(); - let bitangent = tangent.cross(face_normal); + //flip normal if face is hit from behind + if face_normal.dot(-intersection_data.ray().direction) < 0.0 { + face_normal *= -1.0; + } + let bitangent = tangent.cross(face_normal).normalize(); /* generate random direction on hemisphere */ let phi = rand::random::() * 2.0 * PI; //allow arbitrary angles - let theta = rand::random::() * 2.0 * PI; + let radius = rand::random::(); + let sqrt_radius = f32::sqrt(rand::random::()); - let sin_theta = f32::sin(theta); - let direction = tangent * f32::cos(phi) * sin_theta + - bitangent * f32::sin(phi) * sin_theta + - face_normal * f32::cos(theta); + let direction = (tangent * f32::cos(phi) * sqrt_radius + + bitangent * f32::sin(phi) * sqrt_radius + + face_normal * f32::sqrt(1.0 - radius)).normalize(); let secondary_ray = Ray { //prevent self-intersection source: intersection_data.intersection_point() + RAY_EPSILON * face_normal, - direction: direction.normalize(), + direction, }; //path trace recursively @@ -193,10 +202,8 @@ fn accumulate_colors(intersection_data: &IntersectionData, let cos_weighting = direction.dot(face_normal); - //make ray contribution decay exponentially let brdf = intersection_data.material() - .get_base_color_alpha(Vector2::new(0.0, 0.0)) - * (1.0 - f32::powf(6.0, -0.5 * recursion_depth_left as f32)); + .get_base_color_alpha(Vector2::new(0.0, 0.0)); //reflected component pixel_radiosity += brdf.mul_element_wise(incoming_radiosity) * cos_weighting * 2.0 * PI; diff --git a/src/scene_data.rs b/src/scene_data.rs index 3cb4897..9156043 100644 --- a/src/scene_data.rs +++ b/src/scene_data.rs @@ -2,22 +2,26 @@ use std::sync::Arc; use cgmath::Vector3; use easy_gltf::{Material}; use easy_gltf::model::Triangle; +use crate::ray::Ray; -pub(crate) struct IntersectionData { +pub(crate) struct IntersectionData<'a> { intersection_point: Vector3, material: Arc, intersected_triangle: Triangle, + ray: &'a Ray, } -impl IntersectionData { +impl IntersectionData<'_>{ pub fn new( intersection_point: Vector3, material: Arc, - intersected_triangle: Triangle) -> IntersectionData { + intersected_triangle: Triangle, + ray: &Ray) -> IntersectionData { IntersectionData { intersection_point, material, intersected_triangle, + ray, } } @@ -32,4 +36,8 @@ impl IntersectionData { pub fn intersected_triangle(&self) -> Triangle { self.intersected_triangle } + + pub fn ray(&self) -> &Ray { + self.ray + } } \ No newline at end of file