aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian T <julian@jtle.dk>2021-02-21 18:01:56 +0100
committerJulian T <julian@jtle.dk>2021-02-21 18:01:56 +0100
commitda1c3949a449f3fafe579c62ff6b14ffd993a197 (patch)
tree754df5c9b5e9f0fa0a8bb7a8cd3dd4b12fe5ad89
parentc695da871a75bb6786c08c3546ef71ed032bd61d (diff)
Add 3d bounding box and merged SceneIntersection and Intersection
-rw-r--r--src/core/bound2.rs (renamed from src/core/bound.rs)17
-rw-r--r--src/core/bound3.rs69
-rw-r--r--src/core/mod.rs31
-rw-r--r--src/core/vector3.rs16
-rw-r--r--src/main.rs2
-rw-r--r--src/material/lambertian.rs3
-rw-r--r--src/material/mod.rs5
-rw-r--r--src/material/reflectant.rs3
-rw-r--r--src/trace/mod.rs6
-rw-r--r--src/trace/pathtrace.rs9
-rw-r--r--src/world/container.rs43
-rw-r--r--src/world/hittable.rs (renamed from src/core/hittable.rs)16
-rw-r--r--src/world/mod.rs16
-rw-r--r--src/world/scene.rs33
-rw-r--r--src/world/shapes/sphere.rs24
15 files changed, 229 insertions, 64 deletions
diff --git a/src/core/bound.rs b/src/core/bound2.rs
index 37918d4..bc0e7cd 100644
--- a/src/core/bound.rs
+++ b/src/core/bound2.rs
@@ -1,7 +1,8 @@
-//! Implements a 2d region
+//! Implements a 2d axis aligned bounding box
use crate::{Number, Float};
use super::vector2::Vector2;
use crate::core;
+use crate::core::{min, max};
/// Implements a region between min and max
#[derive(Clone)]
@@ -13,20 +14,6 @@ pub struct Bound2<T: Number> {
pub type Bound2i = Bound2<i32>;
pub type Bound2f = Bound2<Float>;
-fn min<T: Number> (a: T, b: T) -> T {
- if b < a {
- return b;
- }
- a
-}
-
-fn max<T: Number> (a: T, b: T) -> T {
- if b > a {
- return b;
- }
- a
-}
-
impl<T: Number> Bound2<T> {
/// Creates a new bound from two points
///
diff --git a/src/core/bound3.rs b/src/core/bound3.rs
new file mode 100644
index 0000000..7f54e03
--- /dev/null
+++ b/src/core/bound3.rs
@@ -0,0 +1,69 @@
+//! Implements 3d axis aligned bounding box
+use crate::{Number, Float};
+use super::vector3::Vector3;
+use crate::core::{min, max};
+use crate::core::Ray;
+
+#[derive(Clone)]
+pub struct Bound3<T: Number> {
+ pub min: Vector3<T>,
+ pub max: Vector3<T>
+}
+
+pub type Bound3f = Bound3<Float>;
+
+impl<T: Number> Bound3<T> {
+ /// Creates a bound from two points
+ pub fn new(p0: Vector3<T>, p1: Vector3<T>) -> Self {
+ // Elliminate some code duplication here
+ let min = Vector3::new_xyz(
+ min(p0.x, p1.x),
+ min(p0.y, p1.y),
+ min(p0.z, p1.z)
+ );
+ let max = Vector3::new_xyz(
+ max(p0.x, p1.x),
+ max(p0.y, p1.y),
+ max(p0.z, p1.z)
+ );
+
+ Self {min, max}
+ }
+}
+
+impl Bound3f {
+ /// Calculate whether there is a intersect between a bounding box and a ray
+ ///
+ /// # Examples:
+ ///
+ /// ```
+ /// use rendering::core::{Bound3f, Vector3f, Ray};
+ /// let b = Bound3f::new(Vector3f::new(7.0), Vector3f::new(10.0));
+ /// let r1 = Ray::new_to(Vector3f::new(0.0), Vector3f::new(5.0));
+ /// let r3 = Ray::new(Vector3f::new_xyz(-1.0, 0.0, 0.0), Vector3f::new_xyz(1.0, 0.0, 0.0));
+ ///
+ /// assert!(b.intersect(&r1));
+ /// assert!(!b.intersect(&r3));
+ /// ```
+ pub fn intersect(&self, ray: &Ray) -> bool {
+ // Method stolen from Ray tracing the next week.
+ // They mention its from pixar
+ for i in 0..3 {
+ let inv = 1.0 / ray.direction[i];
+ let mut t0 = (self.min[i] - ray.origin[i]) * inv;
+ let mut t1 = (self.max[i] - ray.origin[i]) * inv;
+
+ if inv < 0.0 {
+ let tmp = t0;
+ t0 = t1;
+ t1 = tmp;
+ }
+
+ if t1 <= t0 {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/src/core/mod.rs b/src/core/mod.rs
index c2b3771..07793ec 100644
--- a/src/core/mod.rs
+++ b/src/core/mod.rs
@@ -2,16 +2,33 @@
//!
//! Also creates a shortcut for some common types
-pub mod vector2;
-pub mod vector3;
-pub mod bound;
-pub mod spectrum;
-pub mod hittable;
+mod vector2;
+mod vector3;
+mod bound2;
+mod bound3;
+mod spectrum;
mod ray;
pub use vector2::{Vector2i, Vector2f};
pub use vector3::Vector3f;
-pub use bound::{Bound2i, Bound2f};
+pub use bound2::{Bound2i, Bound2f};
+pub use bound3::Bound3f;
pub use spectrum::Spectrum;
pub use ray::Ray;
-pub use hittable::{Hittable, Intersection};
+
+use crate::Number;
+
+fn min<T: Number> (a: T, b: T) -> T {
+ if b < a {
+ return b;
+ }
+ a
+}
+
+fn max<T: Number> (a: T, b: T) -> T {
+ if b > a {
+ return b;
+ }
+ a
+}
+
diff --git a/src/core/vector3.rs b/src/core/vector3.rs
index e26c07c..1cc6f60 100644
--- a/src/core/vector3.rs
+++ b/src/core/vector3.rs
@@ -2,7 +2,7 @@
//!
//! Also add more 3d math things needed for shading and 3d calculations.
use crate::{Float, Number, NEAR_ZERO};
-use std::ops::{Mul, Sub, Add, DivAssign, Neg, AddAssign};
+use std::ops::{Mul, Sub, Add, DivAssign, Neg, AddAssign, Index};
use std::fmt;
#[derive(Clone, Copy)]
@@ -107,6 +107,20 @@ impl<T: Number> fmt::Display for Vector3<T> {
}
}
+// Ohh god
+impl<T: Number> Index<u32> for Vector3<T> {
+ type Output = T;
+
+ fn index(&self, i: u32) -> &Self::Output {
+ match i {
+ 0 => &self.x,
+ 1 => &self.y,
+ 2 => &self.z,
+ _ => panic!("index out of bounds: index {} is not possible with 3d vector", i)
+ }
+ }
+}
+
impl Vector3f {
pub const ZERO: Self = Vector3f {x: 0.0, y: 0.0, z: 0.0};
diff --git a/src/main.rs b/src/main.rs
index 07228a0..9ffcac4 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -40,7 +40,7 @@ fn main() {
let mut film = Film::new(res);
{
- let coord = RenderCoord::new(&mut film, Vector2i::new_xy(32, 32), 300);
+ let coord = RenderCoord::new(&mut film, Vector2i::new_xy(32, 32), 100);
coord.run_threaded(&ctx, &mut sampler, 4);
}
diff --git a/src/material/lambertian.rs b/src/material/lambertian.rs
index 8ef5e50..3df6522 100644
--- a/src/material/lambertian.rs
+++ b/src/material/lambertian.rs
@@ -1,5 +1,6 @@
use super::Material;
-use crate::core::{Intersection, Ray, Spectrum};
+use crate::core::{Ray, Spectrum};
+use crate::world::Intersection;
use crate::sample::Sampler;
use std::rc::Rc;
diff --git a/src/material/mod.rs b/src/material/mod.rs
index c939385..7f920e2 100644
--- a/src/material/mod.rs
+++ b/src/material/mod.rs
@@ -1,4 +1,5 @@
-use crate::core::{Ray, Intersection, Spectrum};
+use crate::core::{Ray, Spectrum};
+use crate::world::Intersection;
use crate::sample::Sampler;
mod lambertian;
@@ -7,6 +8,6 @@ mod reflectant;
pub use lambertian::Lambertian;
pub use reflectant::Reflectant;
-pub trait Material {
+pub trait Material: Sync + Send {
fn scatter(&self, ray: &Ray, i: &Intersection, sampler: &mut dyn Sampler) -> Option<(Spectrum, Ray)>;
}
diff --git a/src/material/reflectant.rs b/src/material/reflectant.rs
index f4b110c..dea7aca 100644
--- a/src/material/reflectant.rs
+++ b/src/material/reflectant.rs
@@ -1,5 +1,6 @@
use crate::Float;
-use crate::core::{Ray, Intersection, Spectrum, Vector3f};
+use crate::core::{Ray, Spectrum, Vector3f};
+use crate::world::Intersection;
use super::Material;
use crate::sample::Sampler;
diff --git a/src/trace/mod.rs b/src/trace/mod.rs
index b7de476..d45fb23 100644
--- a/src/trace/mod.rs
+++ b/src/trace/mod.rs
@@ -1,4 +1,4 @@
-use crate::world::Scene;
+use crate::world::{Hittable, Scene};
use crate::core::{Spectrum, Ray, Vector3f};
use crate::sample::Sampler;
@@ -30,8 +30,8 @@ impl NormTracer<'_> {
impl Tracer for NormTracer<'_> {
fn trace(&self, _: &mut dyn Sampler, ray: &Ray) -> Spectrum {
// Trace ray, we dont care about material
- if let Some(si) = self.scn.intersect(ray) {
- let norm = si.i.n * 0.5 + Vector3f::new(0.5);
+ if let Some(i) = self.scn.intersect(ray) {
+ let norm = i.n * 0.5 + Vector3f::new(0.5);
return Spectrum::new_rgb(norm.x, norm.y, norm.z);
}
diff --git a/src/trace/pathtrace.rs b/src/trace/pathtrace.rs
index 47c6835..2d15976 100644
--- a/src/trace/pathtrace.rs
+++ b/src/trace/pathtrace.rs
@@ -1,11 +1,13 @@
-use crate::world::Scene;
+use crate::world::{Hittable, Scene};
use crate::core::{Ray, Spectrum};
use crate::sample::Sampler;
+use crate::material::{Lambertian, Material};
use super::Tracer;
pub struct PathTracer<'a> {
depth: i32,
scn: &'a Scene,
+ default_mat: Box<dyn Material>,
}
impl PathTracer<'_> {
@@ -15,6 +17,7 @@ impl PathTracer<'_> {
PathTracer {
depth,
scn,
+ default_mat: Box::new(Lambertian::new(Spectrum::ZERO)),
}
}
@@ -24,8 +27,8 @@ impl PathTracer<'_> {
return Spectrum::ZERO;
}
- if let Some(si) = self.scn.intersect(ray) {
- if let Some((scalar, nray)) = si.mat.scatter(ray, &si.i, sampler) {
+ if let Some(i) = self.scn.intersect(ray) {
+ if let Some((scalar, nray)) = i.m.unwrap_or(self.default_mat.as_ref()).scatter(ray, &i, sampler) {
return self.trace_recur(sampler, &nray, depth-1) * scalar;
} else {
return Spectrum::ZERO;
diff --git a/src/world/container.rs b/src/world/container.rs
new file mode 100644
index 0000000..6f8c182
--- /dev/null
+++ b/src/world/container.rs
@@ -0,0 +1,43 @@
+use super::{Hittable, Intersection};
+use crate::core::Ray;
+
+pub struct HittableList {
+ elems: Vec<Box<dyn Hittable>>,
+}
+
+impl HittableList {
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ pub fn add(&mut self, h: Box<dyn Hittable>) {
+ self.elems.push(h);
+ }
+}
+
+impl Hittable for HittableList {
+ fn intersect(&self, ray: &Ray) -> Option<Intersection> {
+ let mut min: Option<Intersection> = None;
+
+ for e in self.elems.iter() {
+ if let Some(i) = e.intersect(&ray) {
+ match min {
+ // Do nothing if distance is bigger than min
+ Some(ref min_i) if min_i.t < i.t => {},
+ // If no existing min or closer than
+ _ => min = Some(i),
+ }
+ }
+ }
+
+ min
+ }
+}
+
+impl Default for HittableList {
+ fn default() -> Self {
+ Self {
+ elems: Vec::new(),
+ }
+ }
+}
diff --git a/src/core/hittable.rs b/src/world/hittable.rs
index e495d5b..720a019 100644
--- a/src/core/hittable.rs
+++ b/src/world/hittable.rs
@@ -1,15 +1,17 @@
-use crate::core::{Vector3f, Ray};
+use crate::core::{Vector3f, Bound3f, Ray};
use crate::Float;
+use crate::material::Material;
/// Returns the context of a intersection
-pub struct Intersection {
+pub struct Intersection<'a> {
/// Normal vector at intersection
pub n: Vector3f,
pub p: Vector3f,
pub t: Float,
+ pub m: Option<&'a dyn Material>,
}
-impl Intersection {
+impl Intersection<'_> {
pub fn norm_against_ray(&self, r: &Ray) -> Vector3f {
if self.n.dot(&r.direction) < 0.0 {
self.n
@@ -20,6 +22,12 @@ impl Intersection {
}
/// Defines a common trait for objects in the scene
-pub trait Hittable {
+pub trait Hittable: Sync + Send {
+ /// Returns the intersection with ray
fn intersect(&self, ray: &Ray) -> Option<Intersection>;
+
+ /// Returns the axis alligned bounding box containing self
+ fn bounding_box(&self) -> Option<Bound3f> {
+ None
+ }
}
diff --git a/src/world/mod.rs b/src/world/mod.rs
index f0ba8d2..43f7530 100644
--- a/src/world/mod.rs
+++ b/src/world/mod.rs
@@ -2,11 +2,15 @@
pub mod shapes;
mod scene;
+mod container;
+mod hittable;
pub use scene::*;
+pub use hittable::{Intersection, Hittable};
+pub use container::HittableList;
use std::sync::Arc;
-use crate::core::Hittable;
use crate::material::Material;
+use crate::core::{Bound3f, Ray};
pub struct Object {
pub shape: Box<dyn Hittable + Sync>,
@@ -21,3 +25,13 @@ impl Object {
}
}
}
+
+impl Hittable for Object {
+ fn intersect(&self, ray: &Ray) -> Option<Intersection> {
+ self.shape.intersect(ray).map(|mut i| {i.m = Some(self.mat.as_ref()); i})
+ }
+
+ fn bounding_box(&self) -> Option<Bound3f> {
+ self.shape.bounding_box()
+ }
+}
diff --git a/src/world/scene.rs b/src/world/scene.rs
index 444e915..03578be 100644
--- a/src/world/scene.rs
+++ b/src/world/scene.rs
@@ -1,15 +1,9 @@
-use crate::core::{Ray, Intersection};
-use crate::material::Material;
+use crate::core::Ray;
-use super::Object;
+use super::{Object, HittableList, Hittable, Intersection};
pub struct Scene {
- objs: Vec<Object>,
-}
-
-pub struct SceneIntersect<'a> {
- pub mat: &'a dyn Material,
- pub i: Intersection,
+ content: HittableList,
}
impl Scene {
@@ -18,7 +12,7 @@ impl Scene {
}
pub fn add_object(&mut self, obj: Object) {
- self.objs.push(obj);
+ self.content.add(Box::new(obj));
}
pub fn add_objects(&mut self, objs: Vec<Object>) {
@@ -26,27 +20,18 @@ impl Scene {
self.add_object(obj);
}
}
+}
- pub fn intersect(&self, ray: &Ray) -> Option<SceneIntersect> {
- let mut min: Option<SceneIntersect> = None;
-
- for obj in self.objs.iter() {
- if let Some(i) = obj.shape.intersect(&ray) {
- match min {
- Some(ref si) if si.i.t < i.t => (),
- _ => min = Some(SceneIntersect {i, mat: obj.mat.as_ref() }),
- }
- }
- }
-
- min
+impl Hittable for Scene {
+ fn intersect(&self, ray: &Ray) -> Option<Intersection> {
+ self.content.intersect(ray)
}
}
impl Default for Scene {
fn default() -> Self {
Self {
- objs: Vec::new(),
+ content: HittableList::new(),
}
}
}
diff --git a/src/world/shapes/sphere.rs b/src/world/shapes/sphere.rs
index 8e0816b..55fc8b4 100644
--- a/src/world/shapes/sphere.rs
+++ b/src/world/shapes/sphere.rs
@@ -2,7 +2,8 @@
//!
//! Spheres are relatively easy to calculate intersections between
use crate::Float;
-use crate::core::{Ray, Vector3f, Hittable, Intersection};
+use crate::core::{Ray, Vector3f, Bound3f};
+use crate::world::{Hittable, Intersection};
pub struct Sphere {
radius: Float,
@@ -45,10 +46,31 @@ impl Hittable for Sphere {
n: self.norm_at(&w),
p: w,
t: distance,
+ m: None,
})
}
}
+
+ /// Box containing the circle
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use rendering::core::{Vector3f, Hittable};
+ /// use rendering::world::shapes::Sphere;
+ ///
+ /// let sph = Sphere::new(1.0, Vector3f::new(0.0));
+ /// let b = sph.bounding_box().unwrap();
+ ///
+ /// assert!(b.min.x == -1.0 && b.min.y == -1.0 && b.min.z == -1.0);
+ /// assert!(b.max.x == 1.0 && b.max.y == 1.0 && b.max.z == 1.0);
+ fn bounding_box(&self) -> Option<Bound3f> {
+ let offset = Vector3f::new(self.radius);
+ Some(
+ Bound3f::new(self.center - offset, self.center + offset)
+ )
+ }
}
#[cfg(test)]