Refactored the project and added the Texture trait
This commit is contained in:
parent
e873ddac40
commit
a278969bbf
38
src/demo.rs
38
src/demo.rs
|
@ -1,19 +1,17 @@
|
|||
use {
|
||||
crate::{
|
||||
types::{
|
||||
material::{Dielectric, Lambertian, Metal},
|
||||
MovingSphere, Ray, Sphere, Vec3,
|
||||
},
|
||||
BvhNode, Camera, Hitable, HORIZONTAL_PARTITION, VERTICAL_PARTITION,
|
||||
},
|
||||
rand::{rngs::SmallRng, Rng, SeedableRng},
|
||||
rayon::prelude::*,
|
||||
std::{
|
||||
fmt::{Display, Formatter, Result as FmtResult},
|
||||
fs::File,
|
||||
io::Write,
|
||||
sync::{Arc, Mutex},
|
||||
},
|
||||
use crate::{
|
||||
materials::{Dielectric, Lambertian, Metal},
|
||||
shapes::{MovingSphere, Sphere},
|
||||
texture::Solid,
|
||||
types::{Ray, Vec3},
|
||||
BvhNode, Camera, Hitable, HORIZONTAL_PARTITION, VERTICAL_PARTITION,
|
||||
};
|
||||
use rand::{rngs::SmallRng, Rng, SeedableRng};
|
||||
use rayon::prelude::*;
|
||||
use std::{
|
||||
fmt::{Display, Formatter, Result as FmtResult},
|
||||
fs::File,
|
||||
io::Write,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -57,7 +55,7 @@ impl Demo {
|
|||
world.push(Arc::new(Sphere::new(
|
||||
Vec3::new(0.0, -1000.0, 0.0),
|
||||
1000.0,
|
||||
Lambertian::new(Vec3::new(0.5, 0.5, 0.5)),
|
||||
Lambertian::new(Solid::new(Vec3::new(0.5, 0.5, 0.5))),
|
||||
)));
|
||||
|
||||
let radius = 0.2;
|
||||
|
@ -79,11 +77,11 @@ impl Demo {
|
|||
0.0,
|
||||
1.0,
|
||||
radius,
|
||||
Lambertian::new(Vec3::new(
|
||||
Lambertian::new(Solid::new(Vec3::new(
|
||||
rng.gen::<f64>() * rng.gen::<f64>(),
|
||||
rng.gen::<f64>() * rng.gen::<f64>(),
|
||||
rng.gen::<f64>() * rng.gen::<f64>(),
|
||||
)),
|
||||
))),
|
||||
)));
|
||||
} else if choose_material_probability < 0.95 {
|
||||
// metal material
|
||||
|
@ -115,7 +113,7 @@ impl Demo {
|
|||
world.push(Arc::new(Sphere::new(
|
||||
Vec3::new(-4.0, 1.0, 0.0),
|
||||
1.0,
|
||||
Lambertian::new(Vec3::new(0.4, 0.2, 0.1)),
|
||||
Lambertian::new(Solid::new(Vec3::new(0.4, 0.2, 0.1))),
|
||||
)));
|
||||
world.push(Arc::new(Sphere::new(
|
||||
Vec3::new(4.0, 1.0, 0.0),
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
types::{Material, Ray, Vec3},
|
||||
Aabb,
|
||||
types::{Ray, Vec3},
|
||||
Aabb, Material,
|
||||
};
|
||||
|
||||
pub struct HitRecord<'a> {
|
||||
|
@ -24,6 +24,10 @@ pub struct HitRecord<'a> {
|
|||
|
||||
/// material if any of the surface
|
||||
pub material: &'a dyn Material,
|
||||
|
||||
/// texture coordinates for an object
|
||||
pub u: f64,
|
||||
pub v: f64,
|
||||
}
|
||||
|
||||
pub trait Hitable {
|
||||
|
|
|
@ -6,6 +6,9 @@ mod camera;
|
|||
mod demo;
|
||||
mod hitable;
|
||||
mod hitable_list;
|
||||
mod materials;
|
||||
mod shapes;
|
||||
mod texture;
|
||||
mod types;
|
||||
|
||||
pub use camera::Camera;
|
||||
|
@ -14,9 +17,11 @@ pub use aabb::Aabb;
|
|||
pub use bvh::BvhNode;
|
||||
pub use hitable::{HitRecord, Hitable};
|
||||
pub use hitable_list::HitableList;
|
||||
pub use materials::Material;
|
||||
use std::time::Instant;
|
||||
pub use texture::Texture;
|
||||
|
||||
const NUM_SAMPLES: u8 = 25;
|
||||
const NUM_SAMPLES: u8 = 20;
|
||||
const VERTICAL_PARTITION: usize = 12;
|
||||
const HORIZONTAL_PARTITION: usize = 12;
|
||||
const WIDTH: usize = 1920;
|
||||
|
|
67
src/materials/dielectric.rs
Normal file
67
src/materials/dielectric.rs
Normal file
|
@ -0,0 +1,67 @@
|
|||
use rand::{prelude::SmallRng, Rng};
|
||||
|
||||
use crate::{
|
||||
materials::{reflect, refract, schlick},
|
||||
types::{Ray, Vec3},
|
||||
HitRecord, Material,
|
||||
};
|
||||
|
||||
pub struct Dielectric {
|
||||
reflection_index: f64,
|
||||
}
|
||||
|
||||
impl Dielectric {
|
||||
pub fn new(reflection_index: f64) -> Self {
|
||||
Self { reflection_index }
|
||||
}
|
||||
}
|
||||
|
||||
impl Material for Dielectric {
|
||||
fn scatter(
|
||||
&self,
|
||||
ray_in: &Ray,
|
||||
hit_rec: &HitRecord,
|
||||
rng: &mut SmallRng,
|
||||
) -> (Vec3, Option<Ray>) {
|
||||
let reflected_ray = reflect(ray_in.direction(), hit_rec.normal);
|
||||
// Glass absorbs nothing! So, Attenuation is always going to be 1.0 for this
|
||||
let attenuation = Vec3::new(1.0, 1.0, 1.0);
|
||||
|
||||
let (outward_normal, ni_over_nt, cosine) = if ray_in.direction().dot(&hit_rec.normal) > 0.0
|
||||
{
|
||||
(
|
||||
-hit_rec.normal,
|
||||
self.reflection_index,
|
||||
(ray_in.direction().dot(&hit_rec.normal) * self.reflection_index)
|
||||
/ ray_in.direction().length(),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
hit_rec.normal,
|
||||
1.0 / self.reflection_index,
|
||||
(-ray_in.direction().dot(&hit_rec.normal)) / ray_in.direction().length(),
|
||||
)
|
||||
};
|
||||
|
||||
if let Some(refracted_ray) = refract(ray_in.direction(), outward_normal, ni_over_nt) {
|
||||
let reflect_prob = schlick(cosine, self.reflection_index);
|
||||
|
||||
if rng.gen::<f64>() < reflect_prob {
|
||||
(
|
||||
attenuation,
|
||||
Some(Ray::new(hit_rec.p, reflected_ray, ray_in.time())),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
attenuation,
|
||||
Some(Ray::new(hit_rec.p, refracted_ray, ray_in.time())),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
(
|
||||
attenuation,
|
||||
Some(Ray::new(hit_rec.p, reflected_ray, ray_in.time())),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
26
src/materials/lambertian.rs
Normal file
26
src/materials/lambertian.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
use rand::prelude::SmallRng;
|
||||
|
||||
use crate::{
|
||||
materials::random_point_in_unit_sphere,
|
||||
types::{Ray, Vec3},
|
||||
HitRecord, Material, Texture,
|
||||
};
|
||||
|
||||
pub struct Lambertian<T: Texture> {
|
||||
albedo: T,
|
||||
}
|
||||
|
||||
impl<T: Texture + Send + Sync> Lambertian<T> {
|
||||
pub fn new(albedo: T) -> Self {
|
||||
Self { albedo }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Texture + Send + Sync> Material for Lambertian<T> {
|
||||
fn scatter(&self, ray: &Ray, hit_rec: &HitRecord, rng: &mut SmallRng) -> (Vec3, Option<Ray>) {
|
||||
let scatter_direction = hit_rec.normal + random_point_in_unit_sphere(rng);
|
||||
let scattered_ray = Ray::new(hit_rec.p, scatter_direction, ray.time());
|
||||
|
||||
(self.albedo.value(hit_rec.u, hit_rec.v), Some(scattered_ray))
|
||||
}
|
||||
}
|
43
src/materials/metal.rs
Normal file
43
src/materials/metal.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
use rand::prelude::SmallRng;
|
||||
|
||||
use crate::{
|
||||
materials::{random_point_in_unit_sphere, reflect},
|
||||
types::{Ray, Vec3},
|
||||
HitRecord, Material,
|
||||
};
|
||||
|
||||
pub struct Metal {
|
||||
albedo: Vec3,
|
||||
fuzz: f64,
|
||||
}
|
||||
|
||||
impl Metal {
|
||||
pub fn new(albedo: Vec3) -> Self {
|
||||
Self { albedo, fuzz: 0.0 }
|
||||
}
|
||||
pub fn with_fuzz(albedo: Vec3, fuzz: f64) -> Self {
|
||||
Self { albedo, fuzz }
|
||||
}
|
||||
}
|
||||
|
||||
impl Material for Metal {
|
||||
fn scatter(
|
||||
&self,
|
||||
ray_in: &Ray,
|
||||
hit_rec: &HitRecord,
|
||||
rng: &mut SmallRng,
|
||||
) -> (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 + random_point_in_unit_sphere(rng) * self.fuzz,
|
||||
ray_in.time(),
|
||||
);
|
||||
|
||||
if scattered_ray.direction().dot(&hit_rec.normal) > 0.0 {
|
||||
(self.albedo, Some(scattered_ray))
|
||||
} else {
|
||||
(self.albedo, None)
|
||||
}
|
||||
}
|
||||
}
|
51
src/materials/mod.rs
Normal file
51
src/materials/mod.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
mod dielectric;
|
||||
mod lambertian;
|
||||
mod metal;
|
||||
|
||||
pub use dielectric::Dielectric;
|
||||
pub use lambertian::Lambertian;
|
||||
pub use metal::Metal;
|
||||
use rand::{prelude::SmallRng, Rng};
|
||||
|
||||
use crate::{
|
||||
types::{Ray, Vec3},
|
||||
HitRecord,
|
||||
};
|
||||
|
||||
pub trait Material: Send + Sync {
|
||||
fn scatter(&self, ray: &Ray, hit_rec: &HitRecord, rng: &mut SmallRng) -> (Vec3, Option<Ray>);
|
||||
}
|
||||
|
||||
// Christophe Schlick's Polynomial approximation to figure out reflectivity as the angle changes
|
||||
// See Fresnel Equations, https://en.wikipedia.org/wiki/Fresnel_equations
|
||||
fn schlick(cosine: f64, reflection_index: f64) -> f64 {
|
||||
let mut r0 = (1.0 - reflection_index) / (1.0 + reflection_index);
|
||||
r0 = r0 * r0;
|
||||
r0 + (1.0 - r0) * (1.0 - cosine).powf(5.0)
|
||||
}
|
||||
|
||||
fn reflect(incident: Vec3, normal: Vec3) -> Vec3 {
|
||||
incident - normal * incident.dot(&normal) * 2.0
|
||||
}
|
||||
|
||||
// Snell's Law
|
||||
fn refract(incident: Vec3, normal: Vec3, ni_over_nt: f64) -> Option<Vec3> {
|
||||
let uv = incident.unit_vector();
|
||||
let dt = uv.dot(&normal);
|
||||
let discriminant = 1.0 - ni_over_nt * ni_over_nt * (1.0 - dt * dt);
|
||||
if discriminant > 0.0 {
|
||||
Some((uv - normal * dt) * ni_over_nt - normal * discriminant.sqrt())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn random_point_in_unit_sphere<R: Rng + ?Sized>(rng: &mut R) -> 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
|
||||
}
|
5
src/shapes/mod.rs
Normal file
5
src/shapes/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
mod moving_sphere;
|
||||
mod sphere;
|
||||
|
||||
pub use moving_sphere::MovingSphere;
|
||||
pub use sphere::Sphere;
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
types::{Material, Ray, Vec3},
|
||||
Aabb, HitRecord, Hitable,
|
||||
types::{Ray, Vec3},
|
||||
Aabb, HitRecord, Hitable, Material,
|
||||
};
|
||||
|
||||
pub struct MovingSphere<T: Material + Sized> {
|
||||
|
@ -36,6 +36,19 @@ impl<T: Material + Sized> MovingSphere<T> {
|
|||
+ (self.center_end - self.center_start)
|
||||
* ((time - self.time_start) / (self.time_end - self.time_start))
|
||||
}
|
||||
|
||||
/// p is a point on the sphere of radius 1 & center at origin
|
||||
/// u is between [0,1]. Angle around Y axis from -X axis
|
||||
/// v is between [0,1]. Angle from -Y to +Y axis
|
||||
pub fn get_uv(p: Vec3) -> (f64, f64) {
|
||||
let theta = (-p.y()).acos();
|
||||
let phi = f64::atan2(-p.z(), p.x()) + std::f64::consts::PI;
|
||||
|
||||
let u = phi / (2.0 * std::f64::consts::PI);
|
||||
let v = theta / std::f64::consts::PI;
|
||||
|
||||
(u, v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Material + Sized> Hitable for MovingSphere<T> {
|
||||
|
@ -52,19 +65,29 @@ impl<T: Material + Sized> Hitable for MovingSphere<T> {
|
|||
let root = (-b - discriminant_root) / a;
|
||||
if root < t_max && root > t_min {
|
||||
let p = ray.point_at_parameter(root);
|
||||
let normal = (p - self.center(ray.time())) / self.radius;
|
||||
let (u, v) = Self::get_uv(normal);
|
||||
|
||||
return Some(HitRecord {
|
||||
t: root,
|
||||
p,
|
||||
normal: (p - self.center(ray.time())) / self.radius,
|
||||
u,
|
||||
v,
|
||||
normal,
|
||||
material: &self.material,
|
||||
});
|
||||
}
|
||||
let root = (-b + discriminant_root) / a;
|
||||
if root < t_max && root > t_min {
|
||||
let p = ray.point_at_parameter(root);
|
||||
let normal = (p - self.center(ray.time())) / self.radius;
|
||||
let (u, v) = Self::get_uv(normal);
|
||||
|
||||
return Some(HitRecord {
|
||||
t: root,
|
||||
p,
|
||||
u,
|
||||
v,
|
||||
normal: (p - self.center(ray.time())) / self.radius,
|
||||
material: &self.material,
|
||||
});
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
types::{Material, Ray, Vec3},
|
||||
Aabb, HitRecord, Hitable,
|
||||
types::{Ray, Vec3},
|
||||
Aabb, HitRecord, Hitable, Material,
|
||||
};
|
||||
|
||||
pub struct Sphere<T: Material + Sized> {
|
||||
|
@ -17,6 +17,19 @@ impl<T: Material + Sized> Sphere<T> {
|
|||
material,
|
||||
}
|
||||
}
|
||||
|
||||
/// p is a point on the sphere of radius 1 & center at origin
|
||||
/// u is between [0,1]. Angle around Y axis from -X axis
|
||||
/// v is between [0,1]. Angle from -Y to +Y axis
|
||||
pub fn get_uv(p: Vec3) -> (f64, f64) {
|
||||
let theta = (-p.y()).acos();
|
||||
let phi = f64::atan2(-p.z(), p.x()) + std::f64::consts::PI;
|
||||
|
||||
let u = phi / (2.0 * std::f64::consts::PI);
|
||||
let v = theta / std::f64::consts::PI;
|
||||
|
||||
(u, v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Material + Sized> Hitable for Sphere<T> {
|
||||
|
@ -39,10 +52,15 @@ impl<T: Material + Sized> Hitable for Sphere<T> {
|
|||
let root = (-b - discriminant_root) / a;
|
||||
if root < t_max && root > t_min {
|
||||
let p = ray.point_at_parameter(root);
|
||||
let normal = (p - self.center) / self.radius;
|
||||
let (u, v) = Self::get_uv(normal);
|
||||
|
||||
return Some(HitRecord {
|
||||
t: root,
|
||||
p,
|
||||
normal: (p - self.center) / self.radius,
|
||||
u,
|
||||
v,
|
||||
normal,
|
||||
material: &self.material,
|
||||
});
|
||||
}
|
||||
|
@ -50,10 +68,14 @@ impl<T: Material + Sized> Hitable for Sphere<T> {
|
|||
let root = (-b + discriminant_root) / a;
|
||||
if root < t_max && root > t_min {
|
||||
let p = ray.point_at_parameter(root);
|
||||
let normal = (p - self.center) / self.radius;
|
||||
let (u, v) = Self::get_uv(normal);
|
||||
|
||||
return Some(HitRecord {
|
||||
t: root,
|
||||
p,
|
||||
u,
|
||||
v,
|
||||
normal: (p - self.center) / self.radius,
|
||||
material: &self.material,
|
||||
});
|
9
src/texture/mod.rs
Normal file
9
src/texture/mod.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
mod solid;
|
||||
|
||||
pub use solid::Solid;
|
||||
|
||||
use crate::types::Vec3;
|
||||
|
||||
pub trait Texture {
|
||||
fn value(&self, u: f64, v: f64) -> Vec3;
|
||||
}
|
17
src/texture/solid.rs
Normal file
17
src/texture/solid.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
use crate::{types::Vec3, Texture};
|
||||
|
||||
pub struct Solid {
|
||||
color: Vec3,
|
||||
}
|
||||
|
||||
impl Solid {
|
||||
pub const fn new(color: Vec3) -> Self {
|
||||
Self { color }
|
||||
}
|
||||
}
|
||||
|
||||
impl Texture for Solid {
|
||||
fn value(&self, _u: f64, _v: f64) -> Vec3 {
|
||||
self.color
|
||||
}
|
||||
}
|
|
@ -1,160 +0,0 @@
|
|||
use {
|
||||
crate::{
|
||||
types::{Ray, Vec3},
|
||||
HitRecord,
|
||||
},
|
||||
rand::{rngs::SmallRng, Rng},
|
||||
};
|
||||
|
||||
pub trait Material: Send + Sync {
|
||||
fn scatter(&self, ray: &Ray, hit_rec: &HitRecord, rng: &mut SmallRng) -> (Vec3, Option<Ray>);
|
||||
}
|
||||
|
||||
pub struct Lambertian {
|
||||
albedo: Vec3,
|
||||
}
|
||||
|
||||
impl Lambertian {
|
||||
pub fn new(a: Vec3) -> Self {
|
||||
Self { albedo: a }
|
||||
}
|
||||
}
|
||||
|
||||
impl Material for Lambertian {
|
||||
fn scatter(&self, ray: &Ray, hit_rec: &HitRecord, rng: &mut SmallRng) -> (Vec3, Option<Ray>) {
|
||||
let target = hit_rec.p + hit_rec.normal + random_point_in_unit_sphere(rng);
|
||||
let scattered_ray = Ray::new(hit_rec.p, target - hit_rec.p, ray.time());
|
||||
|
||||
(self.albedo, Some(scattered_ray))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Metal {
|
||||
albedo: Vec3,
|
||||
fuzz: f64,
|
||||
}
|
||||
|
||||
impl Metal {
|
||||
pub fn new(albedo: Vec3) -> Self {
|
||||
Self { albedo, fuzz: 0.0 }
|
||||
}
|
||||
pub fn with_fuzz(albedo: Vec3, fuzz: f64) -> Self {
|
||||
Self { albedo, fuzz }
|
||||
}
|
||||
}
|
||||
|
||||
impl Material for Metal {
|
||||
fn scatter(
|
||||
&self,
|
||||
ray_in: &Ray,
|
||||
hit_rec: &HitRecord,
|
||||
rng: &mut SmallRng,
|
||||
) -> (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 + random_point_in_unit_sphere(rng) * self.fuzz,
|
||||
ray_in.time(),
|
||||
);
|
||||
|
||||
if scattered_ray.direction().dot(&hit_rec.normal) > 0.0 {
|
||||
(self.albedo, Some(scattered_ray))
|
||||
} else {
|
||||
(self.albedo, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Dielectric {
|
||||
reflection_index: f64,
|
||||
}
|
||||
|
||||
impl Dielectric {
|
||||
pub fn new(reflection_index: f64) -> Self {
|
||||
Self { reflection_index }
|
||||
}
|
||||
}
|
||||
|
||||
impl Material for Dielectric {
|
||||
fn scatter(
|
||||
&self,
|
||||
ray_in: &Ray,
|
||||
hit_rec: &HitRecord,
|
||||
rng: &mut SmallRng,
|
||||
) -> (Vec3, Option<Ray>) {
|
||||
let reflected_ray = reflect(ray_in.direction(), hit_rec.normal);
|
||||
// Glass absorbs nothing! So, Attenuation is always going to be 1.0 for this
|
||||
let attenuation = Vec3::new(1.0, 1.0, 1.0);
|
||||
|
||||
let (outward_normal, ni_over_nt, cosine) = if ray_in.direction().dot(&hit_rec.normal) > 0.0
|
||||
{
|
||||
(
|
||||
-hit_rec.normal,
|
||||
self.reflection_index,
|
||||
(ray_in.direction().dot(&hit_rec.normal) * self.reflection_index)
|
||||
/ ray_in.direction().length(),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
hit_rec.normal,
|
||||
1.0 / self.reflection_index,
|
||||
(-ray_in.direction().dot(&hit_rec.normal)) / ray_in.direction().length(),
|
||||
)
|
||||
};
|
||||
|
||||
if let Some(refracted_ray) = refract(ray_in.direction(), outward_normal, ni_over_nt) {
|
||||
let reflect_prob = schlick(cosine, self.reflection_index);
|
||||
|
||||
if rng.gen::<f64>() < reflect_prob {
|
||||
(
|
||||
attenuation,
|
||||
Some(Ray::new(hit_rec.p, reflected_ray, ray_in.time())),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
attenuation,
|
||||
Some(Ray::new(hit_rec.p, refracted_ray, ray_in.time())),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
(
|
||||
attenuation,
|
||||
Some(Ray::new(hit_rec.p, reflected_ray, ray_in.time())),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Christophe Schlick's Polynomial approximation to figure out reflectivity as the angle changes
|
||||
// See Fresnel Equations, https://en.wikipedia.org/wiki/Fresnel_equations
|
||||
fn schlick(cosine: f64, reflection_index: f64) -> f64 {
|
||||
let mut r0 = (1.0 - reflection_index) / (1.0 + reflection_index);
|
||||
r0 = r0 * r0;
|
||||
r0 + (1.0 - r0) * (1.0 - cosine).powf(5.0)
|
||||
}
|
||||
|
||||
fn reflect(incident: Vec3, normal: Vec3) -> Vec3 {
|
||||
incident - normal * incident.dot(&normal) * 2.0
|
||||
}
|
||||
|
||||
// Snell's Law
|
||||
fn refract(incident: Vec3, normal: Vec3, ni_over_nt: f64) -> Option<Vec3> {
|
||||
let uv = incident.unit_vector();
|
||||
let dt = uv.dot(&normal);
|
||||
let discriminant = 1.0 - ni_over_nt * ni_over_nt * (1.0 - dt * dt);
|
||||
if discriminant > 0.0 {
|
||||
Some((uv - normal * dt) * ni_over_nt - normal * discriminant.sqrt())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn random_point_in_unit_sphere<R: Rng + ?Sized>(rng: &mut R) -> 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
|
||||
}
|
|
@ -1,11 +1,5 @@
|
|||
pub mod material;
|
||||
mod moving_sphere;
|
||||
mod ray;
|
||||
mod sphere;
|
||||
mod vec3;
|
||||
|
||||
pub use material::Material;
|
||||
pub use moving_sphere::MovingSphere;
|
||||
pub use ray::Ray;
|
||||
pub use sphere::Sphere;
|
||||
pub use vec3::Vec3;
|
||||
|
|
Loading…
Reference in New Issue
Block a user