diff --git a/Cargo.lock b/Cargo.lock index 2d9ec2d..af82969 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -126,6 +126,9 @@ dependencies = [ [[package]] name = "color" version = "0.1.0" +dependencies = [ + "colorbox", +] [[package]] name = "colorbox" diff --git a/src/color.rs b/src/color.rs index e438609..ee4dc60 100644 --- a/src/color.rs +++ b/src/color.rs @@ -1,13 +1,8 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign}; use crate::math::Float4; -pub use color::{ - rec709_e_to_xyz, rec709_to_xyz, xyz_to_aces_ap0, xyz_to_aces_ap0_e, xyz_to_rec709, - xyz_to_rec709_e, -}; use compact::fluv::fluv32; use half::f16; -use spectral_upsampling::meng::{spectrum_xyz_to_p_4, EQUAL_ENERGY_REFLECTANCE}; use crate::{lerp::Lerp, math::fast_exp}; @@ -600,8 +595,14 @@ impl DivAssign for XYZ { /// Colours" by Meng et al. #[inline(always)] fn xyz_to_spectrum_4(xyz: (f32, f32, f32), wavelengths: Float4) -> Float4 { - spectrum_xyz_to_p_4(wavelengths, xyz) * Float4::splat(1.0 / EQUAL_ENERGY_REFLECTANCE) - // aces_to_spectrum_p4(wavelengths, xyz_to_aces_ap0_e(xyz)) + use spectral_upsampling as su; + + // su::meng::spectrum_xyz_to_p_4(wavelengths, xyz) + // * Float4::splat(1.0 / su::meng::EQUAL_ENERGY_REFLECTANCE) + + su::jakob::rec2020_to_spectrum_p4(wavelengths, color::xyz_to_rec2020_e(xyz)) + + // su::jakob::rec709_to_spectrum_p4(wavelengths, color::xyz_to_rec709_e(xyz)) } /// Close analytic approximations of the CIE 1931 XYZ color curves. diff --git a/src/image.rs b/src/image.rs index 454a6d1..df61080 100644 --- a/src/image.rs +++ b/src/image.rs @@ -13,7 +13,9 @@ use std::{ use half::f16; -use crate::color::{xyz_to_rec709_e, XYZ}; +pub use color::{rec709_e_to_xyz, xyz_to_rec709_e}; + +use crate::color::XYZ; #[derive(Debug)] #[allow(clippy::type_complexity)] diff --git a/src/parse/psy.rs b/src/parse/psy.rs index b6ea518..5db2d92 100644 --- a/src/parse/psy.rs +++ b/src/parse/psy.rs @@ -6,14 +6,11 @@ use nom::{combinator::all_consuming, sequence::tuple, IResult}; use kioku::Arena; +use color::rec709_e_to_xyz; + use crate::{ - camera::Camera, - color::{rec709_e_to_xyz, Color}, - light::WorldLightSource, - math::Xform, - renderer::Renderer, - scene::Scene, - scene::World, + camera::Camera, color::Color, light::WorldLightSource, math::Xform, renderer::Renderer, + scene::Scene, scene::World, }; use super::{ diff --git a/src/renderer.rs b/src/renderer.rs index 5e57c71..1638006 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -285,8 +285,7 @@ impl<'a> Renderer<'a> { // Pre-calculate base64 encoding if needed let base64_enc = if do_blender_output { - use crate::color::xyz_to_rec709_e; - Some(img_bucket.rgba_base64(xyz_to_rec709_e)) + Some(img_bucket.rgba_base64(color::xyz_to_rec709_e)) } else { None }; diff --git a/src/tracer.rs b/src/tracer.rs index 7bbaba3..3228d5e 100644 --- a/src/tracer.rs +++ b/src/tracer.rs @@ -1,5 +1,5 @@ use crate::{ - color::{rec709_to_xyz, Color}, + color::Color, lerp::lerp_slice, math::XformFull, ray::{LocalRay, Ray}, @@ -109,7 +109,7 @@ impl<'a> Tracer<'a> { match *obj { Object::Surface(surface) => { let unassigned_shader = SimpleSurfaceShader::Emit { - color: Color::new_xyz(rec709_to_xyz((1.0, 0.0, 1.0))), + color: Color::new_xyz(color::rec709_to_xyz((1.0, 0.0, 1.0))), }; let shader = surface_shader.unwrap_or(&unassigned_shader); @@ -119,7 +119,7 @@ impl<'a> Tracer<'a> { Object::SurfaceLight(surface) => { // Lights don't use shaders let bogus_shader = SimpleSurfaceShader::Emit { - color: Color::new_xyz(rec709_to_xyz((1.0, 0.0, 1.0))), + color: Color::new_xyz(color::rec709_to_xyz((1.0, 0.0, 1.0))), }; surface.intersect_ray(ray, local_ray, space, isect, &bogus_shader); diff --git a/sub_crates/color/Cargo.toml b/sub_crates/color/Cargo.toml index 8634bbb..f991acf 100644 --- a/sub_crates/color/Cargo.toml +++ b/sub_crates/color/Cargo.toml @@ -6,6 +6,9 @@ edition = "2018" license = "MIT, Apache 2.0" build = "build.rs" +[build-dependencies] +colorbox = "0.3" + [lib] name = "color" path = "src/lib.rs" diff --git a/sub_crates/color/build.rs b/sub_crates/color/build.rs index 4c6dcfe..192e1e0 100644 --- a/sub_crates/color/build.rs +++ b/sub_crates/color/build.rs @@ -1,76 +1,46 @@ use std::{env, fs::File, io::Write, path::Path}; -#[derive(Copy, Clone)] -struct Chromaticities { - r: (f64, f64), - g: (f64, f64), - b: (f64, f64), - w: (f64, f64), -} +use colorbox::{ + chroma::{self, Chromaticities}, + matrix::{invert, rgb_to_xyz_matrix, xyz_chromatic_adaptation_matrix, AdaptationMethod}, + matrix_compose, +}; fn main() { let out_dir = env::var("OUT_DIR").unwrap(); // Rec709 { - let chroma = Chromaticities { - r: (0.640, 0.330), - g: (0.300, 0.600), - b: (0.150, 0.060), - w: (0.3127, 0.3290), - }; - let dest_path = Path::new(&out_dir).join("rec709_inc.rs"); let mut f = File::create(&dest_path).unwrap(); - write_conversion_functions("rec709", chroma, &mut f); + write_conversion_functions("rec709", chroma::REC709, &mut f); } // Rec2020 { - let chroma = Chromaticities { - r: (0.708, 0.292), - g: (0.170, 0.797), - b: (0.131, 0.046), - w: (0.3127, 0.3290), - }; - let dest_path = Path::new(&out_dir).join("rec2020_inc.rs"); let mut f = File::create(&dest_path).unwrap(); - write_conversion_functions("rec2020", chroma, &mut f); + write_conversion_functions("rec2020", chroma::REC2020, &mut f); } // ACES AP0 { - let chroma = Chromaticities { - r: (0.73470, 0.26530), - g: (0.00000, 1.00000), - b: (0.00010, -0.07700), - w: (0.32168, 0.33767), - }; - let dest_path = Path::new(&out_dir).join("aces_ap0_inc.rs"); let mut f = File::create(&dest_path).unwrap(); - write_conversion_functions("aces_ap0", chroma, &mut f); + write_conversion_functions("aces_ap0", chroma::ACES_AP0, &mut f); } // ACES AP1 { - let chroma = Chromaticities { - r: (0.713, 0.293), - g: (0.165, 0.830), - b: (0.128, 0.044), - w: (0.32168, 0.33767), - }; - let dest_path = Path::new(&out_dir).join("aces_ap1_inc.rs"); let mut f = File::create(&dest_path).unwrap(); - write_conversion_functions("aces_ap1", chroma, &mut f); + write_conversion_functions("aces_ap1", chroma::ACES_AP1, &mut f); } } /// Generates conversion functions for the given rgb to xyz transform matrix. fn write_conversion_functions(space_name: &str, chroma: Chromaticities, f: &mut File) { - let to_xyz = rgb_to_xyz(chroma, 1.0); + let to_xyz = rgb_to_xyz_matrix(chroma); f.write_all( format!( @@ -99,7 +69,7 @@ pub fn {}_to_xyz(rgb: (f32, f32, f32)) -> (f32, f32, f32) {{ ) .unwrap(); - let inv = inverse(to_xyz); + let inv = invert(to_xyz).unwrap(); f.write_all( format!( r#" @@ -127,12 +97,14 @@ pub fn xyz_to_{}(xyz: (f32, f32, f32)) -> (f32, f32, f32) {{ ) .unwrap(); - let e_chroma = { - let mut e_chroma = chroma; - e_chroma.w = (1.0 / 3.0, 1.0 / 3.0); - e_chroma - }; - let e_to_xyz = rgb_to_xyz(e_chroma, 1.0); + let e_to_xyz = matrix_compose!( + rgb_to_xyz_matrix(chroma), + xyz_chromatic_adaptation_matrix( + chroma.w, + (1.0 / 3.0, 1.0 / 3.0), + AdaptationMethod::Bradford, + ), + ); f.write_all( format!( r#" @@ -160,7 +132,7 @@ pub fn {}_e_to_xyz(rgb: (f32, f32, f32)) -> (f32, f32, f32) {{ ) .unwrap(); - let inv_e = inverse(e_to_xyz); + let inv_e = invert(e_to_xyz).unwrap(); f.write_all( format!( r#" @@ -188,135 +160,3 @@ pub fn xyz_to_{}_e(xyz: (f32, f32, f32)) -> (f32, f32, f32) {{ ) .unwrap(); } - -/// Port of the RGBtoXYZ function from the ACES CTL reference implementation. -/// See lib/IlmCtlMath/CtlColorSpace.cpp in the CTL reference implementation. -/// -/// This takes the chromaticities of an RGB colorspace and generates a -/// transform matrix from that space to XYZ. -/// -/// * `chroma` is the chromaticities. -/// * `y` is the XYZ "Y" value that should map to RGB (1,1,1) -fn rgb_to_xyz(chroma: Chromaticities, y: f64) -> [[f64; 3]; 3] { - // X and Z values of RGB value (1, 1, 1), or "white" - let x = chroma.w.0 * y / chroma.w.1; - let z = (1.0 - chroma.w.0 - chroma.w.1) * y / chroma.w.1; - - // Scale factors for matrix rows - let d = chroma.r.0 * (chroma.b.1 - chroma.g.1) - + chroma.b.0 * (chroma.g.1 - chroma.r.1) - + chroma.g.0 * (chroma.r.1 - chroma.b.1); - - let sr = (x * (chroma.b.1 - chroma.g.1) - - chroma.g.0 * (y * (chroma.b.1 - 1.0) + chroma.b.1 * (x + z)) - + chroma.b.0 * (y * (chroma.g.1 - 1.0) + chroma.g.1 * (x + z))) - / d; - - let sg = (x * (chroma.r.1 - chroma.b.1) - + chroma.r.0 * (y * (chroma.b.1 - 1.0) + chroma.b.1 * (x + z)) - - chroma.b.0 * (y * (chroma.r.1 - 1.0) + chroma.r.1 * (x + z))) - / d; - - let sb = (x * (chroma.g.1 - chroma.r.1) - - chroma.r.0 * (y * (chroma.g.1 - 1.0) + chroma.g.1 * (x + z)) - + chroma.g.0 * (y * (chroma.r.1 - 1.0) + chroma.r.1 * (x + z))) - / d; - - // Assemble the matrix - let mut mat = [[0.0; 3]; 3]; - - mat[0][0] = sr * chroma.r.0; - mat[0][1] = sg * chroma.g.0; - mat[0][2] = sb * chroma.b.0; - - mat[1][0] = sr * chroma.r.1; - mat[1][1] = sg * chroma.g.1; - mat[1][2] = sb * chroma.b.1; - - mat[2][0] = sr * (1.0 - chroma.r.0 - chroma.r.1); - mat[2][1] = sg * (1.0 - chroma.g.0 - chroma.g.1); - mat[2][2] = sb * (1.0 - chroma.b.0 - chroma.b.1); - - mat -} - -/// Calculates the inverse of the given 3x3 matrix. -/// -/// Ported to Rust from `gjInverse()` in IlmBase's Imath/ImathMatrix.h -fn inverse(m: [[f64; 3]; 3]) -> [[f64; 3]; 3] { - let mut s = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]; - let mut t = m; - - // Forward elimination - for i in 0..2 { - let mut pivot = i; - let mut pivotsize = t[i][i]; - - if pivotsize < 0.0 { - pivotsize = -pivotsize; - } - - for j in (i + 1)..3 { - let mut tmp = t[j][i]; - - if tmp < 0.0 { - tmp = -tmp; - } - - if tmp > pivotsize { - pivot = j; - pivotsize = tmp; - } - } - - if pivotsize == 0.0 { - panic!("Cannot invert singular matrix."); - } - - if pivot != i { - for j in 0..3 { - let mut tmp = t[i][j]; - t[i][j] = t[pivot][j]; - t[pivot][j] = tmp; - - tmp = s[i][j]; - s[i][j] = s[pivot][j]; - s[pivot][j] = tmp; - } - } - - for j in (i + 1)..3 { - let f = t[j][i] / t[i][i]; - - for k in 0..3 { - t[j][k] -= f * t[i][k]; - s[j][k] -= f * s[i][k]; - } - } - } - - // Backward substitution - for i in (0..3).rev() { - let f = t[i][i]; - - if t[i][i] == 0.0 { - panic!("Cannot invert singular matrix."); - } - - for j in 0..3 { - t[i][j] /= f; - s[i][j] /= f; - } - - for j in 0..i { - let f = t[j][i]; - - for k in 0..3 { - t[j][k] -= f * t[i][k]; - s[j][k] -= f * s[i][k]; - } - } - } - - s -}