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 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);
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);
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);
scene.addLight("red", light);
let light = Ambient(V(0.3,0.3,0.3));
light.active(false);
scene.addLight("ambient", light);
let tri = TriangleUnit();
let tri_node = Node(tri, material);
tri_node.active(false);
@@ -39,7 +41,7 @@ scene.addNode("cone", cone_node);
let torus = Torus(0.5, 1.5);
let torus_node = Node(torus, material);
torus_node.active(true);
torus_node.active(false);
scene.addNode("torus", torus_node);
@@ -59,7 +61,8 @@ scene.addNode("ground", ground_node);
let mesh = Mesh("obj/cat.obj");
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);

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);
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));
light.active(false);
scene.addLight("blue", light);
// let light = Light( P(2.0,7.0,0.0), V(0.0,1.0,0.0), V(0.1, 0.01, 0.001));
// light.active(false);
// scene.addLight("green", light);
let light = Light( P(2.0,7.0,0.0), V(0.0,1.0,0.0), V(0.1, 0.01, 0.001));
light.active(false);
scene.addLight("green", light);
// let light = Light( P(2.0,7.0,2.0), V(1.0,0.5,0.5), V(0.0, 0.00, 0.001));
// scene.addLight("red", light);
let light = Light( P(2.0,7.0,2.0), V(1.0,0.5,0.5), V(0.0, 0.00, 0.001));
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);
// let sphere = Sphere(P(0.0,0.0,0.0), 1.0 );
// let sphere_node = Node( sphere, material);
// scene.addNode("sphere",sphere_node);
let sphere = Sphere(P(0.0,-10.0,0.0), 10.0 );
let sphere_node = Node( sphere, material);
scene.addNode("sphere",sphere_node);
//let mesh = Mesh("obj/cow.obj" );
//let mesh_node = Node(mesh);
//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);
// child.translate(V(1.0,1.0,1.0));
//scene.addNode(child);
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);
//let gnonom = Gnonom();
//let gnonom_node = Node(gnonom);
//let gnonom_node = Node(gnonom, material);
//scene.addNode("gnonom", gnonom_node);
//let cylinder = Cylinder(2.0,1.0 );

View File

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

View File

