Use faster routines where precision isn't needed.

This commit is contained in:
Nathan Vegdahl 2022-07-16 01:09:33 -07:00
parent 8dcf093dbb
commit ea4ba81110
10 changed files with 49 additions and 46 deletions

View File

@ -81,7 +81,7 @@ impl<'a> RectangleLight<'a> {
let area_2 = spherical_triangle_solid_angle(sp4, sp1, sp3); let area_2 = spherical_triangle_solid_angle(sp4, sp1, sp3);
// World-space surface normal // World-space surface normal
let normal = Normal::new(0.0, 0.0, 1.0).xform(space); let normal = Normal::new(0.0, 0.0, 1.0).xform_fast(space);
// PDF // PDF
if (area_1 + area_2) < SIMPLE_SAMPLING_THRESHOLD { if (area_1 + area_2) < SIMPLE_SAMPLING_THRESHOLD {
@ -156,7 +156,7 @@ impl<'a> SurfaceLight for RectangleLight<'a> {
let area_2 = spherical_triangle_solid_angle(sp4, sp1, sp3); let area_2 = spherical_triangle_solid_angle(sp4, sp1, sp3);
// Calculate world-space surface normal // Calculate world-space surface normal
let normal = Normal::new(0.0, 0.0, 1.0).xform(space); let normal = Normal::new(0.0, 0.0, 1.0).xform_fast(space);
if (area_1 + area_2) < SIMPLE_SAMPLING_THRESHOLD { if (area_1 + area_2) < SIMPLE_SAMPLING_THRESHOLD {
// Simple sampling for more distant lights // Simple sampling for more distant lights

View File

@ -157,7 +157,7 @@ impl<'a> SurfaceLight for SphereLight<'a> {
let point = normal * radius as f32; let point = normal * radius as f32;
( (
point.into_point().xform(space), point.into_point().xform(space),
normal.into_normal().xform(space), normal.into_normal().xform_fast(space),
) )
}; };
let pdf = uniform_sample_cone_pdf(cos_theta_max); let pdf = uniform_sample_cone_pdf(cos_theta_max);
@ -175,7 +175,7 @@ impl<'a> SurfaceLight for SphereLight<'a> {
let point = normal * radius as f32; let point = normal * radius as f32;
( (
point.into_point().xform(space), point.into_point().xform(space),
normal.into_normal().xform(space), normal.into_normal().xform_fast(space),
) )
}; };
let pdf = 1.0 / (4.0 * PI_64); let pdf = 1.0 / (4.0 * PI_64);
@ -296,7 +296,7 @@ impl<'a> Surface for SphereLight<'a> {
// TODO: proper error bounds. // TODO: proper error bounds.
let pos_err = 0.001; let pos_err = 0.001;
let normal = unit_pos.into_normal().xform(&xform); let normal = unit_pos.into_normal().xform_fast(&xform);
let intersection_data = SurfaceIntersectionData { let intersection_data = SurfaceIntersectionData {
incoming: rays.dir(ray_idx), incoming: rays.dir(ray_idx),

View File

@ -3,7 +3,8 @@
use std::f32; use std::f32;
pub use rmath::{ pub use rmath::{
cross, dot, wide4::Float4, CrossProduct, DotProduct, Normal, Point, Vector, Xform, XformFull, cross, cross_fast, dot, dot_fast, wide4::Float4, CrossProduct, DotProduct, Normal, Point,
Vector, Xform, XformFull,
}; };
/// Clamps a value between a min and max. /// Clamps a value between a min and max.

View File

@ -2,7 +2,7 @@
use std::{f32::consts::FRAC_PI_4 as QPI_32, f32::consts::PI as PI_32, f64::consts::PI as PI_64}; use std::{f32::consts::FRAC_PI_4 as QPI_32, f32::consts::PI as PI_32, f64::consts::PI as PI_64};
use crate::math::{cross, dot, Point, Vector}; use crate::math::{cross_fast, dot_fast, Point, Vector};
/// Maps the unit square to the unit circle. /// Maps the unit square to the unit circle.
/// NOTE: x and y should be distributed within [-1, 1], /// NOTE: x and y should be distributed within [-1, 1],
@ -90,7 +90,7 @@ pub fn uniform_sample_triangle(va: Vector, vb: Vector, vc: Vector, i: f32, j: f3
/// Calculates the surface area of a triangle. /// Calculates the surface area of a triangle.
pub fn triangle_surface_area(p0: Point, p1: Point, p2: Point) -> f32 { pub fn triangle_surface_area(p0: Point, p1: Point, p2: Point) -> f32 {
0.5 * cross(p1 - p0, p2 - p0).length() 0.5 * cross_fast(p1 - p0, p2 - p0).length()
} }
/// Calculates the projected solid angle of a spherical triangle. /// Calculates the projected solid angle of a spherical triangle.
@ -98,9 +98,9 @@ pub fn triangle_surface_area(p0: Point, p1: Point, p2: Point) -> f32 {
/// A, B, and C are the points of the triangle on a unit sphere. /// A, B, and C are the points of the triangle on a unit sphere.
pub fn spherical_triangle_solid_angle(va: Vector, vb: Vector, vc: Vector) -> f32 { pub fn spherical_triangle_solid_angle(va: Vector, vb: Vector, vc: Vector) -> f32 {
// Calculate sines and cosines of the spherical triangle's edge lengths // Calculate sines and cosines of the spherical triangle's edge lengths
let cos_a: f64 = dot(vb, vc).max(-1.0).min(1.0) as f64; let cos_a: f64 = dot_fast(vb, vc).max(-1.0).min(1.0) as f64;
let cos_b: f64 = dot(vc, va).max(-1.0).min(1.0) as f64; let cos_b: f64 = dot_fast(vc, va).max(-1.0).min(1.0) as f64;
let cos_c: f64 = dot(va, vb).max(-1.0).min(1.0) as f64; let cos_c: f64 = dot_fast(va, vb).max(-1.0).min(1.0) as f64;
let sin_a: f64 = (1.0 - (cos_a * cos_a)).sqrt(); let sin_a: f64 = (1.0 - (cos_a * cos_a)).sqrt();
let sin_b: f64 = (1.0 - (cos_b * cos_b)).sqrt(); let sin_b: f64 = (1.0 - (cos_b * cos_b)).sqrt();
let sin_c: f64 = (1.0 - (cos_c * cos_c)).sqrt(); let sin_c: f64 = (1.0 - (cos_c * cos_c)).sqrt();
@ -141,9 +141,9 @@ pub fn uniform_sample_spherical_triangle(
j: f32, j: f32,
) -> Vector { ) -> Vector {
// Calculate sines and cosines of the spherical triangle's edge lengths // Calculate sines and cosines of the spherical triangle's edge lengths
let cos_a: f64 = dot(vb, vc).max(-1.0).min(1.0) as f64; let cos_a: f64 = dot_fast(vb, vc).max(-1.0).min(1.0) as f64;
let cos_b: f64 = dot(vc, va).max(-1.0).min(1.0) as f64; let cos_b: f64 = dot_fast(vc, va).max(-1.0).min(1.0) as f64;
let cos_c: f64 = dot(va, vb).max(-1.0).min(1.0) as f64; let cos_c: f64 = dot_fast(va, vb).max(-1.0).min(1.0) as f64;
let sin_a: f64 = (1.0 - (cos_a * cos_a)).sqrt(); let sin_a: f64 = (1.0 - (cos_a * cos_a)).sqrt();
let sin_b: f64 = (1.0 - (cos_b * cos_b)).sqrt(); let sin_b: f64 = (1.0 - (cos_b * cos_b)).sqrt();
let sin_c: f64 = (1.0 - (cos_c * cos_c)).sqrt(); let sin_c: f64 = (1.0 - (cos_c * cos_c)).sqrt();
@ -191,10 +191,10 @@ pub fn uniform_sample_spherical_triangle(
let q_bottom = ((v * s) + (u * t)) * sin_va; let q_bottom = ((v * s) + (u * t)) * sin_va;
let q = q_top / q_bottom; let q = q_top / q_bottom;
let vc_2 = let vc_2 = (va * q as f32)
(va * q as f32) + ((vc - (va * dot(vc, va))).normalized() * (1.0 - (q * q)).sqrt() as f32); + ((vc - (va * dot_fast(vc, va))).normalized() * (1.0 - (q * q)).sqrt() as f32);
let z = 1.0 - (j * (1.0 - dot(vc_2, vb))); let z = 1.0 - (j * (1.0 - dot_fast(vc_2, vb)));
(vb * z) + ((vc_2 - (vb * dot(vc_2, vb))).normalized() * (1.0 - (z * z)).sqrt()) (vb * z) + ((vc_2 - (vb * dot_fast(vc_2, vb))).normalized() * (1.0 - (z * z)).sqrt())
} }

View File

@ -70,8 +70,8 @@ impl<'a> Assembly<'a> {
if let Some((light_i, sel_pdf, whittled_n)) = self.light_accel.select( if let Some((light_i, sel_pdf, whittled_n)) = self.light_accel.select(
idata.incoming.xform_inv(&sel_xform), idata.incoming.xform_inv(&sel_xform),
idata.pos.xform_inv(&sel_xform), idata.pos.xform_inv(&sel_xform),
idata.nor.xform_inv(&sel_xform), idata.nor.xform_inv_fast(&sel_xform),
idata.nor_g.xform_inv(&sel_xform), idata.nor_g.xform_inv_fast(&sel_xform),
&closure, &closure,
time, time,
n, n,

View File

@ -5,7 +5,7 @@ use std::f32::consts::PI as PI_32;
use crate::{ use crate::{
color::{Color, SpectralSample}, color::{Color, SpectralSample},
lerp::{lerp, Lerp}, lerp::{lerp, Lerp},
math::{clamp, dot, zup_to_vec, Float4, Normal, Vector}, math::{clamp, dot_fast, zup_to_vec, Float4, Normal, Vector},
sampling::cosine_sample_hemisphere, sampling::cosine_sample_hemisphere,
}; };
@ -287,7 +287,7 @@ mod lambert_closure {
uv: (f32, f32), uv: (f32, f32),
wavelength: f32, wavelength: f32,
) -> (Vector, SpectralSample, f32) { ) -> (Vector, SpectralSample, f32) {
let (nn, flipped_nor_g) = if dot(nor_g.into_vector(), inc) <= 0.0 { let (nn, flipped_nor_g) = if dot_fast(nor_g.into_vector(), inc) <= 0.0 {
(nor.normalized().into_vector(), nor_g.into_vector()) (nor.normalized().into_vector(), nor_g.into_vector())
} else { } else {
(-nor.normalized().into_vector(), -nor_g.into_vector()) (-nor.normalized().into_vector(), -nor_g.into_vector())
@ -300,7 +300,7 @@ mod lambert_closure {
let out = zup_to_vec(dir, nn); let out = zup_to_vec(dir, nn);
// Make sure it's not on the wrong side of the geometric normal. // Make sure it's not on the wrong side of the geometric normal.
if dot(flipped_nor_g, out) >= 0.0 { if dot_fast(flipped_nor_g, out) >= 0.0 {
(out, color.to_spectral_sample(wavelength) * pdf, pdf) (out, color.to_spectral_sample(wavelength) * pdf, pdf)
} else { } else {
(out, SpectralSample::new(0.0), 0.0) (out, SpectralSample::new(0.0), 0.0)
@ -315,14 +315,14 @@ mod lambert_closure {
nor_g: Normal, nor_g: Normal,
wavelength: f32, wavelength: f32,
) -> (SpectralSample, f32) { ) -> (SpectralSample, f32) {
let (nn, flipped_nor_g) = if dot(nor_g.into_vector(), inc) <= 0.0 { let (nn, flipped_nor_g) = if dot_fast(nor_g.into_vector(), inc) <= 0.0 {
(nor.normalized().into_vector(), nor_g.into_vector()) (nor.normalized().into_vector(), nor_g.into_vector())
} else { } else {
(-nor.normalized().into_vector(), -nor_g.into_vector()) (-nor.normalized().into_vector(), -nor_g.into_vector())
}; };
if dot(flipped_nor_g, out) >= 0.0 { if dot_fast(flipped_nor_g, out) >= 0.0 {
let fac = dot(nn, out.normalized()).max(0.0) * INV_PI; let fac = dot_fast(nn, out.normalized()).max(0.0) * INV_PI;
(color.to_spectral_sample(wavelength) * fac, fac) (color.to_spectral_sample(wavelength) * fac, fac)
} else { } else {
(SpectralSample::new(0.0), 0.0) (SpectralSample::new(0.0), 0.0)
@ -381,14 +381,14 @@ mod lambert_closure {
let cos_theta_max = (1.0 - sin_theta_max2).sqrt(); let cos_theta_max = (1.0 - sin_theta_max2).sqrt();
let v = to_light_center.normalized(); let v = to_light_center.normalized();
let nn = if dot(nor_g.into_vector(), inc) <= 0.0 { let nn = if dot_fast(nor_g.into_vector(), inc) <= 0.0 {
nor.normalized() nor.normalized()
} else { } else {
-nor.normalized() -nor.normalized()
} }
.into_vector(); .into_vector();
let cos_nv = dot(nn, v).max(-1.0).min(1.0); let cos_nv = dot_fast(nn, v).max(-1.0).min(1.0);
// Alt implementation from the SPI paper. // Alt implementation from the SPI paper.
// Worse sampling, but here for reference. // Worse sampling, but here for reference.
@ -426,7 +426,7 @@ mod ggx_closure {
wavelength: f32, wavelength: f32,
) -> (Vector, SpectralSample, f32) { ) -> (Vector, SpectralSample, f32) {
// Get normalized surface normal // Get normalized surface normal
let (nn, flipped_nor_g) = if dot(nor_g.into_vector(), inc) <= 0.0 { let (nn, flipped_nor_g) = if dot_fast(nor_g.into_vector(), inc) <= 0.0 {
(nor.normalized().into_vector(), nor_g.into_vector()) (nor.normalized().into_vector(), nor_g.into_vector())
} else { } else {
(-nor.normalized().into_vector(), -nor_g.into_vector()) (-nor.normalized().into_vector(), -nor_g.into_vector())
@ -440,10 +440,10 @@ mod ggx_closure {
let mut half_dir = Vector::new(angle.cos() * theta_sin, angle.sin() * theta_sin, theta_cos); let mut half_dir = Vector::new(angle.cos() * theta_sin, angle.sin() * theta_sin, theta_cos);
half_dir = zup_to_vec(half_dir, nn).normalized(); half_dir = zup_to_vec(half_dir, nn).normalized();
let out = inc - (half_dir * 2.0 * dot(inc, half_dir)); let out = inc - (half_dir * 2.0 * dot_fast(inc, half_dir));
// Make sure it's not on the wrong side of the geometric normal. // Make sure it's not on the wrong side of the geometric normal.
if dot(flipped_nor_g, out) >= 0.0 { if dot_fast(flipped_nor_g, out) >= 0.0 {
let (filter, pdf) = evaluate(col, roughness, fresnel, inc, out, nor, nor_g, wavelength); let (filter, pdf) = evaluate(col, roughness, fresnel, inc, out, nor, nor_g, wavelength);
(out, filter, pdf) (out, filter, pdf)
} else { } else {
@ -467,23 +467,23 @@ mod ggx_closure {
let hh = (aa + bb).normalized(); // Half-way between aa and bb let hh = (aa + bb).normalized(); // Half-way between aa and bb
// Surface normal // Surface normal
let (nn, flipped_nor_g) = if dot(nor_g.into_vector(), inc) <= 0.0 { let (nn, flipped_nor_g) = if dot_fast(nor_g.into_vector(), inc) <= 0.0 {
(nor.normalized().into_vector(), nor_g.into_vector()) (nor.normalized().into_vector(), nor_g.into_vector())
} else { } else {
(-nor.normalized().into_vector(), -nor_g.into_vector()) (-nor.normalized().into_vector(), -nor_g.into_vector())
}; };
// Make sure everything's on the correct side of the surface // Make sure everything's on the correct side of the surface
if dot(nn, aa) < 0.0 || dot(nn, bb) < 0.0 || dot(flipped_nor_g, bb) < 0.0 { if dot_fast(nn, aa) < 0.0 || dot_fast(nn, bb) < 0.0 || dot_fast(flipped_nor_g, bb) < 0.0 {
return (SpectralSample::new(0.0), 0.0); return (SpectralSample::new(0.0), 0.0);
} }
// Calculate needed dot products // Calculate needed dot products
let na = clamp(dot(nn, aa), -1.0, 1.0); let na = clamp(dot_fast(nn, aa), -1.0, 1.0);
let nb = clamp(dot(nn, bb), -1.0, 1.0); let nb = clamp(dot_fast(nn, bb), -1.0, 1.0);
let ha = clamp(dot(hh, aa), -1.0, 1.0); let ha = clamp(dot_fast(hh, aa), -1.0, 1.0);
let hb = clamp(dot(hh, bb), -1.0, 1.0); let hb = clamp(dot_fast(hh, bb), -1.0, 1.0);
let nh = clamp(dot(nn, hh), -1.0, 1.0); let nh = clamp(dot_fast(nn, hh), -1.0, 1.0);
// Calculate F - Fresnel // Calculate F - Fresnel
let col_f = { let col_f = {
@ -554,7 +554,7 @@ mod ggx_closure {
assert!(cos_theta_max <= 1.0); assert!(cos_theta_max <= 1.0);
// Surface normal // Surface normal
let nn = if dot(nor.into_vector(), inc) < 0.0 { let nn = if dot_fast(nor.into_vector(), inc) < 0.0 {
nor.normalized() nor.normalized()
} else { } else {
-nor.normalized() // If back-facing, flip normal -nor.normalized() // If back-facing, flip normal
@ -572,9 +572,9 @@ mod ggx_closure {
// let vv = Halton::sample(1, i); // let vv = Halton::sample(1, i);
// let mut samp = uniform_sample_cone(uu, vv, cos_theta_max); // let mut samp = uniform_sample_cone(uu, vv, cos_theta_max);
// samp = zup_to_vec(samp, bb).normalized(); // samp = zup_to_vec(samp, bb).normalized();
// if dot(nn, samp) > 0.0 { // if dot_fast(nn, samp) > 0.0 {
// let hh = (aa+samp).normalized(); // let hh = (aa+samp).normalized();
// fac += ggx_d(dot(nn, hh), roughness); // fac += ggx_d(dot_fast(nn, hh), roughness);
// } // }
//} //}
//fac /= N * N; //fac /= N * N;
@ -582,7 +582,7 @@ mod ggx_closure {
// Approximate method // Approximate method
let theta = cos_theta_max.acos(); let theta = cos_theta_max.acos();
let hh = (aa + bb).normalized(); let hh = (aa + bb).normalized();
let nh = clamp(dot(nn, hh), -1.0, 1.0); let nh = clamp(dot_fast(nn, hh), -1.0, 1.0);
let fac = ggx_d(nh, (1.0f32).min(roughness.sqrt() + (2.0 * theta / PI_32))); let fac = ggx_d(nh, (1.0f32).min(roughness.sqrt() + (2.0 * theta / PI_32)));
fac * (1.0f32).min(1.0 - cos_theta_max) * INV_PI fac * (1.0f32).min(1.0 - cos_theta_max) * INV_PI

View File

@ -319,7 +319,7 @@ impl<'a> MicropolyBatch<'a> {
let n1 = lerp_slice(n1_slice, ray_time).normalized(); let n1 = lerp_slice(n1_slice, ray_time).normalized();
let n2 = lerp_slice(n2_slice, ray_time).normalized(); let n2 = lerp_slice(n2_slice, ray_time).normalized();
let s_nor = ((n0 * b0) + (n1 * b1) + (n2 * b2)).xform(&mat_space); let s_nor = ((n0 * b0) + (n1 * b1) + (n2 * b2)).xform_fast(&mat_space);
if dot(s_nor, geo_normal) >= 0.0 { if dot(s_nor, geo_normal) >= 0.0 {
s_nor s_nor
} else { } else {

View File

@ -297,7 +297,7 @@ impl<'a> Surface for TriangleMesh<'a> {
let n1 = lerp_slice(n1_slice, ray_time).normalized(); let n1 = lerp_slice(n1_slice, ray_time).normalized();
let n2 = lerp_slice(n2_slice, ray_time).normalized(); let n2 = lerp_slice(n2_slice, ray_time).normalized();
let s_nor = ((n0 * b0) + (n1 * b1) + (n2 * b2)).xform(&mat_space); let s_nor = ((n0 * b0) + (n1 * b1) + (n2 * b2)).xform_fast(&mat_space);
if dot(s_nor, geo_normal) >= 0.0 { if dot(s_nor, geo_normal) >= 0.0 {
s_nor s_nor
} else { } else {

View File

@ -34,7 +34,7 @@ impl Float4 {
/// `(self * a) + b` with only one rounding error. /// `(self * a) + b` with only one rounding error.
#[inline(always)] #[inline(always)]
pub fn mul_add(self, a: Self, b: Self) -> Self { pub fn mul_add(self, a: Self, b: Self) -> Self {
if cfg!(feature = "fma") { if is_x86_feature_detected!("fma") {
Self(unsafe { _mm_fmadd_ps(self.0, a.0, b.0) }) Self(unsafe { _mm_fmadd_ps(self.0, a.0, b.0) })
} else { } else {
Self::new( Self::new(

View File

@ -8,6 +8,8 @@
/// the coefficents for even more efficient evaluation later on. /// the coefficents for even more efficient evaluation later on.
use rmath::wide4::Float4; use rmath::wide4::Float4;
pub const EQUAL_ENERGY_REFLECTANCE: f32 = 1.0;
/// How many polynomial coefficients? /// How many polynomial coefficients?
const RGB2SPEC_N_COEFFS: usize = 3; const RGB2SPEC_N_COEFFS: usize = 3;
@ -134,7 +136,7 @@ fn small_rgb_to_spectrum_p4(
#[inline(always)] #[inline(always)]
fn rgb2spec_fma_4(a: Float4, b: Float4, c: Float4) -> Float4 { fn rgb2spec_fma_4(a: Float4, b: Float4, c: Float4) -> Float4 {
(a * b) + c a.mul_add(b, c)
} }
fn rgb2spec_eval_4(coeff: [f32; RGB2SPEC_N_COEFFS], lambda: Float4) -> Float4 { fn rgb2spec_eval_4(coeff: [f32; RGB2SPEC_N_COEFFS], lambda: Float4) -> Float4 {