diff options
Diffstat (limited to 'src/texture')
-rw-r--r-- | src/texture/checker_texture.rs | 29 | ||||
-rw-r--r-- | src/texture/image_texture.rs | 63 | ||||
-rw-r--r-- | src/texture/mod.rs | 15 | ||||
-rw-r--r-- | src/texture/noise_texture.rs | 22 | ||||
-rw-r--r-- | src/texture/perlin.rs | 98 | ||||
-rw-r--r-- | src/texture/solid_color.rs | 20 |
6 files changed, 247 insertions, 0 deletions
diff --git a/src/texture/checker_texture.rs b/src/texture/checker_texture.rs new file mode 100644 index 0000000..385017b --- /dev/null +++ b/src/texture/checker_texture.rs @@ -0,0 +1,29 @@ +use std::sync::Arc; + +use super::{Texture, SolidColor}; +use crate::{vec3::Color, vec3::Point3}; + +pub struct CheckerTexture { + odd: Arc<dyn Texture>, + even: Arc<dyn Texture>, +} + +impl CheckerTexture { + pub fn from_colors(odd: Color, even: Color) -> Self { + Self { + odd: Arc::new(SolidColor::from_color(odd)), + even: Arc::new(SolidColor::from_color(even)), + } + } +} + +impl Texture for CheckerTexture { + fn value(&self, u: f64, v: f64, p: &Point3) -> Color { + let sines = (10.0 * p.x).sin() * (10.0 * p.y).sin() * (10.0 * p.z).sin(); + if sines < 0.0 { + self.odd.value(u, v, p) + } else { + self.even.value(u, v, p) + } + } +} diff --git a/src/texture/image_texture.rs b/src/texture/image_texture.rs new file mode 100644 index 0000000..763c3f0 --- /dev/null +++ b/src/texture/image_texture.rs @@ -0,0 +1,63 @@ +use super::Texture; +use crate::{vec3::Color, vec3::Point3}; + +// assume 24 bit depth +const BYTES_PER_PIXEL: usize = 3; +pub struct ImageTexture { + data: Vec<u8>, + width: usize, + height: usize, + bytes_per_scanline: usize, +} + +impl ImageTexture { + pub fn from_bmp_data(bmp_data: &Vec<u8>) -> Self { + let data_position = u32::from_le_bytes([ + bmp_data[0x0A], + bmp_data[0x0B], + bmp_data[0x0C], + bmp_data[0x0D], + ]); + // assuming windows BITMAPINFOHEADER, these are i32 + let width = i32::from_le_bytes([ + bmp_data[0x12], + bmp_data[0x13], + bmp_data[0x14], + bmp_data[0x15], + ]) as usize; + let height = i32::from_le_bytes([ + bmp_data[0x16], + bmp_data[0x17], + bmp_data[0x18], + bmp_data[0x19], + ]) as usize; + Self { + data: bmp_data[(data_position as usize)..bmp_data.len()].to_vec(), + height, + width, + bytes_per_scanline: BYTES_PER_PIXEL * width, + } + } +} + +impl Texture for ImageTexture { + fn value(&self, u: f64, v: f64, _: &Point3) -> Color { + let u = u.clamp(0.0, 1.0); + // This is a deviation from the book, where v gets flipped. + // This is probably because the BMP loader loads in stuff upside down. + //let v = 1.0 - v.clamp(0.0, 1.0); + let v = v.clamp(0.0, 1.0); + let mut i = (u * self.width as f64) as usize; + let mut j = (v * self.height as f64) as usize; + + if i >= self.width { i = self.width - 1 }; + if j >= self.height { j = self.height - 1 }; + let color_scale = 1.0 / 255.0; + let pixel = j * self.bytes_per_scanline + i * BYTES_PER_PIXEL; + Color { + x: color_scale * *self.data.get(pixel + 2).unwrap() as f64, + y: color_scale * *self.data.get(pixel + 1).unwrap() as f64, + z: color_scale * *self.data.get(pixel).unwrap() as f64, + } + } +} diff --git a/src/texture/mod.rs b/src/texture/mod.rs new file mode 100644 index 0000000..55bc9cf --- /dev/null +++ b/src/texture/mod.rs @@ -0,0 +1,15 @@ +mod perlin; +mod solid_color; +pub use solid_color::SolidColor; +mod checker_texture; +pub use checker_texture::CheckerTexture; +mod noise_texture; +pub use noise_texture::NoiseTexture; +mod image_texture; +pub use image_texture::ImageTexture; + +use crate::{vec3::Color, vec3::Point3}; + +pub trait Texture: Send + Sync { + fn value(&self, u: f64, v: f64, p: &Point3) -> Color; +} diff --git a/src/texture/noise_texture.rs b/src/texture/noise_texture.rs new file mode 100644 index 0000000..92ac166 --- /dev/null +++ b/src/texture/noise_texture.rs @@ -0,0 +1,22 @@ +use super::{Texture, perlin::Perlin}; +use crate::{vec3::Color, vec3::Point3}; + +pub struct NoiseTexture { + noise: Perlin, + scale: f64, +} + +impl NoiseTexture { + pub fn new(scale: f64) -> Self { + Self { + noise: Perlin::new(), + scale, + } + } +} + +impl Texture for NoiseTexture { + fn value(&self, _: f64, _: f64, p: &Point3) -> Color { + Color { x: 1.0, y: 1.0, z: 1.0 } * 0.5 * (1.0 + (self.scale * p.z + 10.0 * self.noise.turb(p, 7)).sin()) + } +} diff --git a/src/texture/perlin.rs b/src/texture/perlin.rs new file mode 100644 index 0000000..43d8f64 --- /dev/null +++ b/src/texture/perlin.rs @@ -0,0 +1,98 @@ +use crate::vec3::{Vec3, Point3}; + +const POINT_COUNT: usize = 256; + +pub struct Perlin { + ranvec: Vec<Vec3>, + perm_x: Vec<usize>, + perm_y: Vec<usize>, + perm_z: Vec<usize>, +} + +impl Perlin { + pub fn new() -> Self { + let mut ranvec = Vec::with_capacity(POINT_COUNT); + for _ in 0..POINT_COUNT { + ranvec.push(Vec3::random_in_range(-1.0, 1.0).unit_vector()); + } + Self { + ranvec, + perm_x: Self::generate_perm(), + perm_y: Self::generate_perm(), + perm_z: Self::generate_perm(), + } + } + + pub fn turb(&self, point: &Point3, depth: u32) -> f64 { + let mut accum = 0.0; + let mut temp_point = point.clone(); + let mut weight = 1.0; + + for _ in 0..depth { + accum += weight * self.noise(&temp_point); + weight *= 0.5; + temp_point *= 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] = Default::default(); + for di in 0..2 { + for dj in 0..2 { + for dk in 0..2 { + c[di][dj][dk] = self + .ranvec + .get( + (self.perm_x.get(((i + di as i32) & 255) as usize).unwrap() + ^ self.perm_y.get(((j + dj as i32) & 255) as usize).unwrap() + ^ self.perm_z.get(((k + dk as i32) & 255) as usize).unwrap()) + as usize, + ) + .unwrap().clone(); + } + } + } + Self::trilinear_interpolate(c, u, v, w) + } + + fn trilinear_interpolate(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: f64 = 0.0; + for i in 0..2 { + for j in 0..2 { + for k in 0..2 { + let i_f = i as f64; + let j_f = j as f64; + let k_f = k as f64; + let weight_v = Vec3 { x: u - i_f, y: v - j_f, z: w - k_f }; + accum += (i_f * uu + (1.0 - i_f) * (1.0 - uu)) * + (j_f * vv + (1.0 - j_f) * (1.0 - vv)) * + (k_f * ww + (1.0 - k_f) * (1.0 - ww)) * + c[i][j][k].dot(&weight_v); + } + } + } + accum + } + + fn generate_perm() -> Vec<usize> { + let mut p = (0..POINT_COUNT).collect(); + Self::permute(&mut p, POINT_COUNT); + p + } + + fn permute(p: &mut Vec<usize>, n: usize) { + for i in (1..n).rev() { + p.swap(i, rand::random::<usize>() % i); + } + } +} diff --git a/src/texture/solid_color.rs b/src/texture/solid_color.rs new file mode 100644 index 0000000..3af46ca --- /dev/null +++ b/src/texture/solid_color.rs @@ -0,0 +1,20 @@ +use super::Texture; +use crate::{vec3::Color, vec3::Point3}; + +pub struct SolidColor { + pub color_value: Color, +} + +impl SolidColor { + pub fn from_color(color_value: Color) -> Self { + Self { + color_value, + } + } +} + +impl Texture for SolidColor { + fn value(&self, _: f64, _: f64, _: &Point3) -> Color { + self.color_value.clone() + } +} |