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:
@@ -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,
|
||||||
}
|
}
|
||||||
|
|||||||
71
src/main.rs
71
src/main.rs
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
|
|||||||
12
src/vec3.rs
12
src/vec3.rs
@@ -92,6 +92,18 @@ impl Vec3{
|
|||||||
let ib = (Vec3::clamp(b, 0.0, 0.999) * 256.0) as i32;
|
let ib = (Vec3::clamp(b, 0.0, 0.999) * 256.0) as i32;
|
||||||
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 +
|
||||||
|
|||||||
Reference in New Issue
Block a user