From ed6e84a240a1d5bd3fb46412ccba9c2af4d40571 Mon Sep 17 00:00:00 2001 From: bijan2005 Date: Sat, 23 Jan 2021 22:52:50 -0500 Subject: [PATCH] Add Light trait and PointLight instance --- README.md | 6 ++--- src/camera.rs | 2 +- src/main.rs | 19 ++-------------- src/object.rs | 26 +++++++--------------- src/object/bound.rs | 4 ++-- src/object/plane.rs | 2 +- src/object/pointlight.rs | 36 ++++++++++++++++++++++++++++++ src/object/triangle.rs | 4 ++-- src/render.rs | 25 +++++++++++++++++++++ src/types.rs | 47 ++++++++++++++++++++++++++++++++++++---- 10 files changed, 123 insertions(+), 48 deletions(-) create mode 100644 src/object/pointlight.rs create mode 100644 src/render.rs diff --git a/README.md b/README.md index 99de8d4..6581e31 100644 --- a/README.md +++ b/README.md @@ -28,11 +28,11 @@ This list may be changed or extended in the future. - [x] Bounding spheres - [ ] Direct lighting - [ ] Point light sources - - [ ] Point source struct - - [ ] Point source illuminance test + - [x] Point source struct + - [x] Point source illuminance test - [ ] Hard shadows - [ ] Soft shadows - - [ ] Light-emitting surfaces + - [ ] ~~Light-emitting surfaces~~ - [ ] Indirect lighting - [ ] Reflection - [ ] Perfectly reflective objects diff --git a/src/camera.rs b/src/camera.rs index 2c14c0d..c269cc0 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,5 +1,5 @@ - extern crate nalgebra as na; + use na::*; use na::geometry::{Point2, Point3}; diff --git a/src/main.rs b/src/main.rs index 8393f05..8c0d281 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,5 @@ extern crate nalgebra as na; -use std::cmp::Ordering; use std::fs::File; use std::io::Write; @@ -10,22 +9,7 @@ use na::geometry::Point3; mod camera; use camera::*; mod types; use types::*; mod object; use object::*; - -fn trace(ray: Ray, objects: &Vec) -> Option<(&Object, f32)> { - objects.iter() - .filter_map(|obj| obj.intersect(ray) - .map(|x| (obj, x))) - .min_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(Ordering::Equal)) -} - -fn cast_ray(ray: Ray, scene: &Scene) -> Color { - if let Some((obj, dist)) = trace(ray, &scene.objects) { - let point = ray.project(dist); - - obj.getcolor(point) - } - else { scene.background } -} +mod render; use render::*; fn render(camera: &Camera, scene: &Scene, filename: &str) -> std::io::Result<()> { let width = camera.image_size.x; @@ -58,6 +42,7 @@ fn main() -> std::io::Result<()> { objects: vec![ Object::new(TriangleMesh::singleton(Point3::new(-1.0, -1.0, 2.0), Point3::new(0.0, 1.0, 2.0), Point3::new(1.0, -1.0, 2.0), |t, u, v| Color::new(t, u, v))) ], + lights: Vec::new(), background: Color::black() }; diff --git a/src/object.rs b/src/object.rs index 0e82a52..eaa73dd 100644 --- a/src/object.rs +++ b/src/object.rs @@ -3,6 +3,7 @@ mod sphere; pub use sphere::*; mod plane; pub use plane::*; mod triangle; pub use triangle::*; mod bound; pub use bound::*; +mod pointlight; pub use pointlight::*; use na::*; @@ -33,24 +34,7 @@ pub struct Object { bound: Bound } -#[allow(dead_code)] impl Object { - // Creates a new object with a custom bounding sphere. - pub fn new_(surface: impl 'static + Surface, center: Point3, radius: f32) -> Self { - Object { - surface: Box::new(surface), - bound: Bound { center: center, radius: radius, bypass: false } - } - } - - // Creates a new object with no bounding sphere. - pub fn new_boundless(surface: impl 'static + Surface) -> Self { - Object { - surface: Box::new(surface), - bound: Bound::bypass() - } - } - // Creates a new object with the default bounding sphere. pub fn new(surface: impl 'static + Surface) -> Self { let bound = surface.bound(); @@ -70,9 +54,15 @@ impl Object { pub fn getcolor(&self, point: Point3) -> Color { self.surface.getcolor(point) } } +pub trait Light { + // Determine if the light is able to illuminate the point. + // If so, return the color of the light. + fn illuminate(&self, point: Point3, objects: &Vec) -> Option; +} + pub struct Scene { pub objects: Vec, - + pub lights: Vec>, pub background: Color } diff --git a/src/object/bound.rs b/src/object/bound.rs index 7988305..04db378 100644 --- a/src/object/bound.rs +++ b/src/object/bound.rs @@ -1,6 +1,6 @@ extern crate nalgebra as na; -use na::distance; +// use na::distance; use na::geometry::Point3; use crate::types::Ray; @@ -24,7 +24,7 @@ impl Bound { l.norm_squared() >= self.radius * self.radius } - pub fn contains(&self, point: &Point3) -> bool { distance(&self.center, point) < self.radius } + // pub fn contains(&self, point: &Point3) -> bool { distance(&self.center, point) < self.radius } pub fn bypass() -> Self { Bound { center: Point3::origin(), radius: 0.0, bypass: true } } } diff --git a/src/object/plane.rs b/src/object/plane.rs index 309c353..87f6a52 100644 --- a/src/object/plane.rs +++ b/src/object/plane.rs @@ -67,7 +67,7 @@ impl Surface for Plane { fn intersect(&self, ray: Ray) -> Option { let d = self.normal.dot(&ray.direction); - if d < 1e-5 { return None; } + if d < 1e-3 { return None; } let t = (self.center - ray.origin).dot(&*self.normal) / d; diff --git a/src/object/pointlight.rs b/src/object/pointlight.rs new file mode 100644 index 0000000..607fb02 --- /dev/null +++ b/src/object/pointlight.rs @@ -0,0 +1,36 @@ +extern crate nalgebra as na; + +use na::*; +use na::geometry::Point3; + +use crate::types::*; +use super::*; + +pub struct PointLight { + pub pos: Point3, + pub color: Color +} + +impl PointLight { + pub fn new(pos: Point3, color: Color) -> PointLight { + PointLight { + pos: pos, + color: color + } + } + + fn check_point(&self, point: Point3, objects: &Vec) -> bool { + let max_d = distance(&self.pos, &point); + objects.iter() + .filter_map(|obj| obj.intersect(Ray::from_points(self.pos, point))) + .all(|d| d > max_d) + } +} + +impl Light for PointLight { + fn illuminate(&self, point: Point3, objects: &Vec) -> Option { + if self.check_point(point, objects) { + Some(self.color) + } else { None } + } +} diff --git a/src/object/triangle.rs b/src/object/triangle.rs index bface56..a759dc6 100644 --- a/src/object/triangle.rs +++ b/src/object/triangle.rs @@ -57,7 +57,7 @@ impl Triangle { let p_vect = ray.direction.cross(&vect3_1); let det = p_vect.dot(&vect2_1); - if det.abs() < 1e-5 { return None; } + if det.abs() < 1e-3 { return None; } let t_vect = ray.origin - self.vertex1(vertices); let u = t_vect.dot(&p_vect) / det; @@ -238,7 +238,7 @@ impl Surface for TriangleMesh { let (center, radius) = smallest_sphere(points, Vec::new()); - Bound { center: center, radius: radius + 0.01, bypass: false } + Bound { center: center, radius: radius + 1e-3, bypass: false } } } diff --git a/src/render.rs b/src/render.rs new file mode 100644 index 0000000..1867e3a --- /dev/null +++ b/src/render.rs @@ -0,0 +1,25 @@ +extern crate nalgebra as na; + +use std::cmp::Ordering; + +use na::*; +use na::geometry::Point3; + +use crate::types::*; +use crate::object::*; + +fn trace(ray: Ray, objects: &Vec) -> Option<(&Object, f32)> { + objects.iter() + .filter_map(|obj| obj.intersect(ray) + .map(|x| (obj, x))) + .min_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(Ordering::Equal)) +} + +pub fn cast_ray(ray: Ray, scene: &Scene) -> Color { + if let Some((obj, dist)) = trace(ray, &scene.objects) { + let point = ray.project(dist); + + obj.getcolor(point) + } + else { scene.background } +} diff --git a/src/types.rs b/src/types.rs index 0546647..2e97a97 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,4 +1,7 @@ extern crate nalgebra as na; + +use std::ops::{Add, Mul}; + use na::*; use na::geometry::Point3; @@ -16,7 +19,7 @@ impl Ray { } } pub fn new(origin: Point3, direction: Vector3) -> Self { Ray::from_parts(origin, Unit::new_normalize(direction)) } - pub fn from_points(a: Point3, b: Point3) -> Self { Ray::new(a, b - a) } + pub fn from_points(origin: Point3, points_to: Point3) -> Self { Ray::new(origin, points_to - origin) } pub fn project(&self, t: f32) -> Point3 { self.origin + t * self.direction.into_inner() } } @@ -34,9 +37,9 @@ pub struct Color { impl Color { pub fn new(red: f32, green: f32, blue: f32) -> Self { Color { - red: clamp(red, 0.0, 1.0), - green: clamp(green, 0.0, 1.0), - blue: clamp(blue, 0.0, 1.0), + red: if red < 0.0 { 0.0 } else { red }, + green: if green < 0.0 { 0.0 } else { green }, + blue: if blue < 0.0 { 0.0 } else { blue }, _private: () } @@ -54,3 +57,39 @@ impl Color { pub fn black() -> Self { Color::gray(0.0) } pub fn white() -> Self { Color::gray(1.0) } } + +impl Add for Color { + type Output = Color; + fn add(self, rhs: Color) -> Color { + Color { + red: self.red + rhs.red, + green: self.green + rhs.green, + blue: self.blue + rhs.blue, + _private: () + } + } +} + +impl Mul for Color { + type Output = Color; + fn mul(self, rhs: Color) -> Color { + Color { + red: self.red * rhs.red, + green: self.green * rhs.green, + blue: self.blue * rhs.blue, + _private: () + } + } +} + +impl Mul for Color { + type Output = Color; + fn mul(self, rhs: f32) -> Color { + Color { + red: self.red * rhs, + green: self.green * rhs, + blue: self.blue * rhs, + _private: () + } + } +}