From e1c91919b8ced0b8a758c0e853dfc9b7a65af72c Mon Sep 17 00:00:00 2001 From: Matthew Gordon Date: Fri, 19 Jun 2020 00:04:01 -0400 Subject: [PATCH] Add RgbSamplesBsdfMaterial (not working yet) --- Cargo.toml | 1 + src/materials/mod.rs | 3 + src/materials/rgb_sampled_bsdf_material.rs | 179 +++++++++++++++++++++ 3 files changed, 183 insertions(+) create mode 100644 src/materials/rgb_sampled_bsdf_material.rs diff --git a/Cargo.toml b/Cargo.toml index 4df6b95..5f009ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ quickcheck_macros = "0.9" rayon = "1.3" sdl2 = "0.32" simba = "0.1.2" +csv = "1.1.3" [dependencies.nalgebra] version = "0.21" diff --git a/src/materials/mod.rs b/src/materials/mod.rs index 376c088..a8c4c5c 100644 --- a/src/materials/mod.rs +++ b/src/materials/mod.rs @@ -16,6 +16,9 @@ pub use phong_material::PhongMaterial; pub mod reflective_material; pub use reflective_material::ReflectiveMaterial; +pub mod rgb_sampled_bsdf_material; +pub use rgb_sampled_bsdf_material::RgbSampledBsdfMaterial; + pub trait Material: Debug + Sync + Send { fn bsdf(&self) -> Bsdf; diff --git a/src/materials/rgb_sampled_bsdf_material.rs b/src/materials/rgb_sampled_bsdf_material.rs new file mode 100644 index 0000000..2d56024 --- /dev/null +++ b/src/materials/rgb_sampled_bsdf_material.rs @@ -0,0 +1,179 @@ +use nalgebra::{convert, Vector3}; + +use super::{Bsdf, Material}; +use crate::colour::ColourRgbF; +use crate::Real; + +use std::error::Error; +use std::fs::File; +use std::io::BufReader; +use std::sync::Arc; + +use std::f64::consts::{FRAC_PI_2, PI}; + +#[derive(Debug)] +pub struct RgbSampledBsdfMaterial { + lut: Arc>>>>>, +} + +fn expand_and_index(v: &mut Vec, i: usize, default: T) -> &mut T { + if v.len() < i + 1 { + v.resize(i + 1, default); + } + &mut v[i] +} + +impl RgbSampledBsdfMaterial { + pub fn from_csv_file(filename: &str) -> Result, Box> { + let csv_file = File::open(filename)?; + let mut reader = csv::Reader::from_reader(BufReader::new(&csv_file)); + let mut lut = Vec::new(); + for row_result in reader.records() { + let row = row_result?; + let theta_in_index = row[0].trim().parse::()?; + let phi_in_index = row[1].trim().parse::()?; + let theta_out_index = row[2].trim().parse::()?; + let phi_out_index = row[3].trim().parse::()?; + let red = row[4].trim().parse::()?; + let green = row[5].trim().parse::()?; + let blue = row[6].trim().parse::()?; + *expand_and_index( + expand_and_index( + expand_and_index( + expand_and_index(&mut lut, theta_in_index, Vec::new()), + phi_in_index, + Vec::new(), + ), + theta_out_index, + Vec::new(), + ), + phi_out_index, + Vector3::zeros(), + ) = Vector3::new(convert(red), convert(green), convert(blue)); + } + let lut = Arc::new(lut); + Ok(RgbSampledBsdfMaterial { lut }) + } +} + +impl<'a, T: Real> Material for RgbSampledBsdfMaterial { + fn bsdf(&self) -> Bsdf { + let lut = Arc::clone(&self.lut); + Box::new(move |w_in, w_out, colour_in| { + if w_in.z < T::zero() || w_out.z < T::zero() { + return ColourRgbF::new(T::zero(), T::zero(), T::zero()); + } + let theta_in = w_in.z.acos(); + let theta_in_index = (theta_in / convert(FRAC_PI_2)).normalized_to_u32(4) as usize; + let phi_in = w_in.y.atan2(w_in.x) + convert(PI); + let phi_in_index = (phi_in / convert(2.0 * PI)).normalized_to_u32(6) as usize; + let theta_out = w_out.z.acos(); + let theta_out_index = (theta_out / convert(FRAC_PI_2)).normalized_to_u32(4) as usize; + let phi_out = w_out.y.atan2(w_out.x) + convert(PI); + let phi_out_index = (phi_out / convert(2.0 * PI)).normalized_to_u32(6) as usize; + ColourRgbF::from_vector3( + &colour_in.as_vector3().component_mul( + &lut[theta_in_index][phi_in_index][theta_out_index][phi_out_index], + ), + ) + }) + } + + fn sample(&self, w_o: &Vector3) -> Vec> { + vec![Vector3::new(-w_o.x, -w_o.y, w_o.z)] + } +} + +fn find_before_and_after<'a, T: Real, C>( + value: T, + vec: &'a Vec<(T, C)>, +) -> (&'a (T, C), &'a (T, C)) { + let first = vec.first().unwrap(); + let (lowest, _) = first; + if value < *lowest { + (first, first) + } else { + vec.iter() + .zip(vec.iter().skip(1)) + .find(|((value1, _), (value2, _))| value1 <= &value && value2 > &value) + .unwrap_or((vec.last().unwrap(), vec.last().unwrap())) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + mod find_before_and_after { + use super::*; + + #[test] + fn returns_element_before_value() { + let test_data = vec![(10.0, 1), (15.0, 2), (20.0, 3), (25.0, 4), (30.0, 5)]; + let ((low_first, low_second), (_, _)) = find_before_and_after(23.0, &test_data); + assert!(*low_first == 20.0); + assert!(*low_second == 3); + } + + #[test] + fn returns_element_after_value() { + let test_data = vec![(10.0, 1), (15.0, 2), (20.0, 3), (25.0, 4), (30.0, 5)]; + let ((_, _), (high_first, high_second)) = find_before_and_after(23.0, &test_data); + assert!(*high_first == 25.0); + assert!(*high_second == 4); + } + + #[test] + fn returns_element_equal_to_value_in_first_position() { + let test_data = vec![(10.0, 1), (15.0, 2), (20.0, 3), (25.0, 4), (30.0, 5)]; + let ((low_first, low_second), (high_first, high_second)) = + find_before_and_after(20.0, &test_data); + assert!(*low_first == 20.0); + assert!(*low_second == 3); + } + + #[test] + fn returns_first_element_twice_when_value_less_than_first() { + let test_data = vec![(10.0, 1), (15.0, 2), (20.0, 3), (25.0, 4), (30.0, 5)]; + let ((low_first, low_second), (high_first, high_second)) = + find_before_and_after(5.0, &test_data); + assert!(*low_first == 10.0); + assert!(*low_second == 1); + assert!(high_first == low_first); + assert!(high_second == high_second); + } + + #[test] + fn returns_last_element_twice_when_value_greater_than_last() { + let test_data = vec![(10.0, 1), (15.0, 2), (20.0, 3), (25.0, 4), (30.0, 5)]; + let ((low_first, low_second), (high_first, high_second)) = + find_before_and_after(35.0, &test_data); + assert!(*low_first == 30.0); + assert!(*low_second == 5); + assert!(high_first == low_first); + assert!(high_second == high_second); + } + + #[test] + fn returns_first_two_elements_when_value_is_between_them() { + let test_data = vec![(10.0, 1), (15.0, 2), (20.0, 3), (25.0, 4), (30.0, 5)]; + let ((low_first, low_second), (high_first, high_second)) = + find_before_and_after(12.0, &test_data); + assert!(*low_first == 10.0); + assert!(*low_second == 1); + assert!(*high_first == 15.0); + assert!(*high_second == 2); + } + + #[test] + fn returns_last_two_elements_when_value_is_between_them() { + let test_data = vec![(10.0, 1), (15.0, 2), (20.0, 3), (25.0, 4), (30.0, 5)]; + let ((low_first, low_second), (high_first, high_second)) = + find_before_and_after(27.0, &test_data); + assert!(*low_first == 25.0); + assert!(*low_second == 4); + assert!(*high_first == 30.0); + assert!(*high_second == 5); + } + } +}