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