Skip to content

Commit

Permalink
Add Plane support
Browse files Browse the repository at this point in the history
This required a fair bit of faffing, and I'm not completely sure I know
what I'm really doing with the boxing. However! My first polymorphism
and traits. I should probably read the book properly, but who's got time
for that.

Some fun also since rust modulo arithmetic seems to be non-intuitive,
and the euclidean versions aren't stable yet.

https://github.com/rust-lang/rfcs/blob/master/text/2169-euclidean-modulo.md
rust-lang/rust#49048
  • Loading branch information
urbanautomaton committed Apr 22, 2019
1 parent 489fc4c commit 32d66cb
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 14 deletions.
4 changes: 2 additions & 2 deletions src/light/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::vector::Vec;
use crate::object::sphere::Sphere;
use crate::object::Object;
use crate::ray::Ray;

pub struct Light {
Expand All @@ -12,7 +12,7 @@ impl Light {
Light { center, power }
}

pub fn illuminate(&self, point: Vec, normal: Vec, objects: &[Sphere]) -> f64 {
pub fn illuminate(&self, point: Vec, normal: Vec, objects: &[Box<Object>]) -> f64 {
let point_to_light = self.center.subtract(point);
let length = point_to_light.length();

Expand Down
23 changes: 14 additions & 9 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,22 @@ mod camera;

use vector::Vec;
use color::Color;
use object::Object;
use object::sphere::Sphere;
use object::plane::Plane;
use light::Light;
use film::Film;
use camera::Camera;
use ray::Ray;


fn trace(objects: &[Sphere], lights: &[Light], ray: Ray, remaining_calls: u32) -> Option<Color> {
fn trace(objects: &[Box<Object>], lights: &[Light], ray: Ray, remaining_calls: u32) -> Option<Color> {
if remaining_calls <= 0 {
return None;
}

let mut min_t = std::f64::INFINITY;
let mut min_object: Option<&Sphere> = None;
let mut min_object: Option<&Box<Object>> = None;

for object in objects {
if let Some(t) = object.intersect(ray) {
Expand All @@ -44,15 +46,16 @@ fn trace(objects: &[Sphere], lights: &[Light], ray: Ray, remaining_calls: u32) -
acc + light.illuminate(intersection, normal, &objects)
);

let illuminated_color = hit.color.scale(energy).scale(1.0 - hit.reflectance);
let surface_color = hit.color_at(intersection);
let illuminated_color = surface_color.scale(energy).scale(1.0 - hit.reflectance());

let dot = ray.direction.dot(normal);
let reflection_direction = ray.direction.subtract(normal.scale(2.0 * dot));
let reflection_point = intersection.add(normal.scale(1e-10));
let reflection_ray = Ray { origin: reflection_point, direction: reflection_direction };

if let Some(incoming_color) = trace(objects, lights, reflection_ray, remaining_calls - 1) {
let reflection_color = hit.color.scale(hit.reflectance / 255.0);
let reflection_color = surface_color.scale(hit.reflectance() / 255.0);
let reflected_color = Color::new(
incoming_color.r * reflection_color.r,
incoming_color.g * reflection_color.g,
Expand All @@ -76,11 +79,13 @@ fn main() {
let film = Film::new(Vec::new(-0.8, 1.2, 1.3), Vec::new(1.2, -0.3, 1.3));
let camera = Camera { eye, film };

let objects = vec![
Sphere::new(Vec::new(-1.0, 1.0, 5.0), 0.8, Color::new(255.0, 50.0, 50.0), 0.2),
Sphere::new(Vec::new(1.0, 1.0, 5.0), 0.8, Color::new(50.0, 255.0, 100.0), 0.8),
Sphere::new(Vec::new(2.5, 1.0, 5.0), 0.8, Color::new(50.0, 100.0, 255.0), 0.0),
Sphere::new(Vec::new(-1.0, 2.0, 4.0), 0.2, Color::new(220.0, 220.0, 75.0), 0.7),
let objects: std::vec::Vec<Box<Object>> = vec![
Box::new(Sphere::new(Vec::new(-1.0, 1.0, 5.0), 0.8, Color::new(255.0, 50.0, 50.0), 0.2)),
Box::new(Sphere::new(Vec::new(1.0, 1.0, 5.0), 0.8, Color::new(50.0, 255.0, 100.0), 0.8)),
Box::new(Sphere::new(Vec::new(2.5, 1.0, 5.0), 0.8, Color::new(50.0, 100.0, 255.0), 0.0)),
Box::new(Sphere::new(Vec::new(-1.0, 2.0, 4.0), 0.2, Color::new(220.0, 220.0, 75.0), 0.7)),

Box::new(Plane::new(Vec::new(0.0, -1.0, 0.0), Vec::new(0.0, 1.0, 0.0), Color::new(100.0, 100.0, 100.0), 0.0)),
];

let lights = vec![
Expand Down
12 changes: 12 additions & 0 deletions src/object/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,13 @@
pub mod sphere;
pub mod plane;

use crate::vector::Vec;
use crate::ray::Ray;
use crate::color::Color;

pub trait Object {
fn intersect(&self, ray: Ray) -> Option<f64>;
fn surface_normal(&self, point: Vec) -> Vec;
fn color_at(&self, point: Vec) -> Color;
fn reflectance(&self) -> f64;
}
47 changes: 47 additions & 0 deletions src/object/plane/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use crate::object::Object;
use crate::vector::Vec;
use crate::ray::Ray;
use crate::color::Color;

#[derive(Debug)]
pub struct Plane {
point: Vec,
normal: Vec,
pub color: Color,
pub reflectance: f64,
}

impl Plane {
pub fn new(point: Vec, normal: Vec, color: Color, reflectance: f64) -> Self {
Self { point, normal, color, reflectance }
}
}

impl Object for Plane {
fn intersect(&self, ray: Ray) -> Option<f64> {
let ndotl = self.normal.dot(ray.direction);

if ndotl.abs() < 1e-10 {
None
} else {
let t = self.normal.dot(self.point.subtract(ray.origin)) / ndotl;
if t < 0.0 { None } else { Some(t) }
}
}

fn surface_normal(&self, _point: Vec) -> Vec {
self.normal
}

fn color_at(&self, point: Vec) -> Color {
if (point.x.round() + point.z.round()).abs() % 2.0 < 1e-10 {
Color::new(10.0, 10.0, 10.0)
} else {
self.color
}
}

fn reflectance(&self) -> f64 {
self.reflectance
}
}
17 changes: 14 additions & 3 deletions src/object/sphere/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::object::Object;
use crate::vector::Vec;
use crate::ray::Ray;
use crate::color::Color;

#[derive(Debug, Copy, Clone, PartialEq)]
#[derive(Debug, PartialEq)]
pub struct Sphere {
center: Vec,
radius: f64,
Expand All @@ -14,8 +15,10 @@ impl Sphere {
pub fn new(center: Vec, radius: f64, color: Color, reflectance: f64) -> Self {
Self { center, radius, color, reflectance }
}
}

pub fn intersect(&self, ray: Ray) -> Option<f64> {
impl Object for Sphere {
fn intersect(&self, ray: Ray) -> Option<f64> {
let oc = ray.origin.subtract(self.center);
let dot = ray.direction.normalize().dot(oc);

Expand All @@ -39,9 +42,17 @@ impl Sphere {
}
}

pub fn surface_normal(&self, point: Vec) -> Vec {
fn surface_normal(&self, point: Vec) -> Vec {
point.subtract(self.center).normalize()
}

fn color_at(&self, _point: Vec) -> Color {
self.color
}

fn reflectance(&self) -> f64 {
self.reflectance
}
}

#[cfg(test)]
Expand Down

0 comments on commit 32d66cb

Please sign in to comment.