2023-01-21 08:03:11 +00:00
|
|
|
// https://www.scratchapixel.com/lessons/procedural-generation-virtual-worlds/procedural-patterns-noise-part-1/creating-simple-2D-noise.html
|
|
|
|
|
2023-01-21 08:22:22 +00:00
|
|
|
use std::mem;
|
2023-01-21 08:03:11 +00:00
|
|
|
|
|
|
|
use rand::{Rng, RngCore};
|
|
|
|
|
|
|
|
use crate::ppm::{Pixel, Ppm};
|
|
|
|
use crate::vec2::Vec2;
|
|
|
|
|
|
|
|
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 {
|
2023-01-21 08:22:22 +00:00
|
|
|
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);
|
2023-01-21 08:03:11 +00:00
|
|
|
}
|
2023-01-21 08:22:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
|
|
}
|
2023-01-21 08:03:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct ValueNoise {
|
2023-01-21 08:22:22 +00:00
|
|
|
r: [f64; MAX_TABLE_SIZE * MAX_TABLE_SIZE],
|
2023-01-21 08:03:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ValueNoise {
|
2023-01-21 08:22:22 +00:00
|
|
|
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);
|
2023-01-21 08:03:11 +00:00
|
|
|
}
|
2023-01-21 08:22:22 +00:00
|
|
|
ValueNoise { r }
|
|
|
|
}
|
2023-01-21 08:03:11 +00:00
|
|
|
|
2023-01-21 08:22:22 +00:00
|
|
|
pub fn eval(&self, point: Vec2) -> f64 {
|
|
|
|
let xi = point.x.floor();
|
|
|
|
let yi = point.y.floor();
|
2023-01-21 08:03:11 +00:00
|
|
|
|
2023-01-21 08:22:22 +00:00
|
|
|
let tx = point.x - xi;
|
|
|
|
let ty = point.y - yi;
|
2023-01-21 08:03:11 +00:00
|
|
|
|
2023-01-21 08:22:22 +00:00
|
|
|
let xi = xi as usize;
|
|
|
|
let yi = yi as usize;
|
2023-01-21 08:03:11 +00:00
|
|
|
|
2023-01-21 08:22:22 +00:00
|
|
|
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;
|
2023-01-21 08:03:11 +00:00
|
|
|
|
2023-01-21 08:22:22 +00:00
|
|
|
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];
|
2023-01-21 08:03:11 +00:00
|
|
|
|
2023-01-21 08:22:22 +00:00
|
|
|
let sx = smooth_step(tx);
|
|
|
|
let sy = smooth_step(ty);
|
2023-01-21 08:03:11 +00:00
|
|
|
|
2023-01-21 08:22:22 +00:00
|
|
|
let nx0 = lerp(c00, c10, sx);
|
|
|
|
let nx1 = lerp(c01, c11, sx);
|
2023-01-21 08:03:11 +00:00
|
|
|
|
2023-01-21 08:22:22 +00:00
|
|
|
lerp(nx0, nx1, sy)
|
|
|
|
}
|
2023-01-21 08:03:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn smooth_step(t: f64) -> f64 {
|
2023-01-21 08:22:22 +00:00
|
|
|
t * t * (3.0 - 2.0 * t)
|
2023-01-21 08:03:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn lerp(lo: f64, hi: f64, t: f64) -> f64 {
|
2023-01-21 08:22:22 +00:00
|
|
|
lo * (1.0 - t) + hi * t
|
2023-01-21 08:03:11 +00:00
|
|
|
}
|