Set up intersection correctly

This commit is contained in:
STP
2023-11-27 16:51:53 -05:00
parent e48bc02daa
commit 5aa73bcc1a
13 changed files with 10723 additions and 280 deletions

2
.gitignore vendored
View File

@@ -1,4 +1,4 @@
/target
/old
/help
sagekit.ipynb

10388
obj/cow.obj Normal file

File diff suppressed because it is too large Load Diff

64
rhai/scene.rhai Normal file
View File

@@ -0,0 +1,64 @@
let scene = Scene();
let distance = 10.0;
let camera = Camera( P(0.0,0.0,distance), P(0.0,0.0,0.0), V(0.0,1.0,0.0));
scene.addCamera("+Z Cam", camera);
let camera = Camera( P(0.0,distance,0.1), P(0.0,0.0,0.0), V(0.0,1.0,0.0));
scene.addCamera("+Y Cam", camera);
let camera = Camera( P(distance,0.0,0.0), P(0.0,0.0,0.0), V(0.0,1.0,0.0));
scene.addCamera("+X Cam", camera);
let camera = Camera( P(0.0,0.0,-distance), P(0.0,0.0,0.0), V(0.0,1.0,0.0));
scene.addCamera("-Z Cam", camera);
let camera = Camera( P(0.0,-distance,0.1), P(0.0,0.0,0.0), V(0.0,1.0,0.0));
scene.addCamera("-Y Cam", camera);
let camera = Camera( P(-distance,0.0,0.0), P(0.0,0.0,0.0), V(0.0,1.0,0.0));
scene.addCamera("-X Cam", camera);
let material = Material(V(0.2,0.2,0.2), V(0.2, 0.8, 0.8), 10.0);
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));
// 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));
// scene.addLight("green", light);
let light = Light( P(-2.0,7.0,0.0), V(1.0,0.0,0.0), V(0.1, 0.01, 0.001));
scene.addLight("red", light);
let light = Ambient(V(0.1,0.1,0.1));
scene.addLight("ambient", light);
let sphere = Sphere(P(0.0,0.0,0.0), 1.0, material);
let sphere_node = Node(sphere);
scene.addNode("sphere", sphere_node);
//let mesh = Mesh("obj/cow.obj", material);
//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, material);
let sphere_node = Node(sphere);
sphere_node.translate(2.0*cos(i.to_float()), -4.0, 2.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(material);
let cube_node = Node(cube);
scene.addNode("cube", cube_node);
//let gnonom = Gnonom(material);
//let gnonom_node = Node(gnonom);
//scene.addNode("gnonom", gnonom_node);
//let cylinder = Cylinder(2.0,1.0, material);
//let cylinder_node = Node(cylinder);
//cylinder_node.scale(1.0,1.0,1.0);
//scene.addNode("cylinder",cylinder_node);
scene

34
rhai/sphere.rhai Normal file
View File

@@ -0,0 +1,34 @@
let scene = Scene();
let distance = 3.0;
let falloff = V(0.0,0.0,0.1);
let camera = Camera( P(0.0,0.0,distance), P(0.0,0.0,0.0), V(0.0,1.0,0.0));
scene.addCamera("+Z Cam", camera);
let camera = Camera( P(0.0,distance,0.1), P(0.0,0.0,0.0), V(0.0,1.0,0.0));
scene.addCamera("+Y Cam", camera);
let camera = Camera( P(distance,0.0,0.0), P(0.0,0.0,0.0), V(0.0,1.0,0.0));
scene.addCamera("+X Cam", camera);
let camera = Camera( P(0.0,0.0,-distance), P(0.0,0.0,0.0), V(0.0,1.0,0.0));
scene.addCamera("-Z Cam", camera);
let camera = Camera( P(0.0,-distance,0.1), P(0.0,0.0,0.0), V(0.0,1.0,0.0));
scene.addCamera("-Y Cam", camera);
let camera = Camera( P(-distance,0.0,0.0), P(0.0,0.0,0.0), V(0.0,1.0,0.0));
scene.addCamera("-X Cam", camera);
let height = 4.0;
let spacing = 13.0;
let light = Light(P(0.0,height,spacing), V(0.0,0.3,0.3), falloff);
scene.addLight("blue", light);
let light = Light(P(0.0,height,0.0), V(0.0,0.6,0.0), falloff);
scene.addLight("green", light);
let light = Light(P(0.0,height,-spacing), V(0.3,0.0,0.0), falloff);
scene.addLight("red", light);
let material = Material(V(0.2,0.2,0.2), V(0.2, 0.8, 0.8), 10.0);
scene.addMaterial("bluegreen", material);
let sphere = Sphere(P(0.0,0.0,0.0), 1.0, material);
let sphere_node = Node(sphere);
scene.addNode("sphere", sphere_node);
scene

22
rhai/test.rhai Normal file
View File

@@ -0,0 +1,22 @@
let scene = Scene();
let material = Material(V(0.2,0.2,0.2), V(0.2, 0.8, 0.8), 10.0);
//let ambient = Light(P(10.0,0.0,0.0), V(1.0,1.0,1.0), V(0.0, 0.0, 0.0));
//scene.addLight(ambient);
let light = Light(P(0.0,0.0,2.0), V(0.0,0.0,1.0), V(0.1, 0.01, 0.001));
scene.addLight("blue", 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));
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));
scene.addLight("red", light);
let light = Ambient(V(0.1,0.1,0.1));
scene.addLight("ambient", light);
let stein = Steiner(material);
let stein_node = Node(stein);
scene.addNode("stein", stein_node);
scene