@@ -210,7 +210,7 @@ impl Gui {
"Rays Per Pass",
RAYS_MIN,
RAYS_MAX,
&mut self.raytracing_option.rays_per_pass,
&mut self.raytracing_option.pixels_per_pass,
);
// Proportion of the window the buffer occupies
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 std::rc::Rc;
use std::sync::Arc;
#[derive(Clone)]
pub struct Node {
//Primitive
pub primitive: Rc<dyn Primitive>,
pub primitive: Arc<dyn Primitive>,
pub material: Material,
pub aabb: AABB,
//Transformations
pub rotation: [f64; 3],
pub scale: [f64; 3],
@@ -20,10 +21,12 @@ pub struct Node {
impl Node {
//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 {
primitive,
material,
aabb,
rotation: [0.0, 0.0, 0.0],
scale: [1.0, 1.0, 1.0],
translation: [0.0, 0.0, 0.0],
@@ -34,7 +37,7 @@ impl Node {
}
}
//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();
child.primitive = primitive;
child
@@ -93,5 +96,6 @@ impl Node {
self.model = (translation_matrix * rotation_matrix * scale_matrix).cast();
// Compute the inverse model matrix by inverting the model matrix
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 std::fs::File;
use std::io::{BufRead, BufReader};
use std::rc::Rc;
use std::sync::Arc;
// PRIMITIVE TRAIT -----------------------------------------------------------------
pub trait Primitive {
pub trait Primitive: Send + Sync {
fn intersect_ray(&self, ray: &Ray) -> Option<Intersection>;
fn get_aabb(&self) -> AABB;
}
@@ -24,11 +24,11 @@ pub struct Sphere {
}
impl Sphere {
pub fn new(position: Point3<f64>, radius: f64) -> Rc<dyn Primitive> {
Rc::new(Sphere { position, radius })
pub fn new(position: Point3<f64>, radius: f64) -> Arc<dyn Primitive> {
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)
}
}
@@ -88,10 +88,10 @@ pub struct 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 constant = normal.dot(&position.coords);
Rc::new(Circle {
Arc::new(Circle {
position,
radius,
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 normal = Vector3::new(0.0, 0.0, -1.0);
let radius = 1.0;
@@ -147,23 +158,23 @@ impl Primitive for Circle {
pub struct Cylinder {
radius: f64,
height: f64,
base_circle: Rc<dyn Primitive>,
top_circle: Rc<dyn Primitive>,
base_circle: Circle,
top_circle: Circle,
}
impl Cylinder {
pub fn new(radius: f64, height: f64) -> Rc<dyn Primitive> {
let base_circle = Circle::new(
pub fn new(radius: f64, height: f64) -> Arc<dyn Primitive> {
let base_circle = Circle::new_unboxed(
Point3::new(0.0, 0.0, 0.0),
radius,
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),
radius,
Vector3::new(0.0, 1.0, 0.0),
);
Rc::new(Cylinder {
Arc::new(Cylinder {
radius,
height,
base_circle,
@@ -264,24 +275,24 @@ impl Primitive for Cylinder {
pub struct Cone {
height: f64,
constant: f64,
circle: Rc<dyn Primitive>,
circle: Circle,
}
impl Cone {
pub fn new(radius: f64, height: f64) -> Rc<dyn Primitive> {
let circle = Circle::new(
pub fn new(radius: f64, height: f64) -> Arc<dyn Primitive> {
let circle = Circle::new_unboxed(
Point3::new(0.0, 0.0, 0.0),
radius,
Vector3::new(0.0, -1.0, 0.0),
);
let constant = radius * radius / (height * height);
Rc::new(Cone {
Arc::new(Cone {
height,
constant,
circle,
})
}
pub fn unit() -> Rc<dyn Primitive> {
pub fn unit() -> Arc<dyn Primitive> {
Cone::new(0.5, 1.0)
}
@@ -378,11 +389,11 @@ impl Primitive for Cone {
// width_direction: Vector3<f64>,
// width: f64,
// height: f64,
// ) -> Rc<dyn Primitive> {
// ) -> Arc<dyn Primitive> {
// let normal = normal.normalize();
// let width_direction = width_direction.normalize();
// let height_direction = width_direction.cross(&normal);
// Rc::new(Rectangle {
// Arc::new(Rectangle {
// position,
// normal: normal.normalize(),
// width_direction: width_direction.normalize(),
@@ -390,7 +401,7 @@ impl Primitive for Cone {
// height,
// })
// }
// pub fn unit() -> Rc<dyn Primitive> {
// pub fn unit() -> Arc<dyn Primitive> {
// Rectangle::new(
// Point3::new(0.0, 0.0, 0.0),
// Vector3::new(0.0, 1.0, 0.0),
@@ -450,11 +461,15 @@ pub struct Cube {
}
impl Cube {
pub fn new(bln: Point3<f64>, trf: Point3<f64>) -> Rc<dyn Primitive> {
Rc::new(Cube { bln, trf })
pub fn new(bln: Point3<f64>, trf: Point3<f64>) -> Arc<dyn Primitive> {
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 trf = Point3::new(1.0, 1.0, 1.0);
Cube::new(bln, trf)
@@ -479,12 +494,12 @@ impl Primitive for Cube {
let intersect = ray.at_t(tmin);
// Check if the intersection is outside the box
if intersect.x < bln.x
|| intersect.x > trf.x
|| intersect.y < bln.y
|| intersect.y > trf.y
|| intersect.z < bln.z
|| intersect.z > trf.z
if intersect.x < bln.x - EPSILON
|| intersect.x > trf.x + EPSILON
|| intersect.y < bln.y - EPSILON
|| intersect.y > trf.y + EPSILON
|| intersect.z < bln.z - EPSILON
|| intersect.z > trf.z + EPSILON
{
return None; // Intersection is outside the box
}
@@ -507,7 +522,7 @@ impl Primitive for Cube {
Some(Intersection {
point: intersect,
normal: normal,
normal,
distance: tmin,
})
} else {
@@ -533,14 +548,14 @@ pub struct 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 uw = w - u;
let normal = uw.cross(&uv).normalize();
Rc::new(Triangle { u, v, w, normal })
Arc::new(Triangle { u, v, w, normal })
}
#[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 v = Point3::new(0.0, 1.0, 0.0);
let w = Point3::new(1.0, -1.0, 0.0);
@@ -606,10 +621,10 @@ pub struct 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
let bounding_box = Mesh::compute_bounding_box(&triangles);
Rc::new(Mesh { triangles })
Arc::new(Mesh { triangles })
}
fn compute_bounding_box(triangles: &Vec<Triangle>) -> AABB {
@@ -626,7 +641,7 @@ impl Mesh {
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 vertices: Vec<Point3<f64>> = Vec::new();
@@ -713,9 +728,9 @@ pub struct 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
Rc::new(Torus {
Arc::new(Torus {
inner_rad,
outer_rad,
})
@@ -834,28 +849,28 @@ impl Primitive for Torus {
// GNOMON -----------------------------------------------------------------
#[derive(Clone)]
pub struct Gnonom {
x_cube: Rc<dyn Primitive>,
y_cube: Rc<dyn Primitive>,
z_cube: Rc<dyn Primitive>,
x_cube: Cube,
y_cube: Cube,
z_cube: Cube,
}
impl Gnonom {
const GNONOM_WIDTH: f64 = 0.1;
const GNONOM_LENGTH: f64 = 2.0;
pub fn new() -> Rc<dyn Primitive> {
let x_cube = Cube::new(
pub fn new() -> Arc<dyn Primitive> {
let x_cube = Cube::new_unboxed(
Point3::new(0.0, -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, 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, Self::GNONOM_LENGTH),
);
Rc::new(Gnonom {
Arc::new(Gnonom {
x_cube,
y_cube,
z_cube,
@@ -901,9 +916,9 @@ impl Primitive for Gnonom {
pub struct 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
Rc::new(CrossCap {})
Arc::new(CrossCap {})
}
}
@@ -992,9 +1007,9 @@ pub struct 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
Rc::new(CrossCap2 { p, q })
Arc::new(CrossCap2 { p, q })
}
}
@@ -1105,9 +1120,9 @@ impl Primitive for CrossCap2 {
pub struct 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
Rc::new(Steiner {})
Arc::new(Steiner {})
}
}
@@ -1182,9 +1197,9 @@ impl Primitive for Steiner {
pub struct 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
Rc::new(Steiner2 {})
Arc::new(Steiner2 {})
}
}
@@ -1272,9 +1287,9 @@ pub struct 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
Rc::new(Roman { k })
Arc::new(Roman { k })
}
}

View File

@@ -86,6 +86,8 @@ impl Ray {
if !node.active {
continue;
}
if node.aabb.intersect_ray(&self) {
// Transform ray into local model cordinates
let ray = self.transform(&node.inv_model);
// Check primitive intersection
@@ -103,6 +105,7 @@ impl Ray {
}
}
}
}
closest_intersect
}
// This function takes a scene and returns the color of the point where the ray intersects the scene
@@ -113,10 +116,12 @@ impl Ray {
options: &RaytracingOption,
sbvh: &Option<BVH>,
) -> Option<Vector3<f32>> {
//If we have exceeded depth then return
if depth == options.ray_depth {
return None;
}
match sbvh {
//We have a bvh so use bvh traversal
Some(bvh) => {
//Intersect the scene with the bvh
if let Some((node, intersect)) = bvh.traverse(&self, 0) {
@@ -126,6 +131,7 @@ impl Ray {
}
return None;
}
//We dont have a bvh so use generic algorithm
None => {
//No BVH given so intersect normally
match self.closest_intersect(scene) {
@@ -175,7 +181,7 @@ impl Ray {
//Niave Shadows
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;
}
@@ -202,38 +208,56 @@ impl Ray {
//Specular component
let mut specular = Vector3::zeros();
if n_dot_l < 0.0 {
if n_dot_l > 0.0 {
let h = (to_light - incidence).normalize();
let n_dot_h = normal.dot(&h).max(0.0) as f32;
specular = material.ks * n_dot_h.powf(material.shininess);
}
//Falloff
// let falloff = 1.0
// / (1.0
// + light.falloff[0]
// + light.falloff[1] * light_distance
// + light.falloff[2] * light_distance * light_distance);
let falloff = 1.0
/ ((1.0 + light.falloff[0])
+ light.falloff[1] * light_distance
+ light.falloff[2] * light_distance * light_distance);
let intensity = light.colour.component_mul(&(diffuse + reflect + specular));
let intensity = light.colour.component_mul(&(diffuse + reflect + specular)) * falloff;
colour += &intensity;
}
colour
}
pub fn light_blocked(&self, scene: &Scene, _node: &Node) -> bool {
pub fn light_blocked(&self, scene: &Scene, _node: &Node, bvh: &Option<BVH>) -> bool {
match bvh {
Some(bvh) => {
//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;
}
None => {
for (_, node) in &scene.nodes {
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;
}
}
}
//Cast a set of rays
pub fn cast_rays(
eye: &Point3<f64>,

View File

@@ -1,5 +1,23 @@
use crate::{camera::Camera, light::Light, material::*, node::*};
use std::collections::HashMap;
use std::rc::Rc;
// pub struct MultiThreadScene {
// pub nodes: Rc<HashMap<String, Node>>,
// pub materials: Rc<HashMap<String, Material>>,
// pub lights: Rc<HashMap<String, Light>>,
// pub cameras: Rc<HashMap<String, Camera>>,
// }
// impl MultiThreadScene {
// pub fn from_scene(scene: &Scene) -> MultiThreadScene {
// MultiThreadScene {
// nodes: Rc::new(scene.nodes.clone()),
// materials: Rc::new(scene.materials.clone()),
// lights: Rc::new(scene.lights.clone()),
// cameras: Rc::new(scene.cameras.clone()),
// }
// }
// }
#[derive(Clone)]
pub struct Scene {

View File

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