diff --git a/result_image.png b/result_image.png index 7bb8cb3..ff5b37b 100644 Binary files a/result_image.png and b/result_image.png differ diff --git a/scenes/emissive_cube.blend b/scenes/emissive_cube.blend index b3469b9..7bde971 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 7b82a52..c49b3d1 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 876df6c..7575737 100644 Binary files a/scenes/emissive_cube.glb and b/scenes/emissive_cube.glb differ diff --git a/src/main.rs b/src/main.rs index d06457c..a8a52f3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,6 +37,9 @@ pub struct Args { ///start in debug mode (e.g. without parallelization) #[arg(long)] debug: bool, + ///path tracing recursion depth + #[arg(long)] + recurse: usize } fn main() { diff --git a/src/renderer.rs b/src/renderer.rs index ac61406..3c83d74 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1,6 +1,6 @@ use std::ops::{Add, Mul}; use std::sync::{Arc, LockResult, Mutex}; -use cgmath::{Angle, ElementWise, Matrix4, Vector2, Vector3, Vector4}; +use cgmath::{Angle, ElementWise, InnerSpace, Matrix4, Vector2, Vector3, Vector4}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use easy_gltf::model::{Mode}; use easy_gltf::{Camera, Material, Projection, Scene}; @@ -9,6 +9,8 @@ use crate::geometry::{Intersectable}; use crate::ray::{construct_primary_rays, Ray}; use crate::scene_data::IntersectionData; +const RAY_EPSILON: f32 = 0.0007; + pub fn render(scenes: &Vec, cl_args: &Args) -> Arc>>>> { let render_scene: &Scene = &scenes[cl_args.scene_index]; @@ -38,13 +40,17 @@ pub fn render(scenes: &Vec, Arc::new(Mutex::new(Vec::with_capacity(cl_args.width))); //prepare the radiosity buffer - (0..cl_args.height).into_iter().for_each(|py| { - radiosity_buffer.lock().unwrap().push(Vec::with_capacity(cl_args.height)) + (0..cl_args.width).into_iter().for_each(|px| { + let mut empty_vector = Vec::with_capacity(cl_args.height); + for _ in 0..cl_args.height { + empty_vector.push(Vector4::new(0.0, 0.0, 0.0, 0.0)); + } + radiosity_buffer.lock().unwrap().push(empty_vector); }); //iterate over all pixels in the image - (0..cl_args.width).into_iter().for_each(|px| { - (0..cl_args.height).into_iter().for_each(|py| { + (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), @@ -60,27 +66,28 @@ pub fn render(scenes: &Vec, //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, 4) + raytrace(ray, render_scene, cl_args.recurse) ); }); //store radiosity into the buffer match radiosity_buffer.clone().lock() { Ok(mut buffer) => { - buffer[px].push(pixel_radiosity); + buffer[px][py] = pixel_radiosity; } - Err(_) => {panic!("Unable to lock pixel buffer!")} + Err(_) => { panic!("Unable to lock pixel buffer!") } } - }); }); radiosity_buffer } -fn raytrace(ray: &Ray, scene: &Scene, recursion_depth: u32) -> Vector4 { - let mut pixel_radiosity: Vector4 = Vector4::new(0.0, 0.0, 0.0, 255.0); +fn raytrace(ray: &Ray, scene: &Scene, recursion_depth_left: usize) -> Vector4 { + let mut pixel_radiosity: Vector4 = Vector4::new(0.0, 0.0, 0.0, 1.0); + //abort if no recursion steps are left + if recursion_depth_left == 0 { return pixel_radiosity; } let mut smallest_t: f32 = f32::MAX; let mut intersection_data: Option = None; @@ -100,8 +107,8 @@ fn raytrace(ray: &Ray, scene: &Scene, recursion_depth: u32) -> Vector4 { //iterate all triangles in a model triangles.iter().for_each(|triangle| { match triangle.test_isec(&ray) { - //boilerplate implementation to set pixel to red if any intersection happened None => {} + //set data if intersection has been detected Some((t, isec)) => { // a new closer Point is found if t < smallest_t { @@ -110,7 +117,9 @@ fn raytrace(ray: &Ray, scene: &Scene, recursion_depth: u32) -> Vector4 { Option::from( IntersectionData::new( isec, - model.material()) + model.material(), + triangle.clone(), + ) ); } } @@ -120,8 +129,9 @@ fn raytrace(ray: &Ray, scene: &Scene, recursion_depth: u32) -> Vector4 { match intersection_data { Some(isec_data) => { - pixel_radiosity = accumulate_colors(isec_data.intersection_point(), - isec_data.material()); + pixel_radiosity = accumulate_colors(&isec_data, + &scene, + recursion_depth_left); } None {} => {} } @@ -130,19 +140,38 @@ fn raytrace(ray: &Ray, scene: &Scene, recursion_depth: u32) -> Vector4 { } /// called iff an intersection is detected to (recursively) accumulate radiosity at intersection -fn accumulate_colors(_intersection_point: Vector3, isec_material: &Arc) -> Vector4 { +fn accumulate_colors(intersection_data: &IntersectionData, + global_scene: &Scene, + recursion_depth_left: usize) -> Vector4 { let mut pixel_radiosity: Vector4 = Vector4::new(0.0, 0.0, 0.0, 1.0); //accumulate colors at point - //emmisive Component + //emissive Component pixel_radiosity += - isec_material.emissive.factor.extend(1.0) + intersection_data.material().emissive.factor.extend(1.0) .mul_element_wise( - isec_material.get_base_color_alpha(Vector2::new(0.0, 0.0)) + intersection_data.material() + .get_base_color_alpha(Vector2::new(0.0, 0.0)) ); + //get the intersected triangle and calculate the face normals + let intersected_triangle = intersection_data.intersected_triangle(); + let face_normal: Vector3 = (intersected_triangle[0].position - intersected_triangle[1].position) + .cross(intersected_triangle[0].position - intersected_triangle[2].position).normalize(); - //TODO: Path tracing. Scatter ray in one random direction + let secondary_ray = Ray { + //prevent self-intersection + source: intersection_data.intersection_point() + RAY_EPSILON * face_normal, + direction: face_normal, + }; + + let incoming_radiosity = raytrace( + &secondary_ray, + global_scene, + recursion_depth_left - 1); + + //reflected component + pixel_radiosity += incoming_radiosity * 0.2; pixel_radiosity } diff --git a/src/scene_data.rs b/src/scene_data.rs index d212691..e7bd1eb 100644 --- a/src/scene_data.rs +++ b/src/scene_data.rs @@ -1,17 +1,23 @@ use std::sync::Arc; use cgmath::Vector3; -use easy_gltf::Material; +use easy_gltf::{Material, Scene}; +use easy_gltf::model::Triangle; -pub (crate) struct IntersectionData{ +pub(crate) struct IntersectionData { intersection_point: Vector3, - material: Arc + material: Arc, + intersected_triangle: Triangle, } -impl IntersectionData{ - pub fn new(intersection_point: Vector3, material: Arc) -> IntersectionData{ - IntersectionData{ +impl IntersectionData { + pub fn new( + intersection_point: Vector3, + material: Arc, + intersected_triangle: Triangle) -> IntersectionData { + IntersectionData { intersection_point, material, + intersected_triangle, } } @@ -22,4 +28,8 @@ impl IntersectionData{ pub fn material(&self) -> &Arc { &self.material } + + pub fn intersected_triangle(&self) -> Triangle { + self.intersected_triangle + } } \ No newline at end of file