Feat: Materials!

Got the enums to work. This match block is going to be absolutely
massive, but whatever. Current materials are Lambertian and Metal.
This commit is contained in:
2023-06-04 10:31:59 -05:00
parent d1bde8a1a8
commit 7926cd2b13
5 changed files with 125 additions and 28 deletions

View File

@@ -2,12 +2,11 @@
use crate::vec3::Vec3; use crate::vec3::Vec3;
use crate::ray::Ray; use crate::ray::Ray;
use crate::material::Material; use crate::material::Material;
use std::rc::Rc;
pub struct HitRecord{ pub struct HitRecord{
pub p: Vec3, pub p: Vec3,
pub normal: Vec3, pub normal: Vec3,
pub material: Option<Rc<dyn Material>>, pub material: Option<Material>,
pub t: f32, pub t: f32,
pub front_face: bool, pub front_face: bool,
} }

View File

@@ -14,6 +14,7 @@ use crate::hittable::{
Hittable, Hittable,
HittableList, HittableList,
}; };
use crate::material::Material;
use crate::camera::Camera; use crate::camera::Camera;
use rand::{Rng, SeedableRng}; use rand::{Rng, SeedableRng};
@@ -32,25 +33,52 @@ fn main() {
// world // world
let mat_ground = Material::Lambertian{ albedo: Vec3::new(0.8, 0.8, 0.0) };
let mat_center = Material::Lambertian{ albedo: Vec3::new(0.7, 0.3, 0.3) };
let mat_left = Material::Metal{ albedo: Vec3::new(0.8, 0.8, 0.8) };
let mat_right = Material::Metal{ albedo: Vec3::new(0.8, 0.6, 0.2) };
let mut world = HittableList::new(); let mut world = HittableList::new();
world.add(
Box::new(
Sphere{
center: Vec3{ x: 0.0, y: 0.0, z: -1.0},
radius: 0.5,
material: None,
}
)
);
world.add( world.add(
Box::new( Box::new(
Sphere{ Sphere{
center: Vec3{ x: 0.0, y: -100.5, z: -1.0 }, center: Vec3{ x: 0.0, y: -100.5, z: -1.0 },
radius: 100.0, radius: 100.0,
material: None, material: Some(mat_ground),
} }
) )
); );
world.add(
Box::new(
Sphere{
center: Vec3{ x: 0.0, y: 0.0, z: -1.0},
radius: 0.5,
material: Some(mat_center),
}
)
);
world.add(
Box::new(
Sphere{
center: Vec3::new(-1.0, 0.0, -1.0),
radius: 0.5,
material: Some(mat_left),
}
)
);
world.add(
Box::new(
Sphere{
center: Vec3::new( 1.0, 0.0, -1.0),
radius: 0.5,
material: Some(mat_right),
}
)
);
// camera // camera
let cam = Camera::new(); let cam = Camera::new();
@@ -78,19 +106,26 @@ fn main() {
fn ray_color(r: Ray, world: &dyn Hittable, depth: u32, srng: &mut SmallRng, distrib: Uniform<f32> ) -> Vec3 { fn ray_color(r: Ray, world: &dyn Hittable, depth: u32, srng: &mut SmallRng, distrib: Uniform<f32> ) -> Vec3 {
// recursion depth guard // recursion depth guard
if depth == 0 { if depth == 0 {
return Vec3::new(0.0, 0.0, 0.0); return Vec3::zero();
} }
if let Some(rec) = world.hit(r, 0.001, f32::INFINITY){ if let Some(rec) = world.hit(r, 0.001, f32::INFINITY){
let target = rec.p + rec.normal + Vec3::rand_unit_vector(srng, distrib); let mut scattered = Ray {
return ray_color( orig: Vec3::zero(),
Ray{ dir: Vec3::zero()
orig: rec.p, };
dir: target - rec.p, let mut attenuation = Vec3::zero();
match rec.material {
Some(mat) => {
if mat.scatter(r, rec, &mut attenuation, &mut scattered, srng, distrib) {
return attenuation * ray_color(scattered, world, depth-1, srng, distrib);
};
}, },
world, depth, srng, distrib None => return Vec3::zero(),
) * 0.5; }
} }
let unitdir = Vec3::as_unit(&r.dir); let unitdir = Vec3::as_unit(&r.dir);
let t = 0.5 * (unitdir.y + 1.0); let t = 0.5 * (unitdir.y + 1.0);
return Vec3::ones() * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t return Vec3::ones() * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t

View File

@@ -3,9 +3,62 @@ use crate::ray::Ray;
use crate::hittable::HitRecord; use crate::hittable::HitRecord;
use crate::Vec3; use crate::Vec3;
pub trait Material { use rand::Rng;
fn scatter( use rand::rngs::SmallRng;
&self, ray_in: Ray, rec: HitRecord, attenuation: Vec3, scattered: Ray use rand::distributions::Uniform;
) -> bool;
#[derive(Copy, Clone, Debug)]
pub enum Material{
Lambertian{ albedo: Vec3 },
Metal{ albedo:Vec3 },
} }
impl Material {
pub fn scatter(
&self,
ray_in: Ray,
rec: HitRecord,
attenuation: &mut Vec3,
scattered:&mut Ray,
srng: &mut SmallRng,
distrib: Uniform<f32>,
) -> bool {
match self {
Material::Lambertian { albedo } => {
let scatter_dir = rec.normal + Vec3::rand_unit_vector(srng, distrib);
// The compiler might be smart enough to compute this ^^^ just once. In which case,
// I don't need to do this weird dance. Oh well. It'll work.
let scatter_dir = if scatter_dir.near_zero() { // if near zero,
rec.normal // replace with normal
} else {
scatter_dir // else preserve current
};
//TODO: Revisit this out-parameter pattern
// It's a side effect of C++'s obtuse move semantics (and the RTIOW author not
// using them at all)
*scattered = Ray{
orig: rec.p,
dir: scatter_dir
};
*attenuation = *albedo; // deref on both sides? Wacky
return true;
},
Material::Metal { albedo } => {
let reflected = Vec3::reflect(
Vec3::as_unit(&ray_in.dir),
rec.normal
);
*scattered = Ray{
orig: rec.p,
dir: reflected,
};
*attenuation = *albedo;
return Vec3::dot(scattered.dir, rec.normal) > 0.0;
},
_ => return false,
}
}
}

View File

@@ -7,12 +7,10 @@ use crate::hittable::{
use crate::material::Material; use crate::material::Material;
use crate::ray::Ray; use crate::ray::Ray;
use std::rc::Rc;
pub struct Sphere{ pub struct Sphere{
pub center: Vec3, pub center: Vec3,
pub radius: f32, pub radius: f32,
pub material: Option<Rc<dyn Material>>, pub material: Option<Material>,
} }
impl Hittable for Sphere { impl Hittable for Sphere {
@@ -39,7 +37,7 @@ impl Hittable for Sphere {
let mut record = HitRecord{ let mut record = HitRecord{
p: r.at(root), p: r.at(root),
normal: (r.at(root) - self.center) / self.radius, normal: (r.at(root) - self.center) / self.radius,
material: self.material.clone(), material: self.material,
t: root, t: root,
front_face: false, front_face: false,
}; };

View File

@@ -93,6 +93,18 @@ impl Vec3{
format!("{} {} {}", ir, ig, ib) format!("{} {} {}", ir, ig, ib)
} }
pub fn near_zero(&self) -> bool {
let epsilon: f32 = 1e-8;
return
self.x < epsilon &&
self.y < epsilon &&
self.z < epsilon
}
pub fn reflect(v: Vec3, n: Vec3) -> Vec3 {
return v - n * Vec3::dot(v, n) * 2.0;
}
pub fn dot(left: Vec3, right: Vec3) -> f32{ pub fn dot(left: Vec3, right: Vec3) -> f32{
left.x * right.x + left.x * right.x +
left.y * right.y + left.y * right.y +