added metals and dielectrics
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
use crate::ray::Ray;
|
||||
use crate::rtweekend::degrees_to_radians;
|
||||
use crate::vec3::*;
|
||||
|
||||
pub struct Camera {
|
||||
@@ -9,10 +10,12 @@ pub struct Camera {
|
||||
}
|
||||
|
||||
impl Camera {
|
||||
pub fn new() -> Self {
|
||||
let aspect_ratio = 1.0 / 1.0;
|
||||
let viewport_height = 2.0;
|
||||
pub fn new(vfov: f64, aspect_ratio: f64) -> Self {
|
||||
let theta = degrees_to_radians(vfov);
|
||||
let h = f64::tan(theta/2.0);
|
||||
let viewport_height = 2.0 * h;
|
||||
let viewport_width = aspect_ratio * viewport_height;
|
||||
|
||||
let focal_length = 1.0;
|
||||
|
||||
let origin = Point3::new(0.0, 0.0, 0.0);
|
||||
|
||||
@@ -12,7 +12,7 @@ pub struct HitRecord<'a> {
|
||||
}
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
|
||||
28
src/main.rs
28
src/main.rs
@@ -20,22 +20,23 @@ use crate::{
|
||||
vec3::*,
|
||||
ray::*,
|
||||
hittable::*,
|
||||
camera::*,
|
||||
camera::*, material::{Lambertian, Dielectric},
|
||||
};
|
||||
|
||||
fn ray_color(r: &Ray, world: &dyn Hittable, depth: i32) -> Color {
|
||||
let mut rec = HitRecord::new_empty(&Metal{ albedo: Color::new_empty() });
|
||||
|
||||
fn ray_color(r: &Ray, world: &mut dyn Hittable, depth: i32) -> Color {
|
||||
// Limit the bounces
|
||||
if depth <= 0 {
|
||||
return Color::new(0.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
if let Some(rec) = world.hit(r, 0.001, INFINITY) {
|
||||
if let Some(rec) = world.hit(r, 0.001, INFINITY) {
|
||||
let mut scattered = Ray::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();
|
||||
@@ -54,11 +55,18 @@ fn main() {
|
||||
|
||||
// World
|
||||
let mut world = HittableList::new_empty();
|
||||
world.add(Box::new(Sphere::new(Point3::new(0.0, 0.0, -1.0), 0.5)));
|
||||
world.add(Box::new(Sphere::new(Point3::new(0.0, -100.5, -1.0), 100.0)));
|
||||
static MATERIAL_GROUND: Lambertian = Lambertian::new(Color::new(0.3, 0.8, 0.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
|
||||
let cam = Camera::new();
|
||||
let cam = Camera::new(120.0, aspect_ratio);
|
||||
|
||||
// Render
|
||||
//let mut rng = rand::thread_rng();
|
||||
@@ -90,4 +98,4 @@ fn main() {
|
||||
println!("Took {:.6}s", now.elapsed().as_secs_f64());
|
||||
println!("Took {:.6}m", now.elapsed().as_secs_f64() / 60.0);
|
||||
println!("Took {:.6}h", now.elapsed().as_secs_f64() / 3600.0);
|
||||
}
|
||||
}
|
||||
@@ -1,51 +1,97 @@
|
||||
use crate::hittable::HitRecord;
|
||||
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: {
|
||||
fn scatter(&self, r_in: &Ray, rec: HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool;
|
||||
}
|
||||
|
||||
pub struct Lambertian {
|
||||
albedo: Color,
|
||||
pub albedo: Color,
|
||||
}
|
||||
|
||||
impl Lambertian {
|
||||
pub fn new(a: &Color) -> Self {
|
||||
Lambertian { albedo: *a }
|
||||
pub const fn new(albedo: Color) -> Self {
|
||||
Lambertian { albedo }
|
||||
}
|
||||
}
|
||||
|
||||
impl Material for Lambertian {
|
||||
fn scatter(&self, r_in: &Ray, rec: HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool {
|
||||
let scatter_direction = rec.normal + random_unit_vector();
|
||||
fn scatter(&self, _r_in: &Ray, rec: HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool {
|
||||
let mut scatter_direction = rec.normal + random_unit_vector();
|
||||
|
||||
if scatter_direction.near_zero() {
|
||||
scatter_direction = rec.normal;
|
||||
}
|
||||
|
||||
scattered = &mut Ray::new(rec.p, scatter_direction);
|
||||
attenuation = &mut self.albedo;
|
||||
*scattered = Ray::new(rec.p, scatter_direction);
|
||||
*attenuation = self.albedo;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Metal {
|
||||
pub albedo: Color
|
||||
pub albedo: Color,
|
||||
pub fuzz: f64,
|
||||
}
|
||||
|
||||
impl Metal {
|
||||
pub fn new(a: &Color) -> Self {
|
||||
Metal { albedo: *a }
|
||||
pub const fn new(albedo: Color, fuzz: f64) -> Self {
|
||||
Metal { albedo, fuzz }
|
||||
}
|
||||
}
|
||||
|
||||
impl Material for Metal {
|
||||
fn scatter(&self, r_in: &Ray, rec: HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool {
|
||||
let reflected = reflect(&r_in.direction(), &rec.normal);
|
||||
scattered = &mut Ray::new(rec.p, rec.normal);
|
||||
attenuation = &mut self.albedo;
|
||||
*scattered = Ray::new(rec.p, reflected + self.fuzz*random_unit_vector());
|
||||
*attenuation = self.albedo;
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -9,20 +9,22 @@ pub struct Sphere<'a> {
|
||||
}
|
||||
|
||||
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}
|
||||
}
|
||||
}
|
||||
|
||||
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 a = r.direction().length_squared();
|
||||
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;
|
||||
if discriminant < 0.0 { return false; }
|
||||
let discriminant = half_b * half_b - a * c;
|
||||
if discriminant < 0.0 {
|
||||
return None;
|
||||
}
|
||||
let sqrtd = discriminant.sqrt();
|
||||
|
||||
// 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 {
|
||||
root = (-half_b + sqrtd) / a;
|
||||
if root < t_min || t_max < root {
|
||||
return false;
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
rec.t = root;
|
||||
rec.p = r.at(rec.t);
|
||||
let outward_normal = (rec.p - self.center) / self.radius;
|
||||
rec.set_face_normal(r, &outward_normal);
|
||||
rec.mat_ptr = self.mat_ptr;
|
||||
let p = r.at(root);
|
||||
let outward_normal = (p - self.center) / self.radius;
|
||||
let front_face = r.direction().dot(&outward_normal) < 0.0;
|
||||
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
26
src/vec3.rs
26
src/vec3.rs
@@ -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};
|
||||
|
||||
@@ -8,7 +8,7 @@ pub struct 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] }
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
type Output = Vec3;
|
||||
|
||||
@@ -225,4 +239,12 @@ pub fn random_in_hemisphere(normal: &Vec3) -> Vec3 {
|
||||
|
||||
pub fn reflect(v: &Vec3, n: &Vec3) -> Vec3 {
|
||||
*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
|
||||
}
|
||||
Reference in New Issue
Block a user