diff --git a/src/camera.rs b/src/camera.rs index 082cc91..1bfcd6b 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,6 +1,7 @@ -use nalgebra::{convert, RealField, Vector3}; +use nalgebra::{clamp, convert, RealField, Vector3}; use super::image::OutputImage; +use super::integrators::{DirectionalLight, Integrator, PhongIntegrator}; use super::raycasting::Ray; use super::scene::Scene; @@ -59,12 +60,22 @@ impl ImageSampler { } } -pub fn render_scene(output_image: &mut OutputImage, scene: &Scene) { +pub fn render_scene(output_image: &mut OutputImage, scene: &Scene) +where + f32: From, +{ let image_sampler = ImageSampler::new( output_image.get_width(), output_image.get_height(), scene.camera_location, ); + let integrator = PhongIntegrator:: { + ambient_light: convert(0.1), + lights: vec![DirectionalLight { + direction: Vector3::new(convert(1.0), convert(1.0), convert(-1.0)).normalize(), + intensity: convert(0.3), + }], + }; for column in 0..output_image.get_width() { for row in 0..output_image.get_height() { let ray = image_sampler.ray_for_pixel(row, column); @@ -79,9 +90,10 @@ pub fn render_scene(output_image: &mut OutputImage, scene: &Scene< }, ); let gray = match hit { - None => 0, - Some(_) => 255, + None => convert(0.0), + Some(intersection_info) => integrator.integrate(&intersection_info), }; + let gray = f32::from(clamp(gray * convert(255.0), convert(0.0), convert(255.0))) as u8; output_image.set_color(row, column, gray, gray, gray); } } diff --git a/src/lib.rs b/src/lib.rs index e06ad18..b8366fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,3 +2,4 @@ pub mod raycasting; pub mod image; pub mod camera; pub mod scene; +pub mod integrators; diff --git a/src/raycasting.rs b/src/raycasting.rs index 613ed06..dcb52fc 100644 --- a/src/raycasting.rs +++ b/src/raycasting.rs @@ -23,6 +23,7 @@ impl Ray { pub struct IntersectionInfo { pub distance: T, pub location: Vector3, + pub normal: Vector3, } pub trait Intersect { @@ -44,15 +45,30 @@ impl Intersect for Sphere { fn intersect(&self, ray: &Ray) -> Option> { // t0/p0 is the point on the ray that's closest to the centre of the sphere let t0 = (self.centre - ray.origin).dot(&ray.direction); - let p0 = ray.point_at(t0); - if (self.centre - p0).norm() <= self.radius { - Some(IntersectionInfo { - distance: t0, - location: p0, - }) - } else { - None + let delta_squared = + t0 * t0 - ((ray.origin - self.centre).norm_squared() - self.radius * self.radius); + if delta_squared > self.radius { + //No initersection + return None; } + let delta = delta_squared.sqrt(); + let distance = if (self.centre - ray.origin).norm() <= self.radius { + // radius origin is inside sphere + t0 + delta + } else { + t0 - delta + }; + if distance < T::zero() { + // Sphere is behind ray origin + return None; + }; + let location = ray.point_at(distance); + let normal = (location - self.centre).normalize(); + Some(IntersectionInfo { + distance, + location, + normal, + }) } } @@ -91,6 +107,7 @@ impl Intersect for Plane { Some(IntersectionInfo { distance: t, location: ray.point_at(t), + normal: self.normal, }) } }