diff --git a/rtiow/src/constant_medium.rs b/rtiow/src/constant_medium.rs new file mode 100644 index 0000000..5d5cb07 --- /dev/null +++ b/rtiow/src/constant_medium.rs @@ -0,0 +1,83 @@ +use std; + +use rand; +use rand::Rng; + +use aabb::AABB; +use hitable::Hit; +use hitable::HitRecord; +use material::Material; +use ray::Ray; +use vec3::Vec3; + +pub struct ConstantMedium +where + H: Hit, + M: Material, +{ + density: f32, + material: M, + hitable: H, +} + +impl ConstantMedium +where + H: Hit, + M: Material, +{ + pub fn new(hitable: H, density: f32, material: M) -> ConstantMedium { + ConstantMedium { + density, + material, + hitable, + } + } +} + +impl Hit for ConstantMedium +where + H: Hit, + M: Material, +{ + fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option { + let mut rng = rand::thread_rng(); + if let Some(mut rec1) = self.hitable.hit(r, std::f32::MIN, std::f32::MAX) { + if let Some(mut rec2) = self.hitable.hit(r, rec1.t + 0.001, std::f32::MAX) { + if rec1.t < t_min { + rec1.t = t_min; + } + if rec2.t > t_max { + rec2.t = t_max; + } + if rec1.t >= rec2.t { + return None; + } + if rec1.t < 0. { + rec1.t = 0.; + } + let distance_inside_boundary = (rec2.t - rec1.t) * r.direction.length(); + let hit_distance = -(1. / self.density) * rng.gen_range::(0., 1.).ln(); + if hit_distance < distance_inside_boundary { + let t = rec1.t + hit_distance / r.direction.length(); + let normal = Vec3::new( + rng.gen_range::(0., 1.), + rng.gen_range::(0., 1.), + rng.gen_range::(0., 1.), + ).unit_vector(); + return Some(HitRecord { + t, + p: r.point_at_parameter(t), + normal, + material: &self.material, + uv: (0., 0.), + }); + } + } + } + None + } + + fn bounding_box(&self, t_min: f32, t_max: f32) -> Option { + self.hitable.bounding_box(t_min, t_max) + } +} diff --git a/rtiow/src/lib.rs b/rtiow/src/lib.rs index c61eff2..4b29c36 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 constant_medium; pub mod cuboid; pub mod flip_normals; pub mod hitable; diff --git a/rtiow/src/material.rs b/rtiow/src/material.rs index 014be3d..277b393 100644 --- a/rtiow/src/material.rs +++ b/rtiow/src/material.rs @@ -214,3 +214,25 @@ where self.emit.value(u, v, p) } } + +#[cfg(test)] +mod tests { + use super::*; + use texture::ConstantTexture; + + #[test] + fn arc_material() { + let white: Arc = + Arc::new(Lambertian::new(ConstantTexture::new([0.73, 0.73, 0.73]))); + + fn material_fn(m: M) + where + M: Material, + { + let _ = m; + } + + material_fn(Arc::clone(&white)); + material_fn(Arc::clone(&white)); + } +} diff --git a/rtiow/src/renderer.rs b/rtiow/src/renderer.rs index c4dc1ed..ed3ef68 100644 --- a/rtiow/src/renderer.rs +++ b/rtiow/src/renderer.rs @@ -27,6 +27,7 @@ pub enum Model { BVH, Test, CornellBox, + CornellSmoke, } impl Model { @@ -38,6 +39,7 @@ impl Model { Model::BVH => scenes::bvh::new(&opt), Model::Test => scenes::test::new(&opt), Model::CornellBox => scenes::cornell_box::new(&opt), + Model::CornellSmoke => scenes::cornell_smoke::new(&opt), } } } @@ -61,6 +63,7 @@ impl str::FromStr for Model { "bvh" => Ok(Model::BVH), "test" => Ok(Model::Test), "cornell_box" => Ok(Model::CornellBox), + "cornell_smoke" => Ok(Model::CornellSmoke), _ => Err(ModelParseError(s.to_owned())), } } @@ -78,8 +81,9 @@ pub struct Opt { /// Sub-samples per pixel #[structopt(short = "s", long = "subsample", default_value = "8")] pub subsamples: usize, - /// Select scene to render, one of: "bench", "book", "tutorial", "bvh", "test", "cornell_box" - #[structopt(long = "model", default_value = "book")] + /// Select scene to render, one of: "bench", "book", "tutorial", "bvh", "test", "cornell_box", + /// "cornell_smoke" + #[structopt(long = "model", default_value = "cornell_smoke")] pub model: Model, /// Path to store pprof profile data, i.e. /tmp/cpuprofile.pprof #[structopt(long = "pprof", parse(from_os_str))] diff --git a/rtiow/src/scenes/cornell_smoke.rs b/rtiow/src/scenes/cornell_smoke.rs new file mode 100644 index 0000000..6f5b05b --- /dev/null +++ b/rtiow/src/scenes/cornell_smoke.rs @@ -0,0 +1,122 @@ +use std::sync::Arc; + +use camera::Camera; +use constant_medium::ConstantMedium; +use cuboid::Cuboid; +use flip_normals::FlipNormals; +use hitable::Hit; +use hitable_list::HitableList; +use kdtree::KDTree; +use material::DiffuseLight; +use material::Lambertian; +use material::Material; +use rect::XYRect; +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 { + let lookfrom = Vec3::new(278., 278., -800.); + let lookat = Vec3::new(278., 278., 0.); + let dist_to_focus = 10.0; + let aperture = 0.0; + let time_min = 0.; + let time_max = 1.; + let camera = Camera::new( + lookfrom, + lookat, + Vec3::new(0., 1., 0.), + 40., + opt.width as f32 / opt.height as f32, + aperture, + dist_to_focus, + time_min, + time_max, + ); + + let red = Lambertian::new(ConstantTexture::new([0.65, 0.05, 0.05])); + let white: Arc = Arc::new(Lambertian::new(ConstantTexture::new([0.73, 0.73, 0.73]))); + let green = Lambertian::new(ConstantTexture::new([0.12, 0.45, 0.15])); + let light = DiffuseLight::new(ConstantTexture::new([7., 7., 7.])); + + let objects: Vec> = vec![ + // White smoke box on the right + Box::new(ConstantMedium::new( + Translate::new( + RotateY::new( + Cuboid::new( + Vec3::new(0., 0., 0.), + Vec3::new(165., 165., 165.), + Arc::clone(&white), + ), + -18., + ), + Vec3::new(130., 0., 65.), + ), + 0.01, + Lambertian::new(ConstantTexture::new([1., 1., 1.])), + )), + // Black smoke box on the left + Box::new(ConstantMedium::new( + Translate::new( + RotateY::new( + Cuboid::new( + Vec3::new(0., 0., 0.), + Vec3::new(165., 330., 165.), + Arc::clone(&white), + ), + 15., + ), + Vec3::new(265., 0., 295.), + ), + 0.01, + Lambertian::new(ConstantTexture::new([0., 0., 0.])), + )), + // Green wall left + Box::new(FlipNormals::new(YZRect::new( + 0., 555., 0., 555., 555., green, + ))), + // Red floor right + Box::new(YZRect::new(0., 555., 0., 555., 0., red)), + // Light in ceiling + Box::new(XZRect::new(113., 443., 127., 432., 554., light)), + // Grey ceiling + Box::new(FlipNormals::new(XZRect::new( + 0., + 555., + 0., + 555., + 555., + Arc::clone(&white), + ))), + // Grey floor + Box::new(XZRect::new(0., 555., 0., 555., 0., Arc::clone(&white))), + // Grey back wall + Box::new(FlipNormals::new(XYRect::new( + 0., + 555., + 0., + 555., + 555., + Arc::clone(&white), + ))), + ]; + let world: Box = if opt.use_accel { + Box::new(KDTree::new(objects, time_min, time_max)) + } else { + Box::new(HitableList::new(objects)) + }; + Scene { + camera, + world, + subsamples: opt.subsamples, + width: opt.width, + height: opt.height, + global_illumination: false, + } +} diff --git a/rtiow/src/scenes/mod.rs b/rtiow/src/scenes/mod.rs index 2dd5240..f6f2b2c 100644 --- a/rtiow/src/scenes/mod.rs +++ b/rtiow/src/scenes/mod.rs @@ -2,5 +2,6 @@ pub mod bench; pub mod book; pub mod bvh; pub mod cornell_box; +pub mod cornell_smoke; pub mod test; pub mod tutorial;