diff --git a/src/demos/perlin_noise_ball.rs b/src/demos/perlin_noise_ball.rs index f1bb98f..8b71beb 100644 --- a/src/demos/perlin_noise_ball.rs +++ b/src/demos/perlin_noise_ball.rs @@ -29,13 +29,13 @@ impl Demo for PerlinNoiseBall { world.push(Arc::new(Sphere::new( Vec3::new(0.0, -1000.0, 0.0), 1000.0, - Lambertian::new(PerlinNoise::new(&mut rng)), + Lambertian::new(PerlinNoise::with_scale(&mut rng, 4.0)), ))); world.push(Arc::new(Sphere::new( Vec3::new(0.0, 2.0, 0.0), 2.0, - Lambertian::new(PerlinNoise::new(&mut rng)), + Lambertian::new(PerlinNoise::with_scale(&mut rng, 4.0)), ))); BvhNode::new(&mut rng, &mut world, 0.0, 1.0) diff --git a/src/main.rs b/src/main.rs index a8753b5..48236a4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,7 +23,7 @@ use demos::Demo; use std::time::Instant; -const NUM_SAMPLES: u8 = 255; +const NUM_SAMPLES: u8 = 25; const VERTICAL_PARTITION: usize = 12; const HORIZONTAL_PARTITION: usize = 12; const WIDTH: usize = 1920; diff --git a/src/materials/lambertian.rs b/src/materials/lambertian.rs index f3642b9..78966e4 100644 --- a/src/materials/lambertian.rs +++ b/src/materials/lambertian.rs @@ -22,7 +22,7 @@ impl Material for Lambertian { let scattered_ray = Ray::new(hit_rec.p, scatter_direction, ray.time()); ( - self.albedo.value(hit_rec.u, hit_rec.v, &hit_rec.p), + self.albedo.value(hit_rec.u, hit_rec.v, hit_rec.p), Some(scattered_ray), ) } diff --git a/src/texture/checker.rs b/src/texture/checker.rs index 3e25b94..bd4dd90 100644 --- a/src/texture/checker.rs +++ b/src/texture/checker.rs @@ -12,7 +12,7 @@ impl Checker { } impl Texture for Checker { - fn value(&self, u: f64, v: f64, p: &Vec3) -> Vec3 { + fn value(&self, u: f64, v: f64, p: Vec3) -> Vec3 { let sine_wave = f64::sin(10.0 * p.x()) * f64::sin(10.0 * p.y()) * f64::sin(10.0 * p.z()); if sine_wave < 0.0 { diff --git a/src/texture/mod.rs b/src/texture/mod.rs index 01201ad..e4d3c10 100644 --- a/src/texture/mod.rs +++ b/src/texture/mod.rs @@ -11,5 +11,5 @@ pub use solid::Solid; use crate::types::Vec3; pub trait Texture { - fn value(&self, u: f64, v: f64, p: &Vec3) -> Vec3; + fn value(&self, u: f64, v: f64, p: Vec3) -> Vec3; } diff --git a/src/texture/perlin.rs b/src/texture/perlin.rs index a3809e5..5881213 100644 --- a/src/texture/perlin.rs +++ b/src/texture/perlin.rs @@ -36,15 +36,61 @@ impl Perlin { p } - pub fn noise(&self, p: &Vec3) -> f64 { - let i = ((4.0 * p.x()) as i32 & 255) as usize; - let j = ((4.0 * p.y()) as i32 & 255) as usize; - let k = ((4.0 * p.z()) as i32 & 255) as usize; + pub fn noise(&self, p: Vec3) -> f64 { + let i = p.x().floor() as i32; + let j = p.y().floor() as i32; + let k = p.z().floor() as i32; - self.points[self.permute_x[i] ^ self.permute_y[j] ^ self.permute_z[k]] + let mut smooth_grid = [[[0.0; 2]; 2]; 2]; + + for (di, a) in smooth_grid.iter_mut().enumerate() { + let di = di as i32; + for (dj, b) in a.iter_mut().enumerate() { + let dj = dj as i32; + for (dk, c) in b.iter_mut().enumerate() { + let dk = dk as i32; + *c = self.points[self.permute_x[((i + di) & 255) as usize] + ^ self.permute_y[((j + dj) & 255) as usize] + ^ self.permute_z[((k + dk) & 255) as usize]] + } + } + } + + let mut u = p.x() - p.x().floor(); + let mut v = p.y() - p.y().floor(); + let mut w = p.z() - p.z().floor(); + + // Hermitian smoothing so we don't see obvious grid features in the picture + // Those features show up when we interpolate colors. Those features are + // also called mach bands + u = u * u * (3.0 - 2.0 * u); + v = v * v * (3.0 - 2.0 * v); + w = w * w * (3.0 - 2.0 * w); + + return trilinear_interpolate(smooth_grid, u, v, w); } } +fn trilinear_interpolate(smooth_grid: [[[f64; 2]; 2]; 2], u: f64, v: f64, w: f64) -> f64 { + let mut acc = 0.0; + + for (di, a) in smooth_grid.iter().enumerate() { + let di = di as f64; + for (dj, b) in a.iter().enumerate() { + let dj = dj as f64; + for (dk, c) in b.iter().enumerate() { + let dk = dk as f64; + acc += (di * u + (1.0 - di) * (1.0 - u)) + * (dj * v + (1.0 - dj) * (1.0 - v)) + * (dk * w + (1.0 - dk) * (1.0 - w)) + * c; + } + } + } + + acc +} + fn permute(rng: &mut R, p: &mut [usize]) { let l = p.len(); diff --git a/src/texture/perlin_noise.rs b/src/texture/perlin_noise.rs index 1f7490c..e7ab463 100644 --- a/src/texture/perlin_noise.rs +++ b/src/texture/perlin_noise.rs @@ -4,18 +4,28 @@ use crate::{texture::Perlin, types::Vec3, Texture}; pub struct PerlinNoise { noise: Perlin, + scale: f64, } impl PerlinNoise { + #[allow(dead_code)] pub fn new(rng: &mut R) -> Self { Self { noise: Perlin::new(rng), + scale: 1.0, + } + } + + pub fn with_scale(rng: &mut R, scale: f64) -> Self { + Self { + noise: Perlin::new(rng), + scale, } } } impl Texture for PerlinNoise { - fn value(&self, _u: f64, _v: f64, p: &Vec3) -> Vec3 { - Vec3::new(1.0, 1.0, 1.0) * self.noise.noise(p) + fn value(&self, _u: f64, _v: f64, p: Vec3) -> Vec3 { + Vec3::new(1.0, 1.0, 1.0) * self.noise.noise(p * self.scale) } } diff --git a/src/texture/solid.rs b/src/texture/solid.rs index e4ce99c..9839394 100644 --- a/src/texture/solid.rs +++ b/src/texture/solid.rs @@ -11,7 +11,7 @@ impl Solid { } impl Texture for Solid { - fn value(&self, _u: f64, _v: f64, _p: &Vec3) -> Vec3 { + fn value(&self, _u: f64, _v: f64, _p: Vec3) -> Vec3 { self.color } }