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;
+}