Bvh added

This commit is contained in:
STP
2023-12-01 16:37:50 -05:00
parent ba45fcadb7
commit d89e7f4951
8 changed files with 503 additions and 266 deletions

BIN
img2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

View File

@@ -1,8 +1,8 @@
let scene = Scene(); let scene = Scene();
let material = Material(V(0.2,0.2,0.2), V(0.2, 0.2, 0.2), 10.0); let material = Material(V(0.9,0.2,0.7), V(0.7, 0.2, 0.7), 10.0);
scene.addMaterial("material", material); scene.addMaterial("material", material);
let material2 = Material(V(0.2,0.7,0.2), V(0.2, 0.2, 0.2), 10.0); let material2 = Material(V(0.2,0.7,0.2), V(0.7, 0.7, 0.7), 10.0);
scene.addMaterial("mat2", material2); scene.addMaterial("mat2", material2);
let camera = Camera(P(0.0,0.0,2.0), P(0.0,0.0,0.0), V(0.0,1.0,0.0)); let camera = Camera(P(0.0,0.0,2.0), P(0.0,0.0,0.0), V(0.0,1.0,0.0));

View File

@@ -14,27 +14,27 @@ 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.2,0.2), V(0.2, 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("bluegreen", 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(false);
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.0,0.0), V(0.1, 0.01, 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); // scene.addLight("red", light);
let light = Ambient(V(0.1,0.1,0.1)); let light = Ambient(V(0.1,0.1,0.1));
scene.addLight("ambient", light); scene.addLight("ambient", light);
let sphere = Sphere(P(0.0,0.0,0.0), 1.0 ); // let sphere = Sphere(P(0.0,0.0,0.0), 1.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);
@@ -42,7 +42,7 @@ let sphere_node = Node(sphere, material);
for i in 0..6 { for i in 0..6 {
let sphere = Sphere(P(0.0,0.0,0.0), 2.0 ); let sphere = Sphere(P(0.0,0.0,0.0), 2.0 );
let sphere_node = Node(sphere, material); let sphere_node = Node( sphere, material);
sphere_node.translate(4.0*cos(i.to_float()), -4.0, 4.0*sin(i.to_float())); sphere_node.translate(4.0*cos(i.to_float()), -4.0, 4.0*sin(i.to_float()));
scene.addNode(i.to_string(), sphere_node); scene.addNode(i.to_string(), sphere_node);
} }
@@ -51,8 +51,8 @@ for i in 0..6 {
//scene.addNode(child); //scene.addNode(child);
let cube = CubeUnit(); let cube = CubeUnit();
let cube_node = Node(cube, material); let cube_node = Node( cube, material);
scene.addNode("cube", cube_node); scene.addNode("cube", cube_node);
//let gnonom = Gnonom(); //let gnonom = Gnonom();
//let gnonom_node = Node(gnonom); //let gnonom_node = Node(gnonom);

View File

@@ -1,7 +1,14 @@
use crate::{node::Node, ray::*, EPSILON}; use crate::{node::Node, ray::*, EPSILON};
use nalgebra::{Point3, Vector3}; use nalgebra::{distance, point, Matrix4, Point3, Vector3};
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::Index; use std::fmt;
// Debuging statics
static mut STATIC0: i32 = 0;
static mut STATIC1: i32 = 0;
static mut STATIC2: i32 = 0;
static mut STATIC3: i32 = 0;
static mut STATIC4: i32 = 0;
// BOUNDING BOX ----------------------------------------------------------------- // BOUNDING BOX -----------------------------------------------------------------
#[derive(Clone)] #[derive(Clone)]
@@ -16,18 +23,28 @@ impl AABB {
pub fn new(bln: Point3<f64>, trf: Point3<f64>) -> AABB { pub fn new(bln: Point3<f64>, trf: Point3<f64>) -> AABB {
let bln = bln + Vector3::new(EPSILON, EPSILON, EPSILON); let bln = bln + Vector3::new(EPSILON, EPSILON, EPSILON);
let trf = trf - Vector3::new(EPSILON, EPSILON, EPSILON); let trf = trf - Vector3::new(EPSILON, EPSILON, EPSILON);
let centroid = bln + (bln - trf) / 2.0; let centroid = bln + (trf - bln) / 2.0;
AABB { bln, trf, centroid } AABB { bln, trf, centroid }
} }
//Empty box
pub fn empty() -> AABB { pub fn empty() -> AABB {
AABB { AABB {
bln: Point3::new(0.0, 0.0, 0.0), bln: Point3::new(f64::MAX, f64::MAX, f64::MAX),
trf: Point3::new(0.0, 0.0, 0.0), trf: Point3::new(f64::MIN, f64::MIN, f64::MIN),
centroid: Point3::new(0.0, 0.0, 0.0), centroid: Point3::new(0.0, 0.0, 0.0),
} }
} }
//Apply a matrix transformation to a box
pub fn transform_mut(&mut self, mat: &Matrix4<f64>) {
let bln = &mut self.bln;
let trf = &mut self.trf;
let centroid = &mut self.centroid;
self.bln = mat.transform_point(bln);
self.trf = mat.transform_point(trf);
self.centroid = mat.transform_point(centroid);
}
// Intersect bounding box exactly // Intersect bounding box exactly
pub fn intersect_bounding_box(&self, ray: &Ray) -> bool { pub fn intersect_ray(&self, ray: &Ray) -> bool {
let bln = &self.bln; let bln = &self.bln;
let trf = &self.trf; let trf = &self.trf;
let t1 = (bln - ray.a).component_div(&ray.b); let t1 = (bln - ray.a).component_div(&ray.b);
@@ -53,7 +70,7 @@ impl AABB {
false false
} }
// Intersect way with some epsilon term // Intersect way with some epsilon term
pub fn intersect_bounding_box_aprox(&self, ray: &Ray) -> bool { pub fn intersect_ray_aprox(&self, ray: &Ray) -> bool {
let bln = &self.bln; let bln = &self.bln;
let trf = &self.trf; let trf = &self.trf;
let t1 = (bln - ray.a).component_div(&ray.b); let t1 = (bln - ray.a).component_div(&ray.b);
@@ -146,91 +163,26 @@ impl AABB {
let size = self.size(); let size = self.size();
2.0 * (size.x * size.y + size.x * size.z + size.y * size.z) 2.0 * (size.x * size.y + size.x * size.z + size.y * size.z)
} }
pub fn area(&self) -> f64 {
let extent = self.trf - self.bln;
return extent.x * extent.y + extent.y * extent.z + extent.z * extent.x;
}
// Volume of the AABB // Volume of the AABB
pub fn volume(&self) -> f64 { pub fn volume(&self) -> f64 {
let size = self.size(); let size = self.size();
size.x * size.y * size.z size.x * size.y * size.z
} }
} }
impl fmt::Display for AABB {
// Index implemntation of the BVH tree fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// pub enum BVHNode { if self.bln[0] == f64::MAX || self.trf[0] == f64::MIN {
// Leaf { writeln!(f, "Empty aabb")
// p_idx: usize, //Parent index } else {
// depth: usize, //Depth in BVH tree writeln!(f, "bln: {}\ntrf: {}", self.bln, self.trf)
// n_idx: usize, //Node index in corrosponding Vec<Node> }
// }, }
// Node { }
// p_idx: usize, //Parent index #[derive(Clone)]
// l_idx: usize, //Left child index
// l_aabb: AABB, //Left AABB
// r_idx: usize, //Right child index
// r_aabb: AABB, //Right AABB
// depth: usize, //Depth in BVH tree
// },
// }
// impl BVHNode {
// //Get parent
// fn get_parent(&self) -> usize {
// match *self {
// BVHNode::Node { p_idx, .. } | BVHNode::Leaf { p_idx, .. } => p_idx,
// }
// }
// //Get the left child of a node
// fn get_child_l(&self) -> usize {
// match *self {
// BVHNode::Leaf { .. } => panic!("Cannot get child of leaf node"),
// BVHNode::Node { l_idx, .. } => l_idx,
// }
// }
// // Get right child
// fn get_child_r(&self) -> usize {
// match *self {
// BVHNode::Leaf { .. } => panic!("Cannot get child of leaf node"),
// BVHNode::Node { r_idx, .. } => r_idx,
// }
// }
// // Get the depth of selected node
// pub fn depth(&self) -> usize {
// match *self {
// BVHNode::Node { depth, .. } | BVHNode::Leaf { depth, .. } => depth,
// }
// }
// // Get the aabb of the current node, if leaf return the primitives aabb
// // If node return the join of the two child nodes
// pub fn get_node_aabb(&self, nodes: &Vec<Node>) -> AABB {
// match *self {
// BVHNode::Node { l_aabb, r_aabb, .. } => l_aabb.join(&r_aabb),
// BVHNode::Leaf { aabb, .. } => aabb,
// }
// }
// }
// //Implementation of the BVH
// pub struct BVHTree<'a> {
// pub nodes: &'a HashMap<String, Node>,
// pub bvh_nodes: Vec<BVHNode>,
// }
// impl<'a> BVHTree<'a> {
// //Generate a BVH tree given a vector of nodes
// pub fn new(nodes: &HashMap<String, Node>) -> BVHTree {
// //We will make an aabb that bounds all shapes
// let mut root_aabb = AABB::empty();
// let mut root_centroid = AABB::empty();
// for (_, node) in nodes {
// let node_aabb = node.primitive.get_aabb();
// root_aabb.join_mut(&node_aabb);
// root_centroid.grow_mut(&node_aabb.get_centroid());
// }
// //We will make an aabb that bounds all centroids
// return BVHTree {
// nodes: &HashMap::new(),
// bvh_nodes: vec![],
// };
// }
// }
pub struct BVHNode { pub struct BVHNode {
aabb: AABB, //The nodes bounding box aabb: AABB, //The nodes bounding box
l_idx: usize, //Child node l, the right node is alway l_idx + 1 l_idx: usize, //Child node l, the right node is alway l_idx + 1
@@ -238,129 +190,282 @@ pub struct BVHNode {
prim_count: usize, //Number of primitives the node encapsulates prim_count: usize, //Number of primitives the node encapsulates
} }
impl BVHNode {
pub fn default() -> BVHNode {
BVHNode {
aabb: AABB::empty(),
l_idx: 0,
first_prim: 0,
prim_count: 0,
}
}
}
impl fmt::Display for BVHNode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "l_idx: {}", self.l_idx)?;
writeln!(f, "First Prim: {}", self.first_prim)?;
writeln!(f, "Prim Count: {}", self.prim_count)?;
writeln!(f, "aabb: {}", self.aabb)
}
}
pub struct BVH { pub struct BVH {
bvh_nodes: Vec<BVHNode>, //BVH nodes with AABBs bvh_nodes: Vec<BVHNode>, //BVH nodes with AABBs
nodes: Vec<Node>, //Nodes with primitives nodes: Vec<Node>, //Nodes with primitives
nodes_used: usize, nodes_used: usize,
root_node_index: usize,
} }
impl BVH { impl BVH {
//Build a bvh by subdividing recursively //Build a bvh by subdividing recursively
fn build(in_nodes: HashMap<String, Node>) -> BVH { pub fn build(in_nodes: &HashMap<String, Node>) -> BVH {
//Make our own vec of nodes so that we can refer to it by index /*
//Might be long to copy scene, so alternative methods may be prefered Make our own vec of nodes so that we can refer to it by index
let nodes = vec![]; This might be expensive so another method is preferred
*/
let mut nodes = vec![];
for (_, node) in in_nodes { for (_, node) in in_nodes {
nodes.push(node); nodes.push(node.clone());
} }
//A BVH tree will be maximum size of 2*n + 1 //A BVH tree will be maximum size of 2*n + 1
//Initialise an empty BVHNode with empty AABB
let n = nodes.len(); let n = nodes.len();
let mut bvh_nodes: Vec<BVHNode> = Vec::with_capacity(2 * n + 1); let bvh_nodes: Vec<BVHNode> = vec![BVHNode::default(); 2 * n + 1];
//Begin constructing our BVH tree //Begin constructing our BVH tree
let root_node_index = 0; //One node used to begin with (The root node)
let nodes_used = 1; let nodes_used = 1;
let tree = BVH { let mut tree = BVH {
nodes, nodes,
bvh_nodes, bvh_nodes,
root_node_index,
nodes_used, nodes_used,
}; };
// Get the root node at index 0
// Get the root node and assign it to index 0 let root = &mut tree.bvh_nodes[0];
let mut root = &bvh_nodes[root_node_index]; root.l_idx = 0; //Root node has no left or right child to begin
root.l_idx = 0; //Root node has no children to begin with
(root.first_prim, root.prim_count) = (0, n); //Make root include all n nodes (root.first_prim, root.prim_count) = (0, n); //Make root include all n nodes
tree.update_bvh_node_aabb(root_node_index); //Fit the root nodes AABB tree.update_bvh_node_aabb(0); //Create the root nodes AABB on the n primitives
tree.subdivide(root_node_index); tree.subdivide(0); //Sub divide the root node
tree tree
} }
// Will update the node's AABB at bvh[index] // Will update the node's AABB at bvh_nodes[index]
fn update_bvh_node_aabb(&mut self, index: usize) { fn update_bvh_node_aabb(&mut self, index: usize) {
// We will make his node bound all its primitives // We will make his node bound all its primitives
let bvh_node = &self.bvh_nodes[index]; //Get the BVHNode we are working let bvh_node = &mut self.bvh_nodes[index]; // Current BVHNode
let bvh_node_aabb = AABB::empty(); //Create the BVHNode's AABB let bvh_node_aabb = &mut bvh_node.aabb; //Current node AABB
let start_index = bvh_node.first_prim; //Start index of the first primitive the node contains let first_prim = bvh_node.first_prim; //Start index of prim
let count = bvh_node.prim_count; //Number of primitives within the nodes aabb let prim_count = bvh_node.prim_count; //Number of primitives within the nodes aabb
for i in 0..count { for i in 0..prim_count {
let primitive = &self.nodes[start_index + i].primitive; //Get the primitive from the Vec<Node> let node = &self.nodes[first_prim + i]; //Get the node from the Vec<Node>
let node_aabb = primitive.get_aabb(); //Get the primitives aabb let mut node_aabb = node.primitive.get_aabb(); //Get the primitive's AABB
bvh_node_aabb.join_mut(&node_aabb); //Join it with the bvh_nodes aabb node_aabb.transform_mut(&node.model); //Transform the AABB to world coordinates
bvh_node_aabb.join_mut(&node_aabb); //Join it with the BVH node's AABB
} }
}
// unsafe {
// println!("UPDATE TO AABB ---- {STATIC0}");
// STATIC0 += 1;
// let bvh_node = &mut self.bvh_nodes[index]; //Get the BVHNode we are working on
// println!("{bvh_node}");
// }
}
// Subdivision, will subdivide a split
fn subdivide(&mut self, index: usize) { fn subdivide(&mut self, index: usize) {
//Get the bvh_node we will be altering
// Determine the axis and position of the split plane // Determine the axis and position of the split plane
// Split the group of primitives in two halves using the split plane // Split the group of primitives in two halves using the split plane
// Create child nodes for each half // Create child nodes for each half
// Recurse into each of the child nodes. // Recurse into each of the child nodes.
// Get information about the node we want to subdivide //Leaf node case, we cannot sub-divide any more
let bvh_node = &self.bvh_nodes[index]; //Get the BVHNode we are working if self.bvh_nodes[index].prim_count == 1 {
return;
/* ----------------- SUBDIVIDE BY CENTROID --------------------- */ };
// let bvh_node_centroid_aabb = AABB::empty(); //Create the BVHNode's AABB
// let start_index = bvh_node.first_prim; //Start index of the first primitive the node contains
// let count = bvh_node.prim_count; //Number of primitives within the nodes aabb
// for i in 0..count {
// let primitive = &self.nodes[start_index + i].primitive; //Get the primitive from the Vec<Node>
// let node_aabb_centroid = primitive.get_aabb().get_centroid(); //Get the primitives aabb centroid
// bvh_node_centroid_aabb.grow_mut(&node_aabb_centroid); // Grow the aabb to include the all centroids
// }
/* ------------ SUBDIVIDE BY LONGEST AXIS ------------ */ /* ------------ SUBDIVIDE BY LONGEST AXIS ------------ */
//Get information about the node we want to subdivide
let (bln, trf) = (bvh_node.aabb.bln, bvh_node.aabb.trf); let (bln, trf) = (
self.bvh_nodes[index].aabb.bln,
self.bvh_nodes[index].aabb.trf,
);
let extent = trf - bln; let extent = trf - bln;
let axis = 0; // Assume that x is longest let mut axis = 0; // Assume that x is longest
if extent.y > extent.x { if extent.y > extent.x {
axis = 1 // Split y if longer axis = 1; // Split y if longest
}; };
if extent.z > extent[axis] { if extent.z > extent[axis] {
axis = 2 // Split z if loner axis = 2; // Split z if longest
}; };
let split_pos = bln[axis] + extent[axis] * 0.5; //Final split along this axis let split_pos = bln[axis] + extent[axis] * 0.5; // Final split down the middle of AABB
//Perform a quicksort our nodes /* --------- SUBDIVIDE BY Surface Area Heuristic ---------*/
let i = bvh_node.first_prim; // let mut best_axis: Option<usize> = None;
let j = i + bvh_node.prim_count - 1; // let mut best_pos = 0.0;
while i <= j { // let mut best_cost = 1e30;
let centroid = self.nodes[i].primitive.get_aabb().get_centroid(); // let first_prim_idx = self.bvh_nodes[index].first_prim;
if centroid[axis] < split_pos { // for axis in 0..2 {
i += 1; //If it is on left split remain in place // for i in 0..self.bvh_nodes[index].prim_count {
} else { // let node = &self.nodes[first_prim_idx + i];
self.nodes.swap(i, j); //Move to right split // //Get the centroid of the bounding box
j -= 1; // let centroid = node.primitive.get_aabb().get_centroid();
// //Place the centroid into world coordinates
// let world_centroid = node.model.transform_point(&centroid);
// //Get the candidate position
// let candidate_pos = world_centroid[axis];
// let cost = self.evaluate_sah(&self.bvh_nodes[index], axis, candidate_pos);
// if cost < best_cost {
// best_pos = candidate_pos;
// best_axis = Some(axis);
// best_cost = cost;
// }
// }
// }
// let axis = match best_axis {
// Some(axis) => axis,
// None => 0,
// };
// let split_pos = best_pos;
let left_count;
let right_count;
let mut i;
let mut j;
{
let bvh_node = &mut self.bvh_nodes[index];
i = bvh_node.first_prim; //Start of array
j = i + bvh_node.prim_count - 1; //End of array
while i <= j {
//Perform a quicksort dependent on location
let node = &self.nodes[i]; // Node we would like to sort
let centroid = node.primitive.get_aabb().get_centroid(); //Centroid of node we would like to sort
let centroid = node.model.transform_point(&centroid); //Transform to world coordinates
if centroid[axis] < split_pos {
i += 1; // On Left-Hand-Side
} else {
self.nodes.swap(i, j);
j -= 1; // On Right-Hand-Side
}
}
//Now we have two splits
//The lhs of the array is in the left split 0..left_count
//The rhs of the array is on the right split left_count + 1..n
left_count = i - bvh_node.first_prim; //Number of prims on lhs
right_count = bvh_node.prim_count - left_count;
//println!("SPLIT INTO: {left_count} {right_count}");
if left_count == 0 || left_count == bvh_node.prim_count {
//Split did nothing
return;
} }
} }
//Now we have two children, the lhs of the array is in the left split, and the rhs of the array is on the right split // unsafe {
let left_count = i - bvh_node.first_prim; //Number of prims on lhs // println!("SUBDIVIDE: {STATIC1}");
if left_count == 0 || left_count == bvh_node.prim_count { // println!("SPLIT INTO: {left_count} ");
return; //If we have no more on the left, disregard // STATIC1 += 1;
} // }
let l_idx = self.nodes_used; //Left child let l_idx = self.nodes_used; //Left child
self.nodes_used += 1; self.bvh_nodes[index].l_idx = l_idx;
let r_idx = self.nodes_used; //Right child self.nodes_used = self.nodes_used + 2;
self.nodes_used += 1;
bvh_node.l_idx = l_idx; //Set left node information
self.bvh_nodes[l_idx].first_prim = self.bvh_nodes[index].first_prim; //Left split begins at parent split
self.bvh_nodes[l_idx].prim_count = left_count; // Left prims
self.bvh_nodes[l_idx].first_prim = bvh_node.first_prim; //Set left split //Set right node information
self.bvh_nodes[l_idx].prim_count = left_count; //We know this info from our quicksort self.bvh_nodes[l_idx + 1].first_prim = i; // Right split start index
self.bvh_nodes[l_idx + 1].prim_count = right_count;
self.bvh_nodes[r_idx].first_prim = i; //Set right split information //Current node is not a leaf node
self.bvh_nodes[r_idx].prim_count = bvh_node.prim_count - left_count; self.bvh_nodes[index].prim_count = 0;
bvh_node.prim_count = 0;
self.update_bvh_node_aabb(l_idx); //Update AABB for left of split self.update_bvh_node_aabb(l_idx); //Update AABB for left of split
self.update_bvh_node_aabb(r_idx); //Update AABB for right of split self.update_bvh_node_aabb(l_idx + 1); //Update AABB for right of split
//Recurse //Recurse
self.subdivide(l_idx); self.subdivide(l_idx); // Subdivide left index
self.subdivide(r_idx); self.subdivide(l_idx + 1); // SUbdivide right index
}
// 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) {
// No intersection with BVH in world coordinates
return None;
}
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 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));
}
}
return None;
} else {
//Recurse down the BVH
//Recurse down the BVH right node
let intersect_l = self.traverse(ray, bvh_node.l_idx);
let intersect_r = self.traverse(ray, bvh_node.l_idx + 1);
match (intersect_l, intersect_r) {
(None, None) => return None,
(Some(intersect), None) => return Some(intersect),
(None, Some(intersect)) => return Some(intersect),
(Some((node_l, inter_l)), Some((node_r, inter_r))) => {
//Compare intersect distance
let dist_l = distance(&ray.a, &inter_l.point);
let dist_r = distance(&ray.a, &inter_r.point);
if dist_l < dist_r {
return Some((node_l, inter_l));
} else {
return Some((node_r, inter_r));
}
}
}
}
}
fn evaluate_sah(&self, node: &BVHNode, axis: usize, pos: f64) -> f64 {
// determine triangle counts and bounds for this split candidate
let mut l_aabb = AABB::empty();
let mut r_aabb = AABB::empty();
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();
if aabb.trf[axis] < pos {
l_count += 1;
l_aabb.grow_mut(&aabb.trf);
} else {
r_count += 1;
r_aabb.grow_mut(&aabb.bln);
}
}
let cost = l_count as f64 * l_aabb.area() + r_count as f64 * r_aabb.area();
match cost > 0.0 {
true => 0.0,
false => 1e30,
}
}
}
impl fmt::Display for BVH {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (i, node) in self.bvh_nodes.iter().enumerate() {
writeln!(f, "Node: {i}")?;
writeln!(f, "{node}")?;
}
write!(f, "")
} }
} }

