From 80f9c3cc2dca4ef9b426fc4e64444b679e2b2830 Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Tue, 24 Mar 2020 11:10:18 +0900 Subject: [PATCH] Implement interpolation of surface closures between micropoly vertices. --- src/color.rs | 108 +++++++++++++++++++++++++-------- src/light/rectangle_light.rs | 3 +- src/light/sphere_light.rs | 2 +- src/shading/surface_closure.rs | 53 ++++++++++++++++ src/surface/micropoly_batch.rs | 26 ++++---- 5 files changed, 154 insertions(+), 38 deletions(-) diff --git a/src/color.rs b/src/color.rs index ff2cefb..f9515b8 100644 --- a/src/color.rs +++ b/src/color.rs @@ -1,5 +1,3 @@ -use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign}; - 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, @@ -249,12 +247,8 @@ impl Color { _ => unreachable!(), } } -} -impl Mul for Color { - type Output = Self; - - fn mul(self, rhs: f32) -> Self { + pub fn scale_brightness(self, rhs: f32) -> Self { match self { Color::XYZ(x, y, z) => Color::XYZ(x * rhs, y * rhs, z * rhs), @@ -277,9 +271,75 @@ impl Mul for Color { } } -impl MulAssign for Color { - fn mul_assign(&mut self, rhs: f32) { - *self = *self * rhs; +// Implemented for interpolation operations, not for any otherwise meaningful +// notion of addition. +impl std::ops::Add for Color { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + match (self, rhs) { + (Color::XYZ(x1, y1, z1), Color::XYZ(x2, y2, z2)) => { + Color::XYZ(x1 + x2, y1 + y2, z1 + z2) + } + + ( + Color::Blackbody { + temperature: tmp1, + factor: fac1, + }, + Color::Blackbody { + temperature: tmp2, + factor: fac2, + }, + ) => Color::Blackbody { + temperature: tmp1 + tmp2, + factor: fac1 + fac2, + }, + + ( + Color::Temperature { + temperature: tmp1, + factor: fac1, + }, + Color::Temperature { + temperature: tmp2, + factor: fac2, + }, + ) => Color::Temperature { + temperature: tmp1 + tmp2, + factor: fac1 + fac2, + }, + + _ => panic!("Cannot add colors with different representations."), + } + } +} + +// Implemented for interpolation operations, not for any otherwise meaningful +// notion of multiplication. +impl std::ops::Mul for Color { + type Output = Self; + + fn mul(self, rhs: f32) -> Self { + match self { + Color::XYZ(x, y, z) => Color::XYZ(x * rhs, y * rhs, z * rhs), + + Color::Blackbody { + temperature, + factor, + } => Color::Blackbody { + temperature: temperature * rhs, + factor: factor * rhs, + }, + + Color::Temperature { + temperature, + factor, + } => Color::Temperature { + temperature: temperature * rhs, + factor: factor * rhs, + }, + } } } @@ -429,7 +489,7 @@ impl SpectralSample { } } -impl Add for SpectralSample { +impl std::ops::Add for SpectralSample { type Output = SpectralSample; fn add(self, rhs: SpectralSample) -> Self::Output { assert_eq!(self.hero_wavelength, rhs.hero_wavelength); @@ -440,14 +500,14 @@ impl Add for SpectralSample { } } -impl AddAssign for SpectralSample { +impl std::ops::AddAssign for SpectralSample { fn add_assign(&mut self, rhs: SpectralSample) { assert_eq!(self.hero_wavelength, rhs.hero_wavelength); self.e = self.e + rhs.e; } } -impl Mul for SpectralSample { +impl std::ops::Mul for SpectralSample { type Output = SpectralSample; fn mul(self, rhs: SpectralSample) -> Self::Output { assert_eq!(self.hero_wavelength, rhs.hero_wavelength); @@ -458,14 +518,14 @@ impl Mul for SpectralSample { } } -impl MulAssign for SpectralSample { +impl std::ops::MulAssign for SpectralSample { fn mul_assign(&mut self, rhs: SpectralSample) { assert_eq!(self.hero_wavelength, rhs.hero_wavelength); self.e = self.e * rhs.e; } } -impl Mul for SpectralSample { +impl std::ops::Mul for SpectralSample { type Output = SpectralSample; fn mul(self, rhs: f32) -> Self::Output { SpectralSample { @@ -475,13 +535,13 @@ impl Mul for SpectralSample { } } -impl MulAssign for SpectralSample { +impl std::ops::MulAssign for SpectralSample { fn mul_assign(&mut self, rhs: f32) { self.e = self.e * rhs; } } -impl Div for SpectralSample { +impl std::ops::Div for SpectralSample { type Output = SpectralSample; fn div(self, rhs: f32) -> Self::Output { SpectralSample { @@ -491,7 +551,7 @@ impl Div for SpectralSample { } } -impl DivAssign for SpectralSample { +impl std::ops::DivAssign for SpectralSample { fn div_assign(&mut self, rhs: f32) { self.e = self.e / rhs; } @@ -538,7 +598,7 @@ impl Lerp for XYZ { } } -impl Add for XYZ { +impl std::ops::Add for XYZ { type Output = XYZ; fn add(self, rhs: XYZ) -> Self::Output { XYZ { @@ -549,7 +609,7 @@ impl Add for XYZ { } } -impl AddAssign for XYZ { +impl std::ops::AddAssign for XYZ { fn add_assign(&mut self, rhs: XYZ) { self.x += rhs.x; self.y += rhs.y; @@ -557,7 +617,7 @@ impl AddAssign for XYZ { } } -impl Mul for XYZ { +impl std::ops::Mul for XYZ { type Output = XYZ; fn mul(self, rhs: f32) -> Self::Output { XYZ { @@ -568,7 +628,7 @@ impl Mul for XYZ { } } -impl MulAssign for XYZ { +impl std::ops::MulAssign for XYZ { fn mul_assign(&mut self, rhs: f32) { self.x *= rhs; self.y *= rhs; @@ -576,7 +636,7 @@ impl MulAssign for XYZ { } } -impl Div for XYZ { +impl std::ops::Div for XYZ { type Output = XYZ; fn div(self, rhs: f32) -> Self::Output { XYZ { @@ -587,7 +647,7 @@ impl Div for XYZ { } } -impl DivAssign for XYZ { +impl std::ops::DivAssign for XYZ { fn div_assign(&mut self, rhs: f32) { self.x /= rhs; self.y /= rhs; diff --git a/src/light/rectangle_light.rs b/src/light/rectangle_light.rs index 4da96e1..4d09552 100644 --- a/src/light/rectangle_light.rs +++ b/src/light/rectangle_light.rs @@ -315,7 +315,8 @@ impl<'a> Surface for RectangleLight<'a> { let closure = { let inv_surface_area = (1.0 / (dim.0 as f64 * dim.1 as f64)) as f32; - let color = lerp_slice(self.colors, time) * inv_surface_area; + let color = lerp_slice(self.colors, time) + .scale_brightness(inv_surface_area); SurfaceClosure::Emit(color) }; diff --git a/src/light/sphere_light.rs b/src/light/sphere_light.rs index dc692de..cfa1635 100644 --- a/src/light/sphere_light.rs +++ b/src/light/sphere_light.rs @@ -321,7 +321,7 @@ impl<'a> Surface for SphereLight<'a> { let closure = { let inv_surface_area = (1.0 / (4.0 * PI_64 * radius as f64 * radius as f64)) as f32; - let color = lerp_slice(self.colors, time) * inv_surface_area; + let color = lerp_slice(self.colors, time).scale_brightness(inv_surface_area); SurfaceClosure::Emit(color) }; diff --git a/src/shading/surface_closure.rs b/src/shading/surface_closure.rs index 10713eb..a769e27 100644 --- a/src/shading/surface_closure.rs +++ b/src/shading/surface_closure.rs @@ -250,6 +250,59 @@ impl SurfaceClosure { } } +// Implemented for interpolation operations, not for any otherwise meaningful +// notion of addition. +impl std::ops::Add for SurfaceClosure { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + match (self, rhs) { + (Lambert(col1), Lambert(col2)) => Lambert(col1 + col2), + ( + GGX { + color: col1, + roughness: rgh1, + fresnel: frs1, + }, + GGX { + color: col2, + roughness: rgh2, + fresnel: frs2, + }, + ) => GGX { + color: col1 + col2, + roughness: rgh1 + rgh2, + fresnel: frs1 + frs2, + }, + (Emit(col1), Emit(col2)) => Emit(col1 + col2), + + _ => panic!("Cannot add two different surface closure types."), + } + } +} + +// Implemented for interpolation operations, not for any otherwise meaningful +// notion of multiplication. +impl std::ops::Mul for SurfaceClosure { + type Output = Self; + + fn mul(self, rhs: f32) -> Self { + match self { + Lambert(col) => Lambert(col * rhs), + GGX { + color: col, + roughness: rgh, + fresnel: frs, + } => GGX { + color: col * rhs, + roughness: rgh * rhs, + fresnel: frs * rhs, + }, + Emit(col) => Emit(col * rhs), + } + } +} + impl Lerp for SurfaceClosure { fn lerp(self, other: SurfaceClosure, alpha: f32) -> SurfaceClosure { match (self, other) { diff --git a/src/surface/micropoly_batch.rs b/src/surface/micropoly_batch.rs index 967f8f4..3dbb75d 100644 --- a/src/surface/micropoly_batch.rs +++ b/src/surface/micropoly_batch.rs @@ -33,9 +33,9 @@ pub struct MicropolyBatch<'a> { normals: &'a [Normal], // Per-vertex shading data. + // TODO: time samples. compressed_vertex_closure_size: usize, // Size in bites of a single compressed closure - vertex_closure_time_sample_count: usize, - compressed_vertex_closures: &'a [u8], // Packed compressed closures + compressed_vertex_closures: &'a [u8], // Packed compressed closures // Micro-triangle indices. Each element of the tuple specifies the index // of a vertex, which indexes into all of the arrays above. @@ -130,7 +130,6 @@ impl<'a> MicropolyBatch<'a> { vertices: vertices, normals: normals, compressed_vertex_closure_size: 0, - vertex_closure_time_sample_count: 1, compressed_vertex_closures: &[], indices: indices, accel: accel, @@ -320,16 +319,19 @@ impl<'a> MicropolyBatch<'a> { }; // Calculate interpolated surface closure. - // TODO: actually interpolate. + // TODO: time sampling. let closure = { - let start_byte = hit_tri_indices.0 as usize - * self.compressed_vertex_closure_size - * self.vertex_closure_time_sample_count; - let end_byte = start_byte + self.compressed_vertex_closure_size; - let (closure, _) = SurfaceClosure::from_compressed( - &self.compressed_vertex_closures[start_byte..end_byte], - ); - closure + let get_closure = |index| { + let start_byte = index * self.compressed_vertex_closure_size; + SurfaceClosure::from_compressed( + &self.compressed_vertex_closures[start_byte..], + ) + .0 + }; + let c0 = get_closure(hit_tri_indices.0 as usize); + let c1 = get_closure(hit_tri_indices.1 as usize); + let c2 = get_closure(hit_tri_indices.2 as usize); + (c0 * b0) + (c1 * b1) + (c2 * b2) }; let intersection_data = SurfaceIntersectionData {