From 5faba9cf26796ca8d437fcbc9f27b59018bc35dd Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Sun, 14 Oct 2018 15:09:57 -0700 Subject: [PATCH] Moved lode/perlin code to noise module. Changed how NoiseTexture is created to allow noise function and parameters to be passed in as options. Allowed setting of noise source from URL parameters. --- rtiow/Cargo.toml | 1 + rtiow/src/bin/noise_explorer.rs | 207 +++++++++++++++++-------------- rtiow/src/lib.rs | 3 +- rtiow/src/{ => noise}/lode.rs | 37 ++---- rtiow/src/noise/mod.rs | 89 +++++++++++++ rtiow/src/{ => noise}/perlin.rs | 13 +- rtiow/src/scenes/final_scene.rs | 6 +- rtiow/src/scenes/perlin_debug.rs | 6 +- rtiow/src/scenes/test.rs | 6 +- rtiow/src/texture/noise.rs | 56 +++++---- rtiow/src/vec3.rs | 8 ++ rtiow/templates/index.html | 15 ++- 12 files changed, 286 insertions(+), 161 deletions(-) rename rtiow/src/{ => noise}/lode.rs (71%) create mode 100644 rtiow/src/noise/mod.rs rename rtiow/src/{ => noise}/perlin.rs (95%) diff --git a/rtiow/Cargo.toml b/rtiow/Cargo.toml index 6099ee5..d3a1759 100644 --- a/rtiow/Cargo.toml +++ b/rtiow/Cargo.toml @@ -22,3 +22,4 @@ serde_derive = "1.0.79" # For better profiling support. [profile.release] debug = true + diff --git a/rtiow/src/bin/noise_explorer.rs b/rtiow/src/bin/noise_explorer.rs index 7e1c4b8..f866943 100644 --- a/rtiow/src/bin/noise_explorer.rs +++ b/rtiow/src/bin/noise_explorer.rs @@ -1,7 +1,5 @@ #[macro_use] extern crate askama; -#[macro_use] -extern crate log; extern crate actix_web; extern crate image; extern crate rand; @@ -15,22 +13,29 @@ extern crate structopt; extern crate rtiow; -use actix_web::Path as WebPath; +use std::fmt; use std::path::PathBuf; use std::time::SystemTime; +use actix_web::http; use actix_web::middleware; use actix_web::server; use actix_web::App; use actix_web::HttpRequest; use actix_web::HttpResponse; +use actix_web::Path as WebPath; use actix_web::Result; use askama::Template; use rand::SeedableRng; use rand::XorShiftRng; use structopt::StructOpt; -use rtiow::lode::Noise; +use rtiow::noise; +use rtiow::noise::lode::Lode; +use rtiow::noise::perlin::Perlin; +use rtiow::noise::NoiseType; +use rtiow::texture::NoiseTexture; +use rtiow::texture::Texture; use rtiow::vec3::Vec3; #[derive(Debug, StructOpt)] @@ -55,62 +60,41 @@ struct Opt { pub height: u32, } -#[derive(Debug, Deserialize)] -#[serde(tag = "type")] -enum NoiseType { - Scale(f32), - Turbulence(usize), - Marble { - period: Vec3, - power: f32, - size: usize, - }, +#[derive(Copy, Clone, Debug, Deserialize, PartialEq)] +enum NoiseSource { + #[serde(rename = "perlin")] + Perlin, + #[serde(rename = "lode")] + Lode, } -impl NoiseType { - fn to_url(&self) -> String { - match &self { - NoiseType::Scale(scale) => format!("scale/{}", scale), - NoiseType::Turbulence(turbulence) => format!("turbulence/{}", turbulence), - NoiseType::Marble { - period, - power, - size, - } => format!( - "marble/period/{},{},{}/power/{}/size/{}", - period.x, period.y, period.z, power, size, - ), +impl fmt::Display for NoiseSource { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + NoiseSource::Perlin => write!(f, "perlin"), + NoiseSource::Lode => write!(f, "lode"), } } +} - fn parameters(&self) -> Vec<(&str, String)> { - match &self { - NoiseType::Scale(scale) => vec![("Scale", scale.to_string())], - NoiseType::Turbulence(turbulence) => vec![("Turbulence", turbulence.to_string())], - NoiseType::Marble { - period, - power, - size, - } => vec![ - ("Period", period.to_string()), - ("Power", power.to_string()), - ("Size", size.to_string()), - ], - } - } +#[derive(Debug, Deserialize)] +struct NoiseSourceParam { + noise_source: NoiseSource, } #[derive(Debug, Deserialize)] struct NoiseParams { width: u32, height: u32, - noise: NoiseType, + noise_source: NoiseSource, + noise_type: NoiseType, } #[derive(Debug, Deserialize)] struct NoiseParamsScale { width: u32, height: u32, + noise_source: NoiseSource, scale: f32, } @@ -118,6 +102,7 @@ struct NoiseParamsScale { struct NoiseParamsTurbulence { width: u32, height: u32, + noise_source: NoiseSource, turbulence: usize, } @@ -125,6 +110,7 @@ struct NoiseParamsTurbulence { struct NoiseParamsMarble { width: u32, height: u32, + noise_source: NoiseSource, x: f32, y: f32, z: f32, @@ -135,15 +121,20 @@ struct NoiseParamsMarble { impl NoiseParams { fn url(&self) -> String { format!( - "/noise/{}/{}/{}", + "/noise/{}/{}/{}/{}", + self.noise_source, self.width, self.height, - self.noise.to_url(), + self.noise_type.to_url(), ) } fn big_url(&self) -> String { - format!("/noise/1024/1024/{}", self.noise.to_url()) + format!( + "/noise/{}/1024/1024/{}", + self.noise_source, + self.noise_type.to_url() + ) } } @@ -151,30 +142,19 @@ fn render_noise(noise_params: &NoiseParams) -> image::GrayImage { const SEED: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; let rng: &mut XorShiftRng = &mut SeedableRng::from_seed(SEED); let mut img = image::GrayImage::new(noise_params.width, noise_params.height); - let tex = Noise::new(rng); + let tex: NoiseTexture> = match noise_params.noise_source { + NoiseSource::Perlin => { + NoiseTexture::new(Box::new(Perlin::new(rng)), noise_params.noise_type) + } + NoiseSource::Lode => NoiseTexture::new(Box::new(Lode::new(rng)), noise_params.noise_type), + }; for x in 0..img.width() { for y in 0..img.height() { let u = x as f32 / 2.; let v = y as f32 / 2.; let p = Vec3::new(u, v, 1.); - let luma = match noise_params.noise { - NoiseType::Scale(scale) => tex.value(p, scale), - - NoiseType::Turbulence(turbulence) => tex.turbulence(p, turbulence), - NoiseType::Marble { - period, - power, - size, - } => tex.marble(p, period, power, size), - }; - - if luma > 1. { - info!("Hot pixel @ {}x{}: {}", x, y, luma); - } - if luma < 0. { - info!("Cold pixel @ {}x{}: {}", x, y, luma); - } + let luma = tex.value(u, v, p).x; let luma = (luma * 255.) as u8; img.put_pixel(x, y, image::Luma([luma])); } @@ -192,21 +172,26 @@ struct Specimen { #[template(path = "index.html")] struct IndexTemplate { render_time: u64, + noise_source: NoiseSource, specimens: Vec, } -fn render_html(specimens: Vec) -> Result { +fn render_html( + noise_source: NoiseSource, + specimens: Vec, +) -> Result { let index = IndexTemplate { render_time: SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .unwrap() .as_secs(), + noise_source, specimens, }; index.render() } -fn build_specimens(width: u32, height: u32) -> Vec { +fn build_specimens(noise_source: NoiseSource, width: u32, height: u32) -> Vec { let mut specimens = Vec::new(); let mut specimen = Specimen { title: "Unperturbed".into(), @@ -216,7 +201,8 @@ fn build_specimens(width: u32, height: u32) -> Vec { let params = NoiseParams { width, height, - noise: NoiseType::Scale(1.), + noise_source, + noise_type: NoiseType::Scale(1.), }; specimen.params.push(params); } @@ -232,7 +218,8 @@ fn build_specimens(width: u32, height: u32) -> Vec { let params = NoiseParams { width, height, - noise: NoiseType::Scale(scale as f32 / 10.), + noise_source, + noise_type: NoiseType::Scale(scale as f32 / 10.), }; specimen.params.push(params); } @@ -246,7 +233,8 @@ fn build_specimens(width: u32, height: u32) -> Vec { let params = NoiseParams { width, height, - noise: NoiseType::Scale(scale as f32), + noise_source, + noise_type: NoiseType::Scale(scale as f32), }; specimen.params.push(params); } @@ -260,7 +248,8 @@ fn build_specimens(width: u32, height: u32) -> Vec { let params = NoiseParams { width, height, - noise: NoiseType::Turbulence(1 << turbulence), + noise_source, + noise_type: NoiseType::Turbulence(1 << turbulence), }; specimen.params.push(params); } @@ -280,7 +269,8 @@ fn build_specimens(width: u32, height: u32) -> Vec { let params = NoiseParams { width, height, - noise: NoiseType::Marble { + noise_source, + noise_type: NoiseType::Marble { period: Vec3::new(5., 10., 0.), power: power as f32, size: 1 << size, @@ -299,7 +289,8 @@ fn build_specimens(width: u32, height: u32) -> Vec { let params = NoiseParams { width, height, - noise: NoiseType::Marble { + noise_source, + noise_type: NoiseType::Marble { period: Vec3::new(0., 1., 0.), power: power as f32, size: 32, @@ -317,14 +308,23 @@ fn style(_req: &HttpRequest) -> Result { Ok(HttpResponse::Ok().content_type("text/css").body(&bytes[..])) } -fn index(_req: &HttpRequest) -> Result { - // TODO(wathiede): get these from app state? - let width = 128; - let height = 128; - let specimens = build_specimens(width, height); +fn index(source: NoiseSource, width: u32, height: u32) -> Result { + let specimens = build_specimens(source, width, height); Ok(HttpResponse::Ok() .content_type("text/html") - .body(render_html(specimens).unwrap())) + .body(render_html(source, specimens).unwrap())) +} + +fn index_perlin(_req: &HttpRequest) -> Result { + let width = 128; + let height = 128; + index(NoiseSource::Perlin, width, height) +} + +fn index_lode(_req: &HttpRequest) -> Result { + let width = 128; + let height = 128; + index(NoiseSource::Lode, width, height) } fn noise(np: &NoiseParams) -> Result { @@ -343,7 +343,8 @@ fn noise_scale(np: WebPath) -> Result { noise(&NoiseParams { width: np.width, height: np.height, - noise: NoiseType::Scale(np.scale), + noise_source: np.noise_source, + noise_type: NoiseType::Scale(np.scale), }) } @@ -352,7 +353,8 @@ fn noise_turbulence(np: WebPath) -> Result noise(&NoiseParams { width: np.width, height: np.height, - noise: NoiseType::Turbulence(np.turbulence), + noise_source: np.noise_source, + noise_type: NoiseType::Turbulence(np.turbulence), }) } @@ -361,7 +363,8 @@ fn noise_marble(np: WebPath) -> Result { noise(&NoiseParams { width: np.width, height: np.height, - noise: NoiseType::Marble { + noise_source: np.noise_source, + noise_type: NoiseType::Marble { period: Vec3::new(np.x, np.y, np.z), power: np.power, size: np.size, @@ -379,17 +382,19 @@ fn main() -> Result<(), std::io::Error> { server::new(|| { App::new() - .resource("/noise/{width}/{height}/scale/{scale}", |r| { + .resource("/noise/{noise_source}/{width}/{height}/scale/{scale}", |r| { r.with(noise_scale); - }).resource("/noise/{width}/{height}/turbulence/{turbulence}", |r| { + }).resource("/noise/{noise_source}/{width}/{height}/turbulence/{turbulence}", |r| { r.with(noise_turbulence); }).resource( - "/noise/{width}/{height}/marble/period/{x},{y},{z}/power/{power}/size/{size}", + "/noise/{noise_source}/{width}/{height}/marble/period/{x},{y},{z}/power/{power}/size/{size}", |r| { r.with(noise_marble); }, ).resource("/style.css", |r| r.f(style)) - .resource("/", |r| r.f(index)) + .resource("/lode", |r| r.f(index_lode)) + .resource("/perlin", |r| r.f(index_perlin)) + .resource("/", |r| r.f(|_| HttpResponse::Found().header(http::header::LOCATION, "/lode").finish())) .middleware(middleware::Logger::default()) }).bind(opt.addr) .unwrap() @@ -413,14 +418,14 @@ mod tests { #[test] fn noise_param_from_req() { let mut srv = TestServer::build().start(|app| { - app.resource("/noise/{width}/{height}/scale/{scale}", |r| { + app.resource("/noise/{noise_source}/{width}/{height}/scale/{scale}", |r| { r.with(|p: Path| format!("{:?}", p)); }); - app.resource("/noise/{width}/{height}/turbulence/{turbulence}", |r| { + app.resource("/noise/{noise_source}/{width}/{height}/turbulence/{turbulence}", |r| { r.with(|p: Path| format!("{:?}", p)); }); app.resource( - "/noise/{width}/{height}/marble/period/{x},{y},{z}/power/{power}/size/{size}", + "/noise/{noise_source}/{width}/{height}/marble/period/{x},{y},{z}/power/{power}/size/{size}", |r| { r.with(|p: Path| format!("{:?}", p)); }, @@ -428,7 +433,7 @@ mod tests { }); let request = srv - .client(Method::GET, "/noise/32/32/scale/1") + .client(Method::GET, "/noise/perlin/32/32/scale/1") .finish() .unwrap(); let response = srv.execute(request.send()).unwrap(); @@ -438,11 +443,11 @@ mod tests { let body = str::from_utf8(&bytes).unwrap(); assert_eq!( body, - "NoiseParamsScale { width: 32, height: 32, scale: 1.0 }" + "NoiseParamsScale { width: 32, height: 32, noise_source: Perlin, scale: 1.0 }" ); let request = srv - .client(Method::GET, "/noise/32/32/turbulence/5") + .client(Method::GET, "/noise/lode/32/32/scale/1") .finish() .unwrap(); let response = srv.execute(request.send()).unwrap(); @@ -452,14 +457,28 @@ mod tests { let body = str::from_utf8(&bytes).unwrap(); assert_eq!( body, - "NoiseParamsTurbulence { width: 32, height: 32, turbulence: 5 }" + "NoiseParamsScale { width: 32, height: 32, noise_source: Lode, scale: 1.0 }" + ); + + let request = srv + .client(Method::GET, "/noise/lode/32/32/turbulence/5") + .finish() + .unwrap(); + let response = srv.execute(request.send()).unwrap(); + assert!(response.status().is_success(), "Response {:?}", response); + + let bytes = srv.execute(response.body()).unwrap(); + let body = str::from_utf8(&bytes).unwrap(); + assert_eq!( + body, + "NoiseParamsTurbulence { width: 32, height: 32, noise_source: Lode, turbulence: 5 }" ); // TODO(wathiede): this isn't working, probably because Vec3 Deserialize is not working. let request = srv .client( Method::GET, - "/noise/32/32/marble/period/1.,2.,3./power/32./size/4", + "/noise/lode/32/32/marble/period/1.,2.,3./power/32./size/4", ).finish() .unwrap(); let response = srv.execute(request.send()).unwrap(); @@ -469,7 +488,7 @@ mod tests { let body = str::from_utf8(&bytes).unwrap(); assert_eq!( body, - "NoiseParamsMarble { width: 32, height: 32, x: 1.0, y: 2.0, z: 3.0, power: 32.0, size: 4 }" + "NoiseParamsMarble { width: 32, height: 32, noise_source: Lode, x: 1.0, y: 2.0, z: 3.0, power: 32.0, size: 4 }" ); } } diff --git a/rtiow/src/lib.rs b/rtiow/src/lib.rs index 3dcf80e..ad9aec2 100644 --- a/rtiow/src/lib.rs +++ b/rtiow/src/lib.rs @@ -7,10 +7,9 @@ pub mod flip_normals; pub mod hitable; pub mod hitable_list; pub mod kdtree; -pub mod lode; pub mod material; pub mod moving_sphere; -pub mod perlin; +pub mod noise; pub mod ray; pub mod rect; pub mod renderer; diff --git a/rtiow/src/lode.rs b/rtiow/src/noise/lode.rs similarity index 71% rename from rtiow/src/lode.rs rename to rtiow/src/noise/lode.rs index 06ee3e5..0cee306 100644 --- a/rtiow/src/lode.rs +++ b/rtiow/src/noise/lode.rs @@ -1,18 +1,17 @@ /// Implements the concepts from https://lodev.org/cgtutor/randomnoise.html -use std::f32::consts::PI; - use rand; +use noise::NoiseSource; use vec3::Vec3; const NOISE_SIZE: usize = 128; -pub struct Noise { +pub struct Lode { // Using fixed array causes stack overflow. noise: Vec>>, //[[[f32; NOISE_SIZE]; NOISE_SIZE]; NOISE_SIZE], } -impl Noise { - pub fn new(rng: &mut R) -> Noise +impl Lode { + pub fn new(rng: &mut R) -> Lode where R: rand::Rng, { @@ -26,32 +25,12 @@ impl Noise { } } } - Noise { noise } + Lode { noise } } +} - pub fn marble(&self, p: Vec3, period: Vec3, power: f32, size: usize) -> f32 { - let xyz_value = p.x * period.x / NOISE_SIZE as f32 - + p.y * period.y / NOISE_SIZE as f32 - + p.z * period.z / NOISE_SIZE as f32 - + power * self.turbulence(p, size); - //info!("p {} xyz_value {}", p, xyz_value); - (xyz_value * PI).sin().abs() - } - - pub fn turbulence(&self, p: Vec3, factor: usize) -> f32 { - let mut value = 0.; - let initial_factor = factor; - let mut factor = factor; - - while factor >= 1 { - value += self.value(p / factor as f32, 1.) * factor as f32; - factor /= 2; - } - 0.5 * value / initial_factor as f32 - } - - pub fn value(&self, p: Vec3, scale: f32) -> f32 { - let p = p / scale; +impl NoiseSource for Lode { + fn value(&self, p: Vec3) -> f32 { // Fractional part of vector components let x = p.x - p.x.floor(); let y = p.y - p.y.floor(); diff --git a/rtiow/src/noise/mod.rs b/rtiow/src/noise/mod.rs new file mode 100644 index 0000000..477c79e --- /dev/null +++ b/rtiow/src/noise/mod.rs @@ -0,0 +1,89 @@ +pub mod lode; +pub mod perlin; + +use std::f32::consts::PI; + +use vec3::Vec3; + +pub trait NoiseSource: Send + Sync { + /// value returns noise on the interval [0., 1.). + fn value(&self, p: Vec3) -> f32; + + fn marble(&self, p: Vec3, period: Vec3, power: f32, size: usize) -> f32 { + // TODO(wathiede): can't understand why 255 works for perlin and lode, maybe it's near 360 + // degrees and interacts with the sine function? + let xyz_value = p.x * period.x / 255. + + p.y * period.y / 255. + + p.z * period.z / 255. + + power * self.turbulence(p, size); + (xyz_value * PI).sin().abs() + } + + fn turbulence(&self, p: Vec3, factor: usize) -> f32 { + let mut value = 0.; + let initial_factor = factor; + let mut factor = factor; + + while factor >= 1 { + value += self.value(p / factor as f32) * factor as f32; + factor /= 2; + } + 0.5 * value / initial_factor as f32 + } + + fn scaled(&self, p: Vec3, scale: f32) -> f32 { + let p = p / scale; + self.value(p) + } +} + +impl NoiseSource for Box { + fn value(&self, p: Vec3) -> f32 { + (**self).value(p) + } +} + +#[derive(Copy, Clone, Debug, Deserialize)] +#[serde(tag = "type")] +pub enum NoiseType { + Scale(f32), + Turbulence(usize), + Marble { + period: Vec3, + power: f32, + size: usize, + }, +} + +impl NoiseType { + pub fn to_url(&self) -> String { + match &self { + NoiseType::Scale(scale) => format!("scale/{}", scale), + NoiseType::Turbulence(turbulence) => format!("turbulence/{}", turbulence), + NoiseType::Marble { + period, + power, + size, + } => format!( + "marble/period/{},{},{}/power/{}/size/{}", + period.x, period.y, period.z, power, size, + ), + } + } + + pub fn parameters(&self) -> Vec<(&str, String)> { + match &self { + NoiseType::Scale(scale) => vec![("Scale", scale.to_string())], + NoiseType::Turbulence(turbulence) => vec![("Turbulence", turbulence.to_string())], + NoiseType::Marble { + period, + power, + size, + } => vec![ + ("Period", period.to_string()), + ("Power", power.to_string()), + ("Size", size.to_string()), + ], + } + } +} diff --git a/rtiow/src/perlin.rs b/rtiow/src/noise/perlin.rs similarity index 95% rename from rtiow/src/perlin.rs rename to rtiow/src/noise/perlin.rs index e66146c..66aef0c 100644 --- a/rtiow/src/perlin.rs +++ b/rtiow/src/noise/perlin.rs @@ -2,6 +2,7 @@ #![cfg_attr(feature = "cargo-clippy", allow(many_single_char_names))] use rand::Rng; +use noise::NoiseSource; use vec3::dot; use vec3::Vec3; @@ -112,18 +113,16 @@ impl Perlin { let mut temp_p = p; let mut weight = 1.; for _ in 0..depth { - accum += weight * self.noise(temp_p); + accum += weight * self.value(temp_p); weight *= 0.5; temp_p = temp_p * 0.2; } accum.abs() } +} - pub fn noise(&self, p: V) -> f32 - where - V: Into, - { - let p = p.into(); +impl NoiseSource for Perlin { + fn value(&self, p: Vec3) -> f32 { let u = p.x - p.x.floor(); let v = p.y - p.y.floor(); let w = p.z - p.z.floor(); @@ -145,6 +144,6 @@ impl Perlin { } } } - perlin_interp(c, u, v, w) + (1. + perlin_interp(c, u, v, w)) * 0.5 } } diff --git a/rtiow/src/scenes/final_scene.rs b/rtiow/src/scenes/final_scene.rs index 659240e..a751eff 100644 --- a/rtiow/src/scenes/final_scene.rs +++ b/rtiow/src/scenes/final_scene.rs @@ -16,6 +16,8 @@ use material::Lambertian; use material::Material; use material::Metal; use moving_sphere::MovingSphere; +use noise::perlin::Perlin; +use noise::NoiseType; use rect::XZRect; use renderer::Opt; use renderer::Scene; @@ -127,7 +129,9 @@ pub fn new(opt: &Opt) -> Scene { ))); // Perlin noise sphere - let pertext = NoiseTexture::with_scale(rng, 0.1); + let noise_source = Perlin::new(rng); + let noise_type = NoiseType::Scale(0.1); + let pertext = NoiseTexture::new(noise_source, noise_type); list.push(Box::new(Sphere::new( [220., 280., 300.], 80., diff --git a/rtiow/src/scenes/perlin_debug.rs b/rtiow/src/scenes/perlin_debug.rs index 448cf2a..9c90c2e 100644 --- a/rtiow/src/scenes/perlin_debug.rs +++ b/rtiow/src/scenes/perlin_debug.rs @@ -7,6 +7,8 @@ use hitable::Hit; use hitable_list::HitableList; use kdtree::KDTree; use material::Lambertian; +use noise::perlin::Perlin; +use noise::NoiseType; use renderer::Opt; use renderer::Scene; use sphere::Sphere; @@ -34,7 +36,9 @@ pub fn new(opt: &Opt) -> Scene { ); // TODO(wathiede): Use XOR rng for predictability? let rng = &mut rand::thread_rng(); - let pertext: Arc = Arc::new(NoiseTexture::with_scale(rng, 10.)); + let noise_source = Perlin::new(rng); + let noise_type = NoiseType::Scale(10.); + let pertext: Arc = Arc::new(NoiseTexture::new(noise_source, noise_type)); let objects: Vec> = vec![ Box::new(Sphere::new( diff --git a/rtiow/src/scenes/test.rs b/rtiow/src/scenes/test.rs index 534f2e9..9c3f361 100644 --- a/rtiow/src/scenes/test.rs +++ b/rtiow/src/scenes/test.rs @@ -7,6 +7,8 @@ use hitable_list::HitableList; use kdtree::KDTree; use material::DiffuseLight; use material::Lambertian; +use noise::perlin::Perlin; +use noise::NoiseType; use rect::XYRect; use rect::XZRect; use rect::YZRect; @@ -45,6 +47,8 @@ pub fn new(opt: &Opt) -> Scene { 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![ // Big sphere Box::new(Sphere::new(Vec3::new(0., 2., 0.), 2.0, Lambertian::new(it))), @@ -53,7 +57,7 @@ pub fn new(opt: &Opt) -> Scene { Vec3::new(0., -1000., 0.), 1000., // Box::new(Lambertian::new(ground_color)), - Lambertian::new(NoiseTexture::with_scale(rng, 10.)), + Lambertian::new(NoiseTexture::new(noise_source, noise_type)), )), Box::new(XZRect::new( -100., diff --git a/rtiow/src/texture/noise.rs b/rtiow/src/texture/noise.rs index 410eb21..bb1717b 100644 --- a/rtiow/src/texture/noise.rs +++ b/rtiow/src/texture/noise.rs @@ -1,39 +1,45 @@ -use rand; - -use perlin::Perlin; +use noise::NoiseSource; +use noise::NoiseType; use texture::Texture; use vec3::Vec3; -pub struct NoiseTexture { - scale: f32, - perlin: Perlin, +pub struct NoiseTexture +where + N: NoiseSource, +{ + noise_source: N, + noise_type: NoiseType, } -impl NoiseTexture { - pub fn new(rng: &mut R) -> NoiseTexture - where - R: rand::Rng, - { - NoiseTexture::with_scale(rng, 1.) - } - pub fn with_scale(rng: &mut R, scale: f32) -> NoiseTexture - where - R: rand::Rng, - { +impl NoiseTexture +where + N: NoiseSource, +{ + pub fn new(noise_source: N, noise_type: NoiseType) -> NoiseTexture { NoiseTexture { - scale, - perlin: Perlin::new(rng), + noise_source, + noise_type, } } } -impl Texture for NoiseTexture { +impl Texture for NoiseTexture +where + N: NoiseSource, +{ fn value(&self, _u: f32, _v: f32, p: Vec3) -> Vec3 { - Vec3::new(1., 1., 1.) * 0.5 * (1. + self.perlin.noise(self.scale * p)) + let v = match self.noise_type { + NoiseType::Scale(scale) => self.noise_source.scaled(p, scale), - //Vec3::new(1., 1., 1.) * turb(self.scale * p, 7) - //Vec3::new(1., 1., 1.) * 0.5 * (1. + turb(self.scale * p, 7)) - //Vec3::new(1., 1., 1.) * 0.5 * (1. + (self.scale * p.z + 10. * self.perlin.turb(p, 7)).sin()) - //Vec3::new(1., 1., 1.) * 0.5 * (1. + GENERATOR.noise(self.scale * p)) + NoiseType::Turbulence(turbulence) => self.noise_source.turbulence(p, turbulence), + NoiseType::Marble { + period, + power, + size, + } => self.noise_source.marble(p, period, power, size), + }; + debug_assert!(v >= 0., "Cold pixel @ {}: {}", p, v); + debug_assert!(v <= 1., "Hot pixel @ {}: {}", p, v); + Vec3::new(1., 1., 1.) * v } } diff --git a/rtiow/src/vec3.rs b/rtiow/src/vec3.rs index 811d73a..049ec4b 100644 --- a/rtiow/src/vec3.rs +++ b/rtiow/src/vec3.rs @@ -33,6 +33,14 @@ impl Vec3 { Vec3 { x, y, z } } + pub fn min(self) -> f32 { + self.x.min(self.y).min(self.z) + } + + pub fn max(self) -> f32 { + self.x.max(self.y).max(self.z) + } + pub fn length(self) -> f32 { self.squared_length().sqrt() } diff --git a/rtiow/templates/index.html b/rtiow/templates/index.html index e0cf1de..7ad5eee 100644 --- a/rtiow/templates/index.html +++ b/rtiow/templates/index.html @@ -6,6 +6,19 @@ +

+ {% if noise_source == NoiseSource::Perlin %} + Perlin + {% else %} + Perlin + {% endif %} + / + {% if noise_source == NoiseSource::Lode %} + Lode + {% else %} + Lode + {% endif %} +

{% for s in specimens %}

{{ s.title }}

{% for np in s.params %} @@ -16,7 +29,7 @@
- {% for p in np.noise.parameters() %} + {% for p in np.noise_type.parameters() %} {% endfor %}
{{ p.0 }}{{ p.1 }}