aboutsummaryrefslogtreecommitdiff
path: root/src/texture
diff options
context:
space:
mode:
Diffstat (limited to 'src/texture')
-rw-r--r--src/texture/checker_texture.rs29
-rw-r--r--src/texture/image_texture.rs63
-rw-r--r--src/texture/mod.rs15
-rw-r--r--src/texture/noise_texture.rs22
-rw-r--r--src/texture/perlin.rs98
-rw-r--r--src/texture/solid_color.rs20
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()
+ }
+}