use crate::{ Normal, Direction, Transport, Float, Vec3 };
use crate::tracer::{
bxdfs, Color, hit::Hit, ray::Ray,
microfacet::MfDistribution,
texture::Texture, pdfs::{Pdf, CosPdf}
};
pub enum Material {
Microfacet(Texture, MfDistribution),
Lambertian(Texture),
Light(Texture),
Mirror,
Glass(Float),
Volumetric(Float, Vec3, Color),
Blank,
}
impl Material {
pub fn microfacet(
texture: Texture,
roughness: Float,
refraction_idx: Float,
metallicity: Float,
transparent: bool
) -> Self {
let mfd = MfDistribution::new(roughness, refraction_idx, metallicity, transparent);
Self::Microfacet(texture, mfd)
}
pub fn metallic(texture: Texture, roughness: Float) -> Self {
Self::microfacet(texture, roughness, 1.5, 1.0, false)
}
pub fn specular(texture: Texture, roughness: Float) -> Self {
Self::microfacet(texture, roughness, 1.5, 0.0, false)
}
pub fn diffuse(texture: Texture) -> Self {
Self::microfacet(texture, 1.0, 1.5, 0.0, false)
}
pub fn transparent(texture: Texture, roughness: Float, refraction_idx: Float) -> Self {
Self::microfacet(texture, roughness, refraction_idx, 0.0, true)
}
pub fn mirror() -> Self {
Self::Mirror
}
pub fn glass(refraction_index: Float) -> Self {
assert!(refraction_index >= 1.0);
Self::Glass(refraction_index)
}
pub fn is_specular(&self) -> bool {
match self {
Self::Mirror | Self::Glass(..) => true,
Self::Microfacet(_, mfd) => mfd.is_specular(),
_ => false,
}
}
pub fn is_delta(&self) -> bool {
match self {
Self::Lambertian(_) => false,
Self::Microfacet(_, mfd) => mfd.is_delta(),
_ => true,
}
}
pub fn emit(&self, h: &Hit) -> Color {
match self {
Self::Light(t) => if h.backface {
Color::BLACK
} else {
t.albedo_at(h)
},
_ => Color::BLACK
}
}
pub fn bsdf_f(
&self,
wo: Direction,
wi: Direction,
mode: Transport,
h: &Hit
) -> Color {
let ns = h.ns;
let ng = h.ng;
match self {
Self::Mirror => Color::WHITE,
Self::Glass(eta) => {
match mode {
Transport::Importance => Color::WHITE,
Transport::Radiance => {
let inside = wi.dot(ng) > 0.0;
if inside {
Color::splat(1.0 / (eta * eta))
} else {
Color::splat(eta * eta)
}
}
}
}
Self::Volumetric(_, sigma_t, sigma_s) => {
let transmittance = (-*sigma_t * h.t).exp();
let pdf = (transmittance * *sigma_t).dot(Vec3::ONE)
/ transmittance.dot(Vec3::ONE);
if pdf == 0.0 { Color::WHITE } else { *sigma_s / pdf }
}
Self::Microfacet(t, mfd) => {
bxdfs::bsdf_microfacet(wo, wi, ng, ns, mode, t.albedo_at(h), mfd)
}
Self::Lambertian(t) => t.albedo_at(h) / crate::PI,
_ => Color::BLACK,
}
}
pub fn shading_cosine(&self, wi: Direction, ns: Normal) -> Float {
match self {
Self::Microfacet(..) | Self::Lambertian(_) => ns.dot(wi).abs(),
_ => 1.0
}
}
pub fn bsdf_pdf(&self, ho: &Hit, ro: &Ray) -> Option<Box<dyn Pdf>> {
match self {
Self::Mirror => bxdfs::brdf_mirror_pdf(ho, ro),
Self::Glass(ridx) => bxdfs::btdf_glass_pdf(ho, ro, *ridx),
Self::Volumetric(g, ..) => bxdfs::brdf_volumetric_pdf(ro, *g),
Self::Lambertian(_) => Some(Box::new(CosPdf::new(ho.ns))),
Self::Microfacet(t, mfd) => {
bxdfs::bsdf_microfacet_pdf(ho, ro, t.albedo_at(ho), mfd)
}
Self::Light(_) | Self::Blank => None,
}
}
}