Added Materials, Lambertian and Metal along with Materials Demo
1. Added Materials Demo and Lambertian/Metal Materials. 2. Added a Multiplication implementation for Vec3 * Vec3
This commit is contained in:
parent
d4bee42b78
commit
edb0b64282
|
@ -39,9 +39,15 @@ impl Demo for DiffuseMaterials {
|
|||
}
|
||||
|
||||
color /= samples as f64;
|
||||
buf[offset] = (255.99 * color.r()) as u8;
|
||||
buf[offset + 1] = (255.99 * color.g()) as u8;
|
||||
buf[offset + 2] = (255.99 * color.b()) as u8;
|
||||
// Without taking square root of each color, we get a picture that
|
||||
// is quite dark
|
||||
// Spheres in this case are absorbing 50% of the light casted on them
|
||||
// So, IRL, It *should* look a bit lighter in color
|
||||
// To do that, We apply gamma correction by a factor of 2
|
||||
// which means multiple rgb values by 1/gamma aka 1/2
|
||||
buf[offset] = (255.99 * color.r().sqrt()) as u8;
|
||||
buf[offset + 1] = (255.99 * color.g().sqrt()) as u8;
|
||||
buf[offset + 2] = (255.99 * color.b().sqrt()) as u8;
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
|
@ -49,8 +55,12 @@ impl Demo for DiffuseMaterials {
|
|||
}
|
||||
|
||||
fn calc_color(ray: Ray, world: &HitableList, rng: &mut rand::rngs::ThreadRng) -> Vec3 {
|
||||
if let Some(hit_rec) = world.hit(&ray, 0.0, std::f64::MAX) {
|
||||
let target = hit_rec.p + hit_rec.normal + random_point_in_unit_sphere(rng);
|
||||
// The value of t_min here could've been 0.0 but since f32/f64 can only be
|
||||
// partially compared, It may cause shadow acne effect.
|
||||
// To combat this problem, We set a bias
|
||||
// More information here, https://www.opengl-tutorial.org/intermediate-tutorials/tutorial-16-shadow-mapping/#shadow-acne
|
||||
if let Some(hit_rec) = world.hit(&ray, 0.001, std::f64::MAX) {
|
||||
let target = hit_rec.p + hit_rec.normal + random_point_in_unit_space(rng);
|
||||
calc_color(Ray::new(hit_rec.p, target - hit_rec.p), &world, rng) * 0.5
|
||||
} else {
|
||||
let unit_direction = ray.direction().unit_vector();
|
||||
|
@ -59,7 +69,7 @@ fn calc_color(ray: Ray, world: &HitableList, rng: &mut rand::rngs::ThreadRng) ->
|
|||
}
|
||||
}
|
||||
|
||||
fn random_point_in_unit_sphere(rng: &mut rand::rngs::ThreadRng) -> Vec3 {
|
||||
fn random_point_in_unit_space(rng: &mut rand::rngs::ThreadRng) -> Vec3 {
|
||||
let mut point = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), rng.gen::<f64>()) * 2.0
|
||||
- Vec3::new(1.0, 1.0, 1.0);
|
||||
while point.sq_len() >= 1.0 {
|
||||
|
|
86
src/demos/materials.rs
Normal file
86
src/demos/materials.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
use {
|
||||
crate::{
|
||||
demos::Demo,
|
||||
types::{material, Hitable, HitableList, Ray, Sphere, Vec3},
|
||||
Camera,
|
||||
},
|
||||
rand::Rng,
|
||||
};
|
||||
|
||||
pub struct Materials;
|
||||
|
||||
impl Demo for Materials {
|
||||
fn name(&self) -> &'static str {
|
||||
"Metal Material"
|
||||
}
|
||||
|
||||
fn render(&self, buf: &mut [u8], width: usize, height: usize, samples: u8) {
|
||||
let world = HitableList {
|
||||
list: vec![
|
||||
Box::new(Sphere::with_material(
|
||||
Vec3::new(0.0, 0.0, -1.0),
|
||||
0.5,
|
||||
Box::new(material::Lambertian::new(Vec3::new(0.8, 0.3, 0.3))),
|
||||
)),
|
||||
Box::new(Sphere::with_material(
|
||||
Vec3::new(0.0, -100.5, -1.0),
|
||||
100.0,
|
||||
Box::new(material::Lambertian::new(Vec3::new(0.8, 0.8, 0.0))),
|
||||
)),
|
||||
Box::new(Sphere::with_material(
|
||||
Vec3::new(1.0, 0.0, -1.0),
|
||||
0.5,
|
||||
Box::new(material::Metal::new(Vec3::new(0.8, 0.6, 0.2))),
|
||||
)),
|
||||
Box::new(Sphere::with_material(
|
||||
Vec3::new(-1.0, 0.0, -1.0),
|
||||
0.5,
|
||||
Box::new(material::Metal::new(Vec3::new(0.8, 0.8, 0.8))),
|
||||
)),
|
||||
],
|
||||
};
|
||||
|
||||
let camera: Camera = Default::default();
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut offset = 0;
|
||||
|
||||
for j in (0..height).rev() {
|
||||
for i in 0..width {
|
||||
let mut color = Vec3::new(0.0, 0.0, 0.0);
|
||||
for _s in 0..samples {
|
||||
let u = (i as f64 + rng.gen::<f64>()) / width as f64;
|
||||
let v = (j as f64 + rng.gen::<f64>()) / height as f64;
|
||||
|
||||
let ray = camera.get_ray(u, v);
|
||||
color += calc_color(ray, &world, 0);
|
||||
}
|
||||
|
||||
color /= samples as f64;
|
||||
// gamma 2 corrected
|
||||
buf[offset] = (255.99 * color.r().sqrt()) as u8;
|
||||
buf[offset + 1] = (255.99 * color.g().sqrt()) as u8;
|
||||
buf[offset + 2] = (255.99 * color.b().sqrt()) as u8;
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 {
|
||||
if let Some(hit_rec) = world.hit(&ray, 0.001, std::f64::MAX) {
|
||||
if depth >= 50 {
|
||||
Vec3::new(0.0, 0.0, 0.0)
|
||||
} else {
|
||||
let material = hit_rec.material.as_ref();
|
||||
if let (attenuation, Some(scattered_ray)) = material.unwrap().scatter(&ray, &hit_rec) {
|
||||
calc_color(scattered_ray, &world, depth + 1) * attenuation
|
||||
} else {
|
||||
Vec3::new(0.0, 0.0, 0.0)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let unit_direction = ray.direction().unit_vector();
|
||||
let t = 0.5 * (unit_direction.y() + 1.0);
|
||||
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
mod diffuse_materials;
|
||||
mod hitable_sphere;
|
||||
mod linear_gradient_rectangle;
|
||||
mod materials;
|
||||
mod simple_antialiasing;
|
||||
mod simple_rectangle;
|
||||
mod simple_sphere;
|
||||
|
@ -9,6 +10,7 @@ mod surface_normal_sphere;
|
|||
pub use diffuse_materials::DiffuseMaterials;
|
||||
pub use hitable_sphere::HitableSphere;
|
||||
pub use linear_gradient_rectangle::LinearGradientRectangle;
|
||||
pub use materials::Materials;
|
||||
pub use simple_antialiasing::SimpleAntialiasing;
|
||||
pub use simple_rectangle::SimpleRectangle;
|
||||
pub use simple_sphere::SimpleSphere;
|
||||
|
|
|
@ -73,6 +73,7 @@ fn main() -> Result<(), String> {
|
|||
Some(Keycode::Num5) => active_demo = Box::new(demos::HitableSphere),
|
||||
Some(Keycode::Num6) => active_demo = Box::new(demos::SimpleAntialiasing),
|
||||
Some(Keycode::Num7) => active_demo = Box::new(demos::DiffuseMaterials),
|
||||
Some(Keycode::Num8) => active_demo = Box::new(demos::Materials),
|
||||
None => unreachable!(),
|
||||
_ => (),
|
||||
};
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use crate::types::{Ray, Vec3};
|
||||
use crate::types::{Material, Ray, Vec3};
|
||||
|
||||
pub struct HitRecord {
|
||||
pub struct HitRecord<'a> {
|
||||
pub t: f64,
|
||||
pub p: Vec3,
|
||||
pub normal: Vec3,
|
||||
pub material: Option<&'a Box<dyn Material>>,
|
||||
}
|
||||
|
||||
pub trait Hitable {
|
||||
|
|
64
src/types/material.rs
Normal file
64
src/types/material.rs
Normal file
|
@ -0,0 +1,64 @@
|
|||
use {
|
||||
crate::types::{HitRecord, Ray, Vec3},
|
||||
rand::Rng,
|
||||
};
|
||||
pub trait Material {
|
||||
fn scatter(&self, ray: &Ray, hit_rec: &HitRecord) -> (Vec3, Option<Ray>);
|
||||
}
|
||||
|
||||
pub struct Lambertian {
|
||||
albedo: Vec3,
|
||||
}
|
||||
|
||||
impl Material for Lambertian {
|
||||
fn scatter(&self, _ray: &Ray, hit_rec: &HitRecord) -> (Vec3, Option<Ray>) {
|
||||
let mut rng = rand::thread_rng();
|
||||
let target = hit_rec.p + hit_rec.normal + random_point_in_unit_space(&mut rng);
|
||||
let scattered_ray = Ray::new(hit_rec.p, target - hit_rec.p);
|
||||
|
||||
(self.albedo, Some(scattered_ray))
|
||||
}
|
||||
}
|
||||
|
||||
impl Lambertian {
|
||||
pub fn new(a: Vec3) -> Self {
|
||||
Self { albedo: a }
|
||||
}
|
||||
}
|
||||
|
||||
fn random_point_in_unit_space(rng: &mut rand::rngs::ThreadRng) -> Vec3 {
|
||||
let mut point = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), rng.gen::<f64>()) * 2.0
|
||||
- Vec3::new(1.0, 1.0, 1.0);
|
||||
while point.sq_len() >= 1.0 {
|
||||
point = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), rng.gen::<f64>()) * 2.0
|
||||
- Vec3::new(1.0, 1.0, 1.0);
|
||||
}
|
||||
point
|
||||
}
|
||||
|
||||
pub struct Metal {
|
||||
albedo: Vec3,
|
||||
}
|
||||
|
||||
impl Metal {
|
||||
pub fn new(albedo: Vec3) -> Self {
|
||||
Self { albedo }
|
||||
}
|
||||
}
|
||||
|
||||
impl Material for Metal {
|
||||
fn scatter(&self, ray_in: &Ray, hit_rec: &HitRecord) -> (Vec3, Option<Ray>) {
|
||||
let reflected_ray = reflect(ray_in.direction().unit_vector(), hit_rec.normal);
|
||||
let scattered_ray = Ray::new(hit_rec.p, reflected_ray);
|
||||
|
||||
if scattered_ray.direction().dot(&hit_rec.normal) > 0.0 {
|
||||
(self.albedo, Some(scattered_ray))
|
||||
} else {
|
||||
(self.albedo, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn reflect(incident: Vec3, normal: Vec3) -> Vec3 {
|
||||
incident - normal * incident.dot(&normal) * 2.0
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
mod hitable;
|
||||
mod hitable_list;
|
||||
pub mod material;
|
||||
mod ray;
|
||||
mod sphere;
|
||||
mod vec3;
|
||||
|
||||
pub use hitable::{HitRecord, Hitable};
|
||||
pub use hitable_list::HitableList;
|
||||
pub use material::Material;
|
||||
pub use ray::Ray;
|
||||
pub use sphere::Sphere;
|
||||
pub use vec3::Vec3;
|
||||
|
|
|
@ -1,12 +1,24 @@
|
|||
use crate::types::{HitRecord, Hitable, Ray, Vec3};
|
||||
use crate::types::{HitRecord, Hitable, Material, Ray, Vec3};
|
||||
pub struct Sphere {
|
||||
center: Vec3,
|
||||
radius: f64,
|
||||
material: Option<Box<dyn Material>>,
|
||||
}
|
||||
|
||||
impl Sphere {
|
||||
pub fn new(center: Vec3, radius: f64) -> Self {
|
||||
Self { center, radius }
|
||||
Self {
|
||||
center,
|
||||
radius,
|
||||
material: None,
|
||||
}
|
||||
}
|
||||
pub fn with_material(center: Vec3, radius: f64, material: Box<dyn Material>) -> Self {
|
||||
Self {
|
||||
center,
|
||||
radius,
|
||||
material: Some(material),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,6 +45,7 @@ impl Hitable for Sphere {
|
|||
t: root,
|
||||
p,
|
||||
normal: (p - self.center) / self.radius,
|
||||
material: self.material.as_ref(),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -44,6 +57,7 @@ impl Hitable for Sphere {
|
|||
t: root,
|
||||
p,
|
||||
normal: (p - self.center) / self.radius,
|
||||
material: self.material.as_ref(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -130,6 +130,13 @@ impl Mul<f64> for Vec3 {
|
|||
}
|
||||
}
|
||||
|
||||
impl Mul<Vec3> for Vec3 {
|
||||
type Output = Vec3;
|
||||
fn mul(self, o: Vec3) -> Vec3 {
|
||||
Vec3([self[0] * o[0], self[1] * o[1], self[2] * o[2]])
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<Vec3> for Vec3 {
|
||||
type Output = Vec3;
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user