rustracer/src/perlin.rs

97 lines
3.0 KiB
Rust

use rand::Rng;
use crate::{Point3, Vec3};
pub struct Perlin {
ranvec: Vec<Vec3>,
perm_x: Vec<usize>,
perm_y: Vec<usize>,
perm_z: Vec<usize>,
}
impl Perlin {
const POINT_COUNT: usize = 256;
pub fn new() -> Self {
let mut ranvec = Vec::with_capacity(Perlin::POINT_COUNT);
(0..Perlin::POINT_COUNT).for_each(|_i|ranvec.push(Vec3::random(-1.0, 1.0)));
Perlin {
ranvec,
perm_x: Perlin::perlin_generate_perm(),
perm_y: Perlin::perlin_generate_perm(),
perm_z: Perlin::perlin_generate_perm(),
}
}
pub fn default_turbulence(&self, point: &Point3) -> f64 {
self.turbulence(point, 7)
}
pub fn turbulence(&self, point: &Point3, depth: i32) -> f64 {
let mut accum = 0.0;
let mut temp_p = point.clone();
let mut weight = 1.0;
for _i in 0..depth {
accum += weight * self.noise(&temp_p);
weight *= 0.5;
temp_p *= 2.0;
}
accum.abs()
}
pub fn noise(&self, point: &Point3) -> f64 {
let u = point.x() - point.x().floor();
let v = point.y() - point.y().floor();
let w = point.z() - point.z().floor();
let i = point.x().floor() as i32;
let j = point.y().floor() as i32;
let k = point.z().floor() as i32;
let mut c: [[[Vec3; 2]; 2]; 2] = [[[Vec3::default(); 2]; 2]; 2];
for di in 0..2 {
for dj in 0..2 {
for dk in 0..2 {
let idx = self.perm_x[((i+di) & 255) as usize] ^
self.perm_y[((j+dj) & 255) as usize] ^
self.perm_z[((k+dk) & 255) as usize];
c[di as usize][dj as usize][dk as usize] = self.ranvec[idx];
}
}
}
Perlin::perlin_interp(c, u, v, w)
}
fn perlin_generate_perm() -> Vec<usize> {
let mut p = Vec::with_capacity(Perlin::POINT_COUNT);
(0..Perlin::POINT_COUNT).for_each(|i| p.push(i));
let mut rng = rand::thread_rng();
(1..Perlin::POINT_COUNT).rev().for_each(|idx| {
let target = rng.gen_range(0..=idx);
p.swap(idx, target);
});
p
}
fn perlin_interp(c: [[[Vec3; 2]; 2]; 2], u: f64, v: f64, w: f64) -> f64 {
let uu = u * u * (3.0 - 2.0 * u);
let vv = v * v * (3.0 - 2.0 * v);
let ww = w * w * (3.0 - 2.0 * w);
let mut accum = 0.0;
for i in 0..2 {
for j in 0..2 {
for k in 0..2 {
let ifl = i as f64;
let jfl = j as f64;
let kfl = k as f64;
let weight_v = Vec3::new(u - ifl, v - jfl, w - kfl);
accum += (ifl*uu + (1.0-ifl)*(1.0-uu))
* (jfl*vv + (1.0-jfl)*(1.0-vv))
* (kfl*ww + (1.0-kfl)*(1.0-ww))
* (c[i][j][k]).dot(&weight_v);
}
}
}
accum
}
}