From e70b479b308e9dcbf9730a94456ac23a6bd9c3bf Mon Sep 17 00:00:00 2001 From: Gillett Hernandez Date: Sun, 7 Jul 2024 19:39:22 -0700 Subject: [PATCH] add proptest usage within ggx tests, along with some fixtures/prop_compose invocations --- Cargo.toml | 4 + proptest-regressions/materials/ggx.txt | 7 ++ src/lib.rs | 3 + src/materials/ggx.rs | 123 +++++++++++++++++++++++++ src/props.rs | 20 ++++ 5 files changed, 157 insertions(+) create mode 100644 proptest-regressions/materials/ggx.txt create mode 100644 src/props.rs diff --git a/Cargo.toml b/Cargo.toml index 488778c..f6ea44c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,8 +71,12 @@ tobj = "~4.0" toml = "~0.8" tracing = "~0.1" tracing-subscriber = "~0.3" +# pinning to 0.8 because of crate version reasons ultraviolet = { version = "~0.8", optional = true } +[dev-dependencies] +proptest = "1.5.0" + [target.'cfg(windows)'.dependencies] win32_notification = "~0.1" diff --git a/proptest-regressions/materials/ggx.txt b/proptest-regressions/materials/ggx.txt new file mode 100644 index 0000000..92a7d36 --- /dev/null +++ b/proptest-regressions/materials/ggx.txt @@ -0,0 +1,7 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 9c0ae291d4fb1b0b7461c99518df9126afd836caa7b93dadd6772b8c1f81fb26 # shrinks to roughness = 8.736748, wi = Vec3(0.54826164, 0.0, -0.83630687), lambda = 400.0, s = Sample2D { x: 0.0, y: 0.0 } diff --git a/src/lib.rs b/src/lib.rs index fbab35a..43e7efa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,6 +39,9 @@ pub mod tonemap; pub mod vec2d; pub mod world; +#[cfg(test)] +pub mod props; + // mauve. universal sign of danger pub const MAUVE: XYZColor = XYZColor::new(0.5199467, 51.48687, 1.0180528); diff --git a/src/materials/ggx.rs b/src/materials/ggx.rs index e45dcc8..63c2bc8 100644 --- a/src/materials/ggx.rs +++ b/src/materials/ggx.rs @@ -599,6 +599,8 @@ impl Material for GGX { #[cfg(test)] mod test { + use proptest::prelude::*; + use crate::props::*; use math::spectral::BOUNDED_VISIBLE_RANGE; use rayon::iter::{IntoParallelIterator, ParallelIterator}; @@ -632,6 +634,127 @@ mod test { GGX::new(roughness, glass, flat_one, flat_zero, 0, 0) } + proptest! { + #![proptest_config(ProptestConfig::with_cases(1000))] + #[test] + fn test_ggx(roughness in valid_ggx_roughness(), wi in unit_vector(), lambda in 400.0..800.0f32, s in uniform_sample()) { + let ggx_glass = ggx_glass(roughness); + let fake_hit_record: HitRecord = HitRecord::new( + 0.0, + Point3::ZERO, + UV(0.0f32, 0.0f32), + lambda, + Vec3::Z, + MaterialId::Material(0), + 0, + None, + ); + + let maybe_wo = ggx_glass.generate( + fake_hit_record.lambda, + fake_hit_record.uv, + fake_hit_record.transport_mode, + s, + wi, + ); + assert!(maybe_wo.is_some()); + + let wo = maybe_wo.unwrap(); + let (orig_f, orig_pdf) = ggx_glass.bsdf( + fake_hit_record.lambda, + fake_hit_record.uv, + fake_hit_record.transport_mode, + wi, + wo, + ); + + // check swapping wi and wo + let (wi, wo) = (wo, wi); + let (sampled_f, sampled_pdf) = ggx_glass.bsdf( + fake_hit_record.lambda, + fake_hit_record.uv, + fake_hit_record.transport_mode, + wi, + wo, + ); + assert!(sampled_f > 0.0, "original f: {:?}, pdf: {:?}, swapped f: {:?}, pdf: {:?}, swapped wi: {:?}, wo: {:?}", orig_f, orig_pdf, sampled_f, sampled_pdf, wi, wo); + assert!(*sampled_pdf >= 0.0, "original f: {:?}, pdf: {:?}, swapped f: {:?}, pdf: {:?}, swapped wi: {:?}, wo: {:?}", orig_f, orig_pdf, sampled_f, sampled_pdf, wi, wo); + assert!(orig_f > 0.0, "original f: {:?}, pdf: {:?}, swapped f: {:?}, pdf: {:?}, swapped wi: {:?}, wo: {:?}", orig_f, orig_pdf, sampled_f, sampled_pdf, wi, wo); + assert!(*orig_pdf >= 0.0, "original f: {:?}, pdf: {:?}, swapped f: {:?}, pdf: {:?}, swapped wi: {:?}, wo: {:?}", orig_f, orig_pdf, sampled_f, sampled_pdf, wi, wo); + + } + + #[test] + fn test_ggx2(roughness in valid_ggx_roughness(), wi in unit_vector(), wo in unit_vector(), lambda in 400.0..800.0f32) { + let ggx_glass = ggx_glass(roughness); + let fake_hit_record: HitRecord = HitRecord::new( + 0.0, + Point3::ZERO, + UV(0.0f32, 0.0f32), + lambda, + Vec3::Z, + MaterialId::Material(0), + 0, + None, + ); + let (orig_f, orig_pdf) = ggx_glass.bsdf( + fake_hit_record.lambda, + fake_hit_record.uv, + fake_hit_record.transport_mode, + wi, + wo, + ); + let (wi, wo) = (wo, wi); + let (sampled_f, sampled_pdf) = ggx_glass.bsdf( + fake_hit_record.lambda, + fake_hit_record.uv, + fake_hit_record.transport_mode, + wi, + wo, + ); + assert!( + sampled_f >= 0.0, + "original f: {:?}, pdf: {:?}, swapped f: {:?}, pdf: {:?}, swapped wi: {:?}, wo: {:?}", + orig_f, + orig_pdf, + sampled_f, + sampled_pdf, + wi, + wo + ); + assert!( + *sampled_pdf >= 0.0, + "original f: {:?}, pdf: {:?}, swapped f: {:?}, pdf: {:?}, swapped wi: {:?}, wo: {:?}", + orig_f, + orig_pdf, + sampled_f, + sampled_pdf, + wi, + wo + ); + assert!( + orig_f >= 0.0, + "original f: {:?}, pdf: {:?}, swapped f: {:?}, pdf: {:?}, swapped wi: {:?}, wo: {:?}", + orig_f, + orig_pdf, + sampled_f, + sampled_pdf, + wi, + wo + ); + assert!( + *orig_pdf >= 0.0, + "original f: {:?}, pdf: {:?}, swapped f: {:?}, pdf: {:?}, swapped wi: {:?}, wo: {:?}", + orig_f, + orig_pdf, + sampled_f, + sampled_pdf, + wi, + wo + ); + } + } + #[test] fn test_ggx_functions() { let mut sampler: Box = Box::new(StratifiedSampler::new(20, 20, 10)); diff --git a/src/props.rs b/src/props.rs new file mode 100644 index 0000000..e240a14 --- /dev/null +++ b/src/props.rs @@ -0,0 +1,20 @@ +use math::{random::random_on_unit_sphere, sample::Sample2D, vec::Vec3}; +use proptest::prelude::*; + +prop_compose! { + pub fn uniform_sample()(u in 0.0..1.0f32, v in 0.0..1.0f32) -> Sample2D { + Sample2D::new(u, v) + } +} + +prop_compose! { + pub fn valid_ggx_roughness()(x in 0.0..1.0f32) -> f32 { + (-(x+f32::EPSILON).ln()).recip() + } +} + +prop_compose! { + pub fn unit_vector()(s in uniform_sample()) -> Vec3 { + random_on_unit_sphere(s) + } +}