Support multiple spheres.

This commit is contained in:
Bill Thiede 2018-08-12 20:58:28 -07:00
parent 277c6ef60b
commit a1de85036d
3 changed files with 96 additions and 22 deletions

View File

@ -7,7 +7,6 @@ pub mod vector;
use image::GenericImage;
use image::Pixel;
use rendering::Intersectable;
use scene::Scene;
pub fn render(scene: &Scene) -> image::DynamicImage {
@ -17,8 +16,8 @@ pub fn render(scene: &Scene) -> image::DynamicImage {
for y in 0..scene.height {
let ray = rendering::Ray::create_prime(x, y, scene);
if scene.sphere.intersect(&ray) {
let c = &scene.sphere.color;
if let Some(intersection) = scene.trace(&ray) {
let c = &intersection.object.color;
image.put_pixel(x, y, image::Rgba(c.to_rgba()))
} else {
image.put_pixel(x, y, black);
@ -38,19 +37,60 @@ fn test_can_render_scene() {
width: 800,
height: 600,
fov: 90.0,
sphere: Sphere {
center: Point {
x: 0.0,
y: 0.0,
z: -5.0,
spheres: vec![
Sphere {
center: Point {
x: 2.0,
y: 1.0,
z: -4.0,
},
radius: 1.5,
color: Color {
red: 1.0,
green: 0.2,
blue: 0.2,
},
},
radius: 1.0,
color: Color {
red: 0.4,
green: 1.0,
blue: 0.4,
Sphere {
center: Point {
x: 0.0,
y: 0.0,
z: -5.0,
},
radius: 1.0,
color: Color {
red: 0.2,
green: 1.0,
blue: 0.2,
},
},
},
Sphere {
center: Point {
x: -3.0,
y: 1.0,
z: -6.0,
},
radius: 2.0,
color: Color {
red: 0.2,
green: 0.2,
blue: 1.0,
},
},
Sphere {
center: Point {
x: 2.0,
y: 1.0,
z: -10.0,
},
radius: 5.0,
color: Color {
red: 1.0,
green: 1.0,
blue: 1.0,
},
},
],
};
let img: image::DynamicImage = render(&scene);

View File

@ -4,7 +4,7 @@ use scene::Scene;
use vector::Vector3;
pub trait Intersectable {
fn intersect(&self, ray: &Ray) -> bool;
fn intersect(&self, ray: &Ray) -> Option<f32>;
}
pub struct Sphere {
@ -14,16 +14,28 @@ pub struct Sphere {
}
impl Intersectable for Sphere {
fn intersect(&self, ray: &Ray) -> bool {
fn intersect(&self, ray: &Ray) -> Option<f32> {
//Create a line segment between the ray origin and the center of the sphere
let l: Vector3 = (self.center - ray.origin).into();
//Use l as a hypotenuse and find the length of the adjacent side
let adj2 = l.dot(&ray.direction);
let adj = l.dot(&ray.direction);
//Find the length-squared of the opposite side
//This is equivalent to (but faster than) (l.length() * l.length()) - (adj2 * adj2)
let d2 = l.dot(&l) - (adj2 * adj2);
//If that length-squared is less than radius squared, the ray intersects the sphere
d2 < (self.radius * self.radius)
//This is equivalent to (but faster than) (l.length() * l.length()) - (adj * adj)
let d2 = l.dot(&l) - (adj * adj);
let radius2 = self.radius * self.radius;
if d2 > radius2 {
return None;
}
let thc = (radius2 - d2).sqrt();
let t0 = adj - thc;
let t1 = adj + thc;
if t0 < 0.0 && t1 < 0.0 {
return None;
}
let distance = if t0 < t1 { t0 } else { t1 };
Some(distance)
}
}

View File

@ -1,5 +1,18 @@
use rendering::Intersectable;
use rendering::Ray;
use rendering::Sphere;
pub struct Intersection<'a> {
pub distance: f32,
pub object: &'a Sphere,
}
impl<'a> Intersection<'a> {
pub fn new(distance: f32, object: &Sphere) -> Intersection {
Intersection { distance, object }
}
}
pub struct Color {
pub red: f32,
pub green: f32,
@ -22,5 +35,14 @@ pub struct Scene {
pub width: u32,
pub height: u32,
pub fov: f32,
pub sphere: Sphere,
pub spheres: Vec<Sphere>,
}
impl Scene {
pub fn trace(&self, ray: &Ray) -> Option<Intersection> {
self.spheres
.iter()
.filter_map(|s| s.intersect(ray).map(|d| Intersection::new(d, s)))
.min_by(|i1, i2| i1.distance.partial_cmp(&i2.distance).unwrap())
}
}