aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian T <julian@jtle.dk>2021-02-07 14:25:06 +0100
committerJulian T <julian@jtle.dk>2021-02-07 14:26:20 +0100
commit33e747fa1c0957546c10e4d7b490ac7fbb0fd2d2 (patch)
tree515b2b2e017329cbcada60e898fb5795fbceb5cd
parenta43ab40a75e9d4c6ee3fbdd583bc93574db19124 (diff)
Abstracted lambertian into material
-rw-r--r--src/core/hittable.rs23
-rw-r--r--src/core/mod.rs2
-rw-r--r--src/core/spectrum.rs12
-rw-r--r--src/core/vector3.rs11
-rw-r--r--src/lib.rs2
-rw-r--r--src/main.rs18
-rw-r--r--src/material/lambertian.rs37
-rw-r--r--src/material/mod.rs10
-rw-r--r--src/scene/mod.rs29
-rw-r--r--src/scene/scene.rs28
-rw-r--r--src/scene/shapes/sphere.rs3
-rw-r--r--src/trace/mod.rs6
-rw-r--r--src/trace/pathtrace.rs9
13 files changed, 141 insertions, 49 deletions
diff --git a/src/core/hittable.rs b/src/core/hittable.rs
new file mode 100644
index 0000000..20c82f7
--- /dev/null
+++ b/src/core/hittable.rs
@@ -0,0 +1,23 @@
+use crate::core::{Vector3f, Ray};
+
+/// Returns the context of a intersection
+pub struct Intersection {
+ /// Normal vector at intersection
+ pub n: Vector3f,
+ pub p: Vector3f,
+}
+
+impl Intersection {
+ pub fn norm_against_ray(&self, r: &Ray) -> Vector3f {
+ if self.n.dot(&r.direction) < 0.0 {
+ self.n
+ } else {
+ -self.n
+ }
+ }
+}
+
+/// Defines a common trait for objects in the scene
+pub trait Hittable {
+ fn intersect(&self, ray: &Ray) -> Option<Intersection>;
+}
diff --git a/src/core/mod.rs b/src/core/mod.rs
index 5723b2d..c2b3771 100644
--- a/src/core/mod.rs
+++ b/src/core/mod.rs
@@ -6,6 +6,7 @@ pub mod vector2;
pub mod vector3;
pub mod bound;
pub mod spectrum;
+pub mod hittable;
mod ray;
pub use vector2::{Vector2i, Vector2f};
@@ -13,3 +14,4 @@ pub use vector3::Vector3f;
pub use bound::{Bound2i, Bound2f};
pub use spectrum::Spectrum;
pub use ray::Ray;
+pub use hittable::{Hittable, Intersection};
diff --git a/src/core/spectrum.rs b/src/core/spectrum.rs
index 727a1b6..9cca7dc 100644
--- a/src/core/spectrum.rs
+++ b/src/core/spectrum.rs
@@ -41,6 +41,18 @@ impl std::ops::Mul<Float> for Spectrum {
}
}
+impl std::ops::Mul for Spectrum {
+ type Output = Spectrum;
+
+ fn mul(self, op: Self) -> Self::Output {
+ Self::Output::new_rgb(
+ self.c[0] * op.c[0],
+ self.c[1] * op.c[1],
+ self.c[2] * op.c[2],
+ )
+ }
+}
+
impl std::ops::Div<Float> for Spectrum {
type Output = Spectrum;
diff --git a/src/core/vector3.rs b/src/core/vector3.rs
index ef067aa..ff5a678 100644
--- a/src/core/vector3.rs
+++ b/src/core/vector3.rs
@@ -1,7 +1,7 @@
//! Implements 3d vectors
//!
//! Also add more 3d math things needed for shading and 3d calculations.
-use crate::{Float, Number};
+use crate::{Float, Number, NEAR_ZERO};
use std::ops::{Mul, Sub, Add, DivAssign, Neg};
use std::fmt;
@@ -149,4 +149,13 @@ impl Vector3f {
)
}
+
+ /// Check if vector is close to [0, 0, 0]
+ ///
+ /// This is based on the NEAR_ZERO constant
+ pub fn near_zero(&self) -> bool {
+ (self.x.abs() < NEAR_ZERO) &&
+ (self.y.abs() < NEAR_ZERO) &&
+ (self.z.abs() < NEAR_ZERO)
+ }
}
diff --git a/src/lib.rs b/src/lib.rs
index 7da6627..a0af705 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -4,6 +4,7 @@ pub mod render;
pub mod scene;
pub mod trace;
pub mod sample;
+pub mod material;
use std::ops::{Add, Sub, Mul, DivAssign, Neg};
use std::cmp;
@@ -34,3 +35,4 @@ impl Number for f64 {}
pub type Float = f64;
pub const M_PI: Float = PI;
+pub const NEAR_ZERO: Float = 1e-8;
diff --git a/src/main.rs b/src/main.rs
index 1604ae3..759cbf9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,10 +1,13 @@
use pathtrace::camera::{Camera, Film, CameraSettings};
-use pathtrace::scene::Scene;
+use pathtrace::scene::{Scene, Object};
use pathtrace::trace::DefaultTracer;
use pathtrace::scene::shapes::Sphere;
-use pathtrace::core::{Vector2i, Vector3f};
+use pathtrace::core::{Vector2i, Vector3f, Spectrum};
use pathtrace::render::{RenderContext, RenderTask};
use pathtrace::sample::UniformSampler;
+use pathtrace::material::Lambertian;
+
+use std::rc::Rc;
fn main() {
let res = Vector2i::new_xy(500, 500);
@@ -17,10 +20,13 @@ fn main() {
screensize: res,
});
+ let brown = Rc::new(Lambertian::new(Spectrum::new_rgb(0.5, 0.3, 0.0)));
+ let blue = Rc::new(Lambertian::new(Spectrum::new_rgb(0.0, 0.3, 0.7)));
+
let mut scn = Scene::new();
- scn.add_shapes(vec![
- Box::new(Sphere::new(0.5, Vector3f::new_xyz(0.0, 0.0, -1.0))),
- Box::new(Sphere::new(100.0, Vector3f::new_xyz(0.0, -100.5, -1.0))),
+ scn.add_objects(vec![
+ Object::new(blue.clone(), Box::new(Sphere::new(0.5, Vector3f::new_xyz(0.0, 0.0, -1.0)))),
+ Object::new(brown.clone(), Box::new(Sphere::new(100.0, Vector3f::new_xyz(0.0, -100.5, -1.0)))),
]);
let tracer = DefaultTracer::new(&scn, None);
@@ -32,7 +38,7 @@ fn main() {
let mut film = Film::new(res);
let tile = film.get_tile(&film.frame);
- let mut task = RenderTask::new(Box::new(tile), 100);
+ let mut task = RenderTask::new(Box::new(tile), 10);
task.render(&ctx, &mut sampler);
film.commit_tile(&task.tile);
diff --git a/src/material/lambertian.rs b/src/material/lambertian.rs
new file mode 100644
index 0000000..28d15d3
--- /dev/null
+++ b/src/material/lambertian.rs
@@ -0,0 +1,37 @@
+use super::Material;
+use crate::core::{Intersection, Ray, Spectrum};
+use crate::sample::Sampler;
+
+use std::rc::Rc;
+
+pub struct Lambertian {
+ color: Spectrum,
+}
+
+impl Lambertian {
+ pub fn new(c: Spectrum) -> Lambertian {
+ Lambertian {
+ color: c,
+ }
+ }
+
+ pub fn new_rc(c: Spectrum) -> Rc<dyn Material> {
+ Rc::new(Self::new(c))
+ }
+}
+
+impl Material for Lambertian {
+ fn scatter(&self, ray: &Ray, i: &Intersection, sampler: &mut dyn Sampler) -> Option<(Spectrum, Ray)> {
+ let mut newray = Ray {
+ origin: i.p,
+ direction: i.n + sampler.get_unit_vector(),
+ };
+
+ // Make sure that the resulting direction is not (0, 0, 0)
+ if newray.direction.near_zero() {
+ newray.direction = i.n;
+ }
+
+ Some((self.color.clone(), newray))
+ }
+}
diff --git a/src/material/mod.rs b/src/material/mod.rs
new file mode 100644
index 0000000..62cbf9a
--- /dev/null
+++ b/src/material/mod.rs
@@ -0,0 +1,10 @@
+use crate::core::{Ray, Intersection, Spectrum};
+use crate::sample::Sampler;
+
+mod lambertian;
+
+pub use lambertian::Lambertian;
+
+pub trait Material {
+ fn scatter(&self, ray: &Ray, i: &Intersection, sampler: &mut dyn Sampler) -> Option<(Spectrum, Ray)>;
+}
diff --git a/src/scene/mod.rs b/src/scene/mod.rs
index f8c1e09..cd07236 100644
--- a/src/scene/mod.rs
+++ b/src/scene/mod.rs
@@ -6,27 +6,20 @@ pub mod shapes;
mod scene;
pub use scene::*;
-use crate::core::{Ray, Vector3f};
-use crate::Float;
+use std::rc::Rc;
+use crate::core::Hittable;
+use crate::material::Material;
-/// Returns the context of a intersection
-pub struct Intersection {
- /// Normal vector at intersection
- pub n: Vector3f,
- pub p: Vector3f,
+pub struct Object {
+ pub shape: Box<dyn Hittable>,
+ pub mat: Rc<dyn Material>,
}
-impl Intersection {
- pub fn norm_against_ray(&self, r: &Ray) -> Vector3f {
- if self.n.dot(&r.direction) < 0.0 {
- self.n
- } else {
- -self.n
+impl Object {
+ pub fn new(mat: Rc<dyn Material>, shape: Box<dyn Hittable>) -> Self {
+ Object {
+ mat,
+ shape,
}
}
}
-
-/// Defines a common trait for objects in the scene
-pub trait Hittable {
- fn intersect(&self, ray: &Ray) -> Option<Intersection>;
-}
diff --git a/src/scene/scene.rs b/src/scene/scene.rs
index 367003e..0ffbe97 100644
--- a/src/scene/scene.rs
+++ b/src/scene/scene.rs
@@ -1,33 +1,33 @@
-use super::{Intersection, Hittable};
-use crate::core::Ray;
+use crate::core::{Ray, Intersection, Hittable};
+use crate::material::Material;
-type Shape = Box<dyn Hittable>;
+use super::Object;
pub struct Scene {
- shps: Vec<Shape>,
+ objs: Vec<Object>,
}
impl Scene {
pub fn new() -> Self {
Self {
- shps: Vec::new(),
+ objs: Vec::new(),
}
}
- pub fn add_shape(&mut self, shp: Shape) {
- self.shps.push(shp);
+ pub fn add_object(&mut self, obj: Object) {
+ self.objs.push(obj);
}
- pub fn add_shapes(&mut self, shps: Vec<Shape>) {
- for shp in shps {
- self.add_shape(shp);
+ pub fn add_objects(&mut self, objs: Vec<Object>) {
+ for obj in objs {
+ self.add_object(obj);
}
}
- pub fn intersect(&self, ray: &Ray) -> Option<Intersection> {
- for shp in self.shps.iter() {
- if let Some(i) = shp.intersect(&ray) {
- return Some(i)
+ pub fn intersect(&self, ray: &Ray) -> Option<(&dyn Material, Intersection)> {
+ for obj in self.objs.iter() {
+ if let Some(i) = obj.shape.intersect(&ray) {
+ return Some((obj.mat.as_ref(), i))
}
}
diff --git a/src/scene/shapes/sphere.rs b/src/scene/shapes/sphere.rs
index acca2b7..3f07b9e 100644
--- a/src/scene/shapes/sphere.rs
+++ b/src/scene/shapes/sphere.rs
@@ -2,8 +2,7 @@
//!
//! Spheres are relatively easy to calculate intersections between
use crate::Float;
-use crate::core::{Ray, Vector3f};
-use crate::scene::{Hittable, Intersection};
+use crate::core::{Ray, Vector3f, Hittable, Intersection};
pub struct Sphere {
radius: Float,
diff --git a/src/trace/mod.rs b/src/trace/mod.rs
index 7f02f6f..cf06246 100644
--- a/src/trace/mod.rs
+++ b/src/trace/mod.rs
@@ -28,9 +28,9 @@ impl NormTracer<'_> {
}
impl Tracer for NormTracer<'_> {
- fn trace(&self, sampler: &mut dyn Sampler, ray: &Ray) -> Spectrum {
- // Trace ray
- if let Some(i) = self.scn.intersect(ray) {
+ fn trace(&self, _: &mut dyn Sampler, ray: &Ray) -> Spectrum {
+ // Trace ray, we dont care about material
+ 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 32a8e15..a3a688a 100644
--- a/src/trace/pathtrace.rs
+++ b/src/trace/pathtrace.rs
@@ -20,11 +20,10 @@ impl PathTracer<'_> {
pub fn trace_recur(&self, sampler: &mut dyn Sampler, ray: &Ray) -> Spectrum {
- if let Some(i) = self.scn.intersect(ray) {
- // Get a random direction in the hemisphere a i.p
- // This is Lambertian reflection
- let target = i.p + i.n + sampler.get_unit_vector();
- return self.trace_recur(sampler, &Ray::new_to(i.p, target)) * 0.5;
+ if let Some((mat, i)) = self.scn.intersect(ray) {
+ if let Some((scalar, nray)) = mat.scatter(ray, &i, sampler) {
+ return self.trace_recur(sampler, &nray) * scalar;
+ }
}
// Simulates a sky