#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Material {
pub specular: f32,
pub shininess: f32,
pub reflectivity: f32,
}
impl Default for Material {
fn default() -> Self {
Self {
specular: 0.12,
shininess: 24.0,
reflectivity: 0.05,
}
}
}
impl Material {
pub fn gloss(specular: f32, shininess: f32) -> Self {
Self {
specular,
shininess,
reflectivity: (specular * 0.5).min(1.0),
}
}
pub fn matte() -> Self {
Self {
specular: 0.0,
shininess: 1.0,
reflectivity: 0.0,
}
}
pub fn reflectivity(mut self, reflectivity: f32) -> Self {
self.reflectivity = reflectivity;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_matte_surface_takes_no_highlight_and_shows_nothing_back() {
let matte = Material::matte();
assert_eq!(matte.specular, 0.0);
assert_eq!(matte.reflectivity, 0.0);
}
#[test]
fn polish_brings_the_sky_with_it() {
let dull = Material::gloss(0.2, 16.0);
let polished = Material::gloss(0.8, 64.0);
assert!(polished.reflectivity > dull.reflectivity);
assert!(polished.shininess > dull.shininess);
assert!(
polished.reflectivity <= 1.0,
"and never more than all of it"
);
}
}
impl Material {
pub fn to_openpbr(self) -> OpenPbrSurface {
let roughness = (2.0 / (self.shininess.max(1.0) + 2.0))
.sqrt()
.clamp(0.02, 1.0);
OpenPbrSurface {
base_color: Color3::new(1.0, 1.0, 1.0),
specular_weight: self.specular.clamp(0.0, 1.0),
specular_roughness: roughness,
specular_ior: 1.5 + self.reflectivity.clamp(0.0, 1.0) * 0.5,
..OpenPbrSurface::default()
}
}
}
use crate::materials::openpbr::{Color3, OpenPbrSurface};
#[cfg(test)]
mod openpbr_tests {
use super::*;
#[test]
fn polish_becomes_smoothness() {
let dull = Material::gloss(0.2, 8.0).to_openpbr();
let polished = Material::gloss(0.8, 128.0).to_openpbr();
assert!(
polished.specular_roughness < dull.specular_roughness,
"a tighter highlight is a smoother surface",
);
assert!(polished.specular_weight > dull.specular_weight);
}
#[test]
fn a_matte_surface_keeps_its_specular_off() {
assert_eq!(Material::matte().to_openpbr().specular_weight, 0.0);
}
#[test]
fn roughness_never_reaches_zero() {
let mirror = Material::gloss(1.0, 1.0e9).to_openpbr();
assert!(mirror.specular_roughness >= 0.02);
}
}