diff --git a/rtiow/src/cuboid.rs b/rtiow/src/cuboid.rs new file mode 100644 index 0000000..14fe523 --- /dev/null +++ b/rtiow/src/cuboid.rs @@ -0,0 +1,90 @@ +use std::sync::Arc; + +use aabb::AABB; +use flip_normals::FlipNormals; +use hitable::Hit; +use hitable::HitRecord; +use hitable_list::HitableList; +use material::Material; +use ray::Ray; +use rect::XYRect; +use rect::XZRect; +use rect::YZRect; +use vec3::Vec3; + +pub struct Cuboid { + p_min: Vec3, + p_max: Vec3, + walls: HitableList, +} + +impl Cuboid { + pub fn new(p_min: Vec3, p_max: Vec3, material: Box) -> Cuboid { + let material = Arc::new(material); + + Cuboid { + p_min, + p_max, + walls: HitableList::new(vec![ + Box::new(XYRect::new( + p_min.x, + p_max.x, + p_min.y, + p_max.y, + p_max.z, + Box::new(Arc::clone(&material)), + )), + Box::new(FlipNormals::new(Box::new(XYRect::new( + p_min.x, + p_max.x, + p_min.y, + p_max.y, + p_min.z, + Box::new(Arc::clone(&material)), + )))), + Box::new(XZRect::new( + p_min.x, + p_max.x, + p_min.z, + p_max.z, + p_max.y, + Box::new(Arc::clone(&material)), + )), + Box::new(FlipNormals::new(Box::new(XZRect::new( + p_min.x, + p_max.x, + p_min.z, + p_max.z, + p_min.y, + Box::new(Arc::clone(&material)), + )))), + Box::new(YZRect::new( + p_min.y, + p_max.y, + p_min.z, + p_max.z, + p_max.x, + Box::new(Arc::clone(&material)), + )), + Box::new(FlipNormals::new(Box::new(YZRect::new( + p_min.y, + p_max.y, + p_min.z, + p_max.z, + p_min.x, + Box::new(Arc::clone(&material)), + )))), + ]), + } + } +} + +impl Hit for Cuboid { + fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option { + self.walls.hit(r, t_min, t_max) + } + + fn bounding_box(&self, _t_min: f32, _t_max: f32) -> Option { + Some(AABB::new(self.p_min, self.p_max)) + } +} diff --git a/rtiow/src/lib.rs b/rtiow/src/lib.rs index fee5d69..c61eff2 100644 --- a/rtiow/src/lib.rs +++ b/rtiow/src/lib.rs @@ -1,6 +1,7 @@ pub mod aabb; pub mod bvh; pub mod camera; +pub mod cuboid; pub mod flip_normals; pub mod hitable; pub mod hitable_list; @@ -11,9 +12,11 @@ pub mod perlin; pub mod ray; pub mod rect; pub mod renderer; +pub mod rotate; pub mod scenes; pub mod sphere; pub mod texture; +pub mod translate; pub mod vec3; extern crate crossbeam_channel; diff --git a/rtiow/src/material.rs b/rtiow/src/material.rs index cfd1b24..f5c3bdc 100644 --- a/rtiow/src/material.rs +++ b/rtiow/src/material.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + extern crate rand; use self::rand::Rng; @@ -36,6 +38,15 @@ pub trait Material: Send + Sync { } } +impl Material for Arc> { + fn scatter(&self, r_in: &Ray, rec: &HitRecord) -> ScatterResponse { + (**self).scatter(r_in, rec) + } + fn emitted(&self, u: f32, v: f32, p: Vec3) -> Vec3 { + (**self).emitted(u, v, p) + } +} + pub struct Lambertian { // TODO(wathiede): implement texture sharing via references albedo: Box, diff --git a/rtiow/src/rotate.rs b/rtiow/src/rotate.rs new file mode 100644 index 0000000..7ebdaf7 --- /dev/null +++ b/rtiow/src/rotate.rs @@ -0,0 +1,90 @@ +use std::f32::consts::PI; +use std::f32::MAX; +use std::f32::MIN; + +use aabb::AABB; +use hitable::Hit; +use hitable::HitRecord; +use ray::Ray; +use vec3::Vec3; + +pub struct RotateY { + hitable: Box, + sin_theta: f32, + cos_theta: f32, + bbox: Option, +} + +impl RotateY { + pub fn new(hitable: Box, angle: f32) -> RotateY { + let radians = PI / 180. * angle; + let sin_theta = radians.sin(); + let cos_theta = radians.cos(); + let mut min = vec![MAX, MAX, MAX]; + let mut max = vec![MIN, MIN, MIN]; + let bbox = hitable.bounding_box(0., 1.).unwrap(); + for i in 0..2 { + for j in 0..2 { + for k in 0..2 { + let x = i as f32 * bbox.max().x + (1 - i) as f32 * bbox.min().x; + let y = j as f32 * bbox.max().y + (1 - j) as f32 * bbox.min().y; + let z = k as f32 * bbox.max().z + (1 - k) as f32 * bbox.min().z; + let new_x = cos_theta * x + sin_theta * z; + let new_z = -sin_theta * x + cos_theta * z; + let tester = Vec3::new(new_x, y, new_z); + for c in 0..3 { + if tester[c] > max[c] { + max[c] = tester[c]; + } + if tester[c] < min[c] { + min[c] = tester[c]; + } + } + } + } + } + RotateY { + hitable, + sin_theta, + cos_theta, + bbox: Some(AABB::new( + Vec3::new(min[0], min[1], min[2]), + Vec3::new(max[0], max[1], max[2]), + )), + } + } +} + +impl Hit for RotateY { + fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option { + let origin = Vec3::new( + self.cos_theta * r.origin[0] - self.sin_theta * r.origin[2], + r.origin.y, + self.sin_theta * r.origin[0] + self.cos_theta * r.origin[2], + ); + let direction = Vec3::new( + self.cos_theta * r.direction[0] - self.sin_theta * r.direction[2], + r.direction.y, + self.sin_theta * r.direction[0] + self.cos_theta * r.direction[2], + ); + let rotated_r = Ray::new(origin, direction, r.time); + if let Some(rec) = self.hitable.hit(rotated_r, t_min, t_max) { + let p = Vec3::new( + self.cos_theta * rec.p[0] + self.sin_theta * rec.p[2], + rec.p[1], + -self.sin_theta * rec.p[0] + self.cos_theta * rec.p[2], + ); + let normal = Vec3::new( + self.cos_theta * rec.normal[0] + self.sin_theta * rec.normal[2], + rec.normal[1], + -self.sin_theta * rec.normal[0] + self.cos_theta * rec.normal[2], + ); + return Some(HitRecord { p, normal, ..rec }); + } + None + } + + fn bounding_box(&self, _t_min: f32, _t_max: f32) -> Option { + self.bbox.clone() + } +} diff --git a/rtiow/src/scenes/cornell_box.rs b/rtiow/src/scenes/cornell_box.rs index af4aacb..f902a0e 100644 --- a/rtiow/src/scenes/cornell_box.rs +++ b/rtiow/src/scenes/cornell_box.rs @@ -1,4 +1,5 @@ use camera::Camera; +use cuboid::Cuboid; use flip_normals::FlipNormals; use hitable::Hit; use hitable_list::HitableList; @@ -10,7 +11,9 @@ use rect::XZRect; use rect::YZRect; use renderer::Opt; use renderer::Scene; +use rotate::RotateY; use texture::ConstantTexture; +use translate::Translate; use vec3::Vec3; pub fn new(opt: &Opt) -> Scene { @@ -33,6 +36,34 @@ pub fn new(opt: &Opt) -> Scene { ); let objects: Vec> = vec![ + // Box1 + Box::new(Translate::new( + Box::new(RotateY::new( + Box::new(Cuboid::new( + Vec3::new(0., 0., 0.), + Vec3::new(165., 165., 165.), + Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new( + 0.73, 0.73, 0.73, + ))))), + )), + -18., + )), + Vec3::new(100., 0., 0.), + )), + // Box2 + Box::new(Translate::new( + Box::new(RotateY::new( + Box::new(Cuboid::new( + Vec3::new(0., 0., 0.), + Vec3::new(165., 330., 165.), + Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new( + 0.73, 0.73, 0.73, + ))))), + )), + 15., + )), + Vec3::new(265., 0., 295.), + )), // Green wall left Box::new(FlipNormals::new(Box::new(YZRect::new( 0., diff --git a/rtiow/src/translate.rs b/rtiow/src/translate.rs new file mode 100644 index 0000000..7ae4b01 --- /dev/null +++ b/rtiow/src/translate.rs @@ -0,0 +1,39 @@ +use aabb::AABB; +use hitable::Hit; +use hitable::HitRecord; +use ray::Ray; +use vec3::Vec3; + +pub struct Translate { + hitable: Box, + offset: Vec3, +} + +impl Translate { + pub fn new(hitable: Box, offset: Vec3) -> Translate { + Translate { hitable, offset } + } +} + +impl Hit for Translate { + fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option { + let moved_r = Ray::new(r.origin - self.offset, r.direction, r.time); + if let Some(rec) = self.hitable.hit(moved_r, t_min, t_max) { + return Some(HitRecord { + p: rec.p + self.offset, + ..rec + }); + } + None + } + + fn bounding_box(&self, t_min: f32, t_max: f32) -> Option { + if let Some(bbox) = self.hitable.bounding_box(t_min, t_max) { + return Some(AABB::new( + bbox.min() + self.offset, + bbox.max() + self.offset, + )); + } + None + } +}