diff options
author | Julian T <julian@jtle.dk> | 2021-02-21 18:01:56 +0100 |
---|---|---|
committer | Julian T <julian@jtle.dk> | 2021-02-21 18:01:56 +0100 |
commit | da1c3949a449f3fafe579c62ff6b14ffd993a197 (patch) | |
tree | 754df5c9b5e9f0fa0a8bb7a8cd3dd4b12fe5ad89 /src | |
parent | c695da871a75bb6786c08c3546ef71ed032bd61d (diff) |
Add 3d bounding box and merged SceneIntersection and Intersection
Diffstat (limited to 'src')
-rw-r--r-- | src/core/bound2.rs (renamed from src/core/bound.rs) | 17 | ||||
-rw-r--r-- | src/core/bound3.rs | 69 | ||||
-rw-r--r-- | src/core/mod.rs | 31 | ||||
-rw-r--r-- | src/core/vector3.rs | 16 | ||||
-rw-r--r-- | src/main.rs | 2 | ||||
-rw-r--r-- | src/material/lambertian.rs | 3 | ||||
-rw-r--r-- | src/material/mod.rs | 5 | ||||
-rw-r--r-- | src/material/reflectant.rs | 3 | ||||
-rw-r--r-- | src/trace/mod.rs | 6 | ||||
-rw-r--r-- | src/trace/pathtrace.rs | 9 | ||||
-rw-r--r-- | src/world/container.rs | 43 | ||||
-rw-r--r-- | src/world/hittable.rs (renamed from src/core/hittable.rs) | 16 | ||||
-rw-r--r-- | src/world/mod.rs | 16 | ||||
-rw-r--r-- | src/world/scene.rs | 33 | ||||
-rw-r--r-- | src/world/shapes/sphere.rs | 24 |
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)] |