raytracers/rtiow/src/lode.rs

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