View File

@@ -1,50 +0,0 @@
let scene = Scene();
let camera = Camera( P(0.0,1.0,1.0), P(0.0,0.0,0.0), V(0.0,1.0,0.0));
scene.addCamera("camera", camera);
let material = Material(V(0.2,0.2,0.2), V(0.2, 0.8, 0.8), 10.0);
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));
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));
scene.addLight("green", light);
let light = Light( P(-2.0,7.0,0.0), V(1.0,0.0,0.0), V(0.1, 0.01, 0.001));
scene.addLight("red", light);
let light = Ambient(V(0.1,0.1,0.1));
scene.addLight("ambient", light);
let sphere = Sphere(P(0.0,0.0,0.0), 1.0, material);
let sphere_node = Node(sphere);
scene.addNode("sphere", sphere_node);
// for i in 0..6 {
// let sphere = Sphere(P(0.0,0.0,0.0), 2.0, material);
// let sphere_node = Node(sphere);
// sphere_node.translate(2.0*cos(i.to_float()), -4.0, 2.0*sin(i.to_float()));
// scene.addNode(sphere_node);
// }
// let child = sphere_node.child(sphere);
// child.translate(V(1.0,1.0,1.0));
//scene.addNode(child);
// let cube = CubeUnit(material);
// let cube_node = Node(cube);
// scene.addNode(cube_node);
let gnonom = Gnonom(material);
let gnonom_node = Node(gnonom);
scene.addNode("gnonom", gnonom_node);
// let cylinder = Cylinder(10.0,1.0, material);
// let cylinder_node = Node(cylinder);
// //cylinder_node.scale(V(4.0,0.3,0.3));
// cylinder_node.translate(V(0.0,-4.0,0.0));
// scene.addNode(cylinder_node);
scene

View File

