From 000000208701c5c60618fc14aab06c5f9928013c Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Sat, 21 Jan 2023 02:22:22 -0600 Subject: [PATCH] turbulence + tuning --- assignment-0/src/main.rs | 41 +++++++++---- assignment-0/src/ppm.rs | 30 +++++----- assignment-0/src/turbulence.rs | 51 ++++++++++++++++ assignment-0/src/value_noise.rs | 103 ++++++++++++++++---------------- assignment-0/src/vec2.rs | 23 +++---- rustfmt.toml | 2 + 6 files changed, 160 insertions(+), 90 deletions(-) create mode 100644 assignment-0/src/turbulence.rs create mode 100644 rustfmt.toml diff --git a/assignment-0/src/main.rs b/assignment-0/src/main.rs index bfb509b..3c787df 100644 --- a/assignment-0/src/main.rs +++ b/assignment-0/src/main.rs @@ -1,33 +1,48 @@ use std::{fs::File, path::PathBuf}; use anyhow::Result; -use clap::Parser; +use clap::{Parser, ValueEnum}; +use turbulence::generate_turbulence; use crate::value_noise::generate_noise; mod ppm; +mod turbulence; mod value_noise; mod vec2; #[derive(Parser)] struct Opt { - #[clap(short = 'o', long = "out", default_value = "out.ppm")] - output_path: PathBuf, + #[clap(long = "algorithm", default_value = "turbulence")] + algorithm: Algorithm, + + #[clap(long = "size", default_value = "1024")] + size: usize, + + #[clap(short = 'o', long = "out", default_value = "out.ppm")] + output_path: PathBuf, +} + +#[derive(ValueEnum, Clone)] +enum Algorithm { + Noise, + Turbulence, } fn main() -> Result<()> { - let opt = Opt::parse(); + let opt = Opt::parse(); - let rng = rand::thread_rng(); + let rng = rand::thread_rng(); - let width = 256; - let height = 256; - let ppm = generate_noise(width, height, rng); + let ppm = match opt.algorithm { + Algorithm::Noise => generate_noise(opt.size, opt.size, rng), + Algorithm::Turbulence => generate_turbulence(opt.size, opt.size, rng), + }; - { - let file = File::create(opt.output_path)?; - ppm.write(file)?; - } + { + let file = File::create(opt.output_path)?; + ppm.write(file)?; + } - Ok(()) + Ok(()) } diff --git a/assignment-0/src/ppm.rs b/assignment-0/src/ppm.rs index 8a2dfb6..668a30e 100644 --- a/assignment-0/src/ppm.rs +++ b/assignment-0/src/ppm.rs @@ -3,24 +3,24 @@ use std::io::{Result, Write}; pub struct Pixel(pub u8, pub u8, pub u8); pub struct Ppm { - pub(crate) width: usize, - pub(crate) height: usize, - pub(crate) data: Vec, + pub(crate) width: usize, + pub(crate) height: usize, + pub(crate) data: Vec, } impl Ppm { - pub fn write(&self, mut w: impl Write) -> Result<()> { - // Header - let header = format!("P3 {} {} 255\n", self.width, self.height); - w.write_all(header.as_bytes())?; + pub fn write(&self, mut w: impl Write) -> Result<()> { + // Header + let header = format!("P3 {} {} 255\n", self.width, self.height); + w.write_all(header.as_bytes())?; - // Pixel data - for pixel in self.data.iter() { - let Pixel(red, green, blue) = pixel; - let pixel = format!("{red} {green} {blue}\n"); - w.write_all(pixel.as_bytes())?; - } - - Ok(()) + // Pixel data + for pixel in self.data.iter() { + let Pixel(red, green, blue) = pixel; + let pixel = format!("{red} {green} {blue}\n"); + w.write_all(pixel.as_bytes())?; } + + Ok(()) + } } diff --git a/assignment-0/src/turbulence.rs b/assignment-0/src/turbulence.rs new file mode 100644 index 000000000..d8b69b2 --- /dev/null +++ b/assignment-0/src/turbulence.rs @@ -0,0 +1,51 @@ +// https://www.scratchapixel.com/lessons/procedural-generation-virtual-worlds/procedural-patterns-noise-part-1/simple-pattern-examples.html + +use rand::RngCore; + +use crate::ppm::{Pixel, Ppm}; +use crate::value_noise::ValueNoise; +use crate::vec2::Vec2; + +pub fn generate_turbulence( + width: usize, + height: usize, + rng: impl RngCore, +) -> Ppm { + let mut noise_map = vec![0.0; width * height]; + let noise = ValueNoise::new(rng); + + let frequency = 0.02; + let frequency_mult = 1.8; + let amplitude_mult = 0.35; + let num_layers = 5; + let mut max_noise_val = 0.0f64; + + for j in 0..height { + for i in 0..width { + let mut noise_point = Vec2::new(i as f64, j as f64) * frequency; + let mut amplitude = 1.0; + + for _ in 0..num_layers { + noise_map[j * width + i] = + (2.0 * noise.eval(noise_point) - 1.0).abs() * amplitude; + noise_point = noise_point * frequency_mult; + amplitude *= amplitude_mult; + } + + max_noise_val = max_noise_val.max(noise_map[j * width + i]); + } + } + + let data = noise_map + .into_iter() + .map(|f| { + let v = (f / max_noise_val * 192.0 + 32.0).floor() as u8; + Pixel(v, v, v) + }) + .collect(); + Ppm { + width, + height, + data, + } +} diff --git a/assignment-0/src/value_noise.rs b/assignment-0/src/value_noise.rs index e8e1352..d738f89 100644 --- a/assignment-0/src/value_noise.rs +++ b/assignment-0/src/value_noise.rs @@ -1,6 +1,6 @@ // https://www.scratchapixel.com/lessons/procedural-generation-virtual-worlds/procedural-patterns-noise-part-1/creating-simple-2D-noise.html -use std::mem::{self}; +use std::mem; use rand::{Rng, RngCore}; @@ -11,78 +11,79 @@ const MAX_TABLE_SIZE: usize = 256; const MAX_TABLE_SIZE_MASK: usize = MAX_TABLE_SIZE - 1; pub fn generate_noise(width: usize, height: usize, rng: impl RngCore) -> Ppm { - let mut noise_map = vec![0.0; width * height]; - let noise = ValueNoise::new(rng); - let frequency = 0.05; + let mut noise_map = vec![0.0; width * height]; + let noise = ValueNoise::new(rng); + let frequency = 0.05; - for j in 0..height { - for i in 0..width { - let vec = Vec2::new(i as f64, j as f64); - noise_map[j * width + i] = noise.eval(vec * frequency); - } + for j in 0..height { + for i in 0..width { + let vec = Vec2::new(i as f64, j as f64); + noise_map[j * width + i] = noise.eval(vec * frequency); } + } - let data = noise_map - .into_iter() - .map(|f| { - let v = (f * 256.0).floor() as u8; - Pixel(v, v, v) - }) - .collect(); - Ppm { - width, - height, - data, - } + let data = noise_map + .into_iter() + .map(|f| { + let v = (f * 256.0).floor() as u8; + Pixel(v, v, v) + }) + .collect(); + Ppm { + width, + height, + data, + } } pub struct ValueNoise { - r: [f64; MAX_TABLE_SIZE * MAX_TABLE_SIZE], + r: [f64; MAX_TABLE_SIZE * MAX_TABLE_SIZE], } impl ValueNoise { - pub fn new(mut rng: impl RngCore) -> Self { - let mut r: [f64; MAX_TABLE_SIZE * MAX_TABLE_SIZE] = unsafe { mem::zeroed() }; - for k in 0..MAX_TABLE_SIZE * MAX_TABLE_SIZE { - r[k] = rng.gen_range(0.0..1.0); - } - ValueNoise { r } + pub fn new(mut rng: impl RngCore) -> Self { + let mut r: [f64; MAX_TABLE_SIZE * MAX_TABLE_SIZE] = + unsafe { mem::zeroed() }; + for k in 0..MAX_TABLE_SIZE * MAX_TABLE_SIZE { + r[k] = rng.gen_range(0.0..1.0); } + ValueNoise { r } + } - pub fn eval(&self, point: Vec2) -> f64 { - let xi = point.x.floor(); - let yi = point.y.floor(); + pub fn eval(&self, point: Vec2) -> f64 { + let xi = point.x.floor(); + let yi = point.y.floor(); - let tx = point.x - xi; - let ty = point.y - yi; + let tx = point.x - xi; + let ty = point.y - yi; - let xi = xi as usize; - let yi = yi as usize; + let xi = xi as usize; + let yi = yi as usize; - let rx0 = xi & MAX_TABLE_SIZE_MASK; - let rx1 = (rx0 + 1) & MAX_TABLE_SIZE_MASK; - let ry0 = yi & MAX_TABLE_SIZE_MASK; - let ry1 = (ry0 + 1) & MAX_TABLE_SIZE_MASK; + let rx0 = xi & MAX_TABLE_SIZE_MASK; + let rx1 = (rx0 + 1) & MAX_TABLE_SIZE_MASK; + let ry0 = yi & MAX_TABLE_SIZE_MASK; + let ry1 = (ry0 + 1) & MAX_TABLE_SIZE_MASK; - let c00 = self.r[ry0 * MAX_TABLE_SIZE_MASK + rx0]; - let c10 = self.r[ry0 * MAX_TABLE_SIZE_MASK + rx1]; - let c01 = self.r[ry1 * MAX_TABLE_SIZE_MASK + rx0]; - let c11 = self.r[ry1 * MAX_TABLE_SIZE_MASK + rx1]; + let c00 = self.r[ry0 * MAX_TABLE_SIZE_MASK + rx0]; + let c10 = self.r[ry0 * MAX_TABLE_SIZE_MASK + rx1]; + let c01 = self.r[ry1 * MAX_TABLE_SIZE_MASK + rx0]; + let c11 = self.r[ry1 * MAX_TABLE_SIZE_MASK + rx1]; - let sx = smooth_step(tx); - let sy = smooth_step(ty); + let sx = smooth_step(tx); + let sy = smooth_step(ty); - let nx0 = lerp(c00, c10, sx); - let nx1 = lerp(c01, c11, sx); + let nx0 = lerp(c00, c10, sx); + let nx1 = lerp(c01, c11, sx); - lerp(nx0, nx1, sy) - } + lerp(nx0, nx1, sy) + } } fn smooth_step(t: f64) -> f64 { - t * t * (3.0 - 2.0 * t) + t * t * (3.0 - 2.0 * t) } fn lerp(lo: f64, hi: f64, t: f64) -> f64 { - lo * (1.0 - t) + hi * t + lo * (1.0 - t) + hi * t } diff --git a/assignment-0/src/vec2.rs b/assignment-0/src/vec2.rs index e38732a..c114dd9 100644 --- a/assignment-0/src/vec2.rs +++ b/assignment-0/src/vec2.rs @@ -1,23 +1,24 @@ use std::ops::Mul; +#[derive(Copy, Clone)] pub struct Vec2 { - pub x: T, - pub y: T, + pub x: T, + pub y: T, } impl Vec2 { - pub fn new(x: T, y: T) -> Self { - Self { x, y } - } + pub fn new(x: T, y: T) -> Self { + Self { x, y } + } } impl> Mul for Vec2 { - type Output = Vec2; + type Output = Vec2; - fn mul(self, rhs: S) -> Self::Output { - Vec2 { - x: self.x * rhs, - y: self.y * rhs, - } + fn mul(self, rhs: S) -> Self::Output { + Vec2 { + x: self.x * rhs, + y: self.y * rhs, } + } } diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 000000000..4c1eefa --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,2 @@ +max_width = 80 +tab_spaces = 2