diff --git a/src/gui.rs b/src/gui.rs index 39f55d7..5baec7a 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -277,6 +277,10 @@ impl Gui { ); // Enable BVH ui.checkbox("Enable BVH", &mut self.raytracing_option.bvh_active); + ui.checkbox("Enable Shadows", &mut self.raytracing_option.shadows); + ui.checkbox("Enable Reflections", &mut self.raytracing_option.reflect); + ui.checkbox("Enable Specular", &mut self.raytracing_option.specular); + ui.checkbox("Enable Diffuse", &mut self.raytracing_option.diffuse); // Apply stored changes if ui.button("Apply") { self.event = Some(GuiEvent::RaytracerOption(self.raytracing_option.clone())); @@ -346,9 +350,7 @@ impl Gui { // SCENE -------------------------------------------- if CollapsingHeader::new("Scene").build(ui) { if ui.button("Update Scene") { - for (_, node) in &mut self.scene.nodes { - node.compute(); - } + self.scene.compute(); self.event = Some(GuiEvent::SceneLoad(self.scene.clone())); } // Edit transformation of nodes diff --git a/src/main.rs b/src/main.rs index 3b8cc8a..032a19c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,23 +1,12 @@ use crate::state::run; -use bvh::BVH; -use camera::Camera; use error_iter::ErrorIter; const EPSILON: f64 = 1e-8; const INFINITY: f64 = 1e10; -use gui::{init_engine}; use log::error; -use nalgebra::Vector3; -use rand::random; -use ray::Ray; -use scene::Scene; -use state::RaytracingOption; use std::env; use std::error::Error; -use std::sync::Arc; -use std::sync::Mutex; -use std::thread; mod bvh; mod camera; @@ -33,143 +22,143 @@ mod state; fn main() { env_logger::init(); env::set_var("RUST_BACKTRACE", "1"); - let args: Vec = env::args().collect(); + //let args: Vec = env::args().collect(); + if let Err(e) = run() { + println!("Error at runtime: {}", e); + }; - if args.len() == 6 { - let width: usize = args[1].parse().unwrap(); - let height: usize = args[2].parse().unwrap(); - let fovy = args[3].parse::().unwrap(); - let filename = &args[4]; - let savefile = &args[5]; - headless( - width, - height, - fovy, - filename.to_string(), - savefile.to_string(), - ); - } else { - if let Err(e) = run() { - println!("Error at runtime: {}", e); - }; - } + // if args.len() == 6 { + // let width: usize = args[1].parse().unwrap(); + // let height: usize = args[2].parse().unwrap(); + // let fovy = args[3].parse::().unwrap(); + // let filename = &args[4]; + // let savefile = &args[5]; + // headless( + // width, + // height, + // fovy, + // filename.to_string(), + // savefile.to_string(), + // ); + // } else { + //} } -fn headless(width: usize, height: usize, fovy: f64, filename: String, savefile: String) { - let options = Arc::new(RaytracingOption { - threads: 12, - ray_samples: 1, - ray_randomness: 100.0, - clear_color: [0x22, 0x00, 0x11, 0x55], - pixel_clear: [0x55, 0x00, 0x22, 0x55], - pixels_per_thread: 200, - buffer_proportion: 1.0, - buffer_fov: 110.0, - ray_depth: 5, - diffuse_rays: 3, - diffuse_coefficient: 0.8, - bvh_active: false, - }); - //Read script from file - let script = match std::fs::read_to_string(&filename) { - Ok(in_script) => in_script, - Err(e) => { - println!("{}", e); - return; - } - }; - //Evaluate scene in file - let engine = init_engine(); - let scene: Arc = match engine.eval(&script) { - Ok(in_scene) => Arc::new(in_scene), - Err(e) => { - println!("{e}"); - return; - } - }; - //Set the camera - let mut camera = Camera::unit(); - for (_, in_camera) in &scene.cameras { - camera = in_camera.clone(); - } - //Cast the rays - let rays = Arc::new(Ray::cast_rays( - &camera.eye, - &camera.target, - &camera.up, - fovy, - width as u32, - height as u32, - )); - //Enable bounding volume heirarchy - let bvh; - match options.bvh_active { - true => bvh = Arc::new(Some(BVH::build(&scene.nodes))), - false => bvh = Arc::new(None), - } - //Create our frame and indexer - let size = width * height; - let frame_mutex = Arc::new(Mutex::new(vec![0; size * 4])); - //Multithreading - let mut handles = vec![]; +// fn headless(width: usize, height: usize, fovy: f64, filename: String, savefile: String) { +// let options = Arc::new(RaytracingOption { +// threads: 12, +// ray_samples: 1, +// ray_randomness: 100.0, +// clear_color: [0x22, 0x00, 0x11, 0x55], +// pixel_clear: [0x55, 0x00, 0x22, 0x55], +// pixels_per_thread: 200, +// buffer_proportion: 1.0, +// buffer_fov: 110.0, +// ray_depth: 5, +// diffuse_rays: 3, +// diffuse_coefficient: 0.8, +// bvh_active: false, +// }); +// //Read script from file +// let script = match std::fs::read_to_string(&filename) { +// Ok(in_script) => in_script, +// Err(e) => { +// println!("{}", e); +// return; +// } +// }; +// //Evaluate scene in file +// let engine = init_engine(); +// let scene: Arc = match engine.eval(&script) { +// Ok(in_scene) => Arc::new(in_scene), +// Err(e) => { +// println!("{e}"); +// return; +// } +// }; +// //Set the camera +// let mut camera = Camera::unit(); +// for (_, in_camera) in &scene.cameras { +// camera = in_camera.clone(); +// } +// //Cast the rays +// let rays = Arc::new(Ray::cast_rays( +// &camera.eye, +// &camera.target, +// &camera.up, +// fovy, +// width as u32, +// height as u32, +// )); +// //Enable bounding volume heirarchy +// let bvh; +// match options.bvh_active { +// true => bvh = Arc::new(Some(BVH::build(&scene.nodes))), +// false => bvh = Arc::new(None), +// } +// //Create our frame and indexer +// let size = width * height; +// let frame_mutex = Arc::new(Mutex::new(vec![0; size * 4])); +// //Multithreading +// let mut handles = vec![]; - for index in 0..size { - for _ in 0..options.threads { - //Get random index from queue - //Create a nre thread for this pixel - let handle = thread::spawn({ - let rays = rays.clone(); - let scene = scene.clone(); - let options = options.clone(); - let bvh = bvh.clone(); - let rays = rays.clone(); - let frame_mutex = frame_mutex.clone(); - move || { - //Shade colour for selected ray - let mut colour: Vector3 = Vector3::zeros(); - //Get the ray we want to make - let shot_ray = &rays[index]; - //Send out ray_samples rays - for _ in 0..options.ray_samples { - let point = shot_ray.a; - let dir = shot_ray.b; - //Generate a random ray - let rx = (random::() - 0.5) / options.ray_randomness; - let ry = (random::() - 0.5) / options.ray_randomness; - let rz = (random::() - 0.5) / options.ray_randomness; - let nx = dir.x + rx; - let ny = dir.y + ry; - let nz = dir.z + rz; - let rand_ray = Ray::new(point, Vector3::new(nx, ny, nz)); +// for index in 0..size { +// for _ in 0..options.threads { +// //Get random index from queue +// //Create a nre thread for this pixel +// let handle = thread::spawn({ +// let rays = rays.clone(); +// let scene = scene.clone(); +// let options = options.clone(); +// let bvh = bvh.clone(); +// let rays = rays.clone(); +// let frame_mutex = frame_mutex.clone(); +// move || { +// //Shade colour for selected ray +// let mut colour: Vector3 = Vector3::zeros(); +// //Get the ray we want to make +// let shot_ray = &rays[index]; +// //Send out ray_samples rays +// for _ in 0..options.ray_samples { +// let point = shot_ray.a; +// let dir = shot_ray.b; +// //Generate a random ray +// let rx = (random::() - 0.5) / options.ray_randomness; +// let ry = (random::() - 0.5) / options.ray_randomness; +// let rz = (random::() - 0.5) / options.ray_randomness; +// let nx = dir.x + rx; +// let ny = dir.y + ry; +// let nz = dir.z + rz; +// let rand_ray = Ray::new(point, Vector3::new(nx, ny, nz)); - if let Some(ray_colour) = rand_ray.shade_ray(&scene, 0, &options, &bvh) { - colour += ray_colour; - } - } - colour = (colour / options.ray_samples as f32) * 255.0; - let rgba = [colour.x as u8, colour.y as u8, colour.z as u8, 0xff]; - { - let frame = &mut frame_mutex.lock().unwrap(); - frame[index * 4..(index + 1) * 4].copy_from_slice(&rgba); - } - } - }); - handles.push(handle); - } - for handle in handles.drain(..) { - handle.join().unwrap(); - } - } - use std::path::Path; - image::save_buffer( - Path::new(&savefile), - &frame_mutex.lock().unwrap(), - width as u32, - height as u32, - image::ColorType::Rgba8, - ) - .unwrap(); -} +// if let Some(ray_colour) = rand_ray.shade_ray(&scene, 0, &options, &bvh) { +// colour += ray_colour; +// } +// } +// colour = (colour / options.ray_samples as f32) * 255.0; +// let rgba = [colour.x as u8, colour.y as u8, colour.z as u8, 0xff]; +// { +// let frame = &mut frame_mutex.lock().unwrap(); +// frame[index * 4..(index + 1) * 4].copy_from_slice(&rgba); +// } +// } +// }); +// handles.push(handle); +// } +// for handle in handles.drain(..) { +// handle.join().unwrap(); +// } +// } +// use std::path::Path; +// image::save_buffer( +// Path::new(&savefile), +// &frame_mutex.lock().unwrap(), +// width as u32, +// height as u32, +// image::ColorType::Rgba8, +// ) +// .unwrap(); +// } fn log_error(method_name: &str, err: E) { error!("{method_name}() failed: {err}"); diff --git a/src/node.rs b/src/node.rs index 60d420e..6e760b9 100644 --- a/src/node.rs +++ b/src/node.rs @@ -1,8 +1,9 @@ use crate::{ bvh::AABB, material::Material, - primitive::{*}, + primitive::*, ray::{Intersection, Ray}, + EPSILON, }; use nalgebra::{Matrix4, Vector3}; use std::sync::Arc; @@ -107,6 +108,9 @@ impl Node { pub fn intersect_ray(&self, ray: &Ray) -> Option { let ray = ray.transform(&self.inv_model); //Transform from world coordinates if let Some(mut intersect) = self.primitive.intersect_ray(&ray) { + if intersect.distance < EPSILON { + return None; + } intersect.transform_mut(&self.model, &self.inv_model); //Transform to world coords return Some(intersect); } diff --git a/src/ray.rs b/src/ray.rs index ffe5f7c..578423f 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -70,8 +70,7 @@ impl Ray { continue; } // Transform ray into local model cordinates - let ray = ray.transform(&node.inv_model); - if node.primitive.intersect_ray(&ray).is_some() { + if node.intersect_ray(&ray).is_some() { return true; } } @@ -93,16 +92,13 @@ impl Ray { } if node.aabb.intersect_ray(&ray) { - // Transform ray into model cordinates - let ray = ray.transform(&node.inv_model); - // Check primitive intersection - if let Some(mut intersect) = node.primitive.intersect_ray(&ray) { + //Check node intersection + if let Some(intersect) = node.intersect_ray(&ray) { // Dont intersect with own primitive if intersect.distance < EPSILON { continue; } // Check for closest distance by converting to world coords - intersect.transform_mut(&node.model, &node.inv_model); let distance = distance(&ray_a, &intersect.point); if distance < closest_distance { closest_distance = distance; @@ -162,9 +158,8 @@ impl Ray { bvh: &Option, ) -> Vector3 { let normal = &intersect.normal; - let point = intersect.point + normal * 0.0001; + let point = &intersect.point; let incidence = &ray.b; - let material = &node.material; // Compute the ambient light component and set it as base colour @@ -185,45 +180,56 @@ impl Ray { let to_light = to_light.normalize(); //Niave Shadows - // let to_light_ray = Ray::new(point, to_light); - // if to_light_ray.light_blocked(scene, node, bvh) { - // continue; - // } + if options.shadows { + let to_light_ray = Ray::new(*point, to_light); + if to_light_ray.light_blocked(scene, node, bvh) { + continue; + } + } let n_dot_l = normal.dot(&to_light).max(0.0) as f32; //Reflected component let mut reflect = Vector3::zeros(); - let reflect_dir = incidence - 2.0 * incidence.dot(&normal) * normal; - let reflect_ray = Ray::new(point, reflect_dir); - if let Some(col) = reflect_ray.shade_ray(scene, depth + 1, options, bvh) { - reflect += col.component_mul(&material.kr) + if options.reflect { + let reflect_dir = incidence - 2.0 * incidence.dot(&normal) * normal; + let reflect_ray = Ray::new(*point, reflect_dir); + if let Some(col) = reflect_ray.shade_ray(scene, depth + 1, options, bvh) { + reflect += col.component_mul(&material.kr) + } } //Diffuse component (Lambertian) let mut diffuse = Vector3::zeros(); - diffuse += material.kd * n_dot_l; - for _ in 0..options.diffuse_rays { - let diffuse_dir = random_unit_vec(); - let diffuse_ray = Ray::new(point.clone(), diffuse_dir + normal); - if let Some(col) = diffuse_ray.shade_ray(scene, depth + 1, options, bvh) { - diffuse += col * options.diffuse_coefficient; + if options.diffuse { + diffuse += material.kd * n_dot_l; + for _ in 0..options.diffuse_rays { + let diffuse_dir = random_unit_vec(); + let diffuse_ray = Ray::new(point.clone(), diffuse_dir + normal); + if let Some(col) = diffuse_ray.shade_ray(scene, depth + 1, options, bvh) { + diffuse += col * options.diffuse_coefficient; + } } } //Specular component let mut specular = Vector3::zeros(); - if n_dot_l > 0.0 { - let h = (to_light - incidence).normalize(); - let n_dot_h = normal.dot(&h).max(0.0) as f32; - specular = material.ks * n_dot_h.powf(material.shininess); + if options.specular { + if n_dot_l > 0.0 { + let h = (to_light - incidence).normalize(); + let n_dot_h = normal.dot(&h).max(0.0) as f32; + specular = material.ks * n_dot_h.powf(material.shininess); + } } //Falloff - let falloff = 1.0 - / ((1.0 + light.falloff[0]) - + light.falloff[1] * light_distance - + light.falloff[2] * light_distance * light_distance); + let mut falloff = 1.0; + if options.falloff { + falloff = 1.0 + / ((1.0 + light.falloff[0]) + + light.falloff[1] * light_distance + + light.falloff[2] * light_distance * light_distance); + } let intensity = light.colour.component_mul(&(diffuse + reflect + specular)) * falloff; colour += &intensity; @@ -253,8 +259,7 @@ impl Ray { continue; } if node.aabb.intersect_ray(self) { - let ray = self.transform(&node.inv_model); - if node.primitive.intersect_ray(&ray).is_some() { + if node.intersect_ray(self).is_some() { return true; } } diff --git a/src/scene.rs b/src/scene.rs index 9ff0a41..81d7091 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -1,24 +1,6 @@ use crate::{camera::Camera, light::Light, material::*, node::*}; use std::collections::HashMap; - -// pub struct MultiThreadScene { -// pub nodes: Rc>, -// pub materials: Rc>, -// pub lights: Rc>, -// pub cameras: Rc>, -// } -// impl MultiThreadScene { -// pub fn from_scene(scene: &Scene) -> MultiThreadScene { -// MultiThreadScene { -// nodes: Rc::new(scene.nodes.clone()), -// materials: Rc::new(scene.materials.clone()), -// lights: Rc::new(scene.lights.clone()), -// cameras: Rc::new(scene.cameras.clone()), -// } -// } -// } - #[derive(Clone)] pub struct Scene { pub nodes: HashMap, @@ -53,4 +35,10 @@ impl Scene { pub fn add_camera(&mut self, label: String, camera: Camera) { self.cameras.insert(label, camera); } + // Compute all matricies for nodes + pub fn compute(&mut self) { + for (_, node) in &mut self.nodes { + node.compute(); + } + } } diff --git a/src/state.rs b/src/state.rs index 9e2dc5b..06e2094 100644 --- a/src/state.rs +++ b/src/state.rs @@ -42,6 +42,11 @@ pub struct RaytracingOption { pub diffuse_rays: u8, pub diffuse_coefficient: f32, pub bvh_active: bool, + pub shadows: bool, + pub diffuse: bool, + pub reflect: bool, + pub specular: bool, + pub falloff: bool, } impl RaytracingOption { pub fn default() -> RaytracingOption { @@ -58,6 +63,11 @@ impl RaytracingOption { diffuse_rays: 3, diffuse_coefficient: 0.8, bvh_active: false, + shadows: true, + diffuse: true, + reflect: true, + specular: true, + falloff: true, } } } @@ -228,6 +238,7 @@ impl State { let mut colour: Vector3 = Vector3::zeros(); let ray = &rays[*index]; for _ in 0..samples { + //Generate a ray in a random direction let point = ray.a; let dir = ray.b; let rx = (random::() - 0.5) / randomness;