diff --git a/src/hittable.rs b/src/hittable.rs index 270c7f3..f4e7c74 100644 --- a/src/hittable.rs +++ b/src/hittable.rs @@ -1,14 +1,18 @@ use crate::{Point3, Ray, Vec3}; use crate::material::{Material}; use crate::aabb::Aabb; +use std::f64::consts::PI; pub type HittableList = Vec>; -#[derive(Debug, Clone, Copy)] +//#[derive(Debug, Clone, Copy)] +#[derive(Clone, Copy)] pub struct HitRecord<'material> { pub point: Point3, pub normal: Vec3, pub t: f64, + pub u: f64, + pub v: f64, pub front_face: bool, pub material: &'material Material } @@ -18,7 +22,7 @@ pub trait Hittable { fn bounding_box(&self, time0: f64, time1: f64) -> Option; } -#[derive(Clone)] +//#[derive(Clone)] pub struct Sphere { pub center: Point3, pub radius: f64, @@ -28,6 +32,19 @@ impl Sphere { pub fn new(center: Point3, radius: f64, material: Material) -> Sphere { Sphere { center, radius, material } } + pub fn uv(point: &Point3) -> (f64, f64) { + // p: a given point on the sphere of radius one, centered at the origin. + // u: returned value [0,1] of angle around the Y axis from X=-1. + // v: returned value [0,1] of angle from Y=-1 to Y=+1. + // <1 0 0> yields <0.50 0.50> <-1 0 0> yields <0.00 0.50> + // <0 1 0> yields <0.50 1.00> < 0 -1 0> yields <0.50 0.00> + // <0 0 1> yields <0.25 0.50> < 0 0 -1> yields <0.75 0.50> + + let theta = -(point.y()).acos(); + let phi = -(point.z()).atan2(point.x()) + PI; + + (phi / (2.0 * PI), theta / PI) + } } impl Default for Sphere { fn default() -> Self { @@ -57,11 +74,14 @@ impl Hittable for Sphere { let normal = (point - self.center) / self.radius; let dot = ray.direction().dot(&normal); let front_face = dot < 0.0; + let (u, v) = Sphere::uv(&normal); let normal = if front_face { normal } else { -normal }; Some(HitRecord { point, normal, t: root, + u, + v, front_face, material: &self.material }) @@ -75,7 +95,7 @@ impl Hittable for Sphere { } } -#[derive(Clone)] +//#[derive(Clone)] pub struct MovableSphere { pub center0: Point3, pub center1: Point3, @@ -130,13 +150,16 @@ impl Hittable for MovableSphere { let normal = (point - self.center(ray.time())) / self.radius; let dot = ray.direction().dot(&normal); let front_face = dot < 0.0; + let (u, v) = Sphere::uv(&normal); let normal = if front_face { normal } else { -normal }; Some(HitRecord { point, normal, t: root, + u, + v, front_face, - material: &self.material + material: &self.material, }) } fn bounding_box(&self, time0: f64, time1: f64) -> Option { diff --git a/src/main.rs b/src/main.rs index afd3b53..2b6b901 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,7 @@ use rayon::iter::{IntoParallelIterator, ParallelIterator}; use crate::material::{Dielectric, Lambertian, Material, Metal}; use crate::aabb::Aabb; use crate::bvh::BVH; +use crate::texture::CheckerTexture; mod vec3; mod ray; @@ -22,11 +23,16 @@ mod camera; mod output; mod aabb; mod bvh; +mod texture; fn random_scene() -> HittableList { let mut world:HittableList = Vec::new(); - let material_ground = Material::Lambertian(Lambertian::new(Color::new(0.5, 0.5, 0.5))); + //let material_ground = Material::Lambertian(Lambertian::new(Color::new(0.5, 0.5, 0.5))); + let checker = CheckerTexture::colored( + Color::new(0.2, 0.3, 0.1), + Color::new(0.9, 0.9, 0.9)); + let material_ground = Material::Lambertian(Lambertian::textured(Box::new(checker))); let ground = Sphere { center: Point3::new(0.0, -1000.0, 0.0), radius: 1000.0, diff --git a/src/material.rs b/src/material.rs index fb22001..14f7425 100644 --- a/src/material.rs +++ b/src/material.rs @@ -1,12 +1,13 @@ use rand::Rng; use crate::hittable::HitRecord; use crate::{Color, Ray, Vec3}; +use crate::texture::{SolidColor, Texture}; pub trait Scatterable { fn scatter(&self, ray: &Ray, hit_record: &HitRecord) -> Option<(Option, Color)>; } -#[derive(Debug, Clone)] +//#[derive(Debug, Clone)] pub enum Material { Lambertian(Lambertian), Metal(Metal), @@ -29,12 +30,16 @@ impl Scatterable for Material { } } -#[derive(Debug, Clone)] +//#[derive(Debug, Clone)] pub struct Lambertian { - pub albedo: Color + pub albedo: Box } impl Lambertian { pub fn new(albedo: Color) -> Self { + let texture = SolidColor::from(albedo); + Lambertian { albedo: Box::new(texture) } + } + pub fn textured(albedo: Box) -> Self { Lambertian { albedo } } } @@ -46,7 +51,7 @@ impl Scatterable for Lambertian { direction = hit_record.normal; } let scattered = Ray::new(hit_record.point, direction, ray.time()); - Some((Some(scattered), self.albedo)) + Some((Some(scattered), self.albedo.value(hit_record.u, hit_record.v, &hit_record.point))) } } diff --git a/src/texture.rs b/src/texture.rs new file mode 100644 index 0000000..9f67d75 --- /dev/null +++ b/src/texture.rs @@ -0,0 +1,56 @@ +use crate::{Point3, Color}; + +pub trait Texture: Sync { + fn value(&self, u: f64, v: f64, point: &Point3) -> Color; +} + +pub struct SolidColor { + color_value: Color +} + +impl SolidColor { + pub fn new(red: f64, green: f64, blue: f64) -> Self { + SolidColor { + color_value: Color::new(red, green, blue) + } + } + +} + +impl From for SolidColor { + fn from(color: Color) -> Self { + SolidColor { color_value: color } + } +} + +impl Texture for SolidColor { + fn value(&self, _u: f64, _v: f64, _point: &Point3) -> Color { + self.color_value + } +} + +pub struct CheckerTexture { + pub odd: Box, + pub even: Box +} + +impl CheckerTexture { + pub fn colored(color1: Color, color2: Color) -> Self { + CheckerTexture { + even: Box::new(SolidColor::from(color1)), + odd: Box::new(SolidColor::from(color2)), + } + } +} + +impl Texture for CheckerTexture { + fn value(&self, u: f64, v: f64, point: &Point3) -> Color { + let sines = (10.0 * point.x().sin()) * + (10.0 * point.y().sin()) * + (10.0 * point.z().sin()); + match sines { + sines if sines < 0.0 => self.odd.value(u, v, point), + _ => self.even.value(u, v, point), + } + } +}