Switched from in-tree float4 lib to glam.

This commit is contained in:
Nathan Vegdahl 2019-07-22 22:30:37 +09:00
parent 5c5a01ecee
commit 88e7365bc4
21 changed files with 301 additions and 2033 deletions

29
Cargo.lock generated
View File

@ -8,6 +8,14 @@ dependencies = [
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "approx"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "atty"
version = "0.2.11"
@ -110,10 +118,6 @@ name = "crossbeam"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "float4"
version = "0.1.0"
[[package]]
name = "fnv"
version = "1.0.6"
@ -124,6 +128,14 @@ name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "glam"
version = "0.7.1"
source = "git+https://github.com/bitshifter/glam-rs.git?rev=0f314f99#0f314f990710ff9357e5896de2b55ec82fe88e0d"
dependencies = [
"approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "half"
version = "1.3.0"
@ -147,7 +159,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "math3d"
version = "0.1.0"
dependencies = [
"float4 0.1.0",
"approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"glam 0.7.1 (git+https://github.com/bitshifter/glam-rs.git?rev=0f314f99)",
]
[[package]]
@ -246,7 +259,7 @@ dependencies = [
"color 0.1.0",
"copy_in_place 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"float4 0.1.0",
"glam 0.7.1 (git+https://github.com/bitshifter/glam-rs.git?rev=0f314f99)",
"half 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"halton 0.1.0",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -449,7 +462,7 @@ version = "0.1.0"
name = "spectral_upsampling"
version = "0.1.0"
dependencies = [
"float4 0.1.0",
"glam 0.7.1 (git+https://github.com/bitshifter/glam-rs.git?rev=0f314f99)",
]
[[package]]
@ -551,6 +564,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3"
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf"
"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643"
@ -567,6 +581,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19"
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum glam 0.7.1 (git+https://github.com/bitshifter/glam-rs.git?rev=0f314f99)" = "<none>"
"checksum half 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9353c2a89d550b58fa0061d8ed8d002a7d8cdf2494eb0e432859bd3a9e543836"
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
"checksum libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "42914d39aad277d9e176efbdad68acb1d5443ab65afe0e0e4f0d49352a950880"

View File

@ -2,7 +2,6 @@
members = [
"sub_crates/bvh_order",
"sub_crates/color",
"sub_crates/float4",
"sub_crates/halton",
"sub_crates/math3d",
"sub_crates/mem_arena",
@ -36,6 +35,7 @@ png_encode_mini = "0.1.2"
rustc-serialize = "0.3"
scoped_threadpool = "0.1"
time = "0.1"
glam = {git="https://github.com/bitshifter/glam-rs.git", rev="0f314f99", default-features=false, features=["approx"]}
# Local crate dependencies
[dependencies.bvh_order]
@ -44,9 +44,6 @@ path = "sub_crates/bvh_order"
[dependencies.color]
path = "sub_crates/color"
[dependencies.float4]
path = "sub_crates/float4"
[dependencies.halton]
path = "sub_crates/halton"

View File

@ -6,6 +6,8 @@
use std::mem::{transmute, MaybeUninit};
use glam::Vec4Mask;
use mem_arena::MemArena;
use crate::{
@ -23,7 +25,6 @@ use super::{
};
use bvh_order::{calc_traversal_code, SplitAxes, TRAVERSAL_TABLE};
use float4::Bool4;
pub fn ray_code(dir: Vector) -> usize {
let ray_sign_is_neg = [dir.x() < 0.0, dir.y() < 0.0, dir.z() < 0.0];
@ -122,12 +123,12 @@ impl<'a> BVH4<'a> {
traversal_code,
} => {
node_tests += ray_stack.ray_count_in_next_task() as u64;
let mut all_hits = Bool4::new_false();
let mut all_hits = Vec4Mask::default();
// Ray testing
ray_stack.pop_do_next_task_and_push_rays(children.len(), |ray_idx| {
if rays.is_done(ray_idx) {
Bool4::new_false()
Vec4Mask::default()
} else {
let hits = if bounds.len() == 1 {
bounds[0].intersect_ray(
@ -148,7 +149,7 @@ impl<'a> BVH4<'a> {
});
// If there were any intersections, create tasks.
if !all_hits.is_all_false() {
if all_hits.any() {
let order_code = traversal_table[traversal_code as usize];
let mut lane_count = 0;
let mut i = children.len() as u8;

View File

@ -45,12 +45,12 @@ impl BBox {
let t2 = (self.max.co - orig.co) * dir_inv.co;
// Find the far and near intersection
let mut far_t = t1.v_max(t2);
let mut near_t = t1.v_min(t2);
far_t.set_3(std::f32::INFINITY);
near_t.set_3(0.0);
let far_hit_t = fast_minf32(far_t.h_min() * BBOX_MAXT_ADJUST, max_t);
let near_hit_t = near_t.h_max();
let mut far_t = t1.max(t2);
let mut near_t = t1.min(t2);
far_t.set_w(std::f32::INFINITY);
near_t.set_w(0.0);
let far_hit_t = fast_minf32(far_t.min_element() * BBOX_MAXT_ADJUST, max_t);
let near_hit_t = near_t.max_element();
// Did we hit?
near_hit_t <= far_hit_t
@ -106,10 +106,10 @@ impl BitOr for BBox {
fn bitor(self, rhs: BBox) -> BBox {
BBox::from_points(
Point {
co: self.min.co.v_min(rhs.min.co),
co: self.min.co.min(rhs.min.co),
},
Point {
co: self.max.co.v_max(rhs.max.co),
co: self.max.co.max(rhs.max.co),
},
)
}
@ -128,10 +128,10 @@ impl BitOr<Point> for BBox {
fn bitor(self, rhs: Point) -> BBox {
BBox::from_points(
Point {
co: self.min.co.v_min(rhs.co),
co: self.min.co.min(rhs.co),
},
Point {
co: self.max.co.v_max(rhs.co),
co: self.max.co.max(rhs.co),
},
)
}

View File

@ -9,16 +9,16 @@ use crate::{
math::{Point, Vector},
};
use float4::{Bool4, Float4};
use glam::{Vec4, Vec4Mask};
const BBOX_MAXT_ADJUST: f32 = 1.00000024;
/// A SIMD set of 4 3D axis-aligned bounding boxes.
#[derive(Debug, Copy, Clone)]
pub struct BBox4 {
pub x: (Float4, Float4), // (min, max)
pub y: (Float4, Float4), // (min, max)
pub z: (Float4, Float4), // (min, max)
pub x: (Vec4, Vec4), // (min, max)
pub y: (Vec4, Vec4), // (min, max)
pub z: (Vec4, Vec4), // (min, max)
}
impl BBox4 {
@ -26,16 +26,16 @@ impl BBox4 {
pub fn new() -> BBox4 {
BBox4 {
x: (
Float4::splat(std::f32::INFINITY),
Float4::splat(std::f32::NEG_INFINITY),
Vec4::splat(std::f32::INFINITY),
Vec4::splat(std::f32::NEG_INFINITY),
),
y: (
Float4::splat(std::f32::INFINITY),
Float4::splat(std::f32::NEG_INFINITY),
Vec4::splat(std::f32::INFINITY),
Vec4::splat(std::f32::NEG_INFINITY),
),
z: (
Float4::splat(std::f32::INFINITY),
Float4::splat(std::f32::NEG_INFINITY),
Vec4::splat(std::f32::INFINITY),
Vec4::splat(std::f32::NEG_INFINITY),
),
}
}
@ -45,30 +45,30 @@ impl BBox4 {
pub fn from_bboxes(b1: BBox, b2: BBox, b3: BBox, b4: BBox) -> BBox4 {
BBox4 {
x: (
Float4::new(b1.min.x(), b2.min.x(), b3.min.x(), b4.min.x()),
Float4::new(b1.max.x(), b2.max.x(), b3.max.x(), b4.max.x()),
Vec4::new(b1.min.x(), b2.min.x(), b3.min.x(), b4.min.x()),
Vec4::new(b1.max.x(), b2.max.x(), b3.max.x(), b4.max.x()),
),
y: (
Float4::new(b1.min.y(), b2.min.y(), b3.min.y(), b4.min.y()),
Float4::new(b1.max.y(), b2.max.y(), b3.max.y(), b4.max.y()),
Vec4::new(b1.min.y(), b2.min.y(), b3.min.y(), b4.min.y()),
Vec4::new(b1.max.y(), b2.max.y(), b3.max.y(), b4.max.y()),
),
z: (
Float4::new(b1.min.z(), b2.min.z(), b3.min.z(), b4.min.z()),
Float4::new(b1.max.z(), b2.max.z(), b3.max.z(), b4.max.z()),
Vec4::new(b1.min.z(), b2.min.z(), b3.min.z(), b4.min.z()),
Vec4::new(b1.max.z(), b2.max.z(), b3.max.z(), b4.max.z()),
),
}
}
// Returns whether the given ray intersects with the bboxes.
pub fn intersect_ray(&self, orig: Point, dir_inv: Vector, max_t: f32) -> Bool4 {
pub fn intersect_ray(&self, orig: Point, dir_inv: Vector, max_t: f32) -> Vec4Mask {
// Get the ray data into SIMD format.
let ro_x = orig.co.all_0();
let ro_y = orig.co.all_1();
let ro_z = orig.co.all_2();
let rdi_x = dir_inv.co.all_0();
let rdi_y = dir_inv.co.all_1();
let rdi_z = dir_inv.co.all_2();
let max_t = Float4::splat(max_t);
let ro_x = Vec4::splat(orig.co.x());
let ro_y = Vec4::splat(orig.co.y());
let ro_z = Vec4::splat(orig.co.z());
let rdi_x = Vec4::splat(dir_inv.co.x());
let rdi_y = Vec4::splat(dir_inv.co.y());
let rdi_z = Vec4::splat(dir_inv.co.z());
let max_t = Vec4::splat(max_t);
// Slab tests
let t1_x = (self.x.0 - ro_x) * rdi_x;
@ -79,24 +79,21 @@ impl BBox4 {
let t2_z = (self.z.1 - ro_z) * rdi_z;
// Get the far and near t hits for each axis.
let t_far_x = t1_x.v_max(t2_x);
let t_far_y = t1_y.v_max(t2_y);
let t_far_z = t1_z.v_max(t2_z);
let t_near_x = t1_x.v_min(t2_x);
let t_near_y = t1_y.v_min(t2_y);
let t_near_z = t1_z.v_min(t2_z);
let t_far_x = t1_x.max(t2_x);
let t_far_y = t1_y.max(t2_y);
let t_far_z = t1_z.max(t2_z);
let t_near_x = t1_x.min(t2_x);
let t_near_y = t1_y.min(t2_y);
let t_near_z = t1_z.min(t2_z);
// Calculate over-all far t hit.
let far_t =
(t_far_x.v_min(t_far_y.v_min(t_far_z)) * Float4::splat(BBOX_MAXT_ADJUST)).v_min(max_t);
let far_t = (t_far_x.min(t_far_y.min(t_far_z)) * Vec4::splat(BBOX_MAXT_ADJUST)).min(max_t);
// Calculate over-all near t hit.
let near_t = t_near_x
.v_max(t_near_y)
.v_max(t_near_z.v_max(Float4::splat(0.0)));
let near_t = t_near_x.max(t_near_y).max(t_near_z.max(Vec4::splat(0.0)));
// Hit results
near_t.lt(far_t)
near_t.cmplt(far_t)
}
}
@ -106,9 +103,9 @@ impl BitOr for BBox4 {
fn bitor(self, rhs: BBox4) -> BBox4 {
BBox4 {
x: (self.x.0.v_min(rhs.x.0), self.x.1.v_max(rhs.x.1)),
y: (self.y.0.v_min(rhs.y.0), self.y.1.v_max(rhs.y.1)),
z: (self.z.0.v_min(rhs.z.0), self.z.1.v_max(rhs.z.1)),
x: (self.x.0.min(rhs.x.0), self.x.1.max(rhs.x.1)),
y: (self.y.0.min(rhs.y.0), self.y.1.max(rhs.y.1)),
z: (self.z.0.min(rhs.z.0), self.z.1.max(rhs.z.1)),
}
}
}

View File

@ -4,7 +4,7 @@ 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 float4::Float4;
use glam::Vec4;
use half::f16;
use spectral_upsampling::meng::{spectrum_xyz_to_p_4, EQUAL_ENERGY_REFLECTANCE};
use trifloat::signed48;
@ -31,10 +31,10 @@ fn nth_wavelength(hero_wavelength: f32, n: usize) -> f32 {
}
}
/// Returns all wavelengths of a hero wavelength set as a Float4
/// Returns all wavelengths of a hero wavelength set as a Vec4
#[inline(always)]
fn wavelengths(hero_wavelength: f32) -> Float4 {
Float4::new(
fn wavelengths(hero_wavelength: f32) -> Vec4 {
Vec4::new(
nth_wavelength(hero_wavelength, 0),
nth_wavelength(hero_wavelength, 1),
nth_wavelength(hero_wavelength, 2),
@ -94,11 +94,11 @@ impl Color {
} => {
SpectralSample::from_parts(
// TODO: make this SIMD
Float4::new(
plancks_law(temperature, wls.get_0()) * factor,
plancks_law(temperature, wls.get_1()) * factor,
plancks_law(temperature, wls.get_2()) * factor,
plancks_law(temperature, wls.get_3()) * factor,
Vec4::new(
plancks_law(temperature, wls.x()) * factor,
plancks_law(temperature, wls.y()) * factor,
plancks_law(temperature, wls.z()) * factor,
plancks_law(temperature, wls.w()) * factor,
),
hero_wavelength,
)
@ -109,11 +109,11 @@ impl Color {
} => {
SpectralSample::from_parts(
// TODO: make this SIMD
Float4::new(
plancks_law_normalized(temperature, wls.get_0()) * factor,
plancks_law_normalized(temperature, wls.get_1()) * factor,
plancks_law_normalized(temperature, wls.get_2()) * factor,
plancks_law_normalized(temperature, wls.get_3()) * factor,
Vec4::new(
plancks_law_normalized(temperature, wls.x()) * factor,
plancks_law_normalized(temperature, wls.y()) * factor,
plancks_law_normalized(temperature, wls.z()) * factor,
plancks_law_normalized(temperature, wls.w()) * factor,
),
hero_wavelength,
)
@ -388,7 +388,7 @@ fn plancks_law_normalized(temperature: f32, wavelength: f32) -> f32 {
#[derive(Copy, Clone, Debug)]
pub struct SpectralSample {
pub e: Float4,
pub e: Vec4,
hero_wavelength: f32,
}
@ -396,7 +396,7 @@ impl SpectralSample {
pub fn new(wavelength: f32) -> SpectralSample {
debug_assert!(wavelength >= WL_MIN && wavelength <= WL_MAX);
SpectralSample {
e: Float4::splat(0.0),
e: Vec4::splat(0.0),
hero_wavelength: wavelength,
}
}
@ -405,12 +405,12 @@ impl SpectralSample {
pub fn from_value(value: f32, wavelength: f32) -> SpectralSample {
debug_assert!(wavelength >= WL_MIN && wavelength <= WL_MAX);
SpectralSample {
e: Float4::splat(value),
e: Vec4::splat(value),
hero_wavelength: wavelength,
}
}
pub fn from_parts(e: Float4, wavelength: f32) -> SpectralSample {
pub fn from_parts(e: Vec4, wavelength: f32) -> SpectralSample {
debug_assert!(wavelength >= WL_MIN && wavelength <= WL_MAX);
SpectralSample {
e: e,
@ -520,10 +520,10 @@ impl XYZ {
}
pub fn from_spectral_sample(ss: &SpectralSample) -> XYZ {
let xyz0 = XYZ::from_wavelength(ss.wl_n(0), ss.e.get_0());
let xyz1 = XYZ::from_wavelength(ss.wl_n(1), ss.e.get_1());
let xyz2 = XYZ::from_wavelength(ss.wl_n(2), ss.e.get_2());
let xyz3 = XYZ::from_wavelength(ss.wl_n(3), ss.e.get_3());
let xyz0 = XYZ::from_wavelength(ss.wl_n(0), ss.e.x());
let xyz1 = XYZ::from_wavelength(ss.wl_n(1), ss.e.y());
let xyz2 = XYZ::from_wavelength(ss.wl_n(2), ss.e.z());
let xyz3 = XYZ::from_wavelength(ss.wl_n(3), ss.e.w());
(xyz0 + xyz1 + xyz2 + xyz3) * 0.75
}
@ -601,8 +601,8 @@ impl DivAssign<f32> for XYZ {
/// the method in the paper "Physically Meaningful Rendering using Tristimulus
/// 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)
fn xyz_to_spectrum_4(xyz: (f32, f32, f32), wavelengths: Vec4) -> Vec4 {
spectrum_xyz_to_p_4(wavelengths, xyz) * Vec4::splat(1.0 / EQUAL_ENERGY_REFLECTANCE)
// aces_to_spectrum_p4(wavelengths, xyz_to_aces_ap0_e(xyz))
}

View File

@ -73,23 +73,15 @@ impl<T: Lerp> Lerp for (T, T) {
}
}
impl Lerp for float4::Float4 {
fn lerp(self, other: float4::Float4, alpha: f32) -> float4::Float4 {
impl Lerp for glam::Vec4 {
fn lerp(self, other: glam::Vec4, alpha: f32) -> glam::Vec4 {
(self * (1.0 - alpha)) + (other * alpha)
}
}
impl Lerp for Matrix4x4 {
fn lerp(self, other: Matrix4x4, alpha: f32) -> Matrix4x4 {
let alpha_minus = 1.0 - alpha;
Matrix4x4 {
values: [
(self[0] * alpha_minus) + (other[0] * alpha),
(self[1] * alpha_minus) + (other[1] * alpha),
(self[2] * alpha_minus) + (other[2] * alpha),
(self[3] * alpha_minus) + (other[3] * alpha),
],
}
(self * (1.0 - alpha)) + (other * alpha)
}
}

View File

@ -1,6 +1,6 @@
#![allow(dead_code)]
use float4::{Bool4, Float4};
use glam::{Vec4, Vec4Mask};
use crate::math::{Matrix4x4, Point, Vector};
@ -86,7 +86,7 @@ impl RayBatch {
pub fn set_from_ray(&mut self, ray: &Ray, is_occlusion: bool, idx: usize) {
self.hot[idx].orig_local = ray.orig;
self.hot[idx].dir_inv_local = Vector {
co: Float4::splat(1.0) / ray.dir.co,
co: Vec4::splat(1.0) / ray.dir.co,
};
self.hot[idx].max_t = ray.max_t;
self.hot[idx].time = ray.time;
@ -122,7 +122,7 @@ impl RayBatch {
pub fn update_local(&mut self, idx: usize, xform: &Matrix4x4) {
self.hot[idx].orig_local = self.cold[idx].orig * *xform;
self.hot[idx].dir_inv_local = Vector {
co: Float4::splat(1.0) / (self.cold[idx].dir * *xform).co,
co: Vec4::splat(1.0) / (self.cold[idx].dir * *xform).co,
};
}
@ -349,7 +349,7 @@ impl RayStack {
/// indicated lanes.
pub fn pop_do_next_task_and_push_rays<F>(&mut self, output_lane_count: usize, mut handle_ray: F)
where
F: FnMut(usize) -> Bool4,
F: FnMut(usize) -> Vec4Mask,
{
// Pop the task and do necessary bookkeeping.
let task = self.tasks.pop().unwrap();
@ -372,9 +372,9 @@ impl RayStack {
// Execute task.
for i in task_range.0..task_range.1 {
let ray_idx = *unsafe { self.lanes[task.lane].idxs.get_unchecked(i) };
let push_mask = handle_ray(ray_idx as usize);
let push_mask = handle_ray(ray_idx as usize).bitmask();
for l in 0..output_lane_count {
if push_mask.get_n(l) {
if (push_mask & (1 << l)) != 0 {
self.lanes[l as usize].idxs.push(ray_idx);
}
}

View File

@ -9,7 +9,7 @@ use std::{
use crossbeam::sync::MsQueue;
use scoped_threadpool::Pool;
use float4::Float4;
use glam::Vec4;
use crate::{
accel::ACCEL_NODE_RAY_TESTS,
@ -374,12 +374,12 @@ pub struct LightPath {
wavelength: f32,
next_bounce_ray: Option<Ray>,
next_attenuation_fac: Float4,
next_attenuation_fac: Vec4,
closure_sample_pdf: f32,
light_attenuation: Float4,
pending_color_addition: Float4,
color: Float4,
light_attenuation: Vec4,
pending_color_addition: Vec4,
color: Vec4,
}
#[allow(clippy::new_ret_no_self)]
@ -405,12 +405,12 @@ impl LightPath {
wavelength: wavelength,
next_bounce_ray: None,
next_attenuation_fac: Float4::splat(1.0),
next_attenuation_fac: Vec4::splat(1.0),
closure_sample_pdf: 1.0,
light_attenuation: Float4::splat(1.0),
pending_color_addition: Float4::splat(0.0),
color: Float4::splat(0.0),
light_attenuation: Vec4::splat(1.0),
pending_color_addition: Vec4::splat(0.0),
color: Vec4::splat(0.0),
},
scene.camera.generate_ray(
image_plane_co.0,
@ -565,7 +565,7 @@ impl LightPath {
// If there's any possible contribution, set up for a
// light ray.
if attenuation.e.h_max() <= 0.0 {
if attenuation.e.max_element() <= 0.0 {
false
} else {
// Calculate and store the light that will be contributed
@ -599,7 +599,7 @@ impl LightPath {
};
// Check if pdf is zero, to avoid NaN's.
if (pdf > 0.0) && (filter.e.h_max() > 0.0) {
if (pdf > 0.0) && (filter.e.max_element() > 0.0) {
// Account for the additional light attenuation from
// this bounce
self.next_attenuation_fac = filter.e;

View File

@ -2,7 +2,7 @@
use std::f32::consts::PI as PI_32;
use float4::Float4;
use glam::Vec4;
use crate::{
color::{Color, SpectralSample},
@ -492,27 +492,27 @@ mod ggx_closure {
let spectrum_sample = col.to_spectral_sample(wavelength);
let rev_fresnel = 1.0 - fresnel;
let c0 = lerp(
schlick_fresnel_from_fac(spectrum_sample.e.get_0(), hb),
spectrum_sample.e.get_0(),
schlick_fresnel_from_fac(spectrum_sample.e.x(), hb),
spectrum_sample.e.x(),
rev_fresnel,
);
let c1 = lerp(
schlick_fresnel_from_fac(spectrum_sample.e.get_1(), hb),
spectrum_sample.e.get_1(),
schlick_fresnel_from_fac(spectrum_sample.e.y(), hb),
spectrum_sample.e.y(),
rev_fresnel,
);
let c2 = lerp(
schlick_fresnel_from_fac(spectrum_sample.e.get_2(), hb),
spectrum_sample.e.get_2(),
schlick_fresnel_from_fac(spectrum_sample.e.z(), hb),
spectrum_sample.e.z(),
rev_fresnel,
);
let c3 = lerp(
schlick_fresnel_from_fac(spectrum_sample.e.get_3(), hb),
spectrum_sample.e.get_3(),
schlick_fresnel_from_fac(spectrum_sample.e.w(), hb),
spectrum_sample.e.w(),
rev_fresnel,
);
SpectralSample::from_parts(Float4::new(c0, c1, c2, c3), wavelength)
SpectralSample::from_parts(Vec4::new(c0, c1, c2, c3), wavelength)
};
// Calculate everything else

View File

@ -163,7 +163,7 @@ pub fn surface_point(tri: (Point, Point, Point), bary: (f32, f32, f32)) -> (Poin
+ (tri.2.into_vector().abs() * bary.2))
* fp_gamma(7))
.co
.h_max();
.max_element();
(pos, pos_err)
}

View File

@ -1,10 +0,0 @@
[package]
name = "float4"
version = "0.1.0"
authors = ["Nathan Vegdahl <cessen@cessen.com>"]
edition = "2018"
license = "MIT"
[lib]
name = "float4"
path = "src/lib.rs"

File diff suppressed because it is too large Load Diff

View File

@ -10,5 +10,6 @@ name = "math3d"
path = "src/lib.rs"
# Local crate dependencies
[dependencies.float4]
path = "../float4"
[dependencies]
glam = {git="https://github.com/bitshifter/glam-rs.git", rev="0f314f99", default-features=false, features=["approx"]}
approx = "0.3"

View File

@ -1,29 +1,21 @@
#![allow(dead_code)]
use std::ops::{Index, IndexMut, Mul};
use std::ops::{Add, Mul};
use float4::{invert, transpose, Float4};
use approx::RelativeEq;
use glam::{Mat4, Vec4};
use super::Point;
/// A 4x4 matrix, used for transforms
#[derive(Debug, Copy, Clone)]
pub struct Matrix4x4 {
pub values: [Float4; 4],
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Matrix4x4(pub Mat4);
impl Matrix4x4 {
/// Creates a new identity matrix
#[inline]
pub fn new() -> Matrix4x4 {
Matrix4x4 {
values: [
Float4::new(1.0, 0.0, 0.0, 0.0),
Float4::new(0.0, 1.0, 0.0, 0.0),
Float4::new(0.0, 0.0, 1.0, 0.0),
Float4::new(0.0, 0.0, 0.0, 1.0),
],
}
Matrix4x4(Mat4::identity())
}
/// Creates a new matrix with the specified values:
@ -52,108 +44,37 @@ impl Matrix4x4 {
o: f32,
p: f32,
) -> Matrix4x4 {
Matrix4x4 {
values: [
Float4::new(a, b, c, d),
Float4::new(e, f, g, h),
Float4::new(i, j, k, l),
Float4::new(m, n, o, p),
],
}
Matrix4x4(Mat4::new(
Vec4::new(a, e, i, m),
Vec4::new(b, f, j, n),
Vec4::new(c, g, k, o),
Vec4::new(d, h, l, p),
))
}
#[inline]
pub fn from_location(loc: Point) -> Matrix4x4 {
Matrix4x4 {
values: [
Float4::new(1.0, 0.0, 0.0, loc.x()),
Float4::new(0.0, 1.0, 0.0, loc.y()),
Float4::new(0.0, 0.0, 1.0, loc.z()),
Float4::new(0.0, 0.0, 0.0, 1.0),
],
}
Matrix4x4(Mat4::from_translation(loc.co.truncate()))
}
/// Returns whether the matrices are approximately equal to each other.
/// Each corresponding element in the matrices cannot have a relative error
/// exceeding `epsilon`.
/// Each corresponding element in the matrices cannot have a relative
/// error exceeding epsilon.
#[inline]
pub fn aprx_eq(&self, other: Matrix4x4, epsilon: f32) -> bool {
let mut result = true;
for y in 0..4 {
for x in 0..4 {
// All of this stuff is just an approximate comparison
// of floating point numbers. See:
// http://floating-point-gui.de/errors/comparison/
// It might be worth breaking this out into a separate funcion,
// but I'm not entirely sure where to put it.
let a = self[y].get_n(x);
let b = other[y].get_n(x);
let aabs = a.abs();
let babs = b.abs();
let diff = (a - b).abs();
if a == b {
} else if (aabs <= std::f32::EPSILON) || (babs <= std::f32::EPSILON) {
result = result && (diff < std::f32::EPSILON);
} else {
let rel = 2.0 * diff / (aabs + babs);
println!("{}", rel);
result = result && (rel < epsilon);
}
}
}
result
self.0.relative_eq(&other.0, std::f32::EPSILON, epsilon)
}
/// Returns the transpose of the matrix
#[inline]
pub fn transposed(&self) -> Matrix4x4 {
let mut m = *self;
transpose(&mut m.values);
m
Matrix4x4(self.0.transpose())
}
/// Returns the inverse of the Matrix
#[inline]
#[allow(clippy::float_cmp)]
pub fn inverse(&self) -> Matrix4x4 {
let mut m = *self;
let det = invert(&mut m.values);
debug_assert_ne!(det, 0.0);
m
}
}
impl Index<usize> for Matrix4x4 {
type Output = Float4;
#[inline(always)]
fn index(&self, _index: usize) -> &Float4 {
&self.values[_index]
}
}
impl IndexMut<usize> for Matrix4x4 {
#[inline(always)]
fn index_mut(&mut self, _index: usize) -> &mut Float4 {
&mut self.values[_index]
}
}
impl PartialEq for Matrix4x4 {
#[inline]
fn eq(&self, other: &Matrix4x4) -> bool {
let mut result = true;
for y in 0..4 {
for x in 0..4 {
result = result && (self[y].get_n(x) == other[y].get_n(x));
}
}
result
Matrix4x4(self.0.inverse())
}
}
@ -164,40 +85,32 @@ impl Default for Matrix4x4 {
}
/// Multiply two matrices together
impl Mul<Matrix4x4> for Matrix4x4 {
type Output = Matrix4x4;
impl Mul for Matrix4x4 {
type Output = Self;
#[inline]
fn mul(self, other: Matrix4x4) -> Matrix4x4 {
let m = self.transposed();
Matrix4x4 {
values: [
Float4::new(
(m[0] * other[0]).h_sum(),
(m[1] * other[0]).h_sum(),
(m[2] * other[0]).h_sum(),
(m[3] * other[0]).h_sum(),
),
Float4::new(
(m[0] * other[1]).h_sum(),
(m[1] * other[1]).h_sum(),
(m[2] * other[1]).h_sum(),
(m[3] * other[1]).h_sum(),
),
Float4::new(
(m[0] * other[2]).h_sum(),
(m[1] * other[2]).h_sum(),
(m[2] * other[2]).h_sum(),
(m[3] * other[2]).h_sum(),
),
Float4::new(
(m[0] * other[3]).h_sum(),
(m[1] * other[3]).h_sum(),
(m[2] * other[3]).h_sum(),
(m[3] * other[3]).h_sum(),
),
],
}
fn mul(self, other: Self) -> Self {
Self(other.0.mul_mat4(&self.0))
}
}
/// Multiply a matrix by a f32
impl Mul<f32> for Matrix4x4 {
type Output = Self;
#[inline]
fn mul(self, other: f32) -> Self {
Self(self.0 * other)
}
}
/// Add two matrices together
impl Add for Matrix4x4 {
type Output = Self;
#[inline]
fn add(self, other: Self) -> Self {
Self(self.0 + other.0)
}
}
@ -218,22 +131,24 @@ mod tests {
}
#[test]
fn aproximate_equality_test() {
fn approximate_equality_test() {
let a = Matrix4x4::new();
let b = Matrix4x4::new_from_values(
1.001, 0.0, 0.0, 0.0, 0.0, 1.001, 0.0, 0.0, 0.0, 0.0, 1.001, 0.0, 0.0, 0.0, 0.0, 1.001,
1.000001, 0.0, 0.0, 0.0, 0.0, 1.000001, 0.0, 0.0, 0.0, 0.0, 1.000001, 0.0, 0.0, 0.0,
0.0, 1.000001,
);
let c = Matrix4x4::new_from_values(
1.003, 0.0, 0.0, 0.0, 0.0, 1.003, 0.0, 0.0, 0.0, 0.0, 1.003, 0.0, 0.0, 0.0, 0.0, 1.003,
1.000003, 0.0, 0.0, 0.0, 0.0, 1.000003, 0.0, 0.0, 0.0, 0.0, 1.000003, 0.0, 0.0, 0.0,
0.0, 1.000003,
);
let d = Matrix4x4::new_from_values(
-1.001, 0.0, 0.0, 0.0, 0.0, -1.001, 0.0, 0.0, 0.0, 0.0, -1.001, 0.0, 0.0, 0.0, 0.0,
-1.001,
-1.000001, 0.0, 0.0, 0.0, 0.0, -1.000001, 0.0, 0.0, 0.0, 0.0, -1.000001, 0.0, 0.0, 0.0,
0.0, -1.000001,
);
assert!(a.aprx_eq(b, 0.002));
assert!(!a.aprx_eq(c, 0.002));
assert!(!a.aprx_eq(d, 0.002));
assert!(a.aprx_eq(b, 0.000001));
assert!(!a.aprx_eq(c, 0.000001));
assert!(!a.aprx_eq(d, 0.000001));
}
#[test]
@ -260,7 +175,7 @@ mod tests {
let b = a.inverse();
let c = Matrix4x4::new();
assert!((a * b).aprx_eq(c, 0.00001));
assert!((dbg!(a * b)).aprx_eq(dbg!(c), 0.0000001));
}
#[test]

View File

@ -5,42 +5,44 @@ use std::{
ops::{Add, Div, Mul, Neg, Sub},
};
use float4::Float4;
use glam::Vec4;
use super::{CrossProduct, DotProduct, Matrix4x4, Vector};
/// A surface normal in 3d homogeneous space.
#[derive(Debug, Copy, Clone)]
pub struct Normal {
pub co: Float4,
pub co: Vec4,
}
impl Normal {
#[inline(always)]
pub fn new(x: f32, y: f32, z: f32) -> Normal {
Normal {
co: Float4::new(x, y, z, 0.0),
co: Vec4::new(x, y, z, 0.0),
}
}
#[inline(always)]
pub fn length(&self) -> f32 {
(self.co * self.co).h_sum().sqrt()
self.co.length()
}
#[inline(always)]
pub fn length2(&self) -> f32 {
(self.co * self.co).h_sum()
self.co.length_squared()
}
#[inline(always)]
pub fn normalized(&self) -> Normal {
*self / self.length()
Normal {
co: self.co.normalize(),
}
}
#[inline(always)]
pub fn into_vector(self) -> Vector {
Vector::new(self.co.get_0(), self.co.get_1(), self.co.get_2())
Vector { co: self.co }
}
#[inline(always)]
@ -55,32 +57,32 @@ impl Normal {
#[inline(always)]
pub fn x(&self) -> f32 {
self.co.get_0()
self.co.x()
}
#[inline(always)]
pub fn y(&self) -> f32 {
self.co.get_1()
self.co.y()
}
#[inline(always)]
pub fn z(&self) -> f32 {
self.co.get_2()
self.co.z()
}
#[inline(always)]
pub fn set_x(&mut self, x: f32) {
self.co.set_0(x);
self.co.set_x(x);
}
#[inline(always)]
pub fn set_y(&mut self, y: f32) {
self.co.set_1(y);
self.co.set_y(y);
}
#[inline(always)]
pub fn set_z(&mut self, z: f32) {
self.co.set_2(z);
self.co.set_z(z);
}
}
@ -129,15 +131,10 @@ impl Mul<Matrix4x4> for Normal {
#[inline]
fn mul(self, other: Matrix4x4) -> Normal {
let mat = other.inverse().transposed();
Normal {
co: Float4::new(
(self.co * mat.values[0]).h_sum(),
(self.co * mat.values[1]).h_sum(),
(self.co * mat.values[2]).h_sum(),
0.0,
),
}
let mat = other.0.inverse().transpose();
let mut co = mat.mul_vec4(self.co);
co.set_w(0.0);
Normal { co: co }
}
}
@ -164,7 +161,7 @@ impl Neg for Normal {
impl DotProduct for Normal {
#[inline(always)]
fn dot(self, other: Normal) -> f32 {
(self.co * other.co).h_sum()
self.co.dot(other.co)
}
}
@ -172,12 +169,7 @@ impl CrossProduct for Normal {
#[inline]
fn cross(self, other: Normal) -> Normal {
Normal {
co: Float4::new(
(self.co.get_1() * other.co.get_2()) - (self.co.get_2() * other.co.get_1()),
(self.co.get_2() * other.co.get_0()) - (self.co.get_0() * other.co.get_2()),
(self.co.get_0() * other.co.get_1()) - (self.co.get_1() * other.co.get_0()),
0.0,
),
co: self.co.truncate().cross(other.co.truncate()).extend(0.0),
}
}
}
@ -186,6 +178,7 @@ impl CrossProduct for Normal {
mod tests {
use super::super::{CrossProduct, DotProduct, Matrix4x4};
use super::*;
use approx::UlpsEq;
#[test]
fn add() {
@ -220,8 +213,10 @@ mod tests {
let m = Matrix4x4::new_from_values(
1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0, 13.0, 7.0, 15.0, 3.0,
);
let nm = Normal::new(-19.258825, 5.717648, -1.770588);
assert!(((n * m) - nm).length2() < 0.00001);
let mut nm = n * m;
nm.co.set_w(0.0);
let nm2 = Normal::new(-19.258825, 5.717648, -1.770588);
assert!(nm.co.ulps_eq(&nm2.co, 0.0, 4));
}
#[test]

View File

@ -5,21 +5,21 @@ use std::{
ops::{Add, Mul, Sub},
};
use float4::Float4;
use glam::Vec4;
use super::{Matrix4x4, Vector};
/// A position in 3d homogeneous space.
#[derive(Debug, Copy, Clone)]
pub struct Point {
pub co: Float4,
pub co: Vec4,
}
impl Point {
#[inline(always)]
pub fn new(x: f32, y: f32, z: f32) -> Point {
Point {
co: Float4::new(x, y, z, 1.0),
co: Vec4::new(x, y, z, 1.0),
}
}
@ -28,7 +28,7 @@ impl Point {
#[inline(always)]
pub fn norm(&self) -> Point {
Point {
co: self.co / self.co.get_3(),
co: self.co / self.co.w(),
}
}
@ -38,7 +38,7 @@ impl Point {
let n2 = other.norm();
Point {
co: n1.co.v_min(n2.co),
co: n1.co.min(n2.co),
}
}
@ -48,13 +48,15 @@ impl Point {
let n2 = other.norm();
Point {
co: n1.co.v_max(n2.co),
co: n1.co.max(n2.co),
}
}
#[inline(always)]
pub fn into_vector(self) -> Vector {
Vector::new(self.co.get_0(), self.co.get_1(), self.co.get_2())
let mut v = Vector { co: self.co };
v.co.set_w(0.0);
v
}
#[inline(always)]
@ -69,32 +71,32 @@ impl Point {
#[inline(always)]
pub fn x(&self) -> f32 {
self.co.get_0()
self.co.x()
}
#[inline(always)]
pub fn y(&self) -> f32 {
self.co.get_1()
self.co.y()
}
#[inline(always)]
pub fn z(&self) -> f32 {
self.co.get_2()
self.co.z()
}
#[inline(always)]
pub fn set_x(&mut self, x: f32) {
self.co.set_0(x);
self.co.set_x(x);
}
#[inline(always)]
pub fn set_y(&mut self, y: f32) {
self.co.set_1(y);
self.co.set_y(y);
}
#[inline(always)]
pub fn set_z(&mut self, z: f32) {
self.co.set_2(z);
self.co.set_z(z);
}
}
@ -144,12 +146,7 @@ impl Mul<Matrix4x4> for Point {
#[inline]
fn mul(self, other: Matrix4x4) -> Point {
Point {
co: Float4::new(
(self.co * other.values[0]).h_sum(),
(self.co * other.values[1]).h_sum(),
(self.co * other.values[2]).h_sum(),
(self.co * other.values[3]).h_sum(),
),
co: other.0.mul_vec4(self.co),
}
}
}
@ -163,7 +160,7 @@ mod tests {
fn norm() {
let mut p1 = Point::new(1.0, 2.0, 3.0);
let p2 = Point::new(2.0, 4.0, 6.0);
p1.co.set_3(0.5);
p1.co.set_w(0.5);
assert_eq!(p2, p1.norm());
}
@ -203,7 +200,7 @@ mod tests {
1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0, 2.0, 3.0, 1.0, 5.0,
);
let mut pm = Point::new(15.5, 54.0, 70.0);
pm.co.set_3(18.5);
pm.co.set_w(18.5);
assert_eq!(p * m, pm);
}

View File

@ -5,37 +5,39 @@ use std::{
ops::{Add, Div, Mul, Neg, Sub},
};
use float4::Float4;
use glam::Vec4;
use super::{CrossProduct, DotProduct, Matrix4x4, Normal, Point};
/// A direction vector in 3d homogeneous space.
#[derive(Debug, Copy, Clone)]
pub struct Vector {
pub co: Float4,
pub co: Vec4,
}
impl Vector {
#[inline(always)]
pub fn new(x: f32, y: f32, z: f32) -> Vector {
Vector {
co: Float4::new(x, y, z, 0.0),
co: Vec4::new(x, y, z, 0.0),
}
}
#[inline(always)]
pub fn length(&self) -> f32 {
(self.co * self.co).h_sum().sqrt()
self.co.length()
}
#[inline(always)]
pub fn length2(&self) -> f32 {
(self.co * self.co).h_sum()
self.co.length_squared()
}
#[inline(always)]
pub fn normalized(&self) -> Vector {
*self / self.length()
Vector {
co: self.co.normalize(),
}
}
#[inline(always)]
@ -65,32 +67,32 @@ impl Vector {
#[inline(always)]
pub fn x(&self) -> f32 {
self.co.get_0()
self.co.x()
}
#[inline(always)]
pub fn y(&self) -> f32 {
self.co.get_1()
self.co.y()
}
#[inline(always)]
pub fn z(&self) -> f32 {
self.co.get_2()
self.co.z()
}
#[inline(always)]
pub fn set_x(&mut self, x: f32) {
self.co.set_0(x);
self.co.set_x(x);
}
#[inline(always)]
pub fn set_y(&mut self, y: f32) {
self.co.set_1(y);
self.co.set_y(y);
}
#[inline(always)]
pub fn set_z(&mut self, z: f32) {
self.co.set_2(z);
self.co.set_z(z);
}
}
@ -140,12 +142,7 @@ impl Mul<Matrix4x4> for Vector {
#[inline]
fn mul(self, other: Matrix4x4) -> Vector {
Vector {
co: Float4::new(
(self.co * other.values[0]).h_sum(),
(self.co * other.values[1]).h_sum(),
(self.co * other.values[2]).h_sum(),
(self.co * other.values[3]).h_sum(),
),
co: other.0.mul_vec4(self.co),
}
}
}
@ -173,7 +170,7 @@ impl Neg for Vector {
impl DotProduct for Vector {
#[inline(always)]
fn dot(self, other: Vector) -> f32 {
(self.co * other.co).h_sum()
self.co.dot(other.co)
}
}
@ -181,12 +178,7 @@ impl CrossProduct for Vector {
#[inline]
fn cross(self, other: Vector) -> Vector {
Vector {
co: Float4::new(
(self.co.get_1() * other.co.get_2()) - (self.co.get_2() * other.co.get_1()),
(self.co.get_2() * other.co.get_0()) - (self.co.get_0() * other.co.get_2()),
(self.co.get_0() * other.co.get_1()) - (self.co.get_1() * other.co.get_0()),
0.0,
),
co: self.co.truncate().cross(other.co.truncate()).extend(0.0),
}
}
}
@ -230,7 +222,7 @@ mod tests {
1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0, 13.0, 7.0, 15.0, 3.0,
);
let mut vm = Vector::new(14.0, 46.0, 58.0);
vm.co.set_3(90.5);
vm.co.set_w(90.5);
assert_eq!(v * m, vm);
}

View File

@ -9,6 +9,5 @@ license = "MIT"
name = "spectral_upsampling"
path = "src/lib.rs"
# Local crate dependencies
[dependencies.float4]
path = "../float4"
[dependencies]
glam = {git="https://github.com/bitshifter/glam-rs.git", rev="0f314f99", default-features=false, features=["approx"]}

View File

@ -6,7 +6,7 @@
/// The provides similar color matching as full Jakob, at the expense of
/// somewhat lower quality spectrums, and the inability to precalculate
/// the coefficents for even more efficient evaluation later on.
use float4::Float4;
use glam::Vec4;
/// How many polynomial coefficients?
const RGB2SPEC_N_COEFFS: usize = 3;
@ -15,7 +15,7 @@ const RGB2SPEC_N_COEFFS: usize = 3;
include!(concat!(env!("OUT_DIR"), "/jakob_table_inc.rs"));
#[inline]
pub fn rec709_to_spectrum_p4(lambdas: Float4, rgb: (f32, f32, f32)) -> Float4 {
pub fn rec709_to_spectrum_p4(lambdas: Vec4, rgb: (f32, f32, f32)) -> Vec4 {
small_rgb_to_spectrum_p4(
REC709_TABLE,
REC709_TABLE_RES,
@ -26,7 +26,7 @@ pub fn rec709_to_spectrum_p4(lambdas: Float4, rgb: (f32, f32, f32)) -> Float4 {
}
#[inline]
pub fn rec2020_to_spectrum_p4(lambdas: Float4, rgb: (f32, f32, f32)) -> Float4 {
pub fn rec2020_to_spectrum_p4(lambdas: Vec4, rgb: (f32, f32, f32)) -> Vec4 {
small_rgb_to_spectrum_p4(
REC2020_TABLE,
REC2020_TABLE_RES,
@ -37,7 +37,7 @@ pub fn rec2020_to_spectrum_p4(lambdas: Float4, rgb: (f32, f32, f32)) -> Float4 {
}
#[inline]
pub fn aces_to_spectrum_p4(lambdas: Float4, rgb: (f32, f32, f32)) -> Float4 {
pub fn aces_to_spectrum_p4(lambdas: Vec4, rgb: (f32, f32, f32)) -> Vec4 {
small_rgb_to_spectrum_p4(
ACES_TABLE,
ACES_TABLE_RES,
@ -55,9 +55,9 @@ fn small_rgb_to_spectrum_p4(
table: &[[(f32, f32, f32); 2]],
table_res: usize,
table_mid_value: f32,
lambdas: Float4,
lambdas: Vec4,
rgb: (f32, f32, f32),
) -> Float4 {
) -> Vec4 {
// Determine largest RGB component, and calculate the other two
// components scaled for lookups.
let (i, max_val, x, y) = if rgb.0 > rgb.1 && rgb.0 > rgb.2 {
@ -70,7 +70,7 @@ fn small_rgb_to_spectrum_p4(
if max_val == 0.0 {
// If max_val is zero, just return zero. This avoids NaN's from
// divide by zero. This is also correct, since it's black.
return Float4::splat(0.0);
return Vec4::splat(0.0);
}
let x = x * 63.0 / max_val;
let y = y * 63.0 / max_val;
@ -90,20 +90,20 @@ fn small_rgb_to_spectrum_p4(
// Convert to SIMD format for faster interpolation.
let a0 = [
Float4::new(a0[0].0, a0[0].1, a0[0].2, 0.0),
Float4::new(a0[1].0, a0[1].1, a0[1].2, 0.0),
Vec4::new(a0[0].0, a0[0].1, a0[0].2, 0.0),
Vec4::new(a0[1].0, a0[1].1, a0[1].2, 0.0),
];
let a1 = [
Float4::new(a1[0].0, a1[0].1, a1[0].2, 0.0),
Float4::new(a1[1].0, a1[1].1, a1[1].2, 0.0),
Vec4::new(a1[0].0, a1[0].1, a1[0].2, 0.0),
Vec4::new(a1[1].0, a1[1].1, a1[1].2, 0.0),
];
let a2 = [
Float4::new(a2[0].0, a2[0].1, a2[0].2, 0.0),
Float4::new(a2[1].0, a2[1].1, a2[1].2, 0.0),
Vec4::new(a2[0].0, a2[0].1, a2[0].2, 0.0),
Vec4::new(a2[1].0, a2[1].1, a2[1].2, 0.0),
];
let a3 = [
Float4::new(a3[0].0, a3[0].1, a3[0].2, 0.0),
Float4::new(a3[1].0, a3[1].1, a3[1].2, 0.0),
Vec4::new(a3[0].0, a3[0].1, a3[0].2, 0.0),
Vec4::new(a3[1].0, a3[1].1, a3[1].2, 0.0),
];
// Do interpolation.
@ -117,16 +117,14 @@ fn small_rgb_to_spectrum_p4(
// Evaluate the spectral function and return the result.
if max_val <= table_mid_value {
rgb2spec_eval_4([c[0].get_0(), c[0].get_1(), c[0].get_2()], lambdas)
* (1.0 / table_mid_value)
* max_val
rgb2spec_eval_4([c[0].x(), c[0].y(), c[0].z()], lambdas) * (1.0 / table_mid_value) * max_val
} else if max_val < 1.0 {
let n = (max_val - table_mid_value) / (1.0 - table_mid_value);
let s0 = rgb2spec_eval_4([c[0].get_0(), c[0].get_1(), c[0].get_2()], lambdas);
let s1 = rgb2spec_eval_4([c[1].get_0(), c[1].get_1(), c[1].get_2()], lambdas);
let s0 = rgb2spec_eval_4([c[0].x(), c[0].y(), c[0].z()], lambdas);
let s1 = rgb2spec_eval_4([c[1].x(), c[1].y(), c[1].z()], lambdas);
(s0 * (1.0 - n)) + (s1 * n)
} else {
rgb2spec_eval_4([c[1].get_0(), c[1].get_1(), c[1].get_2()], lambdas) * max_val
rgb2spec_eval_4([c[1].x(), c[1].y(), c[1].z()], lambdas) * max_val
}
}
@ -134,18 +132,22 @@ fn small_rgb_to_spectrum_p4(
// Coefficient -> eval functions
#[inline(always)]
fn rgb2spec_fma_4(a: Float4, b: Float4, c: Float4) -> Float4 {
a.fmadd(b, c)
fn rgb2spec_fma_4(a: Vec4, b: Vec4, c: Vec4) -> Vec4 {
(a * b) + c
}
fn rgb2spec_eval_4(coeff: [f32; RGB2SPEC_N_COEFFS], lambda: Float4) -> Float4 {
let co0 = Float4::splat(coeff[0]);
let co1 = Float4::splat(coeff[1]);
let co2 = Float4::splat(coeff[2]);
fn rgb2spec_eval_4(coeff: [f32; RGB2SPEC_N_COEFFS], lambda: Vec4) -> Vec4 {
let co0 = Vec4::splat(coeff[0]);
let co1 = Vec4::splat(coeff[1]);
let co2 = Vec4::splat(coeff[2]);
let x = rgb2spec_fma_4(rgb2spec_fma_4(co0, lambda, co1), lambda, co2);
let y = Float4::splat(1.0) / (rgb2spec_fma_4(x, x, Float4::splat(1.0))).sqrt();
let y = {
// TODO: replace this with a SIMD sqrt op.
let (x, y, z, w) = rgb2spec_fma_4(x, x, Vec4::splat(1.0)).into();
Vec4::new(x.sqrt(), y.sqrt(), z.sqrt(), w.sqrt()).reciprocal()
};
rgb2spec_fma_4(Float4::splat(0.5) * x, y, Float4::splat(0.5))
rgb2spec_fma_4(Vec4::splat(0.5) * x, y, Vec4::splat(0.5))
}

View File

@ -6,7 +6,7 @@
use std::f32;
use float4::Float4;
use glam::Vec4;
mod meng_spectra_tables;
@ -174,9 +174,9 @@ pub fn spectrum_xyz_to_p(lambda: f32, xyz: (f32, f32, f32)) -> f32 {
///
/// Works on 4 wavelengths at once via SIMD.
#[inline]
pub fn spectrum_xyz_to_p_4(lambdas: Float4, xyz: (f32, f32, f32)) -> Float4 {
assert!(lambdas.h_min() >= SPECTRUM_SAMPLE_MIN);
assert!(lambdas.h_max() <= SPECTRUM_SAMPLE_MAX);
pub fn spectrum_xyz_to_p_4(lambdas: Vec4, xyz: (f32, f32, f32)) -> Vec4 {
assert!(lambdas.min_element() >= SPECTRUM_SAMPLE_MIN);
assert!(lambdas.max_element() <= SPECTRUM_SAMPLE_MAX);
let inv_norm = xyz.0 + xyz.1 + xyz.2;
let norm = {
@ -184,7 +184,7 @@ pub fn spectrum_xyz_to_p_4(lambdas: Float4, xyz: (f32, f32, f32)) -> Float4 {
if norm < f32::MAX {
norm
} else {
return Float4::splat(0.0);
return Vec4::splat(0.0);
}
};
@ -197,7 +197,7 @@ pub fn spectrum_xyz_to_p_4(lambdas: Float4, xyz: (f32, f32, f32)) -> Float4 {
|| uv.1 < 0.0
|| uv.1 >= SPECTRUM_GRID_HEIGHT as f32
{
return Float4::splat(0.0);
return Vec4::splat(0.0);
}
let uvi = (uv.0 as i32, uv.1 as i32);
@ -214,53 +214,48 @@ pub fn spectrum_xyz_to_p_4(lambdas: Float4, xyz: (f32, f32, f32)) -> Float4 {
// If the cell has no points, nothing we can do, so return 0.0
if num == 0 {
return Float4::splat(0.0);
return Vec4::splat(0.0);
}
// Normalize lambda to spectrum table index range.
let sb: Float4 = (lambdas - Float4::splat(SPECTRUM_SAMPLE_MIN))
let sb: Vec4 = (lambdas - Vec4::splat(SPECTRUM_SAMPLE_MIN))
/ (SPECTRUM_SAMPLE_MAX - SPECTRUM_SAMPLE_MIN)
* (SPECTRUM_NUM_SAMPLES as f32 - 1.0);
debug_assert!(sb.h_min() >= 0.0);
debug_assert!(sb.h_max() <= SPECTRUM_NUM_SAMPLES as f32);
debug_assert!(sb.min_element() >= 0.0);
debug_assert!(sb.max_element() <= SPECTRUM_NUM_SAMPLES as f32);
// Get the spectral values for the vertices of the grid cell.
// TODO: use integer SIMD intrinsics to make this part faster.
let mut p = [Float4::splat(0.0); 6];
let sb0: [i32; 4] = [
sb.get_0() as i32,
sb.get_1() as i32,
sb.get_2() as i32,
sb.get_3() as i32,
];
let mut p = [Vec4::splat(0.0); 6];
let sb0: [i32; 4] = [sb.x() as i32, sb.y() as i32, sb.z() as i32, sb.w() as i32];
assert!(sb0[0].max(sb0[1]).max(sb0[2].max(sb0[3])) < SPECTRUM_NUM_SAMPLES);
let sb1: [i32; 4] = [
(sb.get_0() as i32 + 1).min(SPECTRUM_NUM_SAMPLES - 1),
(sb.get_1() as i32 + 1).min(SPECTRUM_NUM_SAMPLES - 1),
(sb.get_2() as i32 + 1).min(SPECTRUM_NUM_SAMPLES - 1),
(sb.get_3() as i32 + 1).min(SPECTRUM_NUM_SAMPLES - 1),
(sb.x() as i32 + 1).min(SPECTRUM_NUM_SAMPLES - 1),
(sb.y() as i32 + 1).min(SPECTRUM_NUM_SAMPLES - 1),
(sb.z() as i32 + 1).min(SPECTRUM_NUM_SAMPLES - 1),
(sb.w() as i32 + 1).min(SPECTRUM_NUM_SAMPLES - 1),
];
let sbf = sb - Float4::new(sb0[0] as f32, sb0[1] as f32, sb0[2] as f32, sb0[3] as f32);
let sbf = sb - Vec4::new(sb0[0] as f32, sb0[1] as f32, sb0[2] as f32, sb0[3] as f32);
for i in 0..(num as usize) {
debug_assert!(idx[i] >= 0);
let spectrum = &SPECTRUM_DATA_POINTS[idx[i] as usize].spectrum;
let p0 = Float4::new(
let p0 = Vec4::new(
spectrum[sb0[0] as usize],
spectrum[sb0[1] as usize],
spectrum[sb0[2] as usize],
spectrum[sb0[3] as usize],
);
let p1 = Float4::new(
let p1 = Vec4::new(
spectrum[sb1[0] as usize],
spectrum[sb1[1] as usize],
spectrum[sb1[2] as usize],
spectrum[sb1[3] as usize],
);
p[i] = p0 * (Float4::splat(1.0) - sbf) + p1 * sbf;
p[i] = p0 * (Vec4::splat(1.0) - sbf) + p1 * sbf;
}
// Linearly interpolate the spectral power of the cell vertices.
let mut interpolated_p = Float4::splat(0.0);
let mut interpolated_p = Vec4::splat(0.0);
if inside {
// Fast path for normal inner quads:
let uv2 = (uv.0 - uvi.0 as f32, uv.1 - uvi.1 as f32);