More gui options!
This commit is contained in:
@@ -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
|
||||
|
||||
275
src/main.rs
275
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<String> = env::args().collect();
|
||||
//let args: Vec<String> = 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::<f64>().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::<f64>().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<Scene> = 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<Scene> = 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<f32> = 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::<f64>() - 0.5) / options.ray_randomness;
|
||||
let ry = (random::<f64>() - 0.5) / options.ray_randomness;
|
||||
let rz = (random::<f64>() - 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<f32> = 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::<f64>() - 0.5) / options.ray_randomness;
|
||||
// let ry = (random::<f64>() - 0.5) / options.ray_randomness;
|
||||
// let rz = (random::<f64>() - 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<E: Error + 'static>(method_name: &str, err: E) {
|
||||
error!("{method_name}() failed: {err}");
|
||||
|
||||
@@ -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<Intersection> {
|
||||
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);
|
||||
}
|
||||
|
||||
71
src/ray.rs
71
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<BVH>,
|
||||
) -> Vector3<f32> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
24
src/scene.rs
24
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<HashMap<String, Node>>,
|
||||
// pub materials: Rc<HashMap<String, Material>>,
|
||||
// pub lights: Rc<HashMap<String, Light>>,
|
||||
// pub cameras: Rc<HashMap<String, Camera>>,
|
||||
// }
|
||||
// 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<String, Node>,
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
11
src/state.rs
11
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<f32> = 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::<f64>() - 0.5) / randomness;
|
||||
|
||||
Reference in New Issue
Block a user