propper multithreading
This commit is contained in:
@@ -3,41 +3,43 @@ let scene = Scene();
|
||||
let distance = 10.0;
|
||||
let camera = Camera( P(0.0,0.0,distance), P(0.0,0.0,0.0), V(0.0,1.0,0.0));
|
||||
scene.addCamera("+Z Cam", camera);
|
||||
let camera = Camera( P(0.0,distance,0.1), P(0.0,0.0,0.0), V(0.0,1.0,0.0));
|
||||
scene.addCamera("+Y Cam", camera);
|
||||
let camera = Camera( P(distance,0.0,0.0), P(0.0,0.0,0.0), V(0.0,1.0,0.0));
|
||||
scene.addCamera("+X Cam", camera);
|
||||
let camera = Camera( P(0.0,0.0,-distance), P(0.0,0.0,0.0), V(0.0,1.0,0.0));
|
||||
scene.addCamera("-Z Cam", camera);
|
||||
let camera = Camera( P(0.0,-distance,0.1), P(0.0,0.0,0.0), V(0.0,1.0,0.0));
|
||||
scene.addCamera("-Y Cam", camera);
|
||||
let camera = Camera( P(-distance,0.0,0.0), P(0.0,0.0,0.0), V(0.0,1.0,0.0));
|
||||
scene.addCamera("-X Cam", camera);
|
||||
// let camera = Camera( P(0.0,distance,0.1), P(0.0,0.0,0.0), V(0.0,1.0,0.0));
|
||||
// scene.addCamera("+Y Cam", camera);
|
||||
// let camera = Camera( P(distance,0.0,0.0), P(0.0,0.0,0.0), V(0.0,1.0,0.0));
|
||||
// scene.addCamera("+X Cam", camera);
|
||||
// let camera = Camera( P(0.0,0.0,-distance), P(0.0,0.0,0.0), V(0.0,1.0,0.0));
|
||||
// scene.addCamera("-Z Cam", camera);
|
||||
// let camera = Camera( P(0.0,-distance,0.1), P(0.0,0.0,0.0), V(0.0,1.0,0.0));
|
||||
// scene.addCamera("-Y Cam", camera);
|
||||
// let camera = Camera( P(-distance,0.0,0.0), P(0.0,0.0,0.0), V(0.0,1.0,0.0));
|
||||
// scene.addCamera("-X Cam", camera);
|
||||
|
||||
let material = Material(V(0.2,0.9,0.8), V(0.3, 0.8, 0.8), 10.0);
|
||||
scene.addMaterial("bluegreen", material);
|
||||
scene.addMaterial("mat1", material);
|
||||
let material2 = Material(V(0.2,0.9,0.8), V(0.3, 0.8, 0.8), 25.0);
|
||||
scene.addMaterial("bluegreen2", material);
|
||||
scene.addMaterial("mat2", material);
|
||||
|
||||
|
||||
let light = Light(P(0.0,7.0,0.0), V(0.0,0.0,1.0), V(0.1, 0.01, 0.001));
|
||||
light.active(false);
|
||||
light.active(true);
|
||||
scene.addLight("blue", light);
|
||||
|
||||
let light = Light( P(2.0,7.0,0.0), V(0.0,1.0,0.0), V(0.1, 0.01, 0.001));
|
||||
light.active(false);
|
||||
scene.addLight("green", light);
|
||||
// let light = Light( P(2.0,7.0,0.0), V(0.0,1.0,0.0), V(0.1, 0.01, 0.001));
|
||||
// light.active(false);
|
||||
// scene.addLight("green", light);
|
||||
|
||||
let light = Light( P(2.0,7.0,2.0), V(1.0,0.5,0.5), V(0.0, 0.00, 0.001));
|
||||
scene.addLight("red", light);
|
||||
// let light = Light( P(2.0,7.0,2.0), V(1.0,0.5,0.5), V(0.0, 0.00, 0.001));
|
||||
// light.active(false);
|
||||
// scene.addLight("red", light);
|
||||
|
||||
let light = Ambient(V(0.5,0.5,0.5));
|
||||
scene.addLight("ambient", light);
|
||||
// let light = Ambient(V(0.5,0.5,0.5));
|
||||
// light.active(false);
|
||||
// scene.addLight("ambient", light);
|
||||
|
||||
|
||||
let sphere = Sphere(P(0.0,-10.0,0.0), 10.0 );
|
||||
let sphere_node = Node( sphere, material);
|
||||
scene.addNode("sphere",sphere_node);
|
||||
//let sphere = Sphere(P(0.0,-10.0,0.0), 10.0 );
|
||||
//let sphere_node = Node( sphere, material);
|
||||
//scene.addNode("sphere",sphere_node);
|
||||
|
||||
//let mesh = Mesh("obj/cow.obj" );
|
||||
//let mesh_node = Node(mesh);
|
||||
@@ -48,11 +50,11 @@ scene.addNode("sphere",sphere_node);
|
||||
// child.translate(V(1.0,1.0,1.0));
|
||||
//scene.addNode(child);
|
||||
|
||||
let cube = CubeUnit();
|
||||
let cube_node = Node( cube, material2);
|
||||
cube_node.rotate(0.1,0.1,45.0);
|
||||
cube_node.translate(0.0,1.0,0.0);
|
||||
scene.addNode("cube", cube_node);
|
||||
let sphere2= SphereUnit();
|
||||
let sphere2_node = Node( sphere2, material2);
|
||||
// sphere2_node.rotate(0.1,0.1,0.0);
|
||||
// sphere2_node.translate(0.0,1.0,0.0);
|
||||
scene.addNode("sphere", sphere2_node);
|
||||
|
||||
//let gnonom = Gnonom();
|
||||
//let gnonom_node = Node(gnonom, material);
|
||||
|
||||
32
rhai/space.rhai
Normal file
32
rhai/space.rhai
Normal file
@@ -0,0 +1,32 @@
|
||||
let scene = Scene();
|
||||
|
||||
let falloff = V(0.0,0.0,0.0);
|
||||
|
||||
//CAMERAS
|
||||
let camera = Camera( P(100.0,100.0,100.0), P(500.0,500.0,500.0), V(0.0,1.0,0.0));
|
||||
scene.addCamera("Main Camera", camera);
|
||||
|
||||
//Light for the sun
|
||||
let light = Light(P(800.0, 800.0, 250.0), V(1.0, 1.0, 0.929), falloff);
|
||||
scene.addLight("Sun", light);
|
||||
|
||||
//Ball for the sun
|
||||
let material = Material(V(1.0, 1.0, 0.9), V(0.9, 0.9, 0.9), 10.0);
|
||||
scene.addMaterial("material_sun", material);
|
||||
let sphere = Sphere(P(800.0, 800.0, 200.0), 50.0);
|
||||
let sphere_node = Node(sphere, material);
|
||||
scene.addNode("sphere", sphere_node);
|
||||
|
||||
//Ball for the planet
|
||||
let material = Material(V(0.2,0.8,0.2), V(0.2, 0.8, 0.8), 10.0);
|
||||
scene.addMaterial("material_planet", material);
|
||||
let sphere = Sphere(P(500.0, 500.0, 500.0), 50.0);
|
||||
let sphere_node = Node(sphere, material);
|
||||
scene.addNode("sphere", sphere_node);
|
||||
|
||||
|
||||
// let sphere = Steiner( material);
|
||||
// let sphere_node = Node(sphere);
|
||||
// scene.addNode("sphere", sphere_node);
|
||||
|
||||
scene
|
||||
13
src/bvh.rs
13
src/bvh.rs
@@ -1,5 +1,5 @@
|
||||
use crate::{node::Node, ray::*, EPSILON};
|
||||
use nalgebra::{distance, point, Matrix4, Point3, Vector3};
|
||||
use nalgebra::{distance, Matrix4, Point3, Vector3};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
|
||||
@@ -386,24 +386,21 @@ impl BVH {
|
||||
// Traverse the BVH, 0 will be needed to start at root node
|
||||
pub fn traverse(&self, ray: &Ray, idx: usize) -> Option<(&Node, Intersection)> {
|
||||
let bvh_node = &self.bvh_nodes[idx];
|
||||
if !bvh_node.aabb.intersect_ray(ray) {
|
||||
if !bvh_node.aabb.intersect_ray(&ray) {
|
||||
// No intersection with BVH in world coordinates
|
||||
return None;
|
||||
}
|
||||
if bvh_node.prim_count > 0 {
|
||||
if bvh_node.prim_count != 0 {
|
||||
// Leaf node intersection
|
||||
let node_idx = bvh_node.first_prim;
|
||||
let node = &self.nodes[node_idx];
|
||||
if !node.active {
|
||||
return None;
|
||||
}
|
||||
let ray = ray.transform(&node.inv_model); //Transform ray to model coords
|
||||
if let Some(intersect) = node.primitive.intersect_ray(&ray) {
|
||||
if let Some(intersect) = node.intersect_ray(&ray) {
|
||||
if intersect.distance < EPSILON {
|
||||
return None;
|
||||
} else {
|
||||
// Convert intersect back to world coords
|
||||
let intersect = intersect.transform(&node.model, &node.inv_model);
|
||||
return Some((node, intersect));
|
||||
}
|
||||
}
|
||||
@@ -438,7 +435,7 @@ impl BVH {
|
||||
let mut l_count = 0;
|
||||
let mut r_count = 0;
|
||||
for i in 0..node.prim_count {
|
||||
let aabb = self.nodes[node.first_prim + i].primitive.get_aabb();
|
||||
let aabb = self.nodes[node.first_prim + i].get_world_aabb();
|
||||
if aabb.trf[axis] < pos {
|
||||
l_count += 1;
|
||||
l_aabb.grow_mut(&aabb.trf);
|
||||
|
||||
30
src/gui.rs
30
src/gui.rs
@@ -18,18 +18,20 @@ const BUFFER_PROPORTION_MIN: f32 = 0.1;
|
||||
const BUFFER_PROPORTION_MAX: f32 = 1.0;
|
||||
|
||||
//RAY CONSTANTS
|
||||
const MIN_THREADS: u32 = 1;
|
||||
const MAX_THREADS: u32 = 12;
|
||||
const RAYS_MIN: u32 = 100;
|
||||
const RAYS_MAX: u32 = 10000;
|
||||
const MIN_DEPTH: u8 = 5;
|
||||
const MAX_DEPTH: u8 = 100;
|
||||
const MIN_SAMPLES: u32 = 5;
|
||||
const MAX_SAMPLES: u32 = 100;
|
||||
const MIN_DEPTH: u8 = 1;
|
||||
const MAX_DEPTH: u8 = 10;
|
||||
const MIN_SAMPLES: u32 = 1;
|
||||
const MAX_SAMPLES: u32 = 10;
|
||||
const MIN_RANDOM: f64 = 100.0;
|
||||
const MAX_RANDOM: f64 = 1000.0;
|
||||
|
||||
//DIFFUSE CONSTANTS
|
||||
const MIN_DIFFUSE_RAYS: u8 = 5;
|
||||
const MAX_DIFFUSE_RAYS: u8 = 100;
|
||||
const MIN_DIFFUSE_RAYS: u8 = 1;
|
||||
const MAX_DIFFUSE_RAYS: u8 = 10;
|
||||
const MIN_DIFFUSE_COEFFICIENT: f32 = 0.0;
|
||||
const MAX_DIFFUSE_COEFFICIENT: f32 = 1.0;
|
||||
|
||||
@@ -205,12 +207,18 @@ impl Gui {
|
||||
|
||||
//Raytracing options -------------------------------------------
|
||||
if CollapsingHeader::new("Raytracer").build(ui) {
|
||||
ui.slider(
|
||||
"Threads",
|
||||
MIN_THREADS,
|
||||
MAX_THREADS,
|
||||
&mut self.raytracing_option.threads,
|
||||
);
|
||||
// Numbers of rays to render per pass
|
||||
ui.slider(
|
||||
"Rays Per Pass",
|
||||
RAYS_MIN,
|
||||
RAYS_MAX,
|
||||
&mut self.raytracing_option.pixels_per_pass,
|
||||
&mut self.raytracing_option.pixels_per_thread,
|
||||
);
|
||||
// Proportion of the window the buffer occupies
|
||||
ui.slider(
|
||||
@@ -521,9 +529,9 @@ pub fn init_engine() -> Engine {
|
||||
engine
|
||||
.register_type::<Mesh>()
|
||||
.register_fn("Mesh", Mesh::from_file);
|
||||
// engine
|
||||
// .register_type::<Rectangle>()
|
||||
// .register_fn("Rectange", Rectangle::new)
|
||||
// .register_fn("RectangleUnit", Rectangle::unit);
|
||||
engine
|
||||
.register_type::<RectangleXY>()
|
||||
.register_fn("Rectange", RectangleXY::new)
|
||||
.register_fn("RectangleUnit", RectangleXY::unit);
|
||||
engine
|
||||
}
|
||||
|
||||
145
src/main.rs
145
src/main.rs
@@ -1,12 +1,23 @@
|
||||
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, Gui};
|
||||
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;
|
||||
@@ -21,11 +32,143 @@ mod state;
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
|
||||
env::set_var("RUST_BACKTRACE", "1");
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
||||
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);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
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) {
|
||||
|
||||
20
src/node.rs
20
src/node.rs
@@ -1,4 +1,9 @@
|
||||
use crate::{bvh::AABB, material::Material, primitive::*};
|
||||
use crate::{
|
||||
bvh::AABB,
|
||||
material::Material,
|
||||
primitive::{self, *},
|
||||
ray::{Intersection, Ray},
|
||||
};
|
||||
use nalgebra::{Matrix4, Vector3};
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -98,4 +103,17 @@ impl Node {
|
||||
self.inv_model = self.model.try_inverse().unwrap();
|
||||
self.aabb.transform_mut(&self.model);
|
||||
}
|
||||
// Intersection of a ray, will convert to model coords and check
|
||||
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) {
|
||||
intersect.transform_mut(&self.model, &self.inv_model); //Transform to world coords
|
||||
return Some(intersect);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
//Gets the bounding box in world coords
|
||||
pub fn get_world_aabb(&self) -> AABB {
|
||||
return self.aabb.clone();
|
||||
}
|
||||
}
|
||||
|
||||
116
src/primitive.rs
116
src/primitive.rs
@@ -373,85 +373,51 @@ impl Primitive for Cone {
|
||||
}
|
||||
|
||||
// RECTANGLE -----------------------------------------------------------------
|
||||
// #[derive(Clone)]
|
||||
// pub struct Rectangle {
|
||||
// position: Point3<f64>,
|
||||
// normal: Vector3<f64>,
|
||||
// width_direction: Vector3<f64>,
|
||||
// width: f64,
|
||||
// height: f64,
|
||||
// }
|
||||
// Normal is (0.0, 0.0, 1.0) always facing towards camera at positive z axis
|
||||
#[derive(Clone)]
|
||||
pub struct RectangleXY {
|
||||
bl: Point3<f64>,
|
||||
tr: Point3<f64>,
|
||||
}
|
||||
|
||||
// impl Rectangle {
|
||||
// pub fn new(
|
||||
// position: Point3<f64>,
|
||||
// normal: Vector3<f64>,
|
||||
// width_direction: Vector3<f64>,
|
||||
// width: f64,
|
||||
// height: f64,
|
||||
// ) -> Arc<dyn Primitive> {
|
||||
// let normal = normal.normalize();
|
||||
// let width_direction = width_direction.normalize();
|
||||
// let height_direction = width_direction.cross(&normal);
|
||||
// Arc::new(Rectangle {
|
||||
// position,
|
||||
// normal: normal.normalize(),
|
||||
// width_direction: width_direction.normalize(),
|
||||
// width,
|
||||
// height,
|
||||
// })
|
||||
// }
|
||||
// pub fn unit() -> Arc<dyn Primitive> {
|
||||
// Rectangle::new(
|
||||
// Point3::new(0.0, 0.0, 0.0),
|
||||
// Vector3::new(0.0, 1.0, 0.0),
|
||||
// Vector3::new(1.0, 0.0, 0.0),
|
||||
// 2.0,
|
||||
// 2.0,
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
impl RectangleXY {
|
||||
pub fn new(bl: Point3<f64>, tr: Point3<f64>) -> Arc<dyn Primitive> {
|
||||
Arc::new(RectangleXY { bl, tr })
|
||||
}
|
||||
pub fn unit() -> Arc<dyn Primitive> {
|
||||
RectangleXY::new(Point3::new(-1.0, -1.0, 0.0), Point3::new(1.0, 1.0, 0.0))
|
||||
}
|
||||
}
|
||||
|
||||
// impl Primitive for Rectangle {
|
||||
// fn intersect_ray(&self, ray: &Ray) -> Option<Intersection> {
|
||||
// let constant = self.position.coords.dot(&self.normal);
|
||||
// let denominator = ray.b.dot(&self.normal);
|
||||
// let t = (constant - ray.a.coords.dot(&self.normal)) / denominator;
|
||||
impl Primitive for RectangleXY {
|
||||
fn intersect_ray(&self, ray: &Ray) -> Option<Intersection> {
|
||||
let z = self.bl.z;
|
||||
let az = ray.a.z;
|
||||
let bz = ray.b.z;
|
||||
let t = (z - az) / bz;
|
||||
if t > INFINITY {
|
||||
return None;
|
||||
}
|
||||
let intersect = ray.at_t(t);
|
||||
let (ix, iy) = (intersect.x, intersect.y);
|
||||
|
||||
// if t > INFINITY {
|
||||
// return None;
|
||||
// }
|
||||
if (ix < self.bl.x) || (ix > self.tr.x) || (iy < self.bl.y) || (iy > self.tr.y) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// let intersect = ray.at_t(t);
|
||||
// let height_direction = self.width_direction.cross(&self.normal);
|
||||
// let (w2, h2) = (self.width / 2.0, self.height / 2.0);
|
||||
// let r1 = w2 * self.width_direction;
|
||||
// let r2 = h2 * height_direction;
|
||||
// let pi = intersect - self.position;
|
||||
// let pi_dot_r1 = pi.dot(&r1);
|
||||
// let pi_dot_r2 = pi.dot(&r2);
|
||||
Some(Intersection {
|
||||
point: intersect,
|
||||
normal: Vector3::new(0.0, 0.0, 1.0),
|
||||
distance: t,
|
||||
})
|
||||
}
|
||||
|
||||
// if pi_dot_r1 >= -w2 && pi_dot_r1 <= w2 && pi_dot_r2 >= -h2 && pi_dot_r2 <= h2 {
|
||||
// return Some(Intersection {
|
||||
// point: intersect,
|
||||
// normal: self.normal,
|
||||
// distance: t,
|
||||
// });
|
||||
// }
|
||||
// None
|
||||
// }
|
||||
|
||||
// fn get_bounding_box(&self) -> AABB {
|
||||
// let position = self.position;
|
||||
// let width = self.width;
|
||||
// let height = self.height;
|
||||
// let width_direction = self.width_direction;
|
||||
// let bln = position - width / 2.0 * width_direction - height / 2.0 * height_direction;
|
||||
// let trf = position + width / 2.0 * width_direction + height / 2.0 * height_direction;
|
||||
// AABB::new(bln, trf);
|
||||
// todo!()
|
||||
// }
|
||||
// }
|
||||
fn get_aabb(&self) -> AABB {
|
||||
let bl = self.bl + Vector3::new(0.0, 0.0, -0.1);
|
||||
let tr = self.tr + Vector3::new(0.0, 0.0, 0.1);
|
||||
AABB::new(bl, tr)
|
||||
}
|
||||
}
|
||||
|
||||
// Cube -----------------------------------------------------------------
|
||||
#[derive(Clone)]
|
||||
@@ -623,7 +589,7 @@ pub struct Mesh {
|
||||
impl Mesh {
|
||||
pub fn new(triangles: Vec<Triangle>) -> Arc<dyn Primitive> {
|
||||
// Calculate the bounding box for the entire mesh based on the bounding boxes of individual triangles
|
||||
let bounding_box = Mesh::compute_bounding_box(&triangles);
|
||||
let _bounding_box = Mesh::compute_bounding_box(&triangles);
|
||||
Arc::new(Mesh { triangles })
|
||||
}
|
||||
|
||||
|
||||
55
src/ray.rs
55
src/ray.rs
@@ -18,14 +18,9 @@ pub struct Intersection {
|
||||
}
|
||||
//Intersection point including point and normal
|
||||
impl Intersection {
|
||||
pub fn transform(&self, trans: &Matrix4<f64>, inv_trans: &Matrix4<f64>) -> Intersection {
|
||||
let point = trans.transform_point(&self.point);
|
||||
let normal = inv_trans.transpose().transform_vector(&self.normal);
|
||||
Intersection {
|
||||
point,
|
||||
normal,
|
||||
distance: self.distance,
|
||||
}
|
||||
pub fn transform_mut(&mut self, trans: &Matrix4<f64>, inv_trans: &Matrix4<f64>) {
|
||||
self.point = trans.transform_point(&self.point);
|
||||
self.normal = inv_trans.transpose().transform_vector(&self.normal);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,15 +57,20 @@ impl Ray {
|
||||
b: trans.transform_vector(&self.b),
|
||||
}
|
||||
}
|
||||
//Transform mutably
|
||||
pub fn transform_mut(&mut self, trans: &Matrix4<f64>) {
|
||||
self.a = trans.transform_point(&self.a);
|
||||
self.b = trans.transform_vector(&self.b);
|
||||
}
|
||||
//This function will determine if the ray hits an object in the scene
|
||||
//This is not optimised as it does not include bounding boxes
|
||||
pub fn hit_scene(&self, scene: &Scene) -> bool {
|
||||
pub fn hit_scene(ray: &Ray, scene: &Scene) -> bool {
|
||||
for (_, node) in &scene.nodes {
|
||||
if !node.active {
|
||||
continue;
|
||||
}
|
||||
// Transform ray into local model cordinates
|
||||
let ray = self.transform(&node.inv_model);
|
||||
let ray = ray.transform(&node.inv_model);
|
||||
if node.primitive.intersect_ray(&ray).is_some() {
|
||||
return true;
|
||||
}
|
||||
@@ -79,26 +79,31 @@ impl Ray {
|
||||
}
|
||||
//This function find the closest intersection point of a ray with an object in the scene
|
||||
//Also not optimised, as it does not include bounding boxes
|
||||
pub fn closest_intersect<'a>(&'a self, scene: &'a Scene) -> Option<(&Node, Intersection)> {
|
||||
pub fn closest_intersect<'a>(
|
||||
ray: &'a Ray,
|
||||
scene: &'a Scene,
|
||||
) -> Option<(&'a Node, Intersection)> {
|
||||
let mut closest_distance = f64::MAX;
|
||||
let mut closest_intersect: Option<(&Node, Intersection)> = None;
|
||||
let ray_a = ray.a;
|
||||
for (_, node) in &scene.nodes {
|
||||
//position of ray in world coords
|
||||
if !node.active {
|
||||
continue;
|
||||
}
|
||||
|
||||
if node.aabb.intersect_ray(&self) {
|
||||
// Transform ray into local model cordinates
|
||||
let ray = self.transform(&node.inv_model);
|
||||
if node.aabb.intersect_ray(&ray) {
|
||||
// Transform ray into model cordinates
|
||||
let ray = ray.transform(&node.inv_model);
|
||||
// Check primitive intersection
|
||||
if let Some(intersect) = node.primitive.intersect_ray(&ray) {
|
||||
// Dont intersect with itself
|
||||
if let Some(mut intersect) = node.primitive.intersect_ray(&ray) {
|
||||
// Dont intersect with own primitive
|
||||
if intersect.distance < EPSILON {
|
||||
continue;
|
||||
}
|
||||
// Check for closest distance by converting to world coords
|
||||
let intersect = intersect.transform(&node.model, &node.inv_model);
|
||||
let distance = distance(&ray.a, &intersect.point);
|
||||
intersect.transform_mut(&node.model, &node.inv_model);
|
||||
let distance = distance(&ray_a, &intersect.point);
|
||||
if distance < closest_distance {
|
||||
closest_distance = distance;
|
||||
closest_intersect = Some((node, intersect));
|
||||
@@ -124,7 +129,7 @@ impl Ray {
|
||||
//We have a bvh so use bvh traversal
|
||||
Some(bvh) => {
|
||||
//Intersect the scene with the bvh
|
||||
if let Some((node, intersect)) = bvh.traverse(&self, 0) {
|
||||
if let Some((node, intersect)) = bvh.traverse(self, 0) {
|
||||
return Some(Ray::phong_shade_point(
|
||||
&scene, &self, &node, &intersect, depth, options, sbvh,
|
||||
));
|
||||
@@ -134,7 +139,7 @@ impl Ray {
|
||||
//We dont have a bvh so use generic algorithm
|
||||
None => {
|
||||
//No BVH given so intersect normally
|
||||
match self.closest_intersect(scene) {
|
||||
match Ray::closest_intersect(self, scene) {
|
||||
Some((node, intersect)) => {
|
||||
Some(Ray::phong_shade_point(
|
||||
&scene, &self, &node, &intersect, depth, options, sbvh,
|
||||
@@ -180,10 +185,10 @@ 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;
|
||||
}
|
||||
// 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;
|
||||
|
||||
@@ -235,7 +240,7 @@ impl Ray {
|
||||
if !node.active {
|
||||
continue;
|
||||
}
|
||||
match bvh.traverse(&self, 0) {
|
||||
match bvh.traverse(self, 0) {
|
||||
Some(_) => return true,
|
||||
None => continue,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{camera::Camera, light::Light, material::*, node::*};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
|
||||
// pub struct MultiThreadScene {
|
||||
// pub nodes: Rc<HashMap<String, Node>>,
|
||||
|
||||
99
src/state.rs
99
src/state.rs
@@ -14,7 +14,6 @@ use rand::{random, thread_rng};
|
||||
|
||||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use anyhow::Result;
|
||||
use pixels::{Pixels, SurfaceTexture};
|
||||
@@ -36,7 +35,7 @@ pub struct RaytracingOption {
|
||||
pub ray_randomness: f64,
|
||||
pub clear_color: [u8; 4],
|
||||
pub pixel_clear: [u8; 4],
|
||||
pub pixels_per_pass: u32,
|
||||
pub pixels_per_thread: u32,
|
||||
pub buffer_proportion: f32,
|
||||
pub buffer_fov: f64,
|
||||
pub ray_depth: u8,
|
||||
@@ -48,16 +47,16 @@ impl RaytracingOption {
|
||||
pub fn default() -> RaytracingOption {
|
||||
RaytracingOption {
|
||||
threads: 12,
|
||||
ray_samples: 10,
|
||||
ray_samples: 1,
|
||||
ray_randomness: 100.0,
|
||||
clear_color: [0x22, 0x00, 0x11, 0x55],
|
||||
pixel_clear: [0x55, 0x00, 0x22, 0x55],
|
||||
pixels_per_pass: 200,
|
||||
pixels_per_thread: 200,
|
||||
buffer_proportion: 1.0,
|
||||
buffer_fov: 110.0,
|
||||
ray_depth: 5,
|
||||
diffuse_rays: 5,
|
||||
diffuse_coefficient: 0.5,
|
||||
diffuse_rays: 3,
|
||||
diffuse_coefficient: 0.8,
|
||||
bvh_active: false,
|
||||
}
|
||||
}
|
||||
@@ -72,11 +71,11 @@ pub struct State {
|
||||
buffer_width: u32,
|
||||
buffer_height: u32,
|
||||
|
||||
pixels: Arc<Mutex<Pixels>>,
|
||||
pixels: Pixels,
|
||||
gui: Gui,
|
||||
|
||||
rays: Arc<Vec<Ray>>,
|
||||
ray_queue: Arc<Mutex<Vec<usize>>>,
|
||||
ray_queue: Vec<usize>,
|
||||
raytracing_options: Arc<RaytracingOption>,
|
||||
}
|
||||
|
||||
@@ -84,7 +83,7 @@ impl State {
|
||||
pub fn new(window: Window, pixels: Pixels, gui: Gui) -> Self {
|
||||
let scene = Arc::new(Scene::empty());
|
||||
let window_size = window.inner_size();
|
||||
let pixels = Arc::new(Mutex::new(pixels));
|
||||
let pixels = pixels;
|
||||
let camera = Camera::unit();
|
||||
let rays = Arc::new(Vec::new());
|
||||
|
||||
@@ -98,7 +97,7 @@ impl State {
|
||||
pixels,
|
||||
gui,
|
||||
rays,
|
||||
ray_queue: Arc::new(Mutex::new(Vec::new())),
|
||||
ray_queue: Vec::new(),
|
||||
raytracing_options: Arc::new(RaytracingOption::default()),
|
||||
}
|
||||
}
|
||||
@@ -133,8 +132,7 @@ impl State {
|
||||
self.reset_queue();
|
||||
}
|
||||
GuiEvent::SaveImage(filename) => {
|
||||
let pixels = &self.pixels.as_ref().lock().unwrap();
|
||||
let frame = pixels.frame();
|
||||
let frame = self.pixels.frame();
|
||||
image::save_buffer(
|
||||
Path::new(&filename),
|
||||
frame,
|
||||
@@ -171,16 +169,15 @@ impl State {
|
||||
));
|
||||
|
||||
// Resize buffer and surface
|
||||
let pixels = &mut self.pixels.as_ref().lock().unwrap();
|
||||
pixels.resize_surface(size.width, size.height)?;
|
||||
pixels.resize_buffer(self.buffer_width, self.buffer_height)?;
|
||||
self.pixels.resize_surface(size.width, size.height)?;
|
||||
self.pixels
|
||||
.resize_buffer(self.buffer_width, self.buffer_height)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resize(&mut self, size: &PhysicalSize<u32>) -> Result<(), Box<dyn Error>> {
|
||||
let pixels = &mut self.pixels.as_ref().lock().unwrap();
|
||||
pixels.resize_surface(size.width, size.height)?;
|
||||
self.pixels.resize_surface(size.width, size.height)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -199,29 +196,38 @@ impl State {
|
||||
let randomness = self.raytracing_options.ray_randomness;
|
||||
let samples = self.raytracing_options.ray_samples;
|
||||
let samples_f32 = samples as f32;
|
||||
|
||||
let num_threads = self.raytracing_options.threads;
|
||||
let pixels_per_thread = self.raytracing_options.pixels_per_thread;
|
||||
|
||||
let mut handles = vec![];
|
||||
|
||||
for _ in 0..self.raytracing_options.pixels_per_pass {
|
||||
for _ in 0..self.raytracing_options.threads {
|
||||
//Get random index from queue
|
||||
let queue = &mut self.ray_queue.clone();
|
||||
let index = match queue.lock().unwrap().pop() {
|
||||
Some(index) => index,
|
||||
None => break,
|
||||
};
|
||||
//Create a nre thread for this pixel
|
||||
let handle = thread::spawn({
|
||||
for _ in 0..num_threads {
|
||||
//Get necessary variables to render
|
||||
let rays = self.rays.clone();
|
||||
let scene = self.scene.clone();
|
||||
let options = self.raytracing_options.clone();
|
||||
let bvh = self.bvh.clone();
|
||||
let rays = rays.clone();
|
||||
let pixels_mutex = self.pixels.clone();
|
||||
|
||||
//Get the workload for a thread
|
||||
let mut load = vec![];
|
||||
for _ in 0..pixels_per_thread {
|
||||
match self.ray_queue.pop() {
|
||||
Some(index) => load.push(index),
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
//The finished queue of the thread
|
||||
let mut finished = vec![];
|
||||
|
||||
//Create a new thread for these pixels
|
||||
let handle = thread::spawn({
|
||||
move || {
|
||||
//Shade colour for selected ray
|
||||
for index in &load {
|
||||
//Shade colour for selected index
|
||||
let mut colour: Vector3<f32> = Vector3::zeros();
|
||||
let ray = &rays[*index];
|
||||
for _ in 0..samples {
|
||||
let ray = &rays[index];
|
||||
let point = ray.a;
|
||||
let dir = ray.b;
|
||||
let rx = (random::<f64>() - 0.5) / randomness;
|
||||
@@ -240,23 +246,37 @@ impl State {
|
||||
}
|
||||
colour = (colour / samples_f32) * 255.0;
|
||||
let rgba = [colour.x as u8, colour.y as u8, colour.z as u8, 0xff];
|
||||
let pixels = &mut pixels_mutex.lock().unwrap();
|
||||
let frame = pixels.frame_mut();
|
||||
frame[index * 4..(index + 1) * 4].copy_from_slice(&rgba);
|
||||
finished.push(rgba);
|
||||
}
|
||||
return (load, finished);
|
||||
}
|
||||
});
|
||||
handles.push(handle);
|
||||
}
|
||||
|
||||
let mut all_results = vec![];
|
||||
|
||||
for handle in handles.drain(..) {
|
||||
handle.join().unwrap();
|
||||
let (load, finished) = handle
|
||||
.join()
|
||||
.map_err(|e| format!("Thread panicked: {:?}", e))?;
|
||||
let thread_results: Vec<_> = load.into_iter().zip(finished.into_iter()).collect();
|
||||
all_results.extend(thread_results);
|
||||
}
|
||||
|
||||
//Now we have two vectors will all the indicies and rgba values, we can upload them to the bufer
|
||||
|
||||
let frame = self.pixels.frame_mut();
|
||||
for result in all_results {
|
||||
let index = result.0;
|
||||
let rgba = result.1;
|
||||
frame[index * 4..(index + 1) * 4].copy_from_slice(&rgba);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clear_buffer(&mut self) -> Result<(), Box<dyn Error>> {
|
||||
let pixels = &mut self.pixels.as_ref().lock().unwrap();
|
||||
let frame = pixels.frame_mut();
|
||||
let frame = self.pixels.frame_mut();
|
||||
for pixel in frame.chunks_exact_mut(4) {
|
||||
pixel.copy_from_slice(&self.raytracing_options.pixel_clear);
|
||||
}
|
||||
@@ -271,7 +291,7 @@ impl State {
|
||||
let size = self.buffer_height as usize * self.buffer_width as usize;
|
||||
let mut ray_queue: Vec<usize> = (0..size).collect();
|
||||
ray_queue.shuffle(&mut thread_rng());
|
||||
self.ray_queue = Arc::new(Mutex::new(ray_queue));
|
||||
self.ray_queue = ray_queue;
|
||||
}
|
||||
|
||||
fn render(&mut self) -> Result<(), Box<dyn Error>> {
|
||||
@@ -289,8 +309,7 @@ impl State {
|
||||
.prepare(&self.window)
|
||||
.expect("gui.prepare() failed");
|
||||
// Try to render pixels
|
||||
let pixels = &mut self.pixels.as_ref().lock().unwrap();
|
||||
if let Err(e) = pixels.render_with(|encoder, render_target, context| {
|
||||
if let Err(e) = self.pixels.render_with(|encoder, render_target, context| {
|
||||
context.scaling_renderer.render(encoder, render_target); // Render pixels
|
||||
self.gui
|
||||
.render(&self.window, encoder, render_target, context)?;
|
||||
|
||||
Reference in New Issue
Block a user