89 lines
3.2 KiB
Rust
89 lines
3.2 KiB
Rust
/// Implements the concepts from https://lodev.org/cgtutor/randomnoise.html
|
|
use std::f32::consts::PI;
|
|
|
|
use rand;
|
|
|
|
use vec3::Vec3;
|
|
|
|
const NOISE_SIZE: usize = 128;
|
|
pub struct Noise {
|
|
// Using fixed array causes stack overflow.
|
|
noise: Vec<Vec<Vec<f32>>>, //[[[f32; NOISE_SIZE]; NOISE_SIZE]; NOISE_SIZE],
|
|
}
|
|
|
|
impl Noise {
|
|
pub fn new<R>(rng: &mut R) -> Noise
|
|
where
|
|
R: rand::Rng,
|
|
{
|
|
let mut noise = vec![vec![vec![0.; NOISE_SIZE]; NOISE_SIZE]; NOISE_SIZE];
|
|
// Squelch warning about only being used to index, as this way reads more consistently.
|
|
#[cfg_attr(feature = "cargo-clippy", allow(needless_range_loop))]
|
|
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::<f32>(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
|
|
}
|
|
}
|