56 lines
1.4 KiB
Rust
56 lines
1.4 KiB
Rust
use core::f32::math::sqrt;
|
|
|
|
use rand::RngExt;
|
|
use serde::Deserialize;
|
|
|
|
use crate::{
|
|
objects::{hit::Hit, materials::traits::Material},
|
|
ray::Ray,
|
|
vec3::Colour,
|
|
};
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct Dielectric {
|
|
refraction_index: f32,
|
|
}
|
|
|
|
impl Dielectric {
|
|
pub fn new(refraction_index: f32) -> Self {
|
|
Self { refraction_index }
|
|
}
|
|
|
|
fn reflectance(cos: f32, refraction_index: f32) -> f32 {
|
|
// Shlick's approximation
|
|
let mut r0 = (1. - refraction_index) / (1. + refraction_index);
|
|
r0 *= r0;
|
|
r0 + (1. - r0) * (1. - cos).powf(5.)
|
|
}
|
|
}
|
|
|
|
impl Material for Dielectric {
|
|
fn scatter(&self, hit: &Hit, ray: &Ray) -> Option<(Option<Ray>, Colour)> {
|
|
let ri = if hit.front_face() {
|
|
1. / self.refraction_index
|
|
} else {
|
|
self.refraction_index
|
|
};
|
|
let unit = ray.dir().get_unit();
|
|
let cos_theta = if -unit.dot(hit.n()) > 1. {
|
|
1.
|
|
} else {
|
|
-unit.dot(hit.n())
|
|
};
|
|
let sin_theta = sqrt(1. - cos_theta * cos_theta);
|
|
let cannot_refract = ri * sin_theta > 1.;
|
|
|
|
let mut rng = rand::rng();
|
|
let dir = if cannot_refract || Dielectric::reflectance(cos_theta, ri) > rng.random::<f32>()
|
|
{
|
|
unit.reflect(hit.n())
|
|
} else {
|
|
unit.refract(hit.n(), ri)
|
|
};
|
|
Some((Some(Ray::new(*hit.p(), dir)), Colour::new(1., 1., 1.)))
|
|
}
|
|
}
|