47 lines
1.2 KiB
Rust
47 lines
1.2 KiB
Rust
use std::f32::consts::PI;
|
|
|
|
use glam::Vec3;
|
|
use rand_distr::{Distribution, Uniform, UnitSphere};
|
|
|
|
use super::Reflector;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct DiscReflector {
|
|
pub roughness: f32,
|
|
}
|
|
|
|
impl Reflector for DiscReflector {
|
|
fn brdf(&self, normal: Vec3, incident: Vec3, reflected: Vec3) -> f32 {
|
|
let specular = incident.reflect(normal);
|
|
let convergence = reflected.dot(specular);
|
|
if convergence <= 1. - self.roughness {
|
|
return 0.;
|
|
}
|
|
let c = -incident.dot(normal) * reflected.dot(normal);
|
|
if c < 0. {
|
|
return 0.;
|
|
}
|
|
0.5 / (self.roughness * PI * (specular.dot(normal) * reflected.dot(normal)).sqrt())
|
|
}
|
|
|
|
fn reflect(&self, rgen: &mut impl rand::Rng, normal: Vec3, incident: Vec3) -> Vec3 {
|
|
let specular = incident.reflect(normal);
|
|
|
|
let dist_theta = Uniform::new(0., (1. - self.roughness).acos()).unwrap();
|
|
let dist_sphere = UnitSphere;
|
|
|
|
loop {
|
|
let theta: f32 = dist_theta.sample(rgen);
|
|
let (xy, z) = theta.sin_cos();
|
|
|
|
let spherical: Vec3 = dist_sphere.sample(rgen).into();
|
|
let off_dir = spherical.reject_from_normalized(specular).normalize();
|
|
|
|
let reflected = xy * off_dir + z * specular;
|
|
if reflected.dot(normal) >= 0. {
|
|
return reflected;
|
|
}
|
|
}
|
|
}
|
|
}
|