From 27c153427309794c25acf829d9a2186daf545489 Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Tue, 19 Feb 2019 21:18:37 -0800 Subject: [PATCH] Add Mandelbrot texture and test scene. --- rtiow/src/renderer.rs | 3 + rtiow/src/scenes/mandelbrot.rs | 156 ++++++++++++++++++++++++++++++++ rtiow/src/scenes/mod.rs | 1 + rtiow/src/texture/mandelbrot.rs | 78 ++++++++++++++++ rtiow/src/texture/mod.rs | 6 +- 5 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 rtiow/src/scenes/mandelbrot.rs create mode 100644 rtiow/src/texture/mandelbrot.rs diff --git a/rtiow/src/renderer.rs b/rtiow/src/renderer.rs index 9e2fbe6..73a09c7 100644 --- a/rtiow/src/renderer.rs +++ b/rtiow/src/renderer.rs @@ -33,6 +33,7 @@ pub enum Model { CornellSmoke, PerlinDebug, Final, + Mandelbrot, } impl Model { @@ -47,6 +48,7 @@ impl Model { Model::CornellSmoke => scenes::cornell_smoke::new(&opt), Model::PerlinDebug => scenes::perlin_debug::new(&opt), Model::Final => scenes::final_scene::new(&opt), + Model::Mandelbrot => scenes::mandelbrot::new(&opt), } } } @@ -73,6 +75,7 @@ impl str::FromStr for Model { "cornell_smoke" => Ok(Model::CornellSmoke), "perlin_debug" => Ok(Model::PerlinDebug), "final" => Ok(Model::Final), + "mandelbrot" => Ok(Model::Mandelbrot), _ => Err(ModelParseError(s.to_owned())), } } diff --git a/rtiow/src/scenes/mandelbrot.rs b/rtiow/src/scenes/mandelbrot.rs new file mode 100644 index 0000000..cfcd1e0 --- /dev/null +++ b/rtiow/src/scenes/mandelbrot.rs @@ -0,0 +1,156 @@ +use image; +use rand; + +use crate::camera::Camera; +use crate::hitable::Hit; +use crate::hitable_list::HitableList; +use crate::kdtree::KDTree; +use crate::material::DiffuseLight; +use crate::material::Lambertian; +use crate::noise::perlin::Perlin; +use crate::noise::NoiseType; +use crate::rect::XYRect; +use crate::rect::XZRect; +use crate::rect::YZRect; +use crate::renderer::Opt; +use crate::renderer::Scene; +use crate::sphere::Sphere; +use crate::texture::ConstantTexture; +use crate::texture::ImageTexture; +use crate::texture::Mandelbrot; +use crate::texture::NoiseTexture; +use crate::vec3::Vec3; + +pub fn new(opt: &Opt) -> Scene { + let lookfrom = Vec3::new(20., 20., 20.); + let lookat = Vec3::new(0., 1., 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.), + 20., + opt.width as f32 / opt.height as f32, + aperture, + dist_to_focus, + time_min, + time_max, + ); + let rng = &mut rand::thread_rng(); + let _ground_color = if opt.use_accel { + Box::new(ConstantTexture::new(Vec3::new(1.0, 0.4, 0.4))) + } else { + Box::new(ConstantTexture::new(Vec3::new(0.4, 1.0, 0.4))) + }; + + let world_image_bytes = include_bytes!("../../images/world.jpg"); + let it = ImageTexture::new(image::load_from_memory(world_image_bytes).unwrap().to_rgb()); + let noise_source = Perlin::new(rng); + let noise_type = NoiseType::Scale(10.); + let objects: Vec> = vec![ + // Textured globe + // Box::new(Sphere::new(Vec3::new(0., 2., 0.), 2.0, Lambertian::new(it))), + Box::new(Sphere::new( + Vec3::new(0., 2., 0.), + 2.0, + DiffuseLight::new(Mandelbrot::new()), + )), + // Earth sized sphere + Box::new(Sphere::new( + Vec3::new(0., -1000., 0.), + 1000., + // Box::new(Lambertian::new(ground_color)), + Lambertian::new(NoiseTexture::new(noise_source, noise_type)), + )), + Box::new(XZRect::new( + -100., + 100., + -100., + 1000., + 60., + DiffuseLight::new(ConstantTexture::new(Vec3::new(1., 1., 1.))), + )), + Box::new(YZRect::new( + 1., + 3., + -1., + 1., + 4., + DiffuseLight::new(ConstantTexture::new(Vec3::new(4., 0., 4.))), + )), + Box::new(YZRect::new( + 1., + 3., + -1., + 1., + -4., + DiffuseLight::new(ConstantTexture::new(Vec3::new(0., 4., 0.))), + )), + Box::new(XZRect::new( + -1., + 1., + -1., + 1., + 6., + DiffuseLight::new(ConstantTexture::new(Vec3::new(4., 4., 0.))), + )), + Box::new(XYRect::new( + -1., + 1., + 1., + 3., + -4., + DiffuseLight::new(ConstantTexture::new(Vec3::new(0., 0., 4.))), + )), + Box::new(XYRect::new( + -1.75, + 1.75, + 1., + 3., + 4., + DiffuseLight::new(Mandelbrot::new()), + )), + /* + Box::new(Sphere::new( + Vec3::new(0., 0., 0.), + 0.5, + Box::new(Lambertian::new(ConstantTexture::new(Vec3::new( + 0.1, 0.2, 0.5, + )))), + )), + // Shiny sphere + Box::new(Sphere::new( + Vec3::new(1., 0., 0.), + 0.5, + Metal::new(Vec3::new(0.8, 0.8, 0.8), 0.2), + )), + Box::new(MovingSphere::new( + Vec3::new(-1., 0., -0.25), + Vec3::new(-1., 0., 0.25), + 0.5, + 0., + 1., + Lambertian::new(ConstantTexture::new(Vec3::new( + 0.2, 0.8, 0.2, + ))), + )), + */ + ]; + 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, + env_map: None, + } +} diff --git a/rtiow/src/scenes/mod.rs b/rtiow/src/scenes/mod.rs index c88a75b..13fea3b 100644 --- a/rtiow/src/scenes/mod.rs +++ b/rtiow/src/scenes/mod.rs @@ -4,6 +4,7 @@ pub mod bvh; pub mod cornell_box; pub mod cornell_smoke; pub mod final_scene; +pub mod mandelbrot; pub mod perlin_debug; pub mod test; pub mod tutorial; diff --git a/rtiow/src/texture/mandelbrot.rs b/rtiow/src/texture/mandelbrot.rs new file mode 100644 index 0000000..85dfb82 --- /dev/null +++ b/rtiow/src/texture/mandelbrot.rs @@ -0,0 +1,78 @@ +use rand; +use rand::Rng; + +use crate::texture::Texture; +use crate::vec3::Vec3; + +#[derive(Debug)] +pub struct Mandelbrot { + scale: f32, + palette: Vec, +} + +// HSV values in [0..1] +// returns [r, g, b] values from 0 to 255 +//From https://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/ +fn hsv_to_rgb(h: f32, s: f32, v: f32) -> Vec3 { + let h_i = (h * 6.) as i32; + let f = h * 6. - h_i as f32; + let p = v * (1. - s); + let q = v * (1. - f * s); + let t = v * (1. - (1. - f) * s); + match h_i { + 0 => Vec3::new(v, t, p), + 1 => Vec3::new(q, v, p), + 2 => Vec3::new(p, v, t), + 3 => Vec3::new(p, q, v), + 4 => Vec3::new(t, p, v), + 5 => Vec3::new(v, p, q), + _ => panic!(format!("Unknown H value {}", h_i)), + } +} + +fn generate_palette(num: usize) -> Vec { + let mut rng = rand::thread_rng(); + let mut random = || rng.gen_range::(0.0, 0.1); + // use golden ratio + let golden_ratio_conjugate = 0.618033988749895; + let mut h = random(); + (0..num) + .map(|_| { + h += golden_ratio_conjugate; + h %= 1.0; + hsv_to_rgb(h, 0.99, 0.99) + }) + .collect() +} + +impl Mandelbrot { + pub fn new() -> Mandelbrot { + Mandelbrot { + scale: 2.0, + palette: generate_palette(10), + } + } +} + +impl Texture for Mandelbrot { + fn value(&self, u: f32, v: f32, _p: Vec3) -> Vec3 { + // scaled x coordinate of pixel (scaled to lie in the Mandelbrot X scale (-2.5, 1)) + let x0 = u * 3.5 - 2.5; + // scaled y coordinate of pixel (scaled to lie in the Mandelbrot Y scale (-1, 1)) + let y0 = v * 2.0 - 1.0; + let mut x = 0.0; + let mut y = 0.0; + let mut iteration = 0; + let max_iteration = 1000; + while (x * x + y * y) <= 2. * 2. && iteration < max_iteration { + let xtemp = x * x - y * y + x0; + y = 2. * x * y + y0; + x = xtemp; + iteration = iteration + 1; + } + if iteration == max_iteration { + return Vec3::default(); + } + self.palette[iteration % self.palette.len()] + } +} diff --git a/rtiow/src/texture/mod.rs b/rtiow/src/texture/mod.rs index e4abead..baf9c88 100644 --- a/rtiow/src/texture/mod.rs +++ b/rtiow/src/texture/mod.rs @@ -1,12 +1,14 @@ mod checker; mod constant; -mod image; -mod noise; mod envmap; +mod image; +mod mandelbrot; +mod noise; pub use crate::texture::checker::CheckerTexture; pub use crate::texture::constant::ConstantTexture; pub use crate::texture::envmap::EnvMap; pub use crate::texture::image::ImageTexture; +pub use crate::texture::mandelbrot::Mandelbrot; pub use crate::texture::noise::NoiseTexture; use std::sync::Arc;