diff --git a/output.png b/output.png index 99d9e35..1a8aacc 100644 Binary files a/output.png and b/output.png differ diff --git a/src/lib.rs b/src/lib.rs index 1d1914f..ba6731a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -185,6 +185,13 @@ impl Ray { dir: self.dir, } } + + pub fn reflected(&self, position: Vector, normal: &UnitVector) -> Ray { + Ray { + pos: position, + dir: self.dir.reflected(normal), + } + } } impl AlmostEqual for Ray { @@ -336,7 +343,13 @@ pub fn posunit_to_unit(value: f32) -> f32 { pub struct Radians(pub f32); -pub fn render(spheres: &[Sphere], camera: &Camera, width: usize, height: usize) -> Image { +pub fn render( + spheres: &[Sphere], + camera: &Camera, + width: usize, + height: usize, + bounces: usize, +) -> Image { let mut image = Image::new(width, height); for i in 0..width { for j in 0..height { @@ -345,19 +358,33 @@ pub fn render(spheres: &[Sphere], camera: &Camera, width: usize, height: usize) i as f32 / (width - 1) as f32, j as f32 / (height - 1) as f32, ); - let color = match closest_intersection(&spheres, &ray) { - None => Color::new_black(), - Some(intersection) => { - let brightness = intersection.normal.0.dot(&-ray.dir.0); - intersection.sphere.color * brightness - } - }; + let color = trace_ray(&spheres, &ray, bounces); image.set_color(i, j, color); } } image } +pub fn trace_ray(spheres: &[Sphere], ray: &Ray, bounces: usize) -> Color { + match closest_intersection(&spheres, &ray) { + None => Color::new_black(), + Some(intersection) => { + let brightness = intersection.normal.0.dot(&-ray.dir.0); + + let mut color = intersection.sphere.color; + if bounces > 0 { + color = color + + trace_ray( + &spheres, + &ray.reflected(intersection.position, &intersection.normal), + bounces - 1, + ); + } + color * brightness + } + } +} + pub fn closest_intersection<'a>(spheres: &'a [Sphere], ray: &Ray) -> Option> { let mut closest_hit = None; let mut closest_hit_distance = f32::MAX; diff --git a/src/main.rs b/src/main.rs index 70837a9..0b6477f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -56,6 +56,6 @@ fn main() { aspect_ratio: 4.0 / 3.0, fovx: Radians(90.0f32.to_radians()), }; - let image = render(&spheres, &camera, 800, 600); + let image = render(&spheres, &camera, 800, 600, 3); image_to_file(&image, &mut file); } diff --git a/tests/lib.rs b/tests/lib.rs index 8469d60..0a2ca7e 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,6 +1,6 @@ use raytracer::{ - closest_intersection, image_to_file, AlmostEqual, Camera, Color, Image, Intersection, Radians, - Ray, Sphere, UnitVector, Vector, + closest_intersection, image_to_file, trace_ray, AlmostEqual, Camera, Color, Image, + Intersection, Radians, Ray, Sphere, UnitVector, Vector, }; use std::str; @@ -450,3 +450,48 @@ fn test_color_addition() { ); assert_almost_eq!(Color::new_red() + Color::new_red(), Color::new_red()); } + +#[test] +fn test_trace_ray() { + let spheres = [ + Sphere { + center: Vector { + x: 2.0, + y: 1.0, + z: 1.0, + }, + radius: 1.0, + color: Color::new_red(), + }, + Sphere { + center: Vector { + x: 4.0, + y: 4.0, + z: 1.0, + }, + radius: 1.0, + color: Color::new_green(), + }, + ]; + let ray = Ray { + pos: Vector { + x: 1.0, + y: 3.0, + z: 1.0, + }, + dir: Vector { + x: 1.0, + y: -1.0, + z: 0.0, + } + .normalized(), + }; + assert_almost_eq!( + trace_ray(&spheres, &ray, 0), + 45.0f32.to_radians().cos() * Color::new_red(), + ); + assert_almost_eq!( + trace_ray(&spheres, &ray, 1), + 45.0f32.to_radians().cos() * (Color::new_red() + Color::new_green()), + ); +}