@@ -17,17 +17,17 @@ const BUFFER_PROPORTION_MIN: f32 = 0.1;
const BUFFER_PROPORTION_MAX: f32 = 1.0;
//RAY CONSTANTS
const RAYS_INIT: i32 = 7000;
const RAYS_INIT: i32 = 100;
const RAYS_MIN: i32 = 100;
const RAYS_MAX: i32 = 30000;
const RAYS_MAX: i32 = 10000;
//MATERIAL CONSTANTS
const MIN_D: f32 = 0.0;
const MIN_S: f32 = 0.0;
const MIN_SHINE: f32 = 0.0;
const MAX_D: f32 = 1.0;
const MAX_S: f32 = 1.0;
const MAX_SHINE: f32 = 50.0;
// const MIN_D: f32 = 0.0;
// const MIN_S: f32 = 0.0;
// const MIN_SHINE: f32 = 0.0;
// const MAX_D: f32 = 1.0;
// const MAX_S: f32 = 1.0;
// const MAX_SHINE: f32 = 50.0;
//TRANSFORMATION CONSTANTS
const MIN_COLOUR: f32 = 0.0;
@@ -310,12 +310,9 @@ impl Gui {
//Use different cameras in the scene
if let Some(_t) = ui.tree_node("Cameras") {
for (label, camera) in &self.scene.cameras {
if let Some(_t) = ui.tree_node(label) {
if ui.button("Use camera") {
if ui.button(label) {
self.camera = camera.clone();
self.event =
Some(GuiEvent::CameraUpdate(camera.clone(), self.camera_fov));
}
self.event = Some(GuiEvent::CameraUpdate(camera.clone(), self.camera_fov));
}
}
}
@@ -413,9 +410,9 @@ pub fn init_engine() -> Engine {
.register_fn("Circle", Circle::new)
.register_fn("CircleUnit", Circle::unit);
engine
.register_type::<Rectangle>()
.register_fn("Rectangle", Rectangle::new)
.register_fn("RectangleUnit", Rectangle::unit);
.register_type::<Cube>()
.register_fn("Cube", Cube::new)
.register_fn("CubeUnit", Cube::unit);
engine
.register_type::<SteinerSurface>()
.register_fn("Steiner", SteinerSurface::new);

View File

@@ -5,7 +5,7 @@ use nalgebra::{distance, Matrix4, Point3, Vector3};
use roots::{find_roots_quadratic, find_roots_quartic, Roots};
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::sync::Arc;
use std::rc::Rc;
// MATERIAL -----------------------------------------------------------------
#[derive(Clone)]
pub struct Material {
@@ -15,41 +15,41 @@ pub struct Material {
}
impl Material {
pub fn new(kd: Vector3<f64>, ks: Vector3<f64>, shininess: f64) -> Arc<Self> {
pub fn new(kd: Vector3<f64>, ks: Vector3<f64>, shininess: f64) -> Rc<Self> {
let kd = kd.cast();
let ks = ks.cast();
let shininess = shininess as f32;
Arc::new(Material { kd, ks, shininess })
Rc::new(Material { kd, ks, shininess })
}
pub fn magenta() -> Arc<Self> {
pub fn magenta() -> Rc<Self> {
let kd = Vector3::new(1.0, 0.0, 1.0);
let ks = Vector3::new(1.0, 0.0, 1.0);
let shininess = 0.5;
Arc::new(Material { kd, ks, shininess })
Rc::new(Material { kd, ks, shininess })
}
pub fn turquoise() -> Arc<Self> {
pub fn turquoise() -> Rc<Self> {
let kd = Vector3::new(0.25, 0.3, 0.7);
let ks = Vector3::new(0.25, 0.3, 0.7);
let shininess = 0.5;
Arc::new(Material { kd, ks, shininess })
Rc::new(Material { kd, ks, shininess })
}
pub fn red() -> Arc<Self> {
pub fn red() -> Rc<Self> {
let kd = Vector3::new(0.8, 0.0, 0.3);
let ks = Vector3::new(0.8, 0.3, 0.0);
let shininess = 0.5;
Arc::new(Material { kd, ks, shininess })
Rc::new(Material { kd, ks, shininess })
}
pub fn blue() -> Arc<Self> {
pub fn blue() -> Rc<Self> {
let kd = Vector3::new(0.0, 0.3, 0.6);
let ks = Vector3::new(0.3, 0.0, 0.6);
let shininess = 0.5;
Arc::new(Material { kd, ks, shininess })
Rc::new(Material { kd, ks, shininess })
}
pub fn green() -> Arc<Self> {
pub fn green() -> Rc<Self> {
let kd = Vector3::new(0.0, 1.0, 0.0);
let ks = Vector3::new(0.0, 1.0, 0.0);
let shininess = 0.5;
Arc::new(Material { kd, ks, shininess })
Rc::new(Material { kd, ks, shininess })
}
}
@@ -59,7 +59,7 @@ pub struct Intersection {
pub point: Point3<f64>,
pub normal: Vector3<f64>,
pub incidence: Vector3<f64>,
pub material: Arc<Material>,
pub material: Rc<Material>,
pub distance: f64,
}
impl Intersection {
@@ -76,7 +76,6 @@ impl Intersection {
// BOUNDING BOX -----------------------------------------------------------------
#[derive(Clone)]
struct BoundingBox {
bln: Point3<f64>,
trf: Point3<f64>,
@@ -88,29 +87,41 @@ impl BoundingBox {
let trf = trf - Vector3::new(EPSILON, EPSILON, EPSILON);
BoundingBox { bln, trf }
}
fn intersect_bounding_box(&self, ray: &Ray) -> Option<Point3<f64>> {
let t1 = (self.bln - ray.a).component_div(&ray.b);
let t2 = (self.trf - ray.a).component_div(&ray.b);
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
let bln = &self.bln;
let trf = &self.trf;
let t1 = (bln - ray.a).component_div(&ray.b);
let t2 = (trf - ray.a).component_div(&ray.b);
let tmin = t1.inf(&t2).min();
let tmax = t1.sup(&t2).max();
if tmax >= tmin {
Some(ray.at_t(tmin))
} else {
None
let intersect = ray.at_t(tmin);
// Check if the intersection is inside the box
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 true; // Intersection is outside the box
}
}
false
}
#[allow(dead_code)]
fn get_centroid(&self) -> Point3<f64> {
self.bln + (self.trf - self.bln) / 2.0
}
}
// PRIMITIVE TRAIT -----------------------------------------------------------------
pub trait Primitive: Send + Sync {
pub trait Primitive {
fn intersect_ray(&self, ray: &Ray) -> Option<Intersection>;
fn intersect_bounding_box(&self, ray: &Ray) -> Option<Point3<f64>>;
fn get_material(&self) -> Arc<Material>;
fn intersect_bounding_box(&self, ray: &Ray) -> bool;
fn get_material(&self) -> Rc<Material>;
}
// SPHERE -----------------------------------------------------------------
@@ -119,16 +130,16 @@ pub struct Sphere {
position: Point3<f64>,
radius: f64,
bounding_box: BoundingBox,
material: Arc<Material>,
material: Rc<Material>,
}
impl Sphere {
pub fn new(position: Point3<f64>, radius: f64, material: Arc<Material>) -> Arc<dyn Primitive> {
pub fn new(position: Point3<f64>, radius: f64, material: Rc<Material>) -> Rc<dyn Primitive> {
let radius_vec = Vector3::new(radius, radius, radius);
let bln = position - radius_vec;
let trf = position + radius_vec;
let bounding_box = BoundingBox::new(bln, trf);
Arc::new(Sphere {
Rc::new(Sphere {
position,
radius,
bounding_box,
@@ -136,7 +147,7 @@ impl Sphere {
})
}
pub fn unit(material: Arc<Material>) -> Arc<dyn Primitive> {
pub fn unit(material: Rc<Material>) -> Rc<dyn Primitive> {
Sphere::new(Point3::new(0.0, 0.0, 0.0), 1.0, material)
}
}
@@ -175,16 +186,16 @@ impl Primitive for Sphere {
point: intersect,
normal,
incidence: ray.b,
material: Arc::clone(&self.material),
material: Rc::clone(&self.material),
distance: t,
})
}
fn get_material(&self) -> Arc<Material> {
Arc::clone(&self.material)
fn get_material(&self) -> Rc<Material> {
Rc::clone(&self.material)
}
fn intersect_bounding_box(&self, ray: &Ray) -> Option<Point3<f64>> {
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
return self.bounding_box.intersect_bounding_box(ray);
}
}
@@ -195,7 +206,7 @@ pub struct Circle {
position: Point3<f64>,
radius: f64,
normal: Vector3<f64>,
material: Arc<Material>,
material: Rc<Material>,
bounding_box: BoundingBox,
}
@@ -204,13 +215,13 @@ impl Circle {
position: Point3<f64>,
radius: f64,
normal: Vector3<f64>,
material: Arc<Material>,
) -> Arc<dyn Primitive> {
material: Rc<Material>,
) -> Rc<dyn Primitive> {
let radius_vec = Vector3::new(radius, radius, radius);
let bln = position - radius_vec;
let trf = position + radius_vec;
let bounding_box = BoundingBox::new(bln, trf);
Arc::new(Circle {
Rc::new(Circle {
position,
radius,
normal: normal.normalize(),
@@ -219,7 +230,7 @@ impl Circle {
})
}
pub fn unit(material: Arc<Material>) -> Arc<dyn Primitive> {
pub fn unit(material: Rc<Material>) -> Rc<dyn Primitive> {
let position = Point3::new(0.0, 0.0, 0.0);
let normal = Vector3::new(0.0, 1.0, 0.0);
let radius = 1.0;
@@ -229,7 +240,7 @@ impl Circle {
let trf = Point3::new(radius, 0.0, EPSILON);
let bounding_box = BoundingBox { bln, trf };
Arc::new(Circle {
Rc::new(Circle {
position,
normal,
radius,
@@ -256,18 +267,18 @@ impl Primitive for Circle {
point: intersect,
normal: self.normal.normalize(),
incidence: ray.b,
material: Arc::clone(&self.material),
material: Rc::clone(&self.material),
distance: t,
})
}
}
}
fn get_material(&self) -> Arc<Material> {
Arc::clone(&self.material)
fn get_material(&self) -> Rc<Material> {
Rc::clone(&self.material)
}
fn intersect_bounding_box(&self, ray: &Ray) -> Option<Point3<f64>> {
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
self.bounding_box.intersect_bounding_box(ray)
}
}
@@ -277,29 +288,29 @@ impl Primitive for Circle {
pub struct Cylinder {
radius: f64,
height: f64,
base_circle: Arc<dyn Primitive>,
top_circle: Arc<dyn Primitive>,
material: Arc<Material>,
base_circle: Rc<dyn Primitive>,
top_circle: Rc<dyn Primitive>,
material: Rc<Material>,
bounding_box: BoundingBox,
}
impl Cylinder {
pub fn new(radius: f64, height: f64, material: Arc<Material>) -> Arc<dyn Primitive> {
pub fn new(radius: f64, height: f64, material: Rc<Material>) -> Rc<dyn Primitive> {
let base_circle = Circle::new(
Point3::new(0.0, 0.0, 0.0),
radius,
Vector3::new(0.0, -1.0, 0.0),
Arc::clone(&material),
Rc::clone(&material),
);
let top_circle = Circle::new(
Point3::new(0.0, height, 0.0),
radius,
Vector3::new(0.0, 1.0, 0.0),
Arc::clone(&material),
Rc::clone(&material),
);
let bln = Point3::new(-radius, 0.0, -radius);
let trf = Point3::new(radius, height, radius);
Arc::new(Cylinder {
Rc::new(Cylinder {
radius,
height,
base_circle,
@@ -347,7 +358,7 @@ impl Primitive for Cylinder {
point: intersect,
normal: normal,
incidence: ray.b,
material: Arc::clone(&self.material),
material: Rc::clone(&self.material),
distance: t,
})
} else {
@@ -390,11 +401,11 @@ impl Primitive for Cylinder {
}
}
fn get_material(&self) -> Arc<Material> {
Arc::clone(&self.material)
fn get_material(&self) -> Rc<Material> {
Rc::clone(&self.material)
}
fn intersect_bounding_box(&self, ray: &Ray) -> Option<Point3<f64>> {
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
self.bounding_box.intersect_bounding_box(ray)
}
}
@@ -405,22 +416,22 @@ pub struct Cone {
radius: f64,
base: f64,
apex: f64,
circle: Arc<dyn Primitive>,
material: Arc<Material>,
circle: Rc<dyn Primitive>,
material: Rc<Material>,
bounding_box: BoundingBox,
}
impl Cone {
pub fn new(radius: f64, apex: f64, base: f64, material: Arc<Material>) -> Arc<dyn Primitive> {
pub fn new(radius: f64, apex: f64, base: f64, material: Rc<Material>) -> Rc<dyn Primitive> {
let circle = Circle::new(
Point3::new(0.0, base, 0.0),
radius,
Vector3::new(0.0, 1.0, 0.0),
Arc::clone(&material),
Rc::clone(&material),
);
let bln = Point3::new(-radius, base, -radius);
let trf = Point3::new(radius, base + apex, radius);
Arc::new(Cone {
Rc::new(Cone {
radius: radius / 2.0,
base,
apex,
@@ -429,7 +440,7 @@ impl Cone {
bounding_box: BoundingBox { bln, trf },
})
}
pub fn unit(material: Arc<Material>) -> Arc<dyn Primitive> {
pub fn unit(material: Rc<Material>) -> Rc<dyn Primitive> {
Cone::new(1.0, 2.0, -1.0, material)
}
@@ -480,7 +491,7 @@ impl Primitive for Cone {
point: intersect,
normal: self.get_normal(intersect),
incidence: ray.b,
material: Arc::clone(&self.material),
material: Rc::clone(&self.material),
distance: t,
}),
false => None,
@@ -505,11 +516,11 @@ impl Primitive for Cone {
}
}
fn get_material(&self) -> Arc<Material> {
Arc::clone(&self.material)
fn get_material(&self) -> Rc<Material> {
Rc::clone(&self.material)
}
fn intersect_bounding_box(&self, ray: &Ray) -> Option<Point3<f64>> {
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
self.bounding_box.intersect_bounding_box(ray)
}
}
@@ -520,7 +531,7 @@ pub struct Rectangle {
position: Point3<f64>,
normal: Vector3<f64>,
width_direction: Vector3<f64>,
material: Arc<Material>,
material: Rc<Material>,
width: f64,
height: f64,
bounding_box: BoundingBox,
@@ -533,14 +544,14 @@ impl Rectangle {
width_direction: Vector3<f64>,
width: f64,
height: f64,
material: Arc<Material>,
) -> Arc<dyn Primitive> {
material: Rc<Material>,
) -> Rc<dyn Primitive> {
let normal = normal.normalize();
let width_direction = width_direction.normalize();
let height_direction = width_direction.cross(&normal);
let bln = position - width / 2.0 * width_direction - height / 2.0 * height_direction;
let trf = position + width / 2.0 * width_direction + height / 2.0 * height_direction;
Arc::new(Rectangle {
Rc::new(Rectangle {
position,
normal: normal.normalize(),
width_direction: width_direction.normalize(),
@@ -550,7 +561,7 @@ impl Rectangle {
bounding_box: BoundingBox { bln, trf },
})
}
pub fn unit(material: Arc<Material>) -> Arc<dyn Primitive> {
pub fn unit(material: Rc<Material>) -> Rc<dyn Primitive> {
Rectangle::new(
Point3::new(0.0, 0.0, 0.0),
Vector3::new(0.0, 1.0, 0.0),
@@ -586,18 +597,18 @@ impl Primitive for Rectangle {
point: intersect,
normal: self.normal,
incidence: ray.b,
material: Arc::clone(&self.material),
material: Rc::clone(&self.material),
distance: t,
});
}
None
}
fn get_material(&self) -> Arc<Material> {
Arc::clone(&self.material)
fn get_material(&self) -> Rc<Material> {
Rc::clone(&self.material)
}
fn intersect_bounding_box(&self, ray: &Ray) -> Option<Point3<f64>> {
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
self.bounding_box.intersect_bounding_box(ray)
}
}
@@ -607,13 +618,13 @@ impl Primitive for Rectangle {
pub struct Cube {
bln: Point3<f64>,
trf: Point3<f64>,
material: Arc<Material>,
material: Rc<Material>,
bounding_box: BoundingBox,
}
impl Cube {
pub fn new(bln: Point3<f64>, trf: Point3<f64>, material: Arc<Material>) -> Arc<dyn Primitive> {
Arc::new(Cube {
pub fn new(bln: Point3<f64>, trf: Point3<f64>, material: Rc<Material>) -> Rc<dyn Primitive> {
Rc::new(Cube {
bln,
trf,
material,
@@ -621,7 +632,7 @@ impl Cube {
})
}
pub fn unit(material: Arc<Material>) -> Arc<dyn Primitive> {
pub fn unit(material: Rc<Material>) -> Rc<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, material)
@@ -646,12 +657,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
}
@@ -675,7 +686,7 @@ impl Primitive for Cube {
point: intersect,
normal: normal,
incidence: ray.b,
material: Arc::clone(&self.material),
material: Rc::clone(&self.material),
distance: tmin,
})
} else {
@@ -683,12 +694,12 @@ impl Primitive for Cube {
}
}
fn intersect_bounding_box(&self, ray: &Ray) -> Option<Point3<f64>> {
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
self.bounding_box.intersect_bounding_box(ray)
}
fn get_material(&self) -> Arc<Material> {
Arc::clone(&self.material)
fn get_material(&self) -> Rc<Material> {
Rc::clone(&self.material)
}
}
@@ -700,7 +711,7 @@ pub struct Triangle {
v: Point3<f64>,
w: Point3<f64>,
normal: Vector3<f64>,
material: Arc<Material>,
material: Rc<Material>,
bounding_box: BoundingBox,
}
@@ -709,15 +720,15 @@ impl Triangle {
u: Point3<f64>,
v: Point3<f64>,
w: Point3<f64>,
material: Arc<Material>,
) -> Arc<dyn Primitive> {
material: Rc<Material>,
) -> Rc<dyn Primitive> {
let uv = v - u;
let uw = w - u;
let normal = uv.cross(&uw).normalize();
let bln = u.inf(&v).inf(&w);
let trf = u.sup(&v).sup(&w);
let bounding_box = BoundingBox { bln, trf };
Arc::new(Triangle {
Rc::new(Triangle {
u,
v,
w,
@@ -727,7 +738,7 @@ impl Triangle {
})
}
#[allow(dead_code)]
pub fn unit(material: Arc<Material>) -> Arc<dyn Primitive> {
pub fn unit(material: Rc<Material>) -> Rc<dyn Primitive> {
let u = Point3::new(-1.0, 0.0, -1.0);
let v = Point3::new(0.0, 0.0, 1.0);
let w = Point3::new(1.0, 0.0, -1.0);
@@ -768,7 +779,7 @@ impl Primitive for Triangle {
point: intersect,
normal: normal,
incidence: ray.b,
material: Arc::clone(&self.material),
material: Rc::clone(&self.material),
distance: t,
})
} else {
@@ -776,11 +787,11 @@ impl Primitive for Triangle {
}
}
fn get_material(&self) -> Arc<Material> {
Arc::clone(&self.material)
fn get_material(&self) -> Rc<Material> {
Rc::clone(&self.material)
}
fn intersect_bounding_box(&self, ray: &Ray) -> Option<Point3<f64>> {
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
self.bounding_box.intersect_bounding_box(ray)
}
}
@@ -788,33 +799,29 @@ impl Primitive for Triangle {
// MESH -----------------------------------------------------------------
#[derive(Clone)]
pub struct Mesh {
triangles: Vec<Arc<Triangle>>,
material: Arc<Material>,
triangles: Vec<Triangle>,
material: Rc<Material>,
bounding_box: BoundingBox,
}
impl Mesh {
#[allow(dead_code)]
pub fn new(triangles: Vec<Arc<Triangle>>, material: Arc<Material>) -> Arc<dyn Primitive> {
pub fn new(triangles: Vec<Triangle>, material: Rc<Material>) -> Rc<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);
Arc::new(Mesh {
Rc::new(Mesh {
triangles,
material,
bounding_box,
})
}
#[allow(dead_code)]
fn compute_bounding_box(triangles: &Vec<Arc<Triangle>>) -> BoundingBox {
fn compute_bounding_box(triangles: &Vec<Triangle>) -> BoundingBox {
let mut bln = Point3::new(INFINITY, INFINITY, INFINITY);
let mut trf = -bln;
for triangle in triangles {
bln = bln.inf(&triangle.u);
bln = bln.inf(&triangle.v);
bln = bln.inf(&triangle.w);
trf = trf.sup(&triangle.u);
trf = trf.sup(&triangle.v);
trf = trf.sup(&triangle.w);
@@ -822,8 +829,8 @@ impl Mesh {
BoundingBox { bln, trf }
}
pub fn from_file(filename: &str, material: Arc<Material>) -> Arc<dyn Primitive> {
let mut triangles: Vec<Arc<dyn Primitive>> = Vec::new();
pub fn from_file(filename: &str, material: Rc<Material>) -> Rc<dyn Primitive> {
let mut triangles: Vec<Triangle> = Vec::new();
let mut vertices: Vec<Point3<f64>> = Vec::new();
let file = File::open(filename).expect("Failed to open file");
@@ -857,10 +864,24 @@ impl Mesh {
let v3: usize =
v3_str.parse().expect("Failed to parse vertex index");
// Indices in OBJ files are 1-based, so subtract 1 to convert to 0-based.
let a = vertices[v1 - 1];
let b = vertices[v2 - 1];
let c = vertices[v3 - 1];
triangles.push(Triangle::new(a, b, c, Arc::clone(&material)));
let u = vertices[v1 - 1];
let v = vertices[v2 - 1];
let w = vertices[v3 - 1];
let uv = u - v;
let uw = w - v;
let normal = uv.cross(&uw).normalize();
let bln = u.inf(&v).inf(&w);
let trf = u.sup(&v).sup(&w);
let bounding_box = BoundingBox { bln, trf };
let material = material.clone();
triangles.push(Triangle {
u,
v,
w,
normal,
material,
bounding_box,
});
}
}
_ => {}
@@ -868,7 +889,7 @@ impl Mesh {
}
}
}
todo!();
Mesh::new(triangles, material)
}
}
@@ -893,11 +914,11 @@ impl Primitive for Mesh {
closest_intersect
}
fn get_material(&self) -> Arc<Material> {
Arc::clone(&self.material)
fn get_material(&self) -> Rc<Material> {
Rc::clone(&self.material)
}
fn intersect_bounding_box(&self, ray: &Ray) -> Option<Point3<f64>> {
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
self.bounding_box.intersect_bounding_box(ray)
}
}
@@ -905,16 +926,16 @@ impl Primitive for Mesh {
// STEINER -----------------------------------------------------------------
#[derive(Clone)]
pub struct SteinerSurface {
material: Arc<Material>,
material: Rc<Material>,
bounding_box: BoundingBox,
}
impl SteinerSurface {
pub fn new(material: Arc<Material>) -> Arc<dyn Primitive> {
pub fn new(material: Rc<Material>) -> Rc<dyn Primitive> {
// I need to find the bounding box for this shape
let trf = Point3::new(1.0, 1.0, 1.0);
let bln = Point3::new(-1.0, -1.0, -1.0);
Arc::new(SteinerSurface {
Rc::new(SteinerSurface {
material,
bounding_box: BoundingBox { bln, trf },
})
@@ -989,17 +1010,17 @@ impl Primitive for SteinerSurface {
point,
normal,
incidence: ray.b,
material: Arc::clone(&self.material),
material: Rc::clone(&self.material),
distance: t,
})
}
fn intersect_bounding_box(&self, ray: &Ray) -> Option<Point3<f64>> {
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
self.bounding_box.intersect_bounding_box(ray)
}
fn get_material(&self) -> Arc<Material> {
Arc::clone(&self.material)
fn get_material(&self) -> Rc<Material> {
Rc::clone(&self.material)
}
}
@@ -1017,16 +1038,16 @@ fn smallest_non_zero(arr: &[f64]) -> Option<f64> {
pub struct Torus {
inner_rad: f64,
outer_rad: f64,
material: Arc<Material>,
material: Rc<Material>,
bounding_box: BoundingBox,
}
impl Torus {
pub fn new(inner_rad: f64, outer_rad: f64, material: Arc<Material>) -> Arc<dyn Primitive> {
pub fn new(inner_rad: f64, outer_rad: f64, material: Rc<Material>) -> Rc<dyn Primitive> {
// I need to find the bounding box for this shape
let trf = Point3::new(1.0, 1.0, 1.0);
let bln = Point3::new(-1.0, -1.0, -1.0);
Arc::new(Torus {
Rc::new(Torus {
inner_rad,
outer_rad,
material,
@@ -1133,34 +1154,34 @@ impl Primitive for Torus {
point,
normal,
incidence: ray.b,
material: Arc::clone(&self.material),
material: Rc::clone(&self.material),
distance: t,
})
}
fn intersect_bounding_box(&self, ray: &Ray) -> Option<Point3<f64>> {
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
self.bounding_box.intersect_bounding_box(ray)
}
fn get_material(&self) -> Arc<Material> {
Arc::clone(&self.material)
fn get_material(&self) -> Rc<Material> {
Rc::clone(&self.material)
}
}
// GNOMON -----------------------------------------------------------------
#[derive(Clone)]
pub struct Gnonom {
x_cube: Arc<dyn Primitive>,
y_cube: Arc<dyn Primitive>,
z_cube: Arc<dyn Primitive>,
material: Arc<Material>,
x_cube: Rc<dyn Primitive>,
y_cube: Rc<dyn Primitive>,
z_cube: Rc<dyn Primitive>,
material: Rc<Material>,
bounding_box: BoundingBox,
}
impl Gnonom {
const GNONOM_WIDTH: f64 = 0.1;
const GNONOM_LENGTH: f64 = 2.0;
pub fn new(material: Arc<Material>) -> Arc<dyn Primitive> {
pub fn new(material: Rc<Material>) -> Rc<dyn Primitive> {
let x_cube = Cube::new(
Point3::new(0.0, -Self::GNONOM_WIDTH, -Self::GNONOM_WIDTH),
Point3::new(Self::GNONOM_LENGTH, Self::GNONOM_WIDTH, Self::GNONOM_WIDTH),
@@ -1188,7 +1209,7 @@ impl Gnonom {
Self::GNONOM_LENGTH,
),
};
Arc::new(Gnonom {
Rc::new(Gnonom {
x_cube,
y_cube,
z_cube,
@@ -1215,11 +1236,11 @@ impl Primitive for Gnonom {
None
}
fn intersect_bounding_box(&self, ray: &Ray) -> Option<Point3<f64>> {
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
self.bounding_box.intersect_bounding_box(ray)
}
fn get_material(&self) -> Arc<Material> {
fn get_material(&self) -> Rc<Material> {
self.material.clone()
}
}

View File

@@ -1,10 +1,5 @@
use crate::{
primitive::Intersection,
raytracer::phong_shade_point,
scene::{Node, Scene},
};
use crate::{primitive::Intersection, raytracer::phong_shade_point, scene::Scene};
use nalgebra::{Matrix4, Point3, Vector3};
use std::collections::HashMap;
#[derive(Clone)]
// Ray struct represents a ray in 3D space with a starting point 'a' and a direction 'b'
@@ -35,42 +30,39 @@ impl Ray {
// 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) -> Option<Vector3<u8>> {
//Get the closest intersection of the ray with the scene
let intersect = self.get_closest_intersection(&scene.nodes);
let mut closest_distance = f64::MAX;
let mut closest_intersect: Option<Intersection> = None;
let mut closest_node = None;
for (_, node) in &scene.nodes {
// Transform ray into local model cordinates
let ray = self.transform(&node.inv_model);
// Check bounding box intersection
if node.primitive.intersect_bounding_box(&ray) {
// Check primitive intersection
if let Some(intersect) = node.primitive.intersect_ray(&ray) {
// Check for closest distance
if intersect.distance < closest_distance {
closest_distance = intersect.distance;
closest_intersect = Some(intersect);
closest_node = Some(node);
}
}
}
}
//Shade the intersection point if there is one
match intersect {
Some(intersect) => Some(phong_shade_point(&scene, &intersect)), // If there is an intersection, shade it
match closest_intersect {
Some(intersect) => {
//Inverse transform back to world coords
let node = closest_node.unwrap();
let intersect = intersect.transform(&node.model, &node.inv_model);
Some(phong_shade_point(&scene, &intersect)) // If there is an intersection, shade it
}
None => None, // If there is no intersection, return None
}
}
// Find the closest intersection
pub fn get_closest_intersection(&self, nodes: &HashMap<String, Node>) -> Option<Intersection> {
//Assign no intersection
let mut closest_distance = f64::MAX;
let mut closest_intersect: Option<Intersection> = None;
for (_, node) in nodes {
// Clone arc to primitive
let primitive = node.primitive.clone();
// Transform ray into local model cordinates
let ray = self.transform(&node.inv_model);
// Check bounding box intersection
if primitive.intersect_bounding_box(&ray).is_some() {
// Check primitive intersection
if let Some(intersect) = primitive.intersect_ray(&ray) {
// Check for closest distance
if intersect.distance < closest_distance {
closest_distance = intersect.distance;
//Convert back to world coords
let intersect = intersect.transform(&node.model, &node.inv_model);
closest_intersect = Some(intersect);
}
}
}
}
//Return None if we find no intersection, some if we do find one
closest_intersect
}
// Return a transformed version of the ray
pub fn transform(&self, trans: &Matrix4<f64>) -> Ray {
Ray {

View File

@@ -1,21 +1,21 @@
use crate::{light::Light, primitive::Intersection, ray::Ray, scene::*, EPSILON};
use crate::{light::Light, primitive::Intersection, ray::Ray, scene::*};
use nalgebra::{Unit, Vector3};
use nalgebra::Vector3;
// Function to shade a point in the scene using Phong shading model
pub fn phong_shade_point(scene: &Scene, intersect: &Intersection) -> Vector3<u8> {
let Intersection {
point,
normal,
incidence,
material,
..
} = intersect;
let point = &intersect.point;
let material = &intersect.material;
let normal = &intersect.normal;
let incidence = &intersect.incidence;
let kd = material.kd;
let ks = material.ks;
let kd = &material.kd;
let ks = &material.ks;
let shininess = material.shininess;
// Point to camera
let to_camera = -incidence;
// Compute the ambient light component and set it as base colour
let mut colour = Vector3::zeros();
@@ -37,13 +37,11 @@ pub fn phong_shade_point(scene: &Scene, intersect: &Intersection) -> Vector3<u8>
let light_distance = to_light.norm() as f32;
let to_light = to_light;
let to_light_ray = Ray::new(point.clone() + normal * EPSILON, to_light);
if light_blocked(scene, to_light_ray) {
continue;
}
//let to_light_ray = Ray::new(point.clone() + normal * EPSILON, to_light);
// if light_blocked(scene, to_light_ray) {
// continue;
// }
// Point to camera
let to_camera = -incidence;
// Diffuse component
let n_dot_l = normal.dot(&to_light).max(0.0) as f32;
let diffuse = n_dot_l * kd;
@@ -51,11 +49,10 @@ pub fn phong_shade_point(scene: &Scene, intersect: &Intersection) -> Vector3<u8>
let mut specular = Vector3::zeros();
if n_dot_l > 0.0 {
// Halfway vector.
let h = Unit::new_normalize(to_camera.lerp(&to_light, 0.5));
let h = to_camera + to_light.normalize();
let n_dot_h = normal.dot(&h).max(0.0) as f32;
specular = ks * n_dot_h.powf(shininess);
}
// Compute light falloff
let falloff = 1.0
/ (1.0
@@ -75,7 +72,7 @@ pub fn phong_shade_point(scene: &Scene, intersect: &Intersection) -> Vector3<u8>
fn light_blocked(scene: &Scene, ray: Ray) -> bool {
for (_, node) in &scene.nodes {
let ray = ray.transform(&node.inv_model);
if node.primitive.intersect_bounding_box(&ray).is_some() {
if node.primitive.intersect_bounding_box(&ray) {
if node.primitive.intersect_ray(&ray).is_some() {
return true;
}

View File

@@ -3,11 +3,11 @@ use crate::light::Light;
use crate::primitive::*;
use nalgebra::{Matrix4, Vector3};
use std::collections::HashMap;
use std::sync::Arc;
use std::rc::Rc;
#[derive(Clone)]
pub struct Node {
//Primitive
pub primitive: Arc<dyn Primitive>,
pub primitive: Rc<dyn Primitive>,
//Transformations
pub rotation: [f64; 3],
pub scale: [f64; 3],
@@ -19,7 +19,7 @@ pub struct Node {
impl Node {
//New node with no transformations
pub fn new(primitive: Arc<dyn Primitive>) -> Node {
pub fn new(primitive: Rc<dyn Primitive>) -> Node {
Node {
primitive,
rotation: [0.0, 0.0, 0.0],
@@ -30,7 +30,7 @@ impl Node {
}
}
//New node with parent transformations
pub fn child(self, primitive: Arc<dyn Primitive>) -> Node {
pub fn child(self, primitive: Rc<dyn Primitive>) -> Node {
let mut child = self.clone();
child.primitive = primitive;
child
@@ -90,7 +90,7 @@ impl Node {
#[derive(Clone)]
pub struct Scene {
pub nodes: HashMap<String, Node>,
pub materials: HashMap<String, Arc<Material>>,
pub materials: HashMap<String, Rc<Material>>,
pub lights: HashMap<String, Light>,
pub cameras: HashMap<String, Camera>,
}
@@ -110,7 +110,7 @@ impl Scene {
self.nodes.insert(label, node);
}
// Adds a material to the scene
pub fn add_material(&mut self, label: String, material: Arc<Material>) {
pub fn add_material(&mut self, label: String, material: Rc<Material>) {
self.materials.insert(label, material);
}
// Adds a light to the scene

View File

@@ -23,7 +23,7 @@ const START_HEIGHT: i32 = 700;
const COLOUR_CLEAR: [u8; 4] = [0x22, 0x00, 0x11, 0xff];
const PIXEL_CLEAR: [u8; 4] = [0x55, 0x00, 0x22, 0xff];
pub const INIT_FILE: &str = "scene.rhai";
pub const INIT_FILE: &str = "rhai/scene.rhai";
pub const SAVE_FILE: &str = "img.png";
pub struct State {
@@ -145,6 +145,7 @@ impl State {
fn draw(&mut self) -> Result<(), Box<dyn Error>> {
//Draw ray_num in a block
let frame = self.pixels.frame_mut();
for _ in 0..self.gui.ray_num {
//Get random index from queue
let index = match self.ray_queue.pop() {
@@ -155,7 +156,6 @@ impl State {
let colour = &self.rays[index].shade_ray(&self.scene);
//Assign colour to pixel in frame
let rgba = colour.map_or(PIXEL_CLEAR, |colour| [colour.x, colour.y, colour.z, 255]);
let frame = self.pixels.frame_mut();
frame[index * 4..(index + 1) * 4].copy_from_slice(&rgba);
}
Ok(())

View File

@@ -1,22 +0,0 @@
let scene = Scene();
let material = Material(V(0.2,0.2,0.2), V(0.2, 0.8, 0.8), 10.0);
//let ambient = Light(P(10.0,0.0,0.0), V(1.0,1.0,1.0), V(0.0, 0.0, 0.0));
//scene.addLight(ambient);
let light = Light(P(0.0,7.0,0.0), V(0.0,0.0,1.0), V(0.1, 0.01, 0.001));
scene.addLight(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));
scene.addLight(light);
let light = Light(P(-2.0,7.0,0.0), V(1.0,0.0,0.0), V(0.1, 0.01, 0.001));
scene.addLight(light);
let light = Ambient(V(0.1,0.1,0.1));
scene.addLight(light);
let sphere = Stein(material);
let sphere_node = Node(sphere);
scene.addNode(sphere_node);
scene