View File

@@ -5,7 +5,7 @@ use crate::{
node::*, node::*,
primitive::*, primitive::*,
scene::*, scene::*,
state::{INIT_FILE, SAVE_FILE}, state::{RaytracingOption, INIT_FILE, SAVE_FILE},
}; };
use imgui::*; use imgui::*;
use nalgebra::{Point3, Vector3}; use nalgebra::{Point3, Vector3};
@@ -14,14 +14,24 @@ use rhai::Engine;
use std::time::Instant; use std::time::Instant;
//BUFFER CONSTANTS //BUFFER CONSTANTS
const BUFFER_PROPORTION_INIT: f32 = 0.2;
const BUFFER_PROPORTION_MIN: f32 = 0.1; 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 RAYS_INIT: i32 = 100; const RAYS_MIN: u32 = 100;
const RAYS_MIN: i32 = 100; const RAYS_MAX: u32 = 10000;
const RAYS_MAX: i32 = 10000; const MIN_DEPTH: u8 = 5;
const MAX_DEPTH: u8 = 100;
const MIN_SAMPLES: u32 = 5;
const MAX_SAMPLES: u32 = 100;
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_COEFFICIENT: f32 = 0.0;
const MAX_DIFFUSE_COEFFICIENT: f32 = 1.0;
//MATERIAL CONSTANTS //MATERIAL CONSTANTS
const MIN_D: f32 = 0.0; const MIN_D: f32 = 0.0;
@@ -47,14 +57,14 @@ const MAX_ROTATION: f64 = 180.0;
const MAX_TRANSLATE: f64 = 10.0; const MAX_TRANSLATE: f64 = 10.0;
// CAMERA CONSTANTS // CAMERA CONSTANTS
const MIN_FOV: f32 = 10.0; const MIN_FOV: f64 = 10.0;
const MAX_FOV: f32 = 160.0; const MAX_FOV: f64 = 160.0;
//const CAMERA_INIT: f32 = 5.0; //const CAMERA_INIT: f32 = 5.0;
/// Manages all state required for rendering Dear ImGui over `Pixels`test. /// Manages all state required for rendering Dear ImGui over `Pixels`test.
pub enum GuiEvent { pub enum GuiEvent {
BufferResize(f32, f32), RaytracerOption(RaytracingOption),
CameraUpdate(Camera, f32), CameraUpdate(Camera),
SceneLoad(Scene), SceneLoad(Scene),
SaveImage(String), SaveImage(String),
} }
@@ -72,12 +82,9 @@ pub struct Gui {
engine: Engine, engine: Engine,
scene: Scene, scene: Scene,
pub ray_num: i32, raytracing_option: RaytracingOption,
buffer_proportion: f32,
camera: Camera, camera: Camera,
camera_fov: f32,
image_filename: String, image_filename: String,
} }
@@ -122,7 +129,7 @@ impl Gui {
let renderer = imgui_wgpu::Renderer::new(&mut imgui, device, queue, config); let renderer = imgui_wgpu::Renderer::new(&mut imgui, device, queue, config);
// Return GUI context // Return GUI context
Self { let mut gui = Self {
imgui, imgui,
platform, platform,
renderer, renderer,
@@ -135,14 +142,29 @@ impl Gui {
engine: init_engine(), engine: init_engine(),
scene: Scene::empty(), scene: Scene::empty(),
ray_num: RAYS_INIT, raytracing_option: RaytracingOption::default(),
buffer_proportion: BUFFER_PROPORTION_INIT,
camera: Camera::unit(), camera: Camera::unit(),
camera_fov: 110.0,
image_filename: String::from(SAVE_FILE), image_filename: String::from(SAVE_FILE),
};
// ------------ TESTING CODE (LOAD SCENE ON START) -----------------
match std::fs::read_to_string(&mut gui.script_filename) {
Ok(script) => {
gui.script = script;
}
Err(e) => println!("{}", e),
} }
match gui.engine.eval(&gui.script) {
Ok(scene) => {
gui.scene = scene;
gui.event = Some(GuiEvent::SceneLoad(gui.scene.clone()));
}
Err(e) => println!("{e}"),
}
// ------------ TESTING CODE (LOAD SCENE ON START) -----------------
gui
} }
/// Prepare Dear ImGui. /// Prepare Dear ImGui.
@@ -183,23 +205,73 @@ impl Gui {
//Raytracing options ------------------------------------------- //Raytracing options -------------------------------------------
if CollapsingHeader::new("Raytracer").build(ui) { if CollapsingHeader::new("Raytracer").build(ui) {
// Numbers of rays to render // Numbers of rays to render per pass
ui.slider("# Rays: ", RAYS_MIN, RAYS_MAX, &mut self.ray_num); ui.slider(
"Rays Per Pass",
RAYS_MIN,
RAYS_MAX,
&mut self.raytracing_option.rays_per_pass,
);
// Proportion of the window the buffer occupies // Proportion of the window the buffer occupies
ui.slider( ui.slider(
"% Buffer: ", "% Buffer: ",
BUFFER_PROPORTION_MIN, BUFFER_PROPORTION_MIN,
BUFFER_PROPORTION_MAX, BUFFER_PROPORTION_MAX,
&mut self.buffer_proportion, &mut self.raytracing_option.buffer_proportion,
);
//Clear colour for scene
ui.slider_config("Clear Colour", 0, 255)
.build_array(&mut self.raytracing_option.clear_color);
//Clear colour if no intersect
ui.slider_config("Pixel Clear Colour", 0, 255)
.build_array(&mut self.raytracing_option.pixel_clear);
//Ray depth slider
ui.slider(
"Ray Depth",
MIN_DEPTH,
MAX_DEPTH,
&mut self.raytracing_option.ray_depth,
);
//Ray samples slider
ui.slider(
"Ray Samples",
MIN_SAMPLES,
MAX_SAMPLES,
&mut self.raytracing_option.ray_samples,
);
//Ray randomness
ui.slider(
"Ray Randomness",
MIN_RANDOM,
MAX_RANDOM,
&mut self.raytracing_option.ray_randomness,
);
//Number of diffuse rays
ui.slider(
"Diffuse Rays",
MIN_DIFFUSE_RAYS,
MAX_DIFFUSE_RAYS,
&mut self.raytracing_option.diffuse_rays,
);
//Diffuse Coefficient
ui.slider(
"Diffuse Coefficient",
MIN_DIFFUSE_COEFFICIENT,
MAX_DIFFUSE_COEFFICIENT,
&mut self.raytracing_option.diffuse_coefficient,
); );
// Fov of the buffer // Fov of the buffer
ui.slider("fov", MIN_FOV, MAX_FOV, &mut self.camera_fov); ui.slider(
"fov",
MIN_FOV,
MAX_FOV,
&mut self.raytracing_option.buffer_fov,
);
// Enable BVH
ui.checkbox("Enable BVH", &mut self.raytracing_option.bvh_active);
// Apply stored changes // Apply stored changes
if ui.button("Apply") { if ui.button("Apply") {
self.event = Some(GuiEvent::BufferResize( self.event = Some(GuiEvent::RaytracerOption(self.raytracing_option.clone()));
self.buffer_proportion,
self.camera_fov,
));
}; };
} }
// CAMERA OPTIONS ---------------------------------------- // CAMERA OPTIONS ----------------------------------------
@@ -214,7 +286,7 @@ impl Gui {
.build_array(self.camera.up.as_mut_slice()); .build_array(self.camera.up.as_mut_slice());
if ui.button("Apply Camera") { if ui.button("Apply Camera") {
println!("Camera changed"); println!("Camera changed");
self.event = Some(GuiEvent::CameraUpdate(self.camera.clone(), self.camera_fov)); self.event = Some(GuiEvent::CameraUpdate(self.camera.clone()));
} }
} }
// SCRIPTING -------------------------------------------- // SCRIPTING --------------------------------------------
@@ -318,7 +390,7 @@ impl Gui {
for (label, camera) in &self.scene.cameras { for (label, camera) in &self.scene.cameras {
if ui.button(label) { if ui.button(label) {
self.camera = camera.clone(); self.camera = camera.clone();
self.event = Some(GuiEvent::CameraUpdate(camera.clone(), self.camera_fov)); self.event = Some(GuiEvent::CameraUpdate(camera.clone()));
} }
} }
} }

View File

@@ -14,7 +14,7 @@ pub struct Node {
//Model matricies //Model matricies
pub model: Matrix4<f64>, pub model: Matrix4<f64>,
pub inv_model: Matrix4<f64>, pub inv_model: Matrix4<f64>,
//If the node is active
pub active: bool, pub active: bool,
} }

View File

@@ -1,11 +1,7 @@
use crate::{node::Node, scene::Scene, EPSILON}; use crate::{bvh::BVH, node::Node, scene::Scene, state::RaytracingOption, EPSILON};
use nalgebra::{distance, Matrix4, Point3, Vector3}; use nalgebra::{distance, Matrix4, Point3, Vector3};
use rand; use rand;
const MAX_DEPTH: u8 = 5;
const DIFFUSE_RAYS: i8 = 5;
const DIFFUSE_COEFFICIENT: f32 = 0.5;
fn random_vec() -> Vector3<f64> { fn random_vec() -> Vector3<f64> {
Vector3::new(rand::random(), rand::random(), rand::random()) Vector3::new(rand::random(), rand::random(), rand::random())
} }
@@ -67,6 +63,7 @@ impl Ray {
} }
} }
//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
pub fn hit_scene(&self, scene: &Scene) -> bool { pub fn hit_scene(&self, scene: &Scene) -> bool {
for (_, node) in &scene.nodes { for (_, node) in &scene.nodes {
if !node.active { if !node.active {
@@ -74,17 +71,14 @@ impl Ray {
} }
// Transform ray into local model cordinates // Transform ray into local model cordinates
let ray = self.transform(&node.inv_model); let ray = self.transform(&node.inv_model);
// Check bounding box intersection if node.primitive.intersect_ray(&ray).is_some() {
if node.primitive.intersect_bounding_box(&ray) { return true;
// Check primitive intersection
if node.primitive.intersect_ray(&ray).is_some() {
return true;
}
} }
} }
false false
} }
//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
pub fn closest_intersect<'a>(&'a self, scene: &'a Scene) -> Option<(&Node, Intersection)> { pub fn closest_intersect<'a>(&'a self, scene: &'a Scene) -> Option<(&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;
@@ -94,38 +88,55 @@ impl Ray {
} }
// Transform ray into local model cordinates // Transform ray into local model cordinates
let ray = self.transform(&node.inv_model); let ray = self.transform(&node.inv_model);
// Check bounding box intersection // Check primitive intersection
if node.primitive.intersect_bounding_box(&ray) { if let Some(intersect) = node.primitive.intersect_ray(&ray) {
// Check primitive intersection // Dont intersect with itself
if let Some(intersect) = node.primitive.intersect_ray(&ray) { if intersect.distance < EPSILON {
// Dont intersect with itself continue;
if intersect.distance < EPSILON { }
continue; // Check for closest distance by converting to world coords
} let intersect = intersect.transform(&node.model, &node.inv_model);
// Check for closest distance by converting to world coords let distance = distance(&ray.a, &intersect.point);
let intersect = intersect.transform(&node.model, &node.inv_model); if distance < closest_distance {
let distance = distance(&ray.a, &intersect.point); closest_distance = distance;
if distance < closest_distance { closest_intersect = Some((node, intersect));
closest_distance = distance;
closest_intersect = Some((node, intersect));
}
} }
} }
} }
closest_intersect closest_intersect
} }
// This function takes a scene and returns the color of the point where the ray intersects the scene // This function takes a scene and returns the color of the point where the ray intersects the scene
pub fn shade_ray(&self, scene: &Scene, depth: u8) -> Option<Vector3<f32>> { pub fn shade_ray(
if depth == MAX_DEPTH { &self,
scene: &Scene,
depth: u8,
options: &RaytracingOption,
sbvh: &Option<BVH>,
) -> Option<Vector3<f32>> {
if depth == options.ray_depth {
return None; return None;
} }
match self.closest_intersect(scene) { match sbvh {
Some((node, intersect)) => { Some(bvh) => {
Some(Ray::phong_shade_point( //Intersect the scene with the bvh
&scene, &self, &node, &intersect, depth, if let Some((node, intersect)) = bvh.traverse(&self, 0) {
)) // If there is an intersection, shade it return Some(Ray::phong_shade_point(
&scene, &self, &node, &intersect, depth, options, sbvh,
));
}
return None;
}
None => {
//No BVH given so intersect normally
match self.closest_intersect(scene) {
Some((node, intersect)) => {
Some(Ray::phong_shade_point(
&scene, &self, &node, &intersect, depth, options, sbvh,
)) // If there is an intersection, shade it
}
None => None, // If there is no intersection, return None
}
} }
None => None, // If there is no intersection, return None
} }
} }
@@ -136,9 +147,11 @@ impl Ray {
node: &Node, node: &Node,
intersect: &Intersection, intersect: &Intersection,
depth: u8, depth: u8,
options: &RaytracingOption,
bvh: &Option<BVH>,
) -> Vector3<f32> { ) -> Vector3<f32> {
let normal = &intersect.normal; let normal = &intersect.normal;
let point = intersect.point; let point = intersect.point + normal * 0.0001;
let incidence = &ray.b; let incidence = &ray.b;
let material = &node.material; let material = &node.material;
@@ -172,18 +185,18 @@ impl Ray {
let mut reflect = Vector3::zeros(); let mut reflect = Vector3::zeros();
let reflect_dir = incidence - 2.0 * incidence.dot(&normal) * normal; let reflect_dir = incidence - 2.0 * incidence.dot(&normal) * normal;
let reflect_ray = Ray::new(point, reflect_dir); let reflect_ray = Ray::new(point, reflect_dir);
if let Some(col) = reflect_ray.shade_ray(scene, depth + 1) { if let Some(col) = reflect_ray.shade_ray(scene, depth + 1, options, bvh) {
reflect += col.component_mul(&material.kr) reflect += col.component_mul(&material.kr)
} }
//Diffuse component (Lambertian) //Diffuse component (Lambertian)
let mut diffuse = Vector3::zeros(); let mut diffuse = Vector3::zeros();
diffuse += material.kd * n_dot_l; diffuse += material.kd * n_dot_l;
for _ in 0..DIFFUSE_RAYS { for _ in 0..options.diffuse_rays {
let diffuse_dir = random_unit_vec(); let diffuse_dir = random_unit_vec();
let diffuse_ray = Ray::new(point.clone(), diffuse_dir + normal); let diffuse_ray = Ray::new(point.clone(), diffuse_dir + normal);
if let Some(col) = diffuse_ray.shade_ray(scene, depth + 1) { if let Some(col) = diffuse_ray.shade_ray(scene, depth + 1, options, bvh) {
diffuse += col * DIFFUSE_COEFFICIENT; diffuse += col * options.diffuse_coefficient;
} }
} }
@@ -215,13 +228,11 @@ impl Ray {
continue; continue;
} }
let ray = self.transform(&node.inv_model); let ray = self.transform(&node.inv_model);
if node.primitive.intersect_bounding_box(&ray) { if node.primitive.intersect_ray(&ray).is_some() {
if node.primitive.intersect_ray(&ray).is_some() { return true;
return true;
}
} }
} }
false return false;
} }
//Cast a set of rays //Cast a set of rays
pub fn cast_rays( pub fn cast_rays(

View File

@@ -1,5 +1,6 @@
//Use linear algebra module //Use linear algebra module
use crate::bvh::BVH;
use crate::camera::Camera; use crate::camera::Camera;
use crate::ray::Ray; use crate::ray::Ray;
use crate::{gui::Gui, scene::Scene}; use crate::{gui::Gui, scene::Scene};
@@ -21,16 +22,45 @@ use winit::window::{Window, WindowBuilder};
const START_WIDTH: i32 = 1200; const START_WIDTH: i32 = 1200;
const START_HEIGHT: i32 = 700; const START_HEIGHT: i32 = 700;
const RAY_SAMPLES: i8 = 5;
const RAY_RANDOMNESS: f64 = 100.0;
const COLOUR_CLEAR: [u8; 4] = [0x22, 0x00, 0x11, 0x55];
const PIXEL_CLEAR: [u8; 4] = [0x55, 0x00, 0x22, 0x55];
pub const INIT_FILE: &str = "rhai/scene.rhai"; pub const INIT_FILE: &str = "rhai/scene.rhai";
pub const SAVE_FILE: &str = "img.png"; pub const SAVE_FILE: &str = "img.png";
#[derive(Clone)]
pub struct RaytracingOption {
pub ray_samples: u32,
pub ray_randomness: f64,
pub clear_color: [u8; 4],
pub pixel_clear: [u8; 4],
pub rays_per_pass: u32,
pub buffer_proportion: f32,
pub buffer_fov: f64,
pub ray_depth: u8,
pub diffuse_rays: u8,
pub diffuse_coefficient: f32,
pub bvh_active: bool,
}
impl RaytracingOption {
pub fn default() -> RaytracingOption {
RaytracingOption {
ray_samples: 10,
ray_randomness: 100.0,
clear_color: [0x22, 0x00, 0x11, 0x55],
pixel_clear: [0x55, 0x00, 0x22, 0x55],
rays_per_pass: 200,
buffer_proportion: 1.0,
buffer_fov: 110.0,
ray_depth: 5,
diffuse_rays: 5,
diffuse_coefficient: 0.5,
bvh_active: false,
}
}
}
pub struct State { pub struct State {
scene: Scene, scene: Scene,
bvh: Option<BVH>,
camera: Camera, camera: Camera,
window: Window, window: Window,
@@ -42,6 +72,7 @@ pub struct State {
rays: Vec<Ray>, rays: Vec<Ray>,
ray_queue: Vec<usize>, ray_queue: Vec<usize>,
raytracing_options: RaytracingOption,
} }
impl State { impl State {
@@ -53,39 +84,46 @@ impl State {
Self { Self {
scene, scene,
bvh: None,
camera, camera,
window, window,
buffer_width: window_size.width as u32, buffer_width: window_size.width as u32,
buffer_height: window_size.height as u32, buffer_height: window_size.height as u32,
pixels: pixels, pixels,
gui, gui,
rays, rays,
ray_queue: Vec::new(), ray_queue: Vec::new(),
raytracing_options: RaytracingOption::default(),
} }
} }
fn update(&mut self) -> Result<(), Box<dyn Error>> { fn update(&mut self) -> Result<(), Box<dyn Error>> {
if let Some(event) = self.gui.event.take() { if let Some(event) = self.gui.event.take() {
match event { match event {
GuiEvent::BufferResize(proportion, fov) => { GuiEvent::RaytracerOption(options) => {
self.resize_buffer(proportion, fov as f64)? self.raytracing_options = options;
match self.raytracing_options.bvh_active {
true => self.bvh = Some(BVH::build(&mut self.scene.nodes)),
false => self.bvh = None,
}
self.resize_buffer()?
} }
GuiEvent::CameraUpdate(camera, fovy) => { GuiEvent::CameraUpdate(camera) => {
self.rays = Ray::cast_rays( self.rays = Ray::cast_rays(
&camera.eye, &camera.eye,
&camera.target, &camera.target,
&camera.up, &camera.up,
fovy as f64, self.raytracing_options.buffer_fov,
self.buffer_width, self.buffer_width,
self.buffer_height, self.buffer_height,
); );
self.camera = camera; self.camera = camera;
self.clear()?; self.clear_buffer()?;
self.reset_queue(); self.reset_queue();
} }
GuiEvent::SceneLoad(scene) => { GuiEvent::SceneLoad(scene) => {
self.scene = scene; self.scene = scene;
self.clear()?; self.clear_buffer()?;
self.reset_queue(); self.reset_queue();
} }
GuiEvent::SaveImage(filename) => { GuiEvent::SaveImage(filename) => {
@@ -103,14 +141,16 @@ impl State {
Ok(()) Ok(())
} }
fn resize_buffer(&mut self, proportion: f32, fovy: f64) -> Result<(), Box<dyn Error>> { fn resize_buffer(&mut self) -> Result<(), Box<dyn Error>> {
// Calculate new buffer dimensions based on proportion // Calculate new buffer dimensions based on proportion
let size = self.window.inner_size(); let size = self.window.inner_size();
let proportion = &self.raytracing_options.buffer_proportion;
let fovy = self.raytracing_options.buffer_fov;
self.buffer_width = (size.width as f32 * proportion) as u32; self.buffer_width = (size.width as f32 * proportion) as u32;
self.buffer_height = (size.height as f32 * proportion) as u32; self.buffer_height = (size.height as f32 * proportion) as u32;
// Clear the buffer and reset the ray queue // Clear the buffer and reset the ray queue
self.clear()?; self.clear_buffer()?;
self.reset_queue(); self.reset_queue();
// Recalculate rays with new buffer dimensions // Recalculate rays with new buffer dimensions
@@ -149,7 +189,10 @@ impl State {
fn draw(&mut self) -> Result<(), Box<dyn Error>> { fn draw(&mut self) -> Result<(), Box<dyn Error>> {
//Draw ray_num in a block //Draw ray_num in a block
let frame = self.pixels.frame_mut(); let frame = self.pixels.frame_mut();
for _ in 0..self.gui.ray_num { let randomness = &self.raytracing_options.ray_randomness;
let samples = &self.raytracing_options.ray_samples;
let samples_f32 = *samples as f32;
for _ in 0..self.raytracing_options.rays_per_pass {
//Get random index from queue //Get random index from queue
let index = match self.ray_queue.pop() { let index = match self.ray_queue.pop() {
Some(index) => index, Some(index) => index,
@@ -157,39 +200,45 @@ impl State {
}; };
//Shade colour for selected ray //Shade colour for selected ray
let mut colour = Vector3::zeros(); let mut colour = Vector3::zeros();
for _ in 0..RAY_SAMPLES { for _ in 0..*samples {
let ray = &self.rays[index]; let ray = &self.rays[index];
let point = ray.a; let point = ray.a;
let dir = ray.b; let dir = ray.b;
let rx = (random::<f64>() - 0.5) / RAY_RANDOMNESS; let rx = (random::<f64>() - 0.5) / randomness;
let ry = (random::<f64>() - 0.5) / RAY_RANDOMNESS; let ry = (random::<f64>() - 0.5) / randomness;
let rz = (random::<f64>() - 0.5) / RAY_RANDOMNESS; let rz = (random::<f64>() - 0.5) / randomness;
let nx = dir.x + rx; let nx = dir.x + rx;
let ny = dir.y + ry; let ny = dir.y + ry;
let nz = dir.z + rz; let nz = dir.z + rz;
let rand_ray = Ray::new(point, Vector3::new(nx, ny, nz)); let rand_ray = Ray::new(point, Vector3::new(nx, ny, nz));
if let Some(ray_colour) = rand_ray.shade_ray(&self.scene, 0) { if let Some(ray_colour) =
rand_ray.shade_ray(&self.scene, 0, &self.raytracing_options, &self.bvh)
{
colour += ray_colour; colour += ray_colour;
}; };
} }
colour = (colour / RAY_SAMPLES as 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];
frame[index * 4..(index + 1) * 4].copy_from_slice(&rgba); frame[index * 4..(index + 1) * 4].copy_from_slice(&rgba);
} }
Ok(()) Ok(())
} }
fn clear(&mut self) -> Result<(), Box<dyn Error>> { fn clear_buffer(&mut self) -> Result<(), Box<dyn Error>> {
let frame = self.pixels.frame_mut(); let frame = self.pixels.frame_mut();
for pixel in frame.chunks_exact_mut(4) { for pixel in frame.chunks_exact_mut(4) {
pixel.copy_from_slice(&COLOUR_CLEAR); pixel.copy_from_slice(&self.raytracing_options.pixel_clear);
} }
Ok(()) Ok(())
} }
fn reset_queue(&mut self) { fn reset_queue(&mut self) {
match self.raytracing_options.bvh_active {
true => self.bvh = Some(BVH::build(&mut self.scene.nodes)),
false => self.bvh = None,
}
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());
@@ -234,7 +283,7 @@ pub fn run() -> Result<(), Box<dyn Error>> {
let gui = Gui::new(&window, &pixels); let gui = Gui::new(&window, &pixels);
let mut state = State::new(window, pixels, gui); let mut state = State::new(window, pixels, gui);
state.resize_buffer(1.0, 90.0)?; state.resize_buffer()?;
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| {
state.gui.handle_event(&state.window, &event); state.gui.handle_event(&state.window, &event);