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,
|
||||
};
|
||||
use float4::Float4;
|
||||
use half::f16;
|
||||
use spectral_upsampling::meng::{spectrum_xyz_to_p_4, EQUAL_ENERGY_REFLECTANCE};
|
||||
use trifloat::signed48;
|
||||
|
||||
use crate::{lerp::Lerp, math::fast_exp};
|
||||
|
||||
|
@ -138,6 +140,115 @@ impl Color {
|
|||
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 {
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::fmt::Debug;
|
|||
|
||||
use crate::{color::Color, surface::SurfaceIntersectionData};
|
||||
|
||||
use self::surface_closure::SurfaceClosure;
|
||||
pub use self::surface_closure::SurfaceClosure;
|
||||
|
||||
/// Trait for surface shaders.
|
||||
pub trait SurfaceShader: Debug + Sync {
|
||||
|
|
|
@ -6,7 +6,7 @@ use float4::Float4;
|
|||
|
||||
use crate::{
|
||||
color::{Color, SpectralSample},
|
||||
lerp::lerp,
|
||||
lerp::{lerp, Lerp},
|
||||
math::{clamp, dot, zup_to_vec, Normal, Vector},
|
||||
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.
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::{
|
|||
lerp::lerp_slice,
|
||||
math::{cross, dot, Matrix4x4, Normal, Point},
|
||||
ray::{RayBatch, RayStack},
|
||||
shading::{SimpleSurfaceShader, SurfaceShader},
|
||||
shading::SurfaceClosure,
|
||||
};
|
||||
|
||||
use super::{triangle, SurfaceIntersection, SurfaceIntersectionData};
|
||||
|
@ -33,7 +33,9 @@ pub struct MicropolyBatch<'a> {
|
|||
normals: &'a [Normal],
|
||||
|
||||
// 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
|
||||
// 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,
|
||||
vertices: vertices,
|
||||
normals: normals,
|
||||
vertex_closures: &[],
|
||||
compressed_vertex_closure_size: 0,
|
||||
vertex_closure_time_sample_count: 1,
|
||||
compressed_vertex_closures: &[],
|
||||
indices: indices,
|
||||
accel: accel,
|
||||
}
|
||||
|
@ -317,8 +321,16 @@ impl<'a> MicropolyBatch<'a> {
|
|||
|
||||
// Calculate interpolated surface closure.
|
||||
// TODO: actually interpolate.
|
||||
let closure = self.vertex_closures
|
||||
[hit_tri_indices.0 as usize * self.time_sample_count];
|
||||
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 intersection_data = SurfaceIntersectionData {
|
||||
incoming: rays.dir(ray_idx),
|
||||
|
@ -334,7 +346,7 @@ impl<'a> MicropolyBatch<'a> {
|
|||
// Fill in intersection data
|
||||
isects[ray_idx] = SurfaceIntersection::Hit {
|
||||
intersection_data: intersection_data,
|
||||
closure: closure.shade(&intersection_data, ray_time),
|
||||
closure: closure,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue
Block a user