diff --git a/rtiow/src/bin/tracer_multiple_spheres.rs b/rtiow/src/bin/tracer_multiple_spheres.rs new file mode 100644 index 0000000..132fcce --- /dev/null +++ b/rtiow/src/bin/tracer_multiple_spheres.rs @@ -0,0 +1,46 @@ +extern crate rtiow; + +use rtiow::hitable::Hit; +use rtiow::hitable_list::HitableList; +use rtiow::ray::Ray; +use rtiow::sphere::Sphere; +use rtiow::vec3::Vec3; + +fn color(r: Ray, world: &Hit) -> Vec3 { + if let Some(rec) = world.hit(r, 0., std::f32::MAX) { + return (rec.normal + 1.) * 0.5; + } + + // No hit, choose color from background. + let unit_direction = r.direction().unit_vector(); + let t = 0.5 * (unit_direction.y + 1.); + Vec3::new(1., 1., 1.) * (1. - t) + Vec3::new(0.5, 0.7, 1.) * t +} + +fn main() -> Result<(), std::io::Error> { + let nx = 200; + let ny = 100; + println!("P3\n{} {}\n255", nx, ny); + let lower_left_corner = Vec3::new(-2., -1., -1.); + let horizontal = Vec3::new(4., 0., 0.); + let vertical = Vec3::new(0., 2., 0.); + let origin: Vec3 = Default::default(); + let objects = vec![ + Sphere::new(Vec3::new(0., 0., -1.), 0.5), + Sphere::new(Vec3::new(0., -100.5, -1.), 100.), + ]; + let world = HitableList::new(objects.iter().map(|o| o).collect()); + for j in (0..ny).rev() { + for i in 0..nx { + let u = i as f32 / nx as f32; + let v = j as f32 / ny as f32; + let r = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v); + let col = color(r, &world); + let ir = (255.99 * col[0]) as u32; + let ig = (255.99 * col[1]) as u32; + let ib = (255.99 * col[2]) as u32; + println!("{} {} {}", ir, ig, ib); + } + } + Ok(()) +} diff --git a/rtiow/src/hitable.rs b/rtiow/src/hitable.rs new file mode 100644 index 0000000..86295ac --- /dev/null +++ b/rtiow/src/hitable.rs @@ -0,0 +1,13 @@ +use ray::Ray; +use vec3::Vec3; + +#[derive(Copy, Clone)] +pub struct HitRecord { + pub t: f32, + pub p: Vec3, + pub normal: Vec3, +} + +pub trait Hit { + fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option; +} diff --git a/rtiow/src/hitable_list.rs b/rtiow/src/hitable_list.rs new file mode 100644 index 0000000..11a271d --- /dev/null +++ b/rtiow/src/hitable_list.rs @@ -0,0 +1,37 @@ +use hitable::Hit; +use hitable::HitRecord; +use ray::Ray; + +#[derive(Default)] +pub struct HitableList<'a, H> +where + H: Hit + 'a, +{ + list: Vec<&'a H>, +} + +impl<'a, H> HitableList<'a, H> +where + H: Hit, +{ + pub fn new(list: Vec<&'a H>) -> HitableList { + HitableList { list } + } +} + +impl<'a, H> Hit for HitableList<'a, H> +where + H: Hit, +{ + fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option { + let mut min_hit = None; + let mut closest_so_far = t_max; + for hitable in &self.list { + if let Some(h) = hitable.hit(r, t_min, closest_so_far) { + closest_so_far = h.t; + min_hit = Some(h); + } + } + min_hit + } +} diff --git a/rtiow/src/lib.rs b/rtiow/src/lib.rs index 1d07923..d1a66f2 100644 --- a/rtiow/src/lib.rs +++ b/rtiow/src/lib.rs @@ -1,2 +1,5 @@ +pub mod hitable; +pub mod hitable_list; pub mod ray; +pub mod sphere; pub mod vec3; diff --git a/rtiow/src/sphere.rs b/rtiow/src/sphere.rs new file mode 100644 index 0000000..d0a900c --- /dev/null +++ b/rtiow/src/sphere.rs @@ -0,0 +1,48 @@ +use hitable::Hit; +use hitable::HitRecord; +use ray::Ray; +use vec3::dot; +use vec3::Vec3; + +#[derive(Copy, Clone, Default)] +pub struct Sphere { + center: Vec3, + radius: f32, +} + +impl Sphere { + pub fn new(center: Vec3, radius: f32) -> Sphere { + Sphere { center, radius } + } +} + +impl Hit for Sphere { + fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option { + let oc = r.origin() - self.center; + let a = dot(r.direction(), r.direction()); + let b = dot(oc, r.direction()); + let c = dot(oc, oc) - self.radius * self.radius; + let discriminant = b * b - a * c; + if discriminant > 0. { + let temp = (-b - (b * b - a * c).sqrt()) / a; + if temp < t_max && temp > t_min { + let p = r.point_at_parameter(temp); + return Some(HitRecord { + t: temp, + p, + normal: (p - self.center) / self.radius, + }); + } + let temp = (-b + (b * b - a * c).sqrt()) / a; + if temp < t_max && temp > t_min { + let p = r.point_at_parameter(temp); + return Some(HitRecord { + t: temp, + p, + normal: (p - self.center) / self.radius, + }); + } + } + None + } +}