added metals and dielectrics

This commit is contained in:
2023-02-02 09:41:11 +01:00
parent 72fb4ce3fd
commit 32cb8b4554
7 changed files with 145 additions and 42 deletions

14
img.ppm

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,5 @@
use crate::ray::Ray; use crate::ray::Ray;
use crate::rtweekend::degrees_to_radians;
use crate::vec3::*; use crate::vec3::*;
pub struct Camera { pub struct Camera {
@@ -9,10 +10,12 @@ pub struct Camera {
} }
impl Camera { impl Camera {
pub fn new() -> Self { pub fn new(vfov: f64, aspect_ratio: f64) -> Self {
let aspect_ratio = 1.0 / 1.0; let theta = degrees_to_radians(vfov);
let viewport_height = 2.0; let h = f64::tan(theta/2.0);
let viewport_height = 2.0 * h;
let viewport_width = aspect_ratio * viewport_height; let viewport_width = aspect_ratio * viewport_height;
let focal_length = 1.0; let focal_length = 1.0;
let origin = Point3::new(0.0, 0.0, 0.0); let origin = Point3::new(0.0, 0.0, 0.0);

View File

@@ -12,7 +12,7 @@ pub struct HitRecord<'a> {
} }
impl HitRecord<'_> { impl HitRecord<'_> {
pub fn new_empty<T: Material>(material: &T) -> Self { pub fn new_empty<T: Material>(material: &'static T) -> Self {
HitRecord { p: Point3::new(0.0, 0.0, 0.0), normal: Vec3::new(0.0, 0.0, 0.0), t: 0.0, front_face: false, mat_ptr: material } HitRecord { p: Point3::new(0.0, 0.0, 0.0), normal: Vec3::new(0.0, 0.0, 0.0), t: 0.0, front_face: false, mat_ptr: material }
} }

View File

@@ -20,12 +20,10 @@ use crate::{
vec3::*, vec3::*,
ray::*, ray::*,
hittable::*, hittable::*,
camera::*, camera::*, material::{Lambertian, Dielectric},
}; };
fn ray_color(r: &Ray, world: &dyn Hittable, depth: i32) -> Color { fn ray_color(r: &Ray, world: &mut dyn Hittable, depth: i32) -> Color {
let mut rec = HitRecord::new_empty(&Metal{ albedo: Color::new_empty() });
// Limit the bounces // Limit the bounces
if depth <= 0 { if depth <= 0 {
return Color::new(0.0, 0.0, 0.0); return Color::new(0.0, 0.0, 0.0);
@@ -35,7 +33,10 @@ fn ray_color(r: &Ray, world: &dyn Hittable, depth: i32) -> Color {
let mut scattered = Ray::new_empty(); let mut scattered = Ray::new_empty();
let mut attenuation = Color::new_empty(); let mut attenuation = Color::new_empty();
//if rec.mat_ptr if rec.mat_ptr.scatter(r, rec, &mut attenuation, &mut scattered) {
return attenuation * ray_color(&scattered, world, depth - 1);
}
return Color::new_empty();
} }
let unit_direction: Vec3 = r.direction().unit_vector(); let unit_direction: Vec3 = r.direction().unit_vector();
@@ -54,11 +55,18 @@ fn main() {
// World // World
let mut world = HittableList::new_empty(); let mut world = HittableList::new_empty();
world.add(Box::new(Sphere::new(Point3::new(0.0, 0.0, -1.0), 0.5))); static MATERIAL_GROUND: Lambertian = Lambertian::new(Color::new(0.3, 0.8, 0.0));
world.add(Box::new(Sphere::new(Point3::new(0.0, -100.5, -1.0), 100.0))); static MATERIAL_CENTER: Lambertian = Lambertian::new(Color::new(0.1, 0.2, 0.5));
static MATERIAL_LEFT: Dielectric = Dielectric::new(1.5);
static MATERIAL_RIGHT: Metal = Metal::new(Color::new(0.8, 0.6, 0.2), 0.0);
world.add(Box::new(Sphere::new(Point3::new(0.0, -100.5, -1.0), 100.0, &MATERIAL_GROUND)));
world.add(Box::new(Sphere::new(Point3::new(0.0, 0.0, -1.0), 0.5, &MATERIAL_CENTER)));
world.add(Box::new(Sphere::new(Point3::new(-1.0, 0.0, -1.0), -0.5, &MATERIAL_LEFT)));
world.add(Box::new(Sphere::new(Point3::new(1.0, 0.0, -1.0), 0.5, &MATERIAL_RIGHT)));
// Camera // Camera
let cam = Camera::new(); let cam = Camera::new(120.0, aspect_ratio);
// Render // Render
//let mut rng = rand::thread_rng(); //let mut rng = rand::thread_rng();

View File

@@ -1,51 +1,97 @@
use crate::hittable::HitRecord; use crate::hittable::HitRecord;
use crate::ray::Ray; use crate::ray::Ray;
use crate::vec3::{Color, random_unit_vector, reflect}; use crate::rtweekend::random_f64;
use crate::vec3::{Color, random_unit_vector, reflect, refract};
pub trait Material: { pub trait Material: {
fn scatter(&self, r_in: &Ray, rec: HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool; fn scatter(&self, r_in: &Ray, rec: HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool;
} }
pub struct Lambertian { pub struct Lambertian {
albedo: Color, pub albedo: Color,
} }
impl Lambertian { impl Lambertian {
pub fn new(a: &Color) -> Self { pub const fn new(albedo: Color) -> Self {
Lambertian { albedo: *a } Lambertian { albedo }
} }
} }
impl Material for Lambertian { impl Material for Lambertian {
fn scatter(&self, r_in: &Ray, rec: HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool { fn scatter(&self, _r_in: &Ray, rec: HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool {
let scatter_direction = rec.normal + random_unit_vector(); let mut scatter_direction = rec.normal + random_unit_vector();
if scatter_direction.near_zero() { if scatter_direction.near_zero() {
scatter_direction = rec.normal; scatter_direction = rec.normal;
} }
scattered = &mut Ray::new(rec.p, scatter_direction); *scattered = Ray::new(rec.p, scatter_direction);
attenuation = &mut self.albedo; *attenuation = self.albedo;
return true; return true;
} }
} }
pub struct Metal { pub struct Metal {
pub albedo: Color pub albedo: Color,
pub fuzz: f64,
} }
impl Metal { impl Metal {
pub fn new(a: &Color) -> Self { pub const fn new(albedo: Color, fuzz: f64) -> Self {
Metal { albedo: *a } Metal { albedo, fuzz }
} }
} }
impl Material for Metal { impl Material for Metal {
fn scatter(&self, r_in: &Ray, rec: HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool { fn scatter(&self, r_in: &Ray, rec: HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool {
let reflected = reflect(&r_in.direction(), &rec.normal); let reflected = reflect(&r_in.direction(), &rec.normal);
scattered = &mut Ray::new(rec.p, rec.normal); *scattered = Ray::new(rec.p, reflected + self.fuzz*random_unit_vector());
attenuation = &mut self.albedo; *attenuation = self.albedo;
scattered.direction().dot(&rec.normal) > 0.0 scattered.direction().dot(&rec.normal) > 0.0
} }
} }
pub struct Dielectric {
ir: f64,
}
impl Dielectric {
pub const fn new(ir: f64) -> Self {
Dielectric { ir }
}
fn reflectance(&self, cosine: f64, ref_idx: f64) -> f64 {
// Schlick
let mut r0 = (1.0-ref_idx) / (1.0+ref_idx);
r0 = r0*r0;
r0 + (1.0-r0)*f64::powf(1.0 - cosine, 5.0)
}
}
impl Material for Dielectric {
fn scatter(&self, r_in: &Ray, rec: HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool {
*attenuation = Color::new(1.0, 1.0, 1.0);
let refraction_ratio: f64 = if rec.front_face {
1.0/self.ir
} else {
self.ir
};
let unit_direction = r_in.direction().unit_vector();
let cos_theta = f64::min(-unit_direction.dot(&rec.normal), 1.0);
let sin_theta = f64::sqrt(1.0 - cos_theta*cos_theta);
let cannot_refract = refraction_ratio * sin_theta > 1.0;
let direction = if cannot_refract || Dielectric::reflectance(self, cos_theta, refraction_ratio) > random_f64() {
reflect(&unit_direction, &rec.normal)
} else {
refract(&unit_direction, &rec.normal, refraction_ratio)
};
*scattered = Ray::new(rec.p, direction);
true
}
}

View File

@@ -9,20 +9,22 @@ pub struct Sphere<'a> {
} }
impl Sphere<'_> { impl Sphere<'_> {
pub fn new<T: Material>(center: Point3, radius: f64, mat_ptr: &T) -> Self { pub fn new<T: Material>(center: Point3, radius: f64, mat_ptr: &'static T) -> Self {
Sphere {center, radius, mat_ptr} Sphere {center, radius, mat_ptr}
} }
} }
impl Hittable for Sphere<'_> { impl Hittable for Sphere<'_> {
fn hit(&mut self, r: &crate::ray::Ray, t_min: f64, t_max: f64, rec: &mut HitRecord) -> bool { fn hit(&mut self, r: &crate::ray::Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
let oc = r.origin() - self.center; let oc = r.origin() - self.center;
let a = r.direction().length_squared(); let a = r.direction().length_squared();
let half_b = oc.dot(&r.direction()); let half_b = oc.dot(&r.direction());
let c = oc.length_squared() - self.radius*self.radius; let c = oc.length_squared() - self.radius * self.radius;
let discriminant = half_b*half_b - a*c; let discriminant = half_b * half_b - a * c;
if discriminant < 0.0 { return false; } if discriminant < 0.0 {
return None;
}
let sqrtd = discriminant.sqrt(); let sqrtd = discriminant.sqrt();
// Find the nearest root that lies in the acceptable range // Find the nearest root that lies in the acceptable range
@@ -30,16 +32,26 @@ impl Hittable for Sphere<'_> {
if root < t_min || t_max < root { if root < t_min || t_max < root {
root = (-half_b + sqrtd) / a; root = (-half_b + sqrtd) / a;
if root < t_min || t_max < root { if root < t_min || t_max < root {
return false; return None;
} }
} }
rec.t = root; let p = r.at(root);
rec.p = r.at(rec.t); let outward_normal = (p - self.center) / self.radius;
let outward_normal = (rec.p - self.center) / self.radius; let front_face = r.direction().dot(&outward_normal) < 0.0;
rec.set_face_normal(r, &outward_normal);
rec.mat_ptr = self.mat_ptr;
return true; let outward_normal = if front_face {
outward_normal
} else {
outward_normal * -1.0
};
Some(HitRecord {
t: root,
p: p,
normal: outward_normal,
front_face,
mat_ptr: self.mat_ptr,
})
} }
} }

View File

@@ -1,4 +1,4 @@
use std::ops::{Add, Sub, AddAssign, SubAssign, Mul, Div, MulAssign, DivAssign, Neg}; use std::{ops::{Add, Sub, AddAssign, SubAssign, Mul, Div, MulAssign, DivAssign, Neg}};
use crate::rtweekend::{random_f64, random_range_f64}; use crate::rtweekend::{random_f64, random_range_f64};
@@ -8,7 +8,7 @@ pub struct Vec3 {
} }
impl Vec3 { impl Vec3 {
pub fn new(x: f64, y: f64, z: f64) -> Self { pub const fn new(x: f64, y: f64, z: f64) -> Self {
Vec3 { elements: [x, y, z] } Vec3 { elements: [x, y, z] }
} }
@@ -126,6 +126,20 @@ impl Mul<f64> for Vec3 {
} }
} }
impl Mul<Self> for Vec3 {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self {
elements: [
self.x() * rhs.x(),
self.y() * rhs.y(),
self.z() * rhs.z()
]
}
}
}
impl Mul<Vec3> for f64 { impl Mul<Vec3> for f64 {
type Output = Vec3; type Output = Vec3;
@@ -226,3 +240,11 @@ pub fn random_in_hemisphere(normal: &Vec3) -> Vec3 {
pub fn reflect(v: &Vec3, n: &Vec3) -> Vec3 { pub fn reflect(v: &Vec3, n: &Vec3) -> Vec3 {
*v - 2.0 * v.dot(n) * *n *v - 2.0 * v.dot(n) * *n
} }
pub fn refract(uv: &Vec3, n: &Vec3, etai_over_etat: f64) -> Vec3 {
let cos_theta = f64::min(-uv.dot(&n), 1.0);
let r_out_perp = etai_over_etat * (*uv + cos_theta * *n);
let r_out_parallel = -f64::sqrt(f64::abs(1.0 - r_out_perp.length_squared())) * *n;
r_out_perp + r_out_parallel
}