BVH and rays complete

This commit is contained in:
STP
2023-12-02 21:52:59 -05:00
parent d89e7f4951
commit d8488f24f7
9 changed files with 277 additions and 190 deletions

View File

@@ -10,18 +10,20 @@ scene.addCamera("Cam", camera);
let falloff = V(0.0,0.0,0.0); let falloff = V(0.0,0.0,0.0);
let light = Light(P(6.0,6.0,6.0), V(0.4,0.4,0.4), falloff); let light = Light(P(6.0,6.0,6.0), V(0.4,0.4,0.4), falloff);
light.active(false); light.active(true);
scene.addLight("white", light); scene.addLight("white", light);
let light = Light(P(2.0,0.0,0.0), V(0.0,1.0,0.0), V(0.1, 0.01, 0.001)); let light = Light(P(2.0,0.0,0.0), V(1.0,1.0,0.0), falloff);
light.active(false); light.active(false);
scene.addLight("green", light); scene.addLight("green", light);
let light = Light(P(-2.0,0.0,0.0), V(1.0,0.0,0.0), V(0.1, 0.01, 0.001)); let light = Light(P(-2.0,0.0,0.0), V(1.0,0.0,0.0), falloff);
light.active(false); light.active(false);
scene.addLight("red", light); scene.addLight("red", light);
let light = Ambient(V(0.3,0.3,0.3)); let light = Ambient(V(0.3,0.3,0.3));
light.active(false);
scene.addLight("ambient", light); scene.addLight("ambient", light);
let tri = TriangleUnit(); let tri = TriangleUnit();
let tri_node = Node(tri, material); let tri_node = Node(tri, material);
tri_node.active(false); tri_node.active(false);
@@ -39,7 +41,7 @@ scene.addNode("cone", cone_node);
let torus = Torus(0.5, 1.5); let torus = Torus(0.5, 1.5);
let torus_node = Node(torus, material); let torus_node = Node(torus, material);
torus_node.active(true); torus_node.active(false);
scene.addNode("torus", torus_node); scene.addNode("torus", torus_node);
@@ -59,7 +61,8 @@ scene.addNode("ground", ground_node);
let mesh = Mesh("obj/cat.obj"); let mesh = Mesh("obj/cat.obj");
let mesh_node = Node(mesh, material); let mesh_node = Node(mesh, material);
mesh_node.active(false); mesh_node.active(true);
mesh_node.rotate(0.0,90.0,90.0);
scene.addNode("mesh", mesh_node); scene.addNode("mesh", mesh_node);

View File

@@ -16,46 +16,46 @@ 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("bluegreen", material);
let material2 = Material(V(0.2,0.9,0.8), V(0.3, 0.8, 0.8), 25.0);
scene.addMaterial("bluegreen2", 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.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); scene.addLight("red", light);
let light = Ambient(V(0.1,0.1,0.1)); let light = Ambient(V(0.5,0.5,0.5));
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,-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);
//scene.addNode("mesh", mesh_node); //scene.addNode("mesh", mesh_node);
for i in 0..6 {
let sphere = Sphere(P(0.0,0.0,0.0), 2.0 );
let sphere_node = Node( sphere, material);
sphere_node.translate(4.0*cos(i.to_float()), -4.0, 4.0*sin(i.to_float()));
scene.addNode(i.to_string(), sphere_node);
}
// let child = sphere_node.child(sphere); // let child = sphere_node.child(sphere);
// 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 cube = CubeUnit();
let cube_node = Node( cube, material); 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); scene.addNode("cube", cube_node);
//let gnonom = Gnonom(); //let gnonom = Gnonom();
//let gnonom_node = Node(gnonom); //let gnonom_node = Node(gnonom, material);
//scene.addNode("gnonom", gnonom_node); //scene.addNode("gnonom", gnonom_node);
//let cylinder = Cylinder(2.0,1.0 ); //let cylinder = Cylinder(2.0,1.0 );

View File

@@ -260,9 +260,7 @@ impl BVH {
for i in 0..prim_count { for i in 0..prim_count {
let node = &self.nodes[first_prim + i]; //Get the node from the Vec<Node> let node = &self.nodes[first_prim + i]; //Get the node from the Vec<Node>
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 node's 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 { // unsafe {
@@ -310,9 +308,7 @@ impl BVH {
// for i in 0..self.bvh_nodes[index].prim_count { // for i in 0..self.bvh_nodes[index].prim_count {
// let node = &self.nodes[first_prim_idx + i]; // let node = &self.nodes[first_prim_idx + i];
// //Get the centroid of the bounding box // //Get the centroid of the bounding box
// let centroid = node.primitive.get_aabb().get_centroid(); // let centroid = node.aabb.get_centroid();
// //Place the centroid into world coordinates
// let world_centroid = node.model.transform_point(&centroid);
// //Get the candidate position // //Get the candidate position
// let candidate_pos = world_centroid[axis]; // let candidate_pos = world_centroid[axis];
// let cost = self.evaluate_sah(&self.bvh_nodes[index], axis, candidate_pos); // let cost = self.evaluate_sah(&self.bvh_nodes[index], axis, candidate_pos);
@@ -340,8 +336,7 @@ impl BVH {
while i <= j { while i <= j {
//Perform a quicksort dependent on location //Perform a quicksort dependent on location
let node = &self.nodes[i]; // Node we would like to sort 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.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 { if centroid[axis] < split_pos {
i += 1; // On Left-Hand-Side i += 1; // On Left-Hand-Side
} else { } else {

View File

@@ -210,7 +210,7 @@ impl Gui {
"Rays Per Pass", "Rays Per Pass",
RAYS_MIN, RAYS_MIN,
RAYS_MAX, RAYS_MAX,
&mut self.raytracing_option.rays_per_pass, &mut self.raytracing_option.pixels_per_pass,
); );
// Proportion of the window the buffer occupies // Proportion of the window the buffer occupies
ui.slider( ui.slider(

View File

@@ -1,12 +1,13 @@
use crate::{material::Material, primitive::*}; use crate::{bvh::AABB, material::Material, primitive::*};
use nalgebra::{Matrix4, Vector3}; use nalgebra::{Matrix4, Vector3};
use std::rc::Rc; use std::sync::Arc;
#[derive(Clone)] #[derive(Clone)]
pub struct Node { pub struct Node {
//Primitive //Primitive
pub primitive: Rc<dyn Primitive>, pub primitive: Arc<dyn Primitive>,
pub material: Material, pub material: Material,
pub aabb: AABB,
//Transformations //Transformations
pub rotation: [f64; 3], pub rotation: [f64; 3],
pub scale: [f64; 3], pub scale: [f64; 3],
@@ -20,10 +21,12 @@ pub struct Node {
impl Node { impl Node {
//New node with no transformations //New node with no transformations
pub fn new(primitive: Rc<dyn Primitive>, material: Material) -> Node { pub fn new(primitive: Arc<dyn Primitive>, material: Material) -> Node {
let aabb = primitive.get_aabb();
Node { Node {
primitive, primitive,
material, material,
aabb,
rotation: [0.0, 0.0, 0.0], rotation: [0.0, 0.0, 0.0],
scale: [1.0, 1.0, 1.0], scale: [1.0, 1.0, 1.0],
translation: [0.0, 0.0, 0.0], translation: [0.0, 0.0, 0.0],
@@ -34,7 +37,7 @@ impl Node {
} }
} }
//New node with parent transformations //New node with parent transformations
pub fn child(self, primitive: Rc<dyn Primitive>) -> Node { pub fn child(self, primitive: Arc<dyn Primitive>) -> Node {
let mut child = self.clone(); let mut child = self.clone();
child.primitive = primitive; child.primitive = primitive;
child child
@@ -93,5 +96,6 @@ impl Node {
self.model = (translation_matrix * rotation_matrix * scale_matrix).cast(); self.model = (translation_matrix * rotation_matrix * scale_matrix).cast();
// Compute the inverse model matrix by inverting the model matrix // Compute the inverse model matrix by inverting the model matrix
self.inv_model = self.model.try_inverse().unwrap(); self.inv_model = self.model.try_inverse().unwrap();
self.aabb.transform_mut(&self.model);
} }
} }

View File

@@ -9,9 +9,9 @@ use nalgebra::{distance, Point3, Vector3};
use roots::{find_roots_quadratic, find_roots_quartic, Roots}; use roots::{find_roots_quadratic, find_roots_quartic, Roots};
use std::fs::File; use std::fs::File;
use std::io::{BufRead, BufReader}; use std::io::{BufRead, BufReader};
use std::rc::Rc; use std::sync::Arc;
// PRIMITIVE TRAIT ----------------------------------------------------------------- // PRIMITIVE TRAIT -----------------------------------------------------------------
pub trait Primitive { pub trait Primitive: Send + Sync {
fn intersect_ray(&self, ray: &Ray) -> Option<Intersection>; fn intersect_ray(&self, ray: &Ray) -> Option<Intersection>;
fn get_aabb(&self) -> AABB; fn get_aabb(&self) -> AABB;
} }
@@ -24,11 +24,11 @@ pub struct Sphere {
} }
impl Sphere { impl Sphere {
pub fn new(position: Point3<f64>, radius: f64) -> Rc<dyn Primitive> { pub fn new(position: Point3<f64>, radius: f64) -> Arc<dyn Primitive> {
Rc::new(Sphere { position, radius }) Arc::new(Sphere { position, radius })
} }
pub fn unit() -> Rc<dyn Primitive> { pub fn unit() -> Arc<dyn Primitive> {
Sphere::new(Point3::new(0.0, 0.0, 0.0), 1.0) Sphere::new(Point3::new(0.0, 0.0, 0.0), 1.0)
} }
} }
@@ -88,10 +88,10 @@ pub struct Circle {
} }
impl Circle { impl Circle {
pub fn new(position: Point3<f64>, radius: f64, normal: Vector3<f64>) -> Rc<dyn Primitive> { pub fn new(position: Point3<f64>, radius: f64, normal: Vector3<f64>) -> Arc<dyn Primitive> {
let normal = normal.normalize(); let normal = normal.normalize();
let constant = normal.dot(&position.coords); let constant = normal.dot(&position.coords);
Rc::new(Circle { Arc::new(Circle {
position, position,
radius, radius,
normal, normal,
@@ -99,7 +99,18 @@ impl Circle {
}) })
} }
pub fn unit() -> Rc<dyn Primitive> { pub fn new_unboxed(position: Point3<f64>, radius: f64, normal: Vector3<f64>) -> Circle {
let normal = normal.normalize();
let constant = normal.dot(&position.coords);
Circle {
position,
radius,
normal,
constant,
}
}
pub fn unit() -> Arc<dyn Primitive> {
let position = Point3::new(0.0, 0.0, 0.0); let position = Point3::new(0.0, 0.0, 0.0);
let normal = Vector3::new(0.0, 0.0, -1.0); let normal = Vector3::new(0.0, 0.0, -1.0);
let radius = 1.0; let radius = 1.0;
@@ -147,23 +158,23 @@ impl Primitive for Circle {
pub struct Cylinder { pub struct Cylinder {
radius: f64, radius: f64,
height: f64, height: f64,
base_circle: Rc<dyn Primitive>, base_circle: Circle,
top_circle: Rc<dyn Primitive>, top_circle: Circle,
} }
impl Cylinder { impl Cylinder {
pub fn new(radius: f64, height: f64) -> Rc<dyn Primitive> { pub fn new(radius: f64, height: f64) -> Arc<dyn Primitive> {
let base_circle = Circle::new( let base_circle = Circle::new_unboxed(
Point3::new(0.0, 0.0, 0.0), Point3::new(0.0, 0.0, 0.0),
radius, radius,
Vector3::new(0.0, -1.0, 0.0), Vector3::new(0.0, -1.0, 0.0),
); );
let top_circle = Circle::new( let top_circle = Circle::new_unboxed(
Point3::new(0.0, height, 0.0), Point3::new(0.0, height, 0.0),
radius, radius,
Vector3::new(0.0, 1.0, 0.0), Vector3::new(0.0, 1.0, 0.0),
); );
Rc::new(Cylinder { Arc::new(Cylinder {
radius, radius,
height, height,
base_circle, base_circle,
@@ -264,24 +275,24 @@ impl Primitive for Cylinder {
pub struct Cone { pub struct Cone {
height: f64, height: f64,
constant: f64, constant: f64,
circle: Rc<dyn Primitive>, circle: Circle,
} }
impl Cone { impl Cone {
pub fn new(radius: f64, height: f64) -> Rc<dyn Primitive> { pub fn new(radius: f64, height: f64) -> Arc<dyn Primitive> {
let circle = Circle::new( let circle = Circle::new_unboxed(
Point3::new(0.0, 0.0, 0.0), Point3::new(0.0, 0.0, 0.0),
radius, radius,
Vector3::new(0.0, -1.0, 0.0), Vector3::new(0.0, -1.0, 0.0),
); );
let constant = radius * radius / (height * height); let constant = radius * radius / (height * height);
Rc::new(Cone { Arc::new(Cone {
height, height,
constant, constant,
circle, circle,
}) })
} }
pub fn unit() -> Rc<dyn Primitive> { pub fn unit() -> Arc<dyn Primitive> {
Cone::new(0.5, 1.0) Cone::new(0.5, 1.0)
} }
@@ -378,11 +389,11 @@ impl Primitive for Cone {
// width_direction: Vector3<f64>, // width_direction: Vector3<f64>,
// width: f64, // width: f64,
// height: f64, // height: f64,
// ) -> Rc<dyn Primitive> { // ) -> Arc<dyn Primitive> {
// let normal = normal.normalize(); // let normal = normal.normalize();
// let width_direction = width_direction.normalize(); // let width_direction = width_direction.normalize();
// let height_direction = width_direction.cross(&normal); // let height_direction = width_direction.cross(&normal);
// Rc::new(Rectangle { // Arc::new(Rectangle {
// position, // position,
// normal: normal.normalize(), // normal: normal.normalize(),
// width_direction: width_direction.normalize(), // width_direction: width_direction.normalize(),
@@ -390,7 +401,7 @@ impl Primitive for Cone {
// height, // height,
// }) // })
// } // }
// pub fn unit() -> Rc<dyn Primitive> { // pub fn unit() -> Arc<dyn Primitive> {
// Rectangle::new( // Rectangle::new(
// Point3::new(0.0, 0.0, 0.0), // Point3::new(0.0, 0.0, 0.0),
// Vector3::new(0.0, 1.0, 0.0), // Vector3::new(0.0, 1.0, 0.0),
@@ -450,11 +461,15 @@ pub struct Cube {
} }
impl Cube { impl Cube {
pub fn new(bln: Point3<f64>, trf: Point3<f64>) -> Rc<dyn Primitive> { pub fn new(bln: Point3<f64>, trf: Point3<f64>) -> Arc<dyn Primitive> {
Rc::new(Cube { bln, trf }) Arc::new(Cube { bln, trf })
} }
pub fn unit() -> Rc<dyn Primitive> { pub fn new_unboxed(bln: Point3<f64>, trf: Point3<f64>) -> Cube {
Cube { bln, trf }
}
pub fn unit() -> Arc<dyn Primitive> {
let bln = Point3::new(-1.0, -1.0, -1.0); let bln = Point3::new(-1.0, -1.0, -1.0);
let trf = Point3::new(1.0, 1.0, 1.0); let trf = Point3::new(1.0, 1.0, 1.0);
Cube::new(bln, trf) Cube::new(bln, trf)
@@ -479,12 +494,12 @@ impl Primitive for Cube {
let intersect = ray.at_t(tmin); let intersect = ray.at_t(tmin);
// Check if the intersection is outside the box // Check if the intersection is outside the box
if intersect.x < bln.x if intersect.x < bln.x - EPSILON
|| intersect.x > trf.x || intersect.x > trf.x + EPSILON
|| intersect.y < bln.y || intersect.y < bln.y - EPSILON
|| intersect.y > trf.y || intersect.y > trf.y + EPSILON
|| intersect.z < bln.z || intersect.z < bln.z - EPSILON
|| intersect.z > trf.z || intersect.z > trf.z + EPSILON
{ {
return None; // Intersection is outside the box return None; // Intersection is outside the box
} }
@@ -507,7 +522,7 @@ impl Primitive for Cube {
Some(Intersection { Some(Intersection {
point: intersect, point: intersect,
normal: normal, normal,
distance: tmin, distance: tmin,
}) })
} else { } else {
@@ -533,14 +548,14 @@ pub struct Triangle {
} }
impl Triangle { impl Triangle {
pub fn new(u: Point3<f64>, v: Point3<f64>, w: Point3<f64>) -> Rc<dyn Primitive> { pub fn new(u: Point3<f64>, v: Point3<f64>, w: Point3<f64>) -> Arc<dyn Primitive> {
let uv = v - u; let uv = v - u;
let uw = w - u; let uw = w - u;
let normal = uw.cross(&uv).normalize(); let normal = uw.cross(&uv).normalize();
Rc::new(Triangle { u, v, w, normal }) Arc::new(Triangle { u, v, w, normal })
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn unit() -> Rc<dyn Primitive> { pub fn unit() -> Arc<dyn Primitive> {
let u = Point3::new(-1.0, -1.0, 0.0); let u = Point3::new(-1.0, -1.0, 0.0);
let v = Point3::new(0.0, 1.0, 0.0); let v = Point3::new(0.0, 1.0, 0.0);
let w = Point3::new(1.0, -1.0, 0.0); let w = Point3::new(1.0, -1.0, 0.0);
@@ -606,10 +621,10 @@ pub struct Mesh {
} }
impl Mesh { impl Mesh {
pub fn new(triangles: Vec<Triangle>) -> Rc<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);
Rc::new(Mesh { triangles }) Arc::new(Mesh { triangles })
} }
fn compute_bounding_box(triangles: &Vec<Triangle>) -> AABB { fn compute_bounding_box(triangles: &Vec<Triangle>) -> AABB {
@@ -626,7 +641,7 @@ impl Mesh {
AABB::new(bln, trf) AABB::new(bln, trf)
} }
pub fn from_file(filename: &str) -> Rc<dyn Primitive> { pub fn from_file(filename: &str) -> Arc<dyn Primitive> {
let mut triangles: Vec<Triangle> = Vec::new(); let mut triangles: Vec<Triangle> = Vec::new();
let mut vertices: Vec<Point3<f64>> = Vec::new(); let mut vertices: Vec<Point3<f64>> = Vec::new();
@@ -713,9 +728,9 @@ pub struct Torus {
} }
impl Torus { impl Torus {
pub fn new(inner_rad: f64, outer_rad: f64) -> Rc<dyn Primitive> { pub fn new(inner_rad: f64, outer_rad: f64) -> Arc<dyn Primitive> {
// I need to find the bounding box for this shape // I need to find the bounding box for this shape
Rc::new(Torus { Arc::new(Torus {
inner_rad, inner_rad,
outer_rad, outer_rad,
}) })
@@ -834,28 +849,28 @@ impl Primitive for Torus {
// GNOMON ----------------------------------------------------------------- // GNOMON -----------------------------------------------------------------
#[derive(Clone)] #[derive(Clone)]
pub struct Gnonom { pub struct Gnonom {
x_cube: Rc<dyn Primitive>, x_cube: Cube,
y_cube: Rc<dyn Primitive>, y_cube: Cube,
z_cube: Rc<dyn Primitive>, z_cube: Cube,
} }
impl Gnonom { impl Gnonom {
const GNONOM_WIDTH: f64 = 0.1; const GNONOM_WIDTH: f64 = 0.1;
const GNONOM_LENGTH: f64 = 2.0; const GNONOM_LENGTH: f64 = 2.0;
pub fn new() -> Rc<dyn Primitive> { pub fn new() -> Arc<dyn Primitive> {
let x_cube = Cube::new( let x_cube = Cube::new_unboxed(
Point3::new(0.0, -Self::GNONOM_WIDTH, -Self::GNONOM_WIDTH), Point3::new(0.0, -Self::GNONOM_WIDTH, -Self::GNONOM_WIDTH),
Point3::new(Self::GNONOM_LENGTH, Self::GNONOM_WIDTH, Self::GNONOM_WIDTH), Point3::new(Self::GNONOM_LENGTH, Self::GNONOM_WIDTH, Self::GNONOM_WIDTH),
); );
let y_cube = Cube::new( let y_cube = Cube::new_unboxed(
Point3::new(-Self::GNONOM_WIDTH, 0.0, -Self::GNONOM_WIDTH), Point3::new(-Self::GNONOM_WIDTH, 0.0, -Self::GNONOM_WIDTH),
Point3::new(Self::GNONOM_WIDTH, Self::GNONOM_LENGTH, Self::GNONOM_WIDTH), Point3::new(Self::GNONOM_WIDTH, Self::GNONOM_LENGTH, Self::GNONOM_WIDTH),
); );
let z_cube = Cube::new( let z_cube = Cube::new_unboxed(
Point3::new(-Self::GNONOM_WIDTH, -Self::GNONOM_WIDTH, 0.0), Point3::new(-Self::GNONOM_WIDTH, -Self::GNONOM_WIDTH, 0.0),
Point3::new(Self::GNONOM_WIDTH, Self::GNONOM_WIDTH, Self::GNONOM_LENGTH), Point3::new(Self::GNONOM_WIDTH, Self::GNONOM_WIDTH, Self::GNONOM_LENGTH),
); );
Rc::new(Gnonom { Arc::new(Gnonom {
x_cube, x_cube,
y_cube, y_cube,
z_cube, z_cube,
@@ -901,9 +916,9 @@ impl Primitive for Gnonom {
pub struct CrossCap {} pub struct CrossCap {}
impl CrossCap { impl CrossCap {
pub fn new() -> Rc<dyn Primitive> { pub fn new() -> Arc<dyn Primitive> {
// I need to find the bounding box for this shape // I need to find the bounding box for this shape
Rc::new(CrossCap {}) Arc::new(CrossCap {})
} }
} }
@@ -992,9 +1007,9 @@ pub struct CrossCap2 {
} }
impl CrossCap2 { impl CrossCap2 {
pub fn new(p: f64, q: f64) -> Rc<dyn Primitive> { pub fn new(p: f64, q: f64) -> Arc<dyn Primitive> {
// I need to find the bounding box for this shape // I need to find the bounding box for this shape
Rc::new(CrossCap2 { p, q }) Arc::new(CrossCap2 { p, q })
} }
} }
@@ -1105,9 +1120,9 @@ impl Primitive for CrossCap2 {
pub struct Steiner {} pub struct Steiner {}
impl Steiner { impl Steiner {
pub fn new() -> Rc<dyn Primitive> { pub fn new() -> Arc<dyn Primitive> {
// I need to find the bounding box for this shape // I need to find the bounding box for this shape
Rc::new(Steiner {}) Arc::new(Steiner {})
} }
} }
@@ -1182,9 +1197,9 @@ impl Primitive for Steiner {
pub struct Steiner2 {} pub struct Steiner2 {}
impl Steiner2 { impl Steiner2 {
pub fn new() -> Rc<dyn Primitive> { pub fn new() -> Arc<dyn Primitive> {
// I need to find the bounding box for this shape // I need to find the bounding box for this shape
Rc::new(Steiner2 {}) Arc::new(Steiner2 {})
} }
} }
@@ -1272,9 +1287,9 @@ pub struct Roman {
} }
impl Roman { impl Roman {
pub fn new(k: f64) -> Rc<dyn Primitive> { pub fn new(k: f64) -> Arc<dyn Primitive> {
// I need to find the bounding box for this shape // I need to find the bounding box for this shape
Rc::new(Roman { k }) Arc::new(Roman { k })
} }
} }

View File

@@ -86,20 +86,23 @@ impl Ray {
if !node.active { if !node.active {
continue; continue;
} }
// Transform ray into local model cordinates
let ray = self.transform(&node.inv_model); if node.aabb.intersect_ray(&self) {
// Check primitive intersection // Transform ray into local model cordinates
if let Some(intersect) = node.primitive.intersect_ray(&ray) { let ray = self.transform(&node.inv_model);
// Dont intersect with itself // Check primitive intersection
if intersect.distance < EPSILON { if let Some(intersect) = node.primitive.intersect_ray(&ray) {
continue; // Dont intersect with itself
} if intersect.distance < EPSILON {
// Check for closest distance by converting to world coords continue;
let intersect = intersect.transform(&node.model, &node.inv_model); }
let distance = distance(&ray.a, &intersect.point); // Check for closest distance by converting to world coords
if distance < closest_distance { let intersect = intersect.transform(&node.model, &node.inv_model);
closest_distance = distance; let distance = distance(&ray.a, &intersect.point);
closest_intersect = Some((node, intersect)); if distance < closest_distance {
closest_distance = distance;
closest_intersect = Some((node, intersect));
}
} }
} }
} }
@@ -113,10 +116,12 @@ impl Ray {
options: &RaytracingOption, options: &RaytracingOption,
sbvh: &Option<BVH>, sbvh: &Option<BVH>,
) -> Option<Vector3<f32>> { ) -> Option<Vector3<f32>> {
//If we have exceeded depth then return
if depth == options.ray_depth { if depth == options.ray_depth {
return None; return None;
} }
match sbvh { match sbvh {
//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) {
@@ -126,6 +131,7 @@ impl Ray {
} }
return None; return None;
} }
//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 self.closest_intersect(scene) {
@@ -175,7 +181,7 @@ impl Ray {
//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) { if to_light_ray.light_blocked(scene, node, bvh) {
continue; continue;
} }
@@ -202,37 +208,55 @@ impl Ray {
//Specular component //Specular component
let mut specular = Vector3::zeros(); let mut specular = Vector3::zeros();
if n_dot_l < 0.0 { if n_dot_l > 0.0 {
let h = (to_light - incidence).normalize(); let h = (to_light - incidence).normalize();
let n_dot_h = normal.dot(&h).max(0.0) as f32; let n_dot_h = normal.dot(&h).max(0.0) as f32;
specular = material.ks * n_dot_h.powf(material.shininess); specular = material.ks * n_dot_h.powf(material.shininess);
} }
//Falloff //Falloff
// let falloff = 1.0 let falloff = 1.0
// / (1.0 / ((1.0 + light.falloff[0])
// + light.falloff[0] + light.falloff[1] * light_distance
// + light.falloff[1] * light_distance + light.falloff[2] * light_distance * light_distance);
// + light.falloff[2] * light_distance * light_distance);
let intensity = light.colour.component_mul(&(diffuse + reflect + specular)); let intensity = light.colour.component_mul(&(diffuse + reflect + specular)) * falloff;
colour += &intensity; colour += &intensity;
} }
colour colour
} }
pub fn light_blocked(&self, scene: &Scene, _node: &Node) -> bool { pub fn light_blocked(&self, scene: &Scene, _node: &Node, bvh: &Option<BVH>) -> bool {
for (_, node) in &scene.nodes { match bvh {
if !node.active { Some(bvh) => {
continue; //We have a bvh so use bvh traversal
for (_, node) in &scene.nodes {
if !node.active {
continue;
}
match bvh.traverse(&self, 0) {
Some(_) => return true,
None => continue,
}
}
return false;
} }
let ray = self.transform(&node.inv_model); None => {
if node.primitive.intersect_ray(&ray).is_some() { for (_, node) in &scene.nodes {
return true; if !node.active {
continue;
}
if node.aabb.intersect_ray(self) {
let ray = self.transform(&node.inv_model);
if node.primitive.intersect_ray(&ray).is_some() {
return true;
}
}
}
return 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,23 @@
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 nodes: Rc<HashMap<String, Node>>,
// pub materials: Rc<HashMap<String, Material>>,
// pub lights: Rc<HashMap<String, Light>>,
// pub cameras: Rc<HashMap<String, Camera>>,
// }
// impl MultiThreadScene {
// pub fn from_scene(scene: &Scene) -> MultiThreadScene {
// MultiThreadScene {
// nodes: Rc::new(scene.nodes.clone()),
// materials: Rc::new(scene.materials.clone()),
// lights: Rc::new(scene.lights.clone()),
// cameras: Rc::new(scene.cameras.clone()),
// }
// }
// }
#[derive(Clone)] #[derive(Clone)]
pub struct Scene { pub struct Scene {

View File

@@ -6,12 +6,15 @@ use crate::ray::Ray;
use crate::{gui::Gui, scene::Scene}; use crate::{gui::Gui, scene::Scene};
use crate::{gui::GuiEvent, log_error}; use crate::{gui::GuiEvent, log_error};
use std::path::Path; use std::path::Path;
use std::thread;
use nalgebra::Vector3; use nalgebra::Vector3;
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use rand::{random, thread_rng}; use rand::{random, thread_rng};
use std::error::Error; use std::error::Error;
use std::sync::Arc;
use std::sync::Mutex;
use anyhow::Result; use anyhow::Result;
use pixels::{Pixels, SurfaceTexture}; use pixels::{Pixels, SurfaceTexture};
@@ -28,11 +31,12 @@ pub const SAVE_FILE: &str = "img.png";
#[derive(Clone)] #[derive(Clone)]
pub struct RaytracingOption { pub struct RaytracingOption {
pub threads: u32,
pub ray_samples: u32, pub ray_samples: u32,
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 rays_per_pass: u32, pub pixels_per_pass: 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,
@@ -43,11 +47,12 @@ pub struct RaytracingOption {
impl RaytracingOption { impl RaytracingOption {
pub fn default() -> RaytracingOption { pub fn default() -> RaytracingOption {
RaytracingOption { RaytracingOption {
threads: 12,
ray_samples: 10, ray_samples: 10,
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],
rays_per_pass: 200, pixels_per_pass: 200,
buffer_proportion: 1.0, buffer_proportion: 1.0,
buffer_fov: 110.0, buffer_fov: 110.0,
ray_depth: 5, ray_depth: 5,
@@ -59,32 +64,33 @@ impl RaytracingOption {
} }
pub struct State { pub struct State {
scene: Scene, scene: Arc<Scene>,
bvh: Option<BVH>, bvh: Arc<Option<BVH>>,
camera: Camera, camera: Camera,
window: Window, window: Window,
buffer_width: u32, buffer_width: u32,
buffer_height: u32, buffer_height: u32,
pixels: Pixels, pixels: Arc<Mutex<Pixels>>,
gui: Gui, gui: Gui,
rays: Vec<Ray>, rays: Arc<Vec<Ray>>,
ray_queue: Vec<usize>, ray_queue: Arc<Mutex<Vec<usize>>>,
raytracing_options: RaytracingOption, raytracing_options: Arc<RaytracingOption>,
} }
impl State { impl State {
pub fn new(window: Window, pixels: Pixels, gui: Gui) -> Self { pub fn new(window: Window, pixels: Pixels, gui: Gui) -> Self {
let scene = 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 camera = Camera::unit(); let camera = Camera::unit();
let rays = Vec::new(); let rays = Arc::new(Vec::new());
Self { Self {
scene, scene,
bvh: None, bvh: Arc::new(None),
camera, camera,
window, window,
buffer_width: window_size.width as u32, buffer_width: window_size.width as u32,
@@ -92,8 +98,8 @@ impl State {
pixels, pixels,
gui, gui,
rays, rays,
ray_queue: Vec::new(), ray_queue: Arc::new(Mutex::new(Vec::new())),
raytracing_options: RaytracingOption::default(), raytracing_options: Arc::new(RaytracingOption::default()),
} }
} }
@@ -101,33 +107,34 @@ impl State {
if let Some(event) = self.gui.event.take() { if let Some(event) = self.gui.event.take() {
match event { match event {
GuiEvent::RaytracerOption(options) => { GuiEvent::RaytracerOption(options) => {
self.raytracing_options = options; self.raytracing_options = Arc::new(options);
match self.raytracing_options.bvh_active { match self.raytracing_options.bvh_active {
true => self.bvh = Some(BVH::build(&mut self.scene.nodes)), true => self.bvh = Arc::new(Some(BVH::build(&self.scene.nodes))),
false => self.bvh = None, false => self.bvh = Arc::new(None),
} }
self.resize_buffer()? self.resize_buffer()?
} }
GuiEvent::CameraUpdate(camera) => { GuiEvent::CameraUpdate(camera) => {
self.rays = Ray::cast_rays( self.rays = Arc::new(Ray::cast_rays(
&camera.eye, &camera.eye,
&camera.target, &camera.target,
&camera.up, &camera.up,
self.raytracing_options.buffer_fov, 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_buffer()?; self.clear_buffer()?;
self.reset_queue(); self.reset_queue();
} }
GuiEvent::SceneLoad(scene) => { GuiEvent::SceneLoad(scene) => {
self.scene = scene; self.scene = Arc::new(scene);
self.clear_buffer()?; self.clear_buffer()?;
self.reset_queue(); self.reset_queue();
} }
GuiEvent::SaveImage(filename) => { GuiEvent::SaveImage(filename) => {
let frame = self.pixels.frame(); let pixels = &self.pixels.as_ref().lock().unwrap();
let frame = pixels.frame();
image::save_buffer( image::save_buffer(
Path::new(&filename), Path::new(&filename),
frame, frame,
@@ -154,17 +161,17 @@ impl State {
self.reset_queue(); self.reset_queue();
// Recalculate rays with new buffer dimensions // Recalculate rays with new buffer dimensions
self.rays = Ray::cast_rays( self.rays = Arc::new(Ray::cast_rays(
&self.camera.eye, &self.camera.eye,
&self.camera.target, &self.camera.target,
&self.camera.up, &self.camera.up,
fovy, fovy,
self.buffer_width, self.buffer_width,
self.buffer_height, self.buffer_height,
); ));
// Resize buffer and surface // Resize buffer and surface
let pixels = &mut self.pixels; let pixels = &mut self.pixels.as_ref().lock().unwrap();
pixels.resize_surface(size.width, size.height)?; pixels.resize_surface(size.width, size.height)?;
pixels.resize_buffer(self.buffer_width, self.buffer_height)?; pixels.resize_buffer(self.buffer_width, self.buffer_height)?;
@@ -172,7 +179,8 @@ impl State {
} }
fn resize(&mut self, size: &PhysicalSize<u32>) -> Result<(), Box<dyn Error>> { fn resize(&mut self, size: &PhysicalSize<u32>) -> Result<(), Box<dyn Error>> {
self.pixels.resize_surface(size.width, size.height)?; let pixels = &mut self.pixels.as_ref().lock().unwrap();
pixels.resize_surface(size.width, size.height)?;
Ok(()) Ok(())
} }
@@ -188,46 +196,67 @@ 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 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 mut handles = vec![];
for _ in 0..self.raytracing_options.rays_per_pass {
//Get random index from queue
let index = match self.ray_queue.pop() {
Some(index) => index,
None => break,
};
//Shade colour for selected ray
let mut colour = Vector3::zeros();
for _ in 0..*samples {
let ray = &self.rays[index];
let point = ray.a;
let dir = ray.b;
let rx = (random::<f64>() - 0.5) / randomness;
let ry = (random::<f64>() - 0.5) / randomness;
let rz = (random::<f64>() - 0.5) / randomness;
let nx = dir.x + rx;
let ny = dir.y + ry;
let nz = dir.z + rz;
let rand_ray = Ray::new(point, Vector3::new(nx, ny, nz)); for _ in 0..self.raytracing_options.pixels_per_pass {
for _ in 0..self.raytracing_options.threads {
if let Some(ray_colour) = //Get random index from queue
rand_ray.shade_ray(&self.scene, 0, &self.raytracing_options, &self.bvh) let queue = &mut self.ray_queue.clone();
{ let index = match queue.lock().unwrap().pop() {
colour += ray_colour; Some(index) => index,
None => break,
}; };
//Create a nre thread for this pixel
let handle = thread::spawn({
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();
move || {
//Shade colour for selected ray
let mut colour: Vector3<f32> = Vector3::zeros();
for _ in 0..samples {
let ray = &rays[index];
let point = ray.a;
let dir = ray.b;
let rx = (random::<f64>() - 0.5) / randomness;
let ry = (random::<f64>() - 0.5) / randomness;
let rz = (random::<f64>() - 0.5) / 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 / 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);
}
});
handles.push(handle);
}
for handle in handles.drain(..) {
handle.join().unwrap();
} }
colour = (colour / samples_f32) * 255.0;
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);
} }
Ok(()) Ok(())
} }
fn clear_buffer(&mut self) -> Result<(), Box<dyn Error>> { fn clear_buffer(&mut self) -> Result<(), Box<dyn Error>> {
let frame = self.pixels.frame_mut(); let pixels = &mut self.pixels.as_ref().lock().unwrap();
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);
} }
@@ -236,33 +265,32 @@ impl State {
fn reset_queue(&mut self) { fn reset_queue(&mut self) {
match self.raytracing_options.bvh_active { match self.raytracing_options.bvh_active {
true => self.bvh = Some(BVH::build(&mut self.scene.nodes)), true => self.bvh = Arc::new(Some(BVH::build(&self.scene.nodes))),
false => self.bvh = None, false => self.bvh = Arc::new(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());
self.ray_queue = ray_queue; self.ray_queue = Arc::new(Mutex::new(ray_queue));
} }
fn render(&mut self) -> Result<(), Box<dyn Error>> { fn render(&mut self) -> Result<(), Box<dyn Error>> {
// Update state // Update state
self.update()?; self.update()?;
// Draw rays if we have remaining rays in queue // Draw rays if we have remaining rays in queue
if !self.ray_queue.is_empty() { match self.draw() {
match self.draw() { Err(e) => {
Err(e) => { println!("ERROR: {}", e);
println!("ERROR: {}", e);
}
_ => {}
} }
_ => {}
} }
// Render Gui // Render Gui
self.gui self.gui
.prepare(&self.window) .prepare(&self.window)
.expect("gui.prepare() failed"); .expect("gui.prepare() failed");
// Try to render pixels // Try to render pixels
if let Err(e) = self.pixels.render_with(|encoder, render_target, context| { let pixels = &mut self.pixels.as_ref().lock().unwrap();
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)?;