use crate::{Direction, Normal, Float, Transport};
use crate::tracer::{
color::Color, hit::Hit, ray::Ray,
microfacet::MfDistribution,
pdfs::{
DeltaPdf, MfdPdf,
Pdf, VolumetricPdf
}
};
pub fn bsdf_microfacet(
wo: Direction,
wi: Direction,
ng: Normal,
ns: Normal,
mode: Transport,
albedo: Color,
mfd: &MfDistribution
) -> Color {
let v = -wo;
let ns_dot_wi = ns.dot(wi).abs();
let ns_dot_v = ns.dot(v).abs();
let rfrct_idx = mfd.get_rfrct_idx();
let ro_inside = ng.dot(v) < 0.0;
let ri_inside = ng.dot(wi) < 0.0;
if ro_inside == ri_inside {
let wh = (wi + v).normalize();
let d = mfd.d(wh, ns);
let f = if mfd.is_transparent() && ri_inside {
let wh_dot_v = wh.dot(v);
let sin2_to = 1.0 - wh_dot_v * wh_dot_v;
let sin2_ti = sin2_to * mfd.get_rfrct_idx() * mfd.get_rfrct_idx();
if sin2_ti > 1.0 {
Color::WHITE
} else {
mfd.f(v, wh, albedo)
}
} else {
mfd.f(v, wh, albedo)
};
let g = mfd.g(v, wi, wh, ns);
let specular = d * f * g / (4.0 * ns_dot_v * ns_dot_wi);
if mfd.is_transparent() {
specular
} else {
let ns_dot_wh = ns.dot(wh);
let diffuse = (Color::WHITE - f) * albedo
* mfd.disney_diffuse(ns_dot_v, ns_dot_wh, ns_dot_wi) / crate::PI;
diffuse + specular
}
} else {
let eta_ratio = if ro_inside {
1.0 / rfrct_idx
} else {
rfrct_idx
};
let scale = match mode {
Transport::Radiance => eta_ratio * eta_ratio,
Transport::Importance => 1.0,
};
let wh = (wi * eta_ratio + v).normalize();
let wh = if wh.dot(v) < 0.0 { -wh } else { wh };
let wh_dot_wi = wh.dot(wi);
let wh_dot_v = wh.dot(v);
let d = mfd.d(wh, ns);
let f = mfd.f(v, wh, albedo);
let g = mfd.g(v, wi, wh, ns);
scale * (wh_dot_wi * wh_dot_v / (ns_dot_wi * ns_dot_v)).abs()
* albedo * d * (Color::WHITE - f) * g
/ (eta_ratio * wh_dot_wi + wh_dot_v).powi(2)
}
}
pub fn bsdf_microfacet_pdf(
ho: &Hit,
ro: &Ray,
albedo: Color,
mfd: &MfDistribution,
) -> Option<Box<dyn Pdf>> {
let ns = ho.ns;
let ng = ho.ng;
let v = -ro.dir;
Some( Box::new(MfdPdf::new(v, ns, ng, albedo, *mfd)) )
}
pub fn brdf_mirror_pdf(ho: &Hit, ro: &Ray) -> Option<Box<dyn Pdf>> {
let wo = ro.dir;
let no = ho.ns;
let wi = reflect(-wo, no);
Some( Box::new(DeltaPdf::new(wi)) )
}
pub fn brdf_volumetric_pdf(ro: &Ray, g: Float) -> Option<Box<dyn Pdf>> {
let v = -ro.dir;
Some( Box::new(VolumetricPdf::new(v, g)) )
}
pub fn btdf_glass_pdf(ho: &Hit, ro: &Ray, rfrct_idx: Float) -> Option<Box<dyn Pdf>> {
let ng = ho.ng;
let v = -ro.dir;
let inside = ng.dot(v) < 0.0;
let eta_ratio = if inside { rfrct_idx } else { 1.0 / rfrct_idx };
let ns = if inside { -ho.ns } else { ho.ns };
let wi = refract(eta_ratio, v, ns);
Some( Box::new(DeltaPdf::new(wi)) )
}
pub fn reflect(v: Direction, no: Normal) -> Direction {
2.0 * v.project_onto(no) - v
}
pub fn refract(eta_ratio: Float, v: Direction, no: Normal) -> Direction {
let cos_to = no.dot(v);
let sin2_to = 1.0 - cos_to * cos_to;
let sin2_ti = eta_ratio * eta_ratio * sin2_to;
if sin2_ti > 1.0 {
reflect(v, no)
} else {
let cos_ti = (1.0 - sin2_ti).sqrt();
-v * eta_ratio + (eta_ratio * cos_to - cos_ti) * no
}
}