diff --git a/rtiow/Cargo.lock b/rtiow/Cargo.lock index 7badcbc..8fe5979 100644 --- a/rtiow/Cargo.lock +++ b/rtiow/Cargo.lock @@ -19,6 +19,37 @@ dependencies = [ "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "askama" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "askama_derive 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "askama_shared 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "askama_derive" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "askama_shared 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "nom 4.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "askama_shared" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "atty" version = "0.2.11" @@ -316,6 +347,16 @@ name = "lzw" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "memchr" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "memoffset" version = "0.2.1" @@ -326,6 +367,14 @@ name = "nodrop" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "nom" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num-derive" version = "0.2.2" @@ -487,6 +536,7 @@ dependencies = [ name = "rtiow" version = "0.1.0" dependencies = [ + "askama 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "cpuprofiler 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-channel 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -535,6 +585,21 @@ name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "serde" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_derive" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "smallvec" version = "0.6.5" @@ -593,6 +658,16 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "syn" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "termcolor" version = "0.3.6" @@ -637,6 +712,14 @@ dependencies = [ "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "toml" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "unicode-width" version = "0.1.5" @@ -711,6 +794,9 @@ dependencies = [ "checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" +"checksum askama 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c796fa924bb44233b38ae23020bfb229244e61910648d5f7bdc3659fcdf0572e" +"checksum askama_derive 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "57968dab3a9512b88af88012896e5e1d353e314afd5d190a98d0d4762c8f0a11" +"checksum askama_shared 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a180253955cc7bc732fdd18fad8aa9f04a4891fc7aa9c6ce525c128a1de3a146" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "346d7644f0b5f9bc73082d3b2236b69a05fd35cce0cfa3724e184e6a5c9e2a2f" "checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0" @@ -746,8 +832,10 @@ dependencies = [ "checksum lock_api 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "949826a5ccf18c1b3a7c3d57692778d21768b79e46eb9dd07bfc4c2160036c54" "checksum log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f" "checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" +"checksum memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4b3629fe9fdbff6daa6c33b90f7c08355c1aca05a3d01fa8063b822fcf185f3b" "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" +"checksum nom 4.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50b5469365a145d6c39ca7eff1a3048465206268c3f46617bb40c7752397be07" "checksum num-derive 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d2c31b75c36a993d30c7a13d70513cb93f02acafdd5b7ba250f9b0e18615de7" "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" "checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" @@ -772,6 +860,8 @@ dependencies = [ "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)" = "84257ccd054dc351472528c8587b4de2dbf0dc0fe2e634030c1a90bfdacebaa9" +"checksum serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)" = "31569d901045afbff7a9479f793177fe9259819aff10ab4f89ef69bbc5f567fe" "checksum smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "153ffa32fd170e9944f7e0838edf824a754ec4c1fc64746fcc9fe1f8fa602e5d" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum stderrlog 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "61dc66b7ae72b65636dbf36326f9638fb3ba27871bb737a62e2c309b87d91b70" @@ -779,11 +869,13 @@ dependencies = [ "checksum structopt 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8e9ad6a11096cbecdcca0cc6aa403fdfdbaeda2fb3323a39c98e6a166a1e45a" "checksum structopt-derive 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4cbce8ccdc62166bd594c14396a3242bf94c337a51dbfa9be1076dd74b3db2af" "checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741" +"checksum syn 0.15.8 (registry+https://github.com/rust-lang/crates.io-index)" = "356d1c5043597c40489e9af2d2498c7fefc33e99b7d75b43be336c8a59b3e45e" "checksum termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "adc4587ead41bf016f11af03e55a624c06568b5a19db4e90fde573d805074f83" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" +"checksum toml 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4a2ecc31b0351ea18b3fe11274b8db6e4d82bce861bbb22e6dbed40417902c65" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" diff --git a/rtiow/Cargo.toml b/rtiow/Cargo.toml index 7dd2a73..cee33af 100644 --- a/rtiow/Cargo.toml +++ b/rtiow/Cargo.toml @@ -14,6 +14,7 @@ stderrlog = "0.4.1" log = "0.4.5" cpuprofiler = "0.0.3" lazy_static = "1.1.0" +askama = "0.7.1" # For better profiling support. [profile.release] diff --git a/rtiow/src/bin/noise_explorer.rs b/rtiow/src/bin/noise_explorer.rs new file mode 100644 index 0000000..2530c86 --- /dev/null +++ b/rtiow/src/bin/noise_explorer.rs @@ -0,0 +1,265 @@ +#[macro_use] +extern crate askama; +#[macro_use] +extern crate log; +extern crate image; +extern crate rand; +extern crate stderrlog; + +#[macro_use] +extern crate structopt; + +extern crate rtiow; + +use std::fs; +use std::path::Path; +use std::path::PathBuf; +use std::time::SystemTime; + +use askama::Template; +use rand::SeedableRng; +use rand::XorShiftRng; +use structopt::StructOpt; + +use rtiow::lode::Noise; +use rtiow::vec3::Vec3; + +#[derive(Debug, StructOpt)] +#[structopt( + name = "noise_explorer", + about = "CLI for exploring Perlin noise" +)] +struct Opt { + /// Output directory + #[structopt(parse(from_os_str), default_value = "/tmp/perlin")] + pub output: PathBuf, + + /// Width of noise images + #[structopt(short = "w", long = "width", default_value = "128")] + pub width: u32, + /// Height of noise images + #[structopt(short = "h", long = "height", default_value = "128")] + pub height: u32, +} + +enum NoiseType { + Scale(f32), + Turbulance(usize), + Marble { + period: Vec3, + power: f32, + size: usize, + }, +} + +struct NoiseParams { + width: u32, + height: u32, + noise: NoiseType, +} + +impl NoiseParams { + fn compact_string(&self) -> String { + let mut s = format!("w{}-h{}", self.width, self.height); + + match self.noise { + NoiseType::Scale(scale) => s.push_str(&format!("-s{:.2}", scale)), + + NoiseType::Turbulance(turbulence) => s.push_str(&format!("-t{}", turbulence)), + NoiseType::Marble { + period, + power, + size, + } => s.push_str(&format!( + "-m{}-{}-{}", + period.to_string().replace(" ", "-"), + power, + size + )), + } + s + } + fn image_name(&self) -> String { + format!("noise-{}.png", self.compact_string()) + } +} + +fn render_noise_to_disk

(output_dir: P, noise_params: &NoiseParams) -> Result<(), std::io::Error> +where + P: AsRef, +{ + const SEED: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + let ref mut rng: XorShiftRng = SeedableRng::from_seed(SEED); + let ref output_dir: &Path = output_dir.as_ref(); + fs::create_dir_all(output_dir)?; + let mut img = image::GrayImage::new(noise_params.width, noise_params.height); + let tex = Noise::new(rng); + + 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::Turbulance(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 = (luma * 255.) as u8; + img.put_pixel(x, y, image::Luma([luma])); + } + } + + let path = output_dir.join(noise_params.image_name()); + info!("Saving {}", path.display()); + img.save(path) +} + +struct Specimen { + title: String, + params: Vec, +} + +#[derive(Template)] +#[template(path = "index.html")] +struct IndexTemplate { + render_time: u64, + specimens: Vec, +} + +fn render_html

(output_dir: P, specimens: Vec) -> Result<(), std::io::Error> +where + P: AsRef, +{ + let index = IndexTemplate { + render_time: SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs(), + specimens, + }; + fs::write( + output_dir.as_ref().join("index.html"), + index.render().unwrap(), + ) +} + +fn main() -> Result<(), std::io::Error> { + stderrlog::new() + .verbosity(3) + .timestamp(stderrlog::Timestamp::Millisecond) + .init() + .unwrap(); + let opt = Opt::from_args(); + + let mut specimens = Vec::new(); + let mut specimen = Specimen { + title: "Unperturbed".into(), + params: Vec::new(), + }; + { + let params = NoiseParams { + width: opt.width, + height: opt.height, + noise: NoiseType::Scale(1.), + }; + render_noise_to_disk(&opt.output, ¶ms)?; + specimen.params.push(params); + } + specimens.push(specimen); + + let mut specimen = Specimen { + title: "Varying Scale Factor <1".into(), + params: Vec::new(), + }; + let debug = false; + if !debug { + for scale in 1..10 { + let params = NoiseParams { + width: opt.width, + height: opt.height, + noise: NoiseType::Scale(scale as f32 / 10.), + }; + render_noise_to_disk(&opt.output, ¶ms)?; + specimen.params.push(params); + } + specimens.push(specimen); + + let mut specimen = Specimen { + title: "Varying Scale Factor >1".into(), + params: Vec::new(), + }; + for scale in 1..10 { + let params = NoiseParams { + width: opt.width, + height: opt.height, + noise: NoiseType::Scale(scale as f32), + }; + render_noise_to_disk(&opt.output, ¶ms)?; + specimen.params.push(params); + } + specimens.push(specimen); + + let mut specimen = Specimen { + title: "Varying Turbulence Factor".into(), + params: Vec::new(), + }; + for turbulence in 0..9 { + let params = NoiseParams { + width: opt.width, + height: opt.height, + noise: NoiseType::Turbulance(1 << turbulence), + }; + render_noise_to_disk(&opt.output, ¶ms)?; + specimen.params.push(params); + } + specimens.push(specimen); + + let mut specimen = Specimen { + title: "Varying Marble".into(), + params: Vec::new(), + }; + for power in 1..10 { + for size in 1..6 { + let params = NoiseParams { + width: opt.width, + height: opt.height, + noise: NoiseType::Marble { + period: Vec3::new(5., 10., 0.), + power: power as f32, + size: 1 << size, + }, + }; + render_noise_to_disk(&opt.output, ¶ms)?; + specimen.params.push(params); + } + } + for power in 1..10 { + let params = NoiseParams { + width: opt.width, + height: opt.height, + noise: NoiseType::Marble { + period: Vec3::new(0., 1., 0.), + power: power as f32, + size: 32, + }, + }; + render_noise_to_disk(&opt.output, ¶ms)?; + specimen.params.push(params); + } + specimens.push(specimen); + } + render_html(&opt.output, specimens) +} diff --git a/rtiow/src/build.rs b/rtiow/src/build.rs new file mode 100644 index 0000000..89e3e6b --- /dev/null +++ b/rtiow/src/build.rs @@ -0,0 +1,5 @@ +extern crate askama; + +fn main() { + askama::rerun_if_templates_changed(); +} diff --git a/rtiow/src/lib.rs b/rtiow/src/lib.rs index 4b29c36..d7d5f75 100644 --- a/rtiow/src/lib.rs +++ b/rtiow/src/lib.rs @@ -7,6 +7,7 @@ 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; @@ -23,8 +24,6 @@ pub mod vec3; extern crate crossbeam_channel; extern crate image; #[macro_use] -extern crate lazy_static; -#[macro_use] extern crate log; extern crate num_cpus; extern crate rand; diff --git a/rtiow/src/lode.rs b/rtiow/src/lode.rs new file mode 100644 index 0000000..7391c17 --- /dev/null +++ b/rtiow/src/lode.rs @@ -0,0 +1,85 @@ +/// Implements the concepts from https://lodev.org/cgtutor/randomnoise.html +use std::f32::consts::PI; + +use rand; + +use vec3::Vec3; + +const NOISE_SIZE: usize = 64; +pub struct Noise { + noise: [[[f32; NOISE_SIZE]; NOISE_SIZE]; NOISE_SIZE], +} + +impl Noise { + pub fn new(rng: &mut R) -> Noise + where + R: rand::Rng, + { + let mut noise = [[[0.; NOISE_SIZE]; NOISE_SIZE]; NOISE_SIZE]; + for x in 0..NOISE_SIZE { + for y in 0..NOISE_SIZE { + for z in 0..NOISE_SIZE { + noise[x][y][z] = rng.gen_range::(0., 1.); + } + } + } + Noise { 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; + // Fractional part of vector components + let x = p.x - p.x.floor(); + let y = p.y - p.y.floor(); + let z = p.z - p.z.floor(); + + // Wrap around the arrays + let x1 = (p.x.floor() as usize + NOISE_SIZE) % NOISE_SIZE; + let y1 = (p.y.floor() as usize + NOISE_SIZE) % NOISE_SIZE; + let z1 = (p.z.floor() as usize + NOISE_SIZE) % NOISE_SIZE; + + // Neighbor values + let x2 = (x1 as usize + NOISE_SIZE - 1) % NOISE_SIZE; + let y2 = (y1 as usize + NOISE_SIZE - 1) % NOISE_SIZE; + let z2 = (z1 as usize + NOISE_SIZE - 1) % NOISE_SIZE; + + trace!(target: "noise", "p {}", p); + trace!(target: "noise", "x {:.2} y {:.2} z {:.2}", x, y, z); + trace!(target: "noise", "x1 {:.2} y1 {:.2} z1 {:.2}", x1, y1, z1); + trace!(target: "noise", "x2 {:.2} y2 {:.2} z2 {:.2}", x2, y2, z2); + let mut value = 0.; + // Smooth the noise with bilinear interpolation. Completely untested + value += x * y * z * self.noise[x1][y1][z1]; + value += (1. - x) * y * z * self.noise[x2][y1][z1]; + value += x * (1. - y) * z * self.noise[x1][y2][z1]; + value += x * y * (1. - z) * self.noise[x1][y1][z2]; + value += (1. - x) * (1. - y) * z * self.noise[x2][y2][z1]; + value += x * (1. - y) * (1. - z) * self.noise[x1][y2][z2]; + value += (1. - x) * y * (1. - z) * self.noise[x2][y1][z2]; + value += (1. - x) * (1. - y) * (1. - z) * self.noise[x2][y2][z2]; + + trace!(target: "noise", "luma {}", value); + value + } +} diff --git a/rtiow/src/perlin.rs b/rtiow/src/perlin.rs index 7e99cb6..69aa5ac 100644 --- a/rtiow/src/perlin.rs +++ b/rtiow/src/perlin.rs @@ -1,16 +1,8 @@ -use rand; use rand::Rng; -//use rand::SeedableRng; -//use rand::XorShiftRng; use vec3::dot; use vec3::Vec3; -lazy_static! { - pub static ref GENERATOR: Perlin = Perlin::new(); -} -//const SEED: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; - pub struct Perlin { ran_vec: Vec, perm_x: Vec, @@ -18,9 +10,10 @@ pub struct Perlin { perm_z: Vec, } -fn perlin_generate() -> Vec { - //let mut rng: XorShiftRng = SeedableRng::from_seed(SEED); - let mut rng = rand::thread_rng(); +fn perlin_generate(rng: &mut R) -> Vec +where + R: Rng, +{ (0..256) .map(|_| { Vec3::new( @@ -31,9 +24,10 @@ fn perlin_generate() -> Vec { }).collect() } -fn perlin_generate_perm() -> Vec { - //let mut rng: XorShiftRng = SeedableRng::from_seed(SEED); - let mut rng = rand::thread_rng(); +fn perlin_generate_perm(rng: &mut R) -> Vec +where + R: Rng, +{ let mut p: Vec = (0..256).map(|i| i).collect(); rng.shuffle(&mut p); p @@ -61,32 +55,73 @@ fn perlin_interp(c: [[[Vec3; 2]; 2]; 2], u: f32, v: f32, w: f32) -> f32 { } } } + //info!("u {} v {} accum {}", u, v, accum); accum } -pub fn turb(p: Vec3, depth: usize) -> f32 { - let mut accum = 0.; - let mut temp_p = p; - let mut weight = 1.; - for _ in 0..depth { - accum += weight * GENERATOR.noise(temp_p); - weight *= 0.5; - temp_p = temp_p * 0.2; - } - accum.abs() -} - impl Perlin { - fn new() -> Perlin { - Perlin { - ran_vec: perlin_generate(), - perm_x: perlin_generate_perm(), - perm_y: perlin_generate_perm(), - perm_z: perlin_generate_perm(), - } + pub fn new(rng: &mut R) -> Perlin + where + R: Rng, + { + let p = Perlin { + ran_vec: perlin_generate(rng), + perm_x: perlin_generate_perm(rng), + perm_y: perlin_generate_perm(rng), + perm_z: perlin_generate_perm(rng), + }; + info!( + "ran_vec: {}", + p.ran_vec + .iter() + .map(|v| v.to_string()) + .collect::>() + .join(", ") + ); + info!( + "perm_x: {}", + p.perm_x + .iter() + .map(|v| v.to_string()) + .collect::>() + .join(", ") + ); + info!( + "perm_y: {}", + p.perm_y + .iter() + .map(|v| v.to_string()) + .collect::>() + .join(", ") + ); + info!( + "perm_z: {}", + p.perm_z + .iter() + .map(|v| v.to_string()) + .collect::>() + .join(", ") + ); + p } - pub fn noise(&self, p: Vec3) -> f32 { + pub fn turb(&self, p: Vec3, depth: usize) -> f32 { + let mut accum = 0.; + let mut temp_p = p; + let mut weight = 1.; + for _ in 0..depth { + accum += weight * self.noise(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(); let u = p.x - p.x.floor(); let v = p.y - p.y.floor(); let w = p.z - p.z.floor(); diff --git a/rtiow/src/renderer.rs b/rtiow/src/renderer.rs index c9ceba7..6514196 100644 --- a/rtiow/src/renderer.rs +++ b/rtiow/src/renderer.rs @@ -234,15 +234,15 @@ pub fn render( io::stdout().flush().unwrap(); if store_intermediate { let path = output_dir.join(format!("iteration{:05}.png", acc_count)); - trace!(target: "renderer", "Saving {}", path.to_string_lossy()); + trace!(target: "renderer", "Saving {}", path.display()); img.save(&path) - .unwrap_or_else(|_| panic!("Failed save {}", path.to_string_lossy())); + .unwrap_or_else(|_| panic!("Failed save {}", path.display())); } } println!(); io::stdout().flush().unwrap(); let path = output_dir.join("final.png"); // Write the contents of this image to the Writer in PNG format. - trace!(target: "renderer", "Saving {}", path.to_string_lossy()); + trace!(target: "renderer", "Saving {}", path.display()); img.save(path) } diff --git a/rtiow/src/scenes/final_scene.rs b/rtiow/src/scenes/final_scene.rs index 5d6fc7a..31338f1 100644 --- a/rtiow/src/scenes/final_scene.rs +++ b/rtiow/src/scenes/final_scene.rs @@ -48,7 +48,7 @@ pub fn new(opt: &Opt) -> Scene { let nb = 20; - let mut rng = rand::thread_rng(); + let ref mut rng = rand::thread_rng(); let mut boxlist: Vec> = Vec::with_capacity(10000); let mut list: Vec> = Vec::new(); let white: Arc = Arc::new(Lambertian::new(ConstantTexture::new([0.73, 0.73, 0.73]))); @@ -127,7 +127,7 @@ pub fn new(opt: &Opt) -> Scene { ))); // Perlin noise sphere - let pertext = NoiseTexture::with_scale(0.1); + let pertext = NoiseTexture::with_scale(rng, 0.1); 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 4edd561..ca4b0aa 100644 --- a/rtiow/src/scenes/perlin_debug.rs +++ b/rtiow/src/scenes/perlin_debug.rs @@ -1,5 +1,7 @@ use std::sync::Arc; +use rand; + use camera::Camera; use hitable::Hit; use hitable_list::HitableList; @@ -30,7 +32,9 @@ pub fn new(opt: &Opt) -> Scene { time_min, time_max, ); - let pertext: Arc = Arc::new(NoiseTexture::with_scale(10.)); + // TODO(wathiede): Use XOR rng for predictability? + let ref mut rng = rand::thread_rng(); + let pertext: Arc = Arc::new(NoiseTexture::with_scale(rng, 10.)); let objects: Vec> = vec![ Box::new(Sphere::new( diff --git a/rtiow/src/scenes/test.rs b/rtiow/src/scenes/test.rs index e771cec..df9dc24 100644 --- a/rtiow/src/scenes/test.rs +++ b/rtiow/src/scenes/test.rs @@ -1,4 +1,5 @@ use image; +use rand; use camera::Camera; use hitable::Hit; @@ -35,6 +36,7 @@ pub fn new(opt: &Opt) -> Scene { time_min, time_max, ); + let ref mut rng = rand::thread_rng(); let _ground_color = if opt.use_accel { Box::new(ConstantTexture::new(Vec3::new(1.0, 0.4, 0.4))) } else { @@ -51,7 +53,7 @@ pub fn new(opt: &Opt) -> Scene { Vec3::new(0., -1000., 0.), 1000., // Box::new(Lambertian::new(ground_color)), - Lambertian::new(NoiseTexture::with_scale(10.)), + Lambertian::new(NoiseTexture::with_scale(rng, 10.)), )), Box::new(XZRect::new( -100., diff --git a/rtiow/src/texture.rs b/rtiow/src/texture.rs index ad5cfcb..0a09ef5 100644 --- a/rtiow/src/texture.rs +++ b/rtiow/src/texture.rs @@ -1,9 +1,9 @@ use std::sync::Arc; use image::RgbImage; +use rand; -use perlin::turb; -use perlin::GENERATOR; +use perlin::Perlin; use vec3::Vec3; pub trait Texture: Send + Sync { @@ -71,24 +71,34 @@ where pub struct NoiseTexture { scale: f32, + perlin: Perlin, } impl NoiseTexture { - pub fn new() -> NoiseTexture { - NoiseTexture { scale: 1. } + pub fn new(rng: &mut R) -> NoiseTexture + where + R: rand::Rng, + { + NoiseTexture::with_scale(rng, 1.) } - pub fn with_scale(scale: f32) -> NoiseTexture { - NoiseTexture { scale } + pub fn with_scale(rng: &mut R, scale: f32) -> NoiseTexture + where + R: rand::Rng, + { + NoiseTexture { + scale, + perlin: Perlin::new(rng), + } } } impl Texture for NoiseTexture { fn value(&self, _u: f32, _v: f32, p: Vec3) -> Vec3 { - let _ = GENERATOR; - let _ = turb; + Vec3::new(1., 1., 1.) * 0.5 * (1. + self.perlin.noise(self.scale * p)) + //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. * turb(p, 7)).sin()) + //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)) } } diff --git a/rtiow/templates/index.html b/rtiow/templates/index.html new file mode 100644 index 0000000..a768fc6 --- /dev/null +++ b/rtiow/templates/index.html @@ -0,0 +1,23 @@ + + + + + Noise test + + + +{% for s in specimens %} +

{{ s.title }}

+{% for np in s.params %} +
+

+ + + +

{{ np.compact_string() }}
+
+{% endfor %} +{% endfor %} +

Rendered at: {{ render_time }}

+ + diff --git a/rtiow/templates/perlin.css b/rtiow/templates/perlin.css new file mode 100644 index 0000000..a984065 --- /dev/null +++ b/rtiow/templates/perlin.css @@ -0,0 +1,10 @@ +figure p { + margin: 0; +} + +figure { + display: inline-block; + margin: 0.25em; + text-align: center; + font-size: smaller; +}