Implemented basic SurfaceClosure compression for storing per-vertex.
This is really simple, and doesn't account for things like constant parameters yet. It's just to get things rolling.
This commit is contained in:
parent
41c2174d59
commit
5c5a01ecee
111
src/color.rs
111
src/color.rs
|
@ -5,7 +5,9 @@ pub use color::{
|
||||||
xyz_to_rec709_e,
|
xyz_to_rec709_e,
|
||||||
};
|
};
|
||||||
use float4::Float4;
|
use float4::Float4;
|
||||||
|
use half::f16;
|
||||||
use spectral_upsampling::meng::{spectrum_xyz_to_p_4, EQUAL_ENERGY_REFLECTANCE};
|
use spectral_upsampling::meng::{spectrum_xyz_to_p_4, EQUAL_ENERGY_REFLECTANCE};
|
||||||
|
use trifloat::signed48;
|
||||||
|
|
||||||
use crate::{lerp::Lerp, math::fast_exp};
|
use crate::{lerp::Lerp, math::fast_exp};
|
||||||
|
|
||||||
|
@ -138,6 +140,115 @@ impl Color {
|
||||||
Color::Temperature { factor, .. } => factor,
|
Color::Temperature { factor, .. } => factor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the post-compression size of this color.
|
||||||
|
pub fn compressed_size(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Color::XYZ(_, _, _) => 7,
|
||||||
|
|
||||||
|
Color::Blackbody { .. } => 5,
|
||||||
|
|
||||||
|
Color::Temperature { .. } => 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes the compressed form of this color to `out_data`.
|
||||||
|
///
|
||||||
|
/// `out_data` must be at least `compressed_size()` bytes long, otherwise
|
||||||
|
/// this method will panic.
|
||||||
|
///
|
||||||
|
/// Returns the number of bytes written.
|
||||||
|
pub fn write_compressed(&self, out_data: &mut [u8]) -> usize {
|
||||||
|
match *self {
|
||||||
|
Color::XYZ(x, y, z) => {
|
||||||
|
out_data[0] = 0; // Discriminant
|
||||||
|
let col = signed48::encode((x, y, z));
|
||||||
|
let col = col.to_le_bytes();
|
||||||
|
(&mut out_data[1..7]).copy_from_slice(&col[0..6]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Color::Blackbody {
|
||||||
|
temperature,
|
||||||
|
factor,
|
||||||
|
} => {
|
||||||
|
out_data[0] = 1; // Discriminant
|
||||||
|
let tmp = (temperature.min(std::u16::MAX as f32) as u16).to_le_bytes();
|
||||||
|
let fac = f16::from_f32(factor).to_bits().to_le_bytes();
|
||||||
|
out_data[1] = tmp[0];
|
||||||
|
out_data[2] = tmp[1];
|
||||||
|
out_data[3] = fac[0];
|
||||||
|
out_data[4] = fac[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
Color::Temperature {
|
||||||
|
temperature,
|
||||||
|
factor,
|
||||||
|
} => {
|
||||||
|
out_data[0] = 2; // Discriminant
|
||||||
|
let tmp = (temperature.min(std::u16::MAX as f32) as u16).to_le_bytes();
|
||||||
|
let fac = f16::from_f32(factor).to_bits().to_le_bytes();
|
||||||
|
out_data[1] = tmp[0];
|
||||||
|
out_data[2] = tmp[1];
|
||||||
|
out_data[3] = fac[0];
|
||||||
|
out_data[4] = fac[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.compressed_size()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a Color from compressed color data, and also returns the
|
||||||
|
/// number of bytes consumed from `in_data`.
|
||||||
|
pub fn from_compressed(in_data: &[u8]) -> (Color, usize) {
|
||||||
|
match in_data[0] {
|
||||||
|
0 => {
|
||||||
|
// XYZ
|
||||||
|
let mut bytes = [0u8; 8];
|
||||||
|
(&mut bytes[0..6]).copy_from_slice(&in_data[1..7]);
|
||||||
|
let (x, y, z) = signed48::decode(u64::from_le_bytes(bytes));
|
||||||
|
(Color::XYZ(x, y, z), 7)
|
||||||
|
}
|
||||||
|
|
||||||
|
1 => {
|
||||||
|
// Blackbody
|
||||||
|
let mut tmp = [0u8; 2];
|
||||||
|
let mut fac = [0u8; 2];
|
||||||
|
tmp[0] = in_data[1];
|
||||||
|
tmp[1] = in_data[2];
|
||||||
|
fac[0] = in_data[3];
|
||||||
|
fac[1] = in_data[4];
|
||||||
|
let tmp = u16::from_le_bytes(tmp);
|
||||||
|
let fac = f16::from_bits(u16::from_le_bytes(fac));
|
||||||
|
(
|
||||||
|
Color::Blackbody {
|
||||||
|
temperature: tmp as f32,
|
||||||
|
factor: fac.into(),
|
||||||
|
},
|
||||||
|
5,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
2 => {
|
||||||
|
// Temperature
|
||||||
|
let mut tmp = [0u8; 2];
|
||||||
|
let mut fac = [0u8; 2];
|
||||||
|
tmp[0] = in_data[1];
|
||||||
|
tmp[1] = in_data[2];
|
||||||
|
fac[0] = in_data[3];
|
||||||
|
fac[1] = in_data[4];
|
||||||
|
let tmp = u16::from_le_bytes(tmp);
|
||||||
|
let fac = f16::from_bits(u16::from_le_bytes(fac));
|
||||||
|
(
|
||||||
|
Color::Temperature {
|
||||||
|
temperature: tmp as f32,
|
||||||
|
factor: fac.into(),
|
||||||
|
},
|
||||||
|
5,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mul<f32> for Color {
|
impl Mul<f32> for Color {
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::fmt::Debug;
|
||||||
|
|
||||||
use crate::{color::Color, surface::SurfaceIntersectionData};
|
use crate::{color::Color, surface::SurfaceIntersectionData};
|
||||||
|
|
||||||
use self::surface_closure::SurfaceClosure;
|
pub use self::surface_closure::SurfaceClosure;
|
||||||
|
|
||||||
/// Trait for surface shaders.
|
/// Trait for surface shaders.
|
||||||
pub trait SurfaceShader: Debug + Sync {
|
pub trait SurfaceShader: Debug + Sync {
|
||||||
|
|
|
@ -6,7 +6,7 @@ use float4::Float4;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
color::{Color, SpectralSample},
|
color::{Color, SpectralSample},
|
||||||
lerp::lerp,
|
lerp::{lerp, Lerp},
|
||||||
math::{clamp, dot, zup_to_vec, Normal, Vector},
|
math::{clamp, dot, zup_to_vec, Normal, Vector},
|
||||||
sampling::cosine_sample_hemisphere,
|
sampling::cosine_sample_hemisphere,
|
||||||
};
|
};
|
||||||
|
@ -153,6 +153,128 @@ impl SurfaceClosure {
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the post-compression size of this closure.
|
||||||
|
pub fn compressed_size(&self) -> usize {
|
||||||
|
1 + match *self {
|
||||||
|
Lambert(color) => color.compressed_size(),
|
||||||
|
GGX { color, .. } => {
|
||||||
|
2 // Roughness
|
||||||
|
+ 2 // Fresnel
|
||||||
|
+ color.compressed_size() // Color
|
||||||
|
}
|
||||||
|
Emit(color) => color.compressed_size(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes the compressed form of this closure to `out_data`.
|
||||||
|
///
|
||||||
|
/// `out_data` must be at least `compressed_size()` bytes long, otherwise
|
||||||
|
/// this method will panic.
|
||||||
|
///
|
||||||
|
/// Returns the number of bytes written.
|
||||||
|
pub fn write_compressed(&self, out_data: &mut [u8]) -> usize {
|
||||||
|
match *self {
|
||||||
|
Lambert(color) => {
|
||||||
|
out_data[0] = 0; // Discriminant
|
||||||
|
color.write_compressed(&mut out_data[1..]);
|
||||||
|
}
|
||||||
|
GGX {
|
||||||
|
color,
|
||||||
|
roughness,
|
||||||
|
fresnel,
|
||||||
|
} => {
|
||||||
|
out_data[0] = 1; // Discriminant
|
||||||
|
|
||||||
|
// Roughness and fresnel (we write these first because they are
|
||||||
|
// constant-size, whereas the color is variable-size, so this
|
||||||
|
// makes things a little easier).
|
||||||
|
let rgh =
|
||||||
|
((roughness.max(0.0).min(1.0) * std::u16::MAX as f32) as u16).to_le_bytes();
|
||||||
|
let frs = ((fresnel.max(0.0).min(1.0) * std::u16::MAX as f32) as u16).to_le_bytes();
|
||||||
|
out_data[1] = rgh[0];
|
||||||
|
out_data[2] = rgh[1];
|
||||||
|
out_data[3] = frs[0];
|
||||||
|
out_data[4] = frs[1];
|
||||||
|
|
||||||
|
// Color
|
||||||
|
color.write_compressed(&mut out_data[5..]); // Color
|
||||||
|
}
|
||||||
|
Emit(color) => {
|
||||||
|
out_data[0] = 2; // Discriminant
|
||||||
|
color.write_compressed(&mut out_data[1..]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.compressed_size()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a SurfaceClosure from compressed closure data, and also
|
||||||
|
/// returns the number of bytes consumed from `in_data`.
|
||||||
|
pub fn from_compressed(in_data: &[u8]) -> (SurfaceClosure, usize) {
|
||||||
|
match in_data[0] {
|
||||||
|
0 => {
|
||||||
|
// Lambert
|
||||||
|
let (col, size) = Color::from_compressed(&in_data[1..]);
|
||||||
|
(SurfaceClosure::Lambert(col), 1 + size)
|
||||||
|
}
|
||||||
|
|
||||||
|
1 => {
|
||||||
|
// GGX
|
||||||
|
let mut rgh = [0u8; 2];
|
||||||
|
let mut frs = [0u8; 2];
|
||||||
|
rgh[0] = in_data[1];
|
||||||
|
rgh[1] = in_data[2];
|
||||||
|
frs[0] = in_data[3];
|
||||||
|
frs[1] = in_data[4];
|
||||||
|
let rgh = u16::from_le_bytes(rgh) as f32 * (1.0 / std::u16::MAX as f32);
|
||||||
|
let frs = u16::from_le_bytes(frs) as f32 * (1.0 / std::u16::MAX as f32);
|
||||||
|
let (col, size) = Color::from_compressed(&in_data[5..]);
|
||||||
|
(
|
||||||
|
SurfaceClosure::GGX {
|
||||||
|
color: col,
|
||||||
|
roughness: rgh,
|
||||||
|
fresnel: frs,
|
||||||
|
},
|
||||||
|
5 + size,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
2 => {
|
||||||
|
// Emit
|
||||||
|
let (col, size) = Color::from_compressed(&in_data[1..]);
|
||||||
|
(SurfaceClosure::Emit(col), 1 + size)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Lerp for SurfaceClosure {
|
||||||
|
fn lerp(self, other: SurfaceClosure, alpha: f32) -> SurfaceClosure {
|
||||||
|
match (self, other) {
|
||||||
|
(Lambert(col1), Lambert(col2)) => Lambert(lerp(col1, col2, alpha)),
|
||||||
|
(
|
||||||
|
GGX {
|
||||||
|
color: col1,
|
||||||
|
roughness: rgh1,
|
||||||
|
fresnel: frs1,
|
||||||
|
},
|
||||||
|
GGX {
|
||||||
|
color: col2,
|
||||||
|
roughness: rgh2,
|
||||||
|
fresnel: frs2,
|
||||||
|
},
|
||||||
|
) => GGX {
|
||||||
|
color: lerp(col1, col2, alpha),
|
||||||
|
roughness: lerp(rgh1, rgh2, alpha),
|
||||||
|
fresnel: lerp(frs1, frs2, alpha),
|
||||||
|
},
|
||||||
|
(Emit(col1), Emit(col2)) => Emit(lerp(col1, col2, alpha)),
|
||||||
|
|
||||||
|
_ => panic!("Cannot lerp between different surface closure types."),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lambert closure code.
|
/// Lambert closure code.
|
||||||
|
|
|
@ -11,7 +11,7 @@ use crate::{
|
||||||
lerp::lerp_slice,
|
lerp::lerp_slice,
|
||||||
math::{cross, dot, Matrix4x4, Normal, Point},
|
math::{cross, dot, Matrix4x4, Normal, Point},
|
||||||
ray::{RayBatch, RayStack},
|
ray::{RayBatch, RayStack},
|
||||||
shading::{SimpleSurfaceShader, SurfaceShader},
|
shading::SurfaceClosure,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{triangle, SurfaceIntersection, SurfaceIntersectionData};
|
use super::{triangle, SurfaceIntersection, SurfaceIntersectionData};
|
||||||
|
@ -33,7 +33,9 @@ pub struct MicropolyBatch<'a> {
|
||||||
normals: &'a [Normal],
|
normals: &'a [Normal],
|
||||||
|
|
||||||
// Per-vertex shading data.
|
// Per-vertex shading data.
|
||||||
vertex_closures: &'a [SimpleSurfaceShader],
|
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
|
||||||
|
|
||||||
// Micro-triangle indices. Each element of the tuple specifies the index
|
// Micro-triangle indices. Each element of the tuple specifies the index
|
||||||
// of a vertex, which indexes into all of the arrays above.
|
// of a vertex, which indexes into all of the arrays above.
|
||||||
|
@ -127,7 +129,9 @@ impl<'a> MicropolyBatch<'a> {
|
||||||
time_sample_count: time_sample_count,
|
time_sample_count: time_sample_count,
|
||||||
vertices: vertices,
|
vertices: vertices,
|
||||||
normals: normals,
|
normals: normals,
|
||||||
vertex_closures: &[],
|
compressed_vertex_closure_size: 0,
|
||||||
|
vertex_closure_time_sample_count: 1,
|
||||||
|
compressed_vertex_closures: &[],
|
||||||
indices: indices,
|
indices: indices,
|
||||||
accel: accel,
|
accel: accel,
|
||||||
}
|
}
|
||||||
|
@ -317,8 +321,16 @@ impl<'a> MicropolyBatch<'a> {
|
||||||
|
|
||||||
// Calculate interpolated surface closure.
|
// Calculate interpolated surface closure.
|
||||||
// TODO: actually interpolate.
|
// TODO: actually interpolate.
|
||||||
let closure = self.vertex_closures
|
let closure = {
|
||||||
[hit_tri_indices.0 as usize * self.time_sample_count];
|
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 intersection_data = SurfaceIntersectionData {
|
let intersection_data = SurfaceIntersectionData {
|
||||||
incoming: rays.dir(ray_idx),
|
incoming: rays.dir(ray_idx),
|
||||||
|
@ -334,7 +346,7 @@ impl<'a> MicropolyBatch<'a> {
|
||||||
// Fill in intersection data
|
// Fill in intersection data
|
||||||
isects[ray_idx] = SurfaceIntersection::Hit {
|
isects[ray_idx] = SurfaceIntersection::Hit {
|
||||||
intersection_data: intersection_data,
|
intersection_data: intersection_data,
|
||||||
closure: closure.shade(&intersection_data, ray_time),
|
closure: closure,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue
Block a user