Compare commits

...

6 commits

30 changed files with 375 additions and 320 deletions

4
.gitignore vendored
View file

@ -15,3 +15,7 @@ tests/
scenes/ scenes/
compile.sh compile.sh
output/ output/
*.md
!README.md
!INSTALL.md
docs/

View file

@ -41,7 +41,7 @@ impl BxDFTrait for DiffuseBxDF {
return None; return None;
} }
let mut wi = sample_cosine_hemisphere(u); let mut wi = sample_cosine_hemisphere(u);
if wo.z() == 0. { if wo.z() < 0. {
wi[2] *= -1.; wi[2] *= -1.;
} }
let pdf = cosine_hemisphere_pdf(abs_cos_theta(wi)); let pdf = cosine_hemisphere_pdf(abs_cos_theta(wi));

View file

@ -114,7 +114,7 @@ impl CameraTrait for OrthographicCamera {
Some(CameraRay { Some(CameraRay {
ray: camera_ray, ray: camera_ray,
weight: SampledSpectrum::default(), weight: SampledSpectrum::new(1.),
}) })
} }

View file

@ -58,7 +58,7 @@ impl CameraTrait for SphericalCamera {
); );
Some(CameraRay { Some(CameraRay {
ray: self.render_from_camera(&ray, &mut None), ray: self.render_from_camera(&ray, &mut None),
weight: SampledSpectrum::default(), weight: SampledSpectrum::new(1.),
}) })
} }
} }

View file

@ -86,7 +86,7 @@ impl From<&SubsurfaceInteraction> for SurfaceInteraction {
dvdx: 0., dvdx: 0.,
dudy: 0., dudy: 0.,
dvdy: 0., dvdy: 0.,
shape: Ptr::from(&Shape::default()), shape: Ptr::null(),
} }
} }
} }

View file

@ -8,6 +8,7 @@ use core::fmt;
use core::ops::{ use core::ops::{
Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign, Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
}; };
use anyhow::{Result, bail};
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use num_traits::Float as NumFloat; use num_traits::Float as NumFloat;
@ -680,6 +681,16 @@ pub enum ColorEncoding {
SRGB(SRGBEncoding), SRGB(SRGBEncoding),
} }
impl ColorEncoding {
pub fn from_name(name: &str) -> Result<Self> {
match name {
"sRGB" | "srgb" => Ok(ColorEncoding::SRGB(SRGBEncoding)),
"linear" => Ok(ColorEncoding::Linear(LinearEncoding)),
_ => bail!("Unknown color encoding: {}", name),
}
}
}
impl fmt::Display for ColorEncoding { impl fmt::Display for ColorEncoding {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Encoding") write!(f, "Encoding")

View file

@ -5,6 +5,7 @@ use crate::core::geometry::{max, min};
use crate::utils::gpu_array_from_fn; use crate::utils::gpu_array_from_fn;
use crate::utils::interval::Interval; use crate::utils::interval::Interval;
use crate::utils::math::lerp; use crate::utils::math::lerp;
use crate::{gamma, gamma_t};
use core::mem; use core::mem;
use core::ops::{Add, Div, DivAssign, Mul, Sub}; use core::ops::{Add, Div, DivAssign, Mul, Sub};
use num_traits::{Bounded, Num}; use num_traits::{Bounded, Num};
@ -220,7 +221,7 @@ where
(center, radius) (center, radius)
} }
pub fn insersect(&self, o: Point3<T>, d: Vector3<T>, t_max: T) -> Option<(T, T)> { pub fn intersect(&self, o: Point3<T>, d: Vector3<T>, t_max: T) -> Option<(T, T)> {
let mut t0 = T::zero(); let mut t0 = T::zero();
let mut t1 = t_max; let mut t1 = t_max;
@ -231,6 +232,8 @@ where
if t_near > t_far { if t_near > t_far {
mem::swap(&mut t_near, &mut t_far); mem::swap(&mut t_near, &mut t_far);
} }
t_far = t_far * (T::one() + (T::one() + T::one()) * gamma_t::<T>(3));
t0 = if t_near > t0 { t_near } else { t0 }; t0 = if t_near > t0 { t_near } else { t0 };
t1 = if t_far < t1 { t_far } else { t1 }; t1 = if t_far < t1 { t_far } else { t1 };
if t0 > t1 { if t0 > t1 {
@ -274,7 +277,10 @@ impl Bounds3f {
// Check Y // Check Y
let ty_min = (bounds[dir_is_neg[1]].y() - o.y()) * inv_dir.y(); let ty_min = (bounds[dir_is_neg[1]].y() - o.y()) * inv_dir.y();
let ty_max = (bounds[1 - dir_is_neg[1]].y() - o.y()) * inv_dir.y(); let mut ty_max = (bounds[1 - dir_is_neg[1]].y() - o.y()) * inv_dir.y();
t_max = t_max * (1. + 2. * gamma(3));
ty_max = ty_max * (1. + 2. * gamma(3));
if t_min > ty_max || ty_min > t_max { if t_min > ty_max || ty_min > t_max {
return None; return None;
@ -288,7 +294,8 @@ impl Bounds3f {
// Check Z // Check Z
let tz_min = (bounds[dir_is_neg[2]].z() - o.z()) * inv_dir.z(); let tz_min = (bounds[dir_is_neg[2]].z() - o.z()) * inv_dir.z();
let tz_max = (bounds[1 - dir_is_neg[2]].z() - o.z()) * inv_dir.z(); let mut tz_max = (bounds[1 - dir_is_neg[2]].z() - o.z()) * inv_dir.z();
tz_max = tz_max * (1. + 2. * gamma(3));
if t_min > tz_max || tz_min > t_max { if t_min > tz_max || tz_min > t_max {
return None; return None;
@ -321,7 +328,11 @@ impl Bounds3f {
let mut t_min = (bounds[dir_is_neg[0]].x() - o.x()) * inv_dir.x(); let mut t_min = (bounds[dir_is_neg[0]].x() - o.x()) * inv_dir.x();
let mut t_max = (bounds[1 - dir_is_neg[0]].x() - o.x()) * inv_dir.x(); let mut t_max = (bounds[1 - dir_is_neg[0]].x() - o.x()) * inv_dir.x();
let ty_min = (bounds[dir_is_neg[1]].y() - o.y()) * inv_dir.y(); let ty_min = (bounds[dir_is_neg[1]].y() - o.y()) * inv_dir.y();
let ty_max = (bounds[1 - dir_is_neg[1]].y() - o.y()) * inv_dir.y(); let mut ty_max = (bounds[1 - dir_is_neg[1]].y() - o.y()) * inv_dir.y();
t_max = t_max * (1. + 2. * gamma(3));
ty_max = ty_max * (1. + 2. * gamma(3));
if t_min > ty_max || ty_min > t_max { if t_min > ty_max || ty_min > t_max {
return false; return false;
@ -334,7 +345,8 @@ impl Bounds3f {
} }
let tz_min = (bounds[dir_is_neg[2]].z() - o.z()) * inv_dir.z(); let tz_min = (bounds[dir_is_neg[2]].z() - o.z()) * inv_dir.z();
let tz_max = (bounds[1 - dir_is_neg[2]].z() - o.z()) * inv_dir.z(); let mut tz_max = (bounds[1 - dir_is_neg[2]].z() - o.z()) * inv_dir.z();
tz_max = tz_max * (1. + 2. * gamma(3));
if t_min > tz_max || tz_min > t_max { if t_min > tz_max || tz_min > t_max {
return false; return false;

View file

@ -52,13 +52,13 @@ impl DirectionCone {
* Vector3f::new( * Vector3f::new(
w.x() w.x()
* (wp.y() * w.y() + wp.z() * w.z() * (wp.y() * w.y() + wp.z() * w.z()
- wp.x() * (square(w.y() + square(w.z())))), - wp.x() * (square(w.y()) + square(w.z()))),
w.y() w.y()
* (wp.x() * w.x() + wp.z() * w.z() * (wp.x() * w.x() + wp.z() * w.z()
- wp.y() * (square(w.x() + square(w.z())))), - wp.y() * (square(w.x()) + square(w.z()))),
w.z() w.z()
* (wp.x() * w.x() + wp.y() * w.y() * (wp.x() * w.x() + wp.y() * w.y()
- wp.z() * (square(w.x() + square(w.y())))), - wp.z() * (square(w.x()) + square(w.y()))),
) )
} }
@ -91,10 +91,10 @@ impl DirectionCone {
let theta_b = safe_acos(b.cos_theta); let theta_b = safe_acos(b.cos_theta);
let theta_d = a.w.angle_between(b.w); let theta_d = a.w.angle_between(b.w);
if (theta_d + theta_b).min(PI) <= theta_b { if (theta_d + theta_b).min(PI) <= theta_a {
return a.clone(); return a.clone();
} }
if (theta_d + theta_a).min(PI) <= theta_a { if (theta_d + theta_a).min(PI) <= theta_b {
return b.clone(); return b.clone();
} }
@ -107,7 +107,7 @@ impl DirectionCone {
// Find the merged cone's axis and return cone union // Find the merged cone's axis and return cone union
let theta_r = theta_o - theta_a; let theta_r = theta_o - theta_a;
let wr = a.w.cross(b.w); let wr = a.w.cross(b.w);
if wr.norm_squared() >= 0. { if wr.norm_squared() == 0. {
return DirectionCone::entire_sphere(); return DirectionCone::entire_sphere();
} }

View file

@ -2,13 +2,13 @@ use super::traits::{SqrtExt, Tuple, VectorLike};
use super::{Float, NumFloat, PI}; use super::{Float, NumFloat, PI};
use crate::utils::interval::Interval; use crate::utils::interval::Interval;
use crate::utils::math::{clamp, difference_of_products, quadratic, safe_asin}; use crate::utils::math::{clamp, difference_of_products, quadratic, safe_asin};
use core::fmt;
use core::hash::{Hash, Hasher}; use core::hash::{Hash, Hasher};
use core::iter::Sum; use core::iter::Sum;
use core::ops::{ use core::ops::{
Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign, Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
}; };
use num_traits::{AsPrimitive, FloatConst, Num, Signed, Zero}; use num_traits::{AsPrimitive, FloatConst, Num, Signed, Zero};
use core::fmt;
pub trait MulAdd<M = Self, A = Self> { pub trait MulAdd<M = Self, A = Self> {
type Output; type Output;
@ -23,6 +23,14 @@ impl MulAdd<Float, Float> for Float {
} }
} }
impl MulAdd<f64, f64> for f64 {
type Output = f64;
#[inline(always)]
fn mul_add(self, multiplier: f64, addend: f64) -> Self::Output {
num_traits::Float::mul_add(self, multiplier, addend)
}
}
// N-dimensional displacement // N-dimensional displacement
#[repr(C)] #[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
@ -217,6 +225,27 @@ macro_rules! impl_tuple_core {
}; };
} }
#[macro_export]
macro_rules! impl_num_zero {
($Struct:ident) => {
impl<T, const N: usize> num_traits::Zero for $Struct<T, N>
where
T: num_traits::Zero + Copy + PartialEq,
{
#[inline]
fn zero() -> Self {
Self([T::zero(); N])
}
#[inline]
fn is_zero(&self) -> bool {
self.0.iter().all(|c| c.is_zero())
}
}
};
}
impl_num_zero!(Vector);
impl_num_zero!(Normal);
#[macro_export] #[macro_export]
macro_rules! impl_scalar_ops { macro_rules! impl_scalar_ops {
($Struct:ident) => { ($Struct:ident) => {
@ -607,33 +636,33 @@ impl<T: Copy> Vector4<T> {
// Vector operations // Vector operations
impl<T> Vector3<T> impl<T> Vector3<T>
where where
T: Num + Copy + Neg<Output = T>, T: Num + Copy + Neg<Output = T> + Zero + MulAdd<T, T, Output = T>,
{ {
pub fn cross(self, rhs: Self) -> Self { pub fn cross(self, rhs: Self) -> Self {
Self([ Self([
self[1] * rhs[2] - self[2] * rhs[1], difference_of_products(self[1], rhs[2], self[2], rhs[1]),
self[2] * rhs[0] - self[0] * rhs[2], difference_of_products(self[2], rhs[0], self[0], rhs[2]),
self[0] * rhs[1] - self[1] * rhs[0], difference_of_products(self[0], rhs[1], self[1], rhs[0]),
]) ])
} }
} }
impl<T> Normal3<T> impl<T> Normal3<T>
where where
T: Num + Copy + Neg<Output = T>, T: Num + Copy + Neg<Output = T> + Zero + MulAdd<T, T, Output = T>,
{ {
pub fn cross(self, rhs: Self) -> Self { pub fn cross(self, rhs: Self) -> Self {
Self([ Self([
self[1] * rhs[2] - self[2] * rhs[1], difference_of_products(self[1], rhs[2], self[2], rhs[1]),
self[2] * rhs[0] - self[0] * rhs[2], difference_of_products(self[2], rhs[0], self[0], rhs[2]),
self[0] * rhs[1] - self[1] * rhs[0], difference_of_products(self[0], rhs[1], self[1], rhs[0]),
]) ])
} }
} }
impl<T> Vector3<T> impl<T> Vector3<T>
where where
T: Num + NumFloat + Copy + Neg<Output = T>, T: Num + NumFloat + Copy + Neg<Output = T> + Zero + MulAdd<T, T, Output = T>,
{ {
pub fn coordinate_system(&self) -> (Self, Self) pub fn coordinate_system(&self) -> (Self, Self)
where where
@ -663,7 +692,7 @@ where
impl<T> Normal3<T> impl<T> Normal3<T>
where where
T: Num + NumFloat + Copy + Neg<Output = T>, T: Num + NumFloat + Copy + Neg<Output = T> + Zero + MulAdd<T, T, Output = T>,
{ {
pub fn coordinate_system(&self) -> (Self, Self) pub fn coordinate_system(&self) -> (Self, Self)
where where

View file

@ -43,25 +43,21 @@ impl Ray {
self.o + self.d * t self.o + self.d * t
} }
pub fn offset_origin(p: &Point3fi, n: &Normal3f, w: &Vector3f) -> Point3f { pub fn offset_origin(pi: &Point3fi, n: &Normal3f, w: &Vector3f) -> Point3f {
let d: Float = Vector3f::from(n.abs()).dot(p.error()); let d: Float = Vector3f::from(n.abs()).dot(pi.error());
let normal: Vector3f = Vector3f::from(*n); let mut disp: Vector3f = Vector3f::from(*n) * d;
if w.dot(Vector3f::from(*n)) < 0.0 {
let mut offset = p.midpoint(); disp = -disp;
if w.dot(normal) < 0.0 {
offset -= normal * d;
} else {
offset += normal * d;
} }
let mut po = pi.midpoint() + disp;
for i in 0..3 { for i in 0..3 {
if n[i] > 0.0 { if disp[i] > 0.0 {
offset[i] = next_float_up(offset[i]); po[i] = next_float_up(po[i]);
} else if n[i] < 0.0 { } else if disp[i] < 0.0 {
offset[i] = next_float_down(offset[i]); po[i] = next_float_down(po[i]);
} }
} }
offset po
} }
pub fn spawn(pi: &Point3fi, n: &Normal3f, time: Float, d: Vector3f) -> Ray { pub fn spawn(pi: &Point3fi, n: &Normal3f, time: Float, d: Vector3f) -> Ray {

View file

@ -2,10 +2,12 @@ use crate::core::color::{ColorEncoding, ColorEncodingTrait, LINEAR};
use crate::core::geometry::{Bounds2f, Point2f, Point2fi, Point2i}; use crate::core::geometry::{Bounds2f, Point2f, Point2fi, Point2i};
use crate::utils::math::{f16_to_f32_software, lerp, square}; use crate::utils::math::{f16_to_f32_software, lerp, square};
use crate::{gvec_with_capacity, Float, GVec}; use crate::{gvec_with_capacity, Float, GVec};
use anyhow::{Result, bail};
use core::hash; use core::hash;
use core::ops::{Deref, DerefMut}; use core::ops::{Deref, DerefMut};
use num_traits::Float as NumFloat; use num_traits::Float as NumFloat;
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum WrapMode { pub enum WrapMode {
Black, Black,
@ -14,6 +16,18 @@ pub enum WrapMode {
OctahedralSphere, OctahedralSphere,
} }
impl WrapMode {
pub fn parse(name: &str) -> Result<WrapMode> {
match name {
"clamp" => Ok(WrapMode::Clamp),
"black" => Ok(WrapMode::Black),
"repeat" => Ok(WrapMode::Repeat),
"octahedralsphere" => Ok(WrapMode::OctahedralSphere),
_ => bail!("{:?}: wrap mode unknown", name)
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WrapMode2D { pub struct WrapMode2D {
pub uv: [WrapMode; 2], pub uv: [WrapMode; 2],
@ -212,6 +226,7 @@ pub struct ImageBase {
pub n_channels: i32, pub n_channels: i32,
} }
#[repr(C)]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Image { pub struct Image {
pub format: PixelFormat, pub format: PixelFormat,

View file

@ -561,6 +561,8 @@ impl SurfaceInteraction {
self.shading.n = ns; self.shading.n = ns;
if orientation { if orientation {
self.common.n = self.n().face_forward(self.shading.n); self.common.n = self.n().face_forward(self.shading.n);
} else {
self.shading.n = self.shading.n.face_forward(self.common.n);
} }
self.shading.dpdu = dpdus; self.shading.dpdu = dpdus;
self.shading.dpdv = dpdvs; self.shading.dpdv = dpdvs;

View file

@ -11,7 +11,7 @@ use crate::utils::containers::SampledGrid;
use crate::utils::math::{clamp, square}; use crate::utils::math::{clamp, square};
use crate::utils::rng::Rng; use crate::utils::rng::Rng;
use crate::utils::transform::Transform; use crate::utils::transform::Transform;
use crate::{gvec_with_capacity, GVec, Ptr}; use crate::{gvec_with_capacity, leak, GVec, Ptr};
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use num_traits::Float as NumFloat; use num_traits::Float as NumFloat;
@ -170,7 +170,7 @@ pub struct RayMajorantSegment {
} }
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Debug)]
pub enum RayMajorantIterator { pub enum RayMajorantIterator {
Homogeneous(HomogeneousMajorantIterator), Homogeneous(HomogeneousMajorantIterator),
DDA(DDAMajorantIterator), DDA(DDAMajorantIterator),
@ -191,7 +191,7 @@ impl Iterator for RayMajorantIterator {
} }
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Debug)]
pub struct HomogeneousMajorantIterator { pub struct HomogeneousMajorantIterator {
called: bool, called: bool,
seg: RayMajorantSegment, seg: RayMajorantSegment,
@ -224,7 +224,7 @@ impl Iterator for HomogeneousMajorantIterator {
} }
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone)]
pub struct DDAMajorantIterator { pub struct DDAMajorantIterator {
sigma_t: SampledSpectrum, sigma_t: SampledSpectrum,
t_min: Float, t_min: Float,
@ -727,33 +727,13 @@ impl Default for MediumInterface {
} }
} }
impl From<Medium> for MediumInterface {
fn from(medium: Medium) -> Self {
Self {
inside: Ptr::from(&medium),
outside: Ptr::from(&medium),
}
}
}
impl From<&Medium> for MediumInterface {
fn from(medium: &Medium) -> Self {
Self::from(medium.clone())
}
}
impl MediumInterface { impl MediumInterface {
pub fn new(inside: &Medium, outside: &Medium) -> Self { pub fn new(inside: Ptr<Medium>, outside: Ptr<Medium>) -> Self {
Self { Self { inside, outside }
inside: Ptr::from(inside),
outside: Ptr::from(outside),
}
} }
pub fn empty() -> Self { pub fn empty() -> Self {
Self::default() Self::default()
} }
pub fn is_medium_transition(&self) -> bool { pub fn is_medium_transition(&self) -> bool {
self.inside != self.outside self.inside != self.outside
} }

View file

@ -1,6 +1,6 @@
use crate::core::geometry::Lerp; use crate::core::geometry::Lerp;
use core::ops::{Add, Mul}; use core::ops::{Add, Mul};
use num_traits::{Num, PrimInt}; use num_traits::{Float as NumFloat, Num, NumCast, PrimInt};
use crate::core::light::LightTrait; use crate::core::light::LightTrait;
use crate::core::shape::Shape; use crate::core::shape::Shape;
@ -104,9 +104,16 @@ pub const PI_OVER_2: Float = core::f32::consts::FRAC_PI_2;
pub const PI_OVER_4: Float = core::f32::consts::FRAC_PI_4; pub const PI_OVER_4: Float = core::f32::consts::FRAC_PI_4;
pub const SQRT_2: Float = core::f32::consts::SQRT_2; pub const SQRT_2: Float = core::f32::consts::SQRT_2;
#[inline]
pub fn gamma_t<T: NumFloat + NumCast>(n: i32) -> T {
let n = T::from(n).unwrap();
let eps = T::epsilon() / (T::one() + T::one());
n * eps / (T::one() - n * eps)
}
#[inline] #[inline]
pub fn gamma(n: i32) -> Float { pub fn gamma(n: i32) -> Float {
n as Float * MACHINE_EPSILON / (1. - n as Float * MACHINE_EPSILON) gamma_t::<Float>(n)
} }
#[cfg(feature = "cpu_debug")] #[cfg(feature = "cpu_debug")]

View file

@ -5,8 +5,7 @@ use crate::utils::math::{
clamp, encode_morton_2, inverse_radical_inverse, lerp, log2_int, clamp, encode_morton_2, inverse_radical_inverse, lerp, log2_int,
owen_scrambled_radical_inverse, permutation_element, radical_inverse, round_up_pow2, owen_scrambled_radical_inverse, permutation_element, radical_inverse, round_up_pow2,
scrambled_radical_inverse, sobol_interval_to_index, sobol_sample, BinaryPermuteScrambler, scrambled_radical_inverse, sobol_interval_to_index, sobol_sample, BinaryPermuteScrambler,
DigitPermutation, FastOwenScrambler, NoRandomizer, OwenScrambler, Scrambler, DigitPermutation, FastOwenScrambler, NoRandomizer, OwenScrambler, Scrambler, PRIME_TABLE_SIZE,
PRIME_TABLE_SIZE,
}; };
use crate::utils::rng::Rng; use crate::utils::rng::Rng;
use crate::utils::sobol::N_SOBOL_DIMENSIONS; use crate::utils::sobol::N_SOBOL_DIMENSIONS;
@ -193,9 +192,10 @@ impl SamplerTrait for HaltonSampler {
} }
fn get1d(&mut self) -> Float { fn get1d(&mut self) -> Float {
if self.dim >= PRIME_TABLE_SIZE as u32 { if self.dim + 1 >= PRIME_TABLE_SIZE as u32 {
self.dim = 2; self.dim = 2;
} }
self.dim += 1;
self.sample_dimension(self.dim) self.sample_dimension(self.dim)
} }
@ -211,7 +211,7 @@ impl SamplerTrait for HaltonSampler {
fn get_pixel2d(&mut self) -> Point2f { fn get_pixel2d(&mut self) -> Point2f {
Point2f::new( Point2f::new(
radical_inverse(0, self.halton_index >> self.base_exponents[0]), radical_inverse(0, self.halton_index >> self.base_exponents[0]),
radical_inverse(1, self.halton_index >> self.base_exponents[1]), radical_inverse(1, self.halton_index / self.base_scales[1]),
) )
} }
} }

View file

@ -9,8 +9,8 @@ use crate::core::shape::{
use crate::utils::splines::{ use crate::utils::splines::{
bound_cubic_bezier, cubic_bezier_control_points, evaluate_cubic_bezier, subdivide_cubic_bezier, bound_cubic_bezier, cubic_bezier_control_points, evaluate_cubic_bezier, subdivide_cubic_bezier,
}; };
use crate::utils::transform::{Transform, look_at}; use crate::utils::transform::{look_at, Transform};
use crate::{Float, PI, gamma}; use crate::{gamma, Float, PI};
use crate::core::geometry::{SqrtExt, Tuple}; use crate::core::geometry::{SqrtExt, Tuple};
use crate::utils::interval::Interval; use crate::utils::interval::Interval;
@ -61,11 +61,10 @@ impl CylinderShape {
let di = self let di = self
.object_from_render .object_from_render
.apply_to_vector_interval(&Vector3fi::new_from_vector(r.d)); .apply_to_vector_interval(&Vector3fi::new_from_vector(r.d));
// Solve quadratic equation to find cylinder t0 and t1 values>> // Solve quadratic equation to find cylinder t0 and t1 values
let a: Interval = square(di.x()) + square(di.y()) + square(di.z()); let a: Interval = square(di.x()) + square(di.y());
let b: Interval = 2. * (di.x() * oi.x() + di.y() * oi.y() + di.z() * oi.z()); let b: Interval = 2. * (di.x() * oi.x() + di.y() * oi.y());
let c: Interval = let c: Interval = square(oi.x()) + square(oi.y()) - square(Interval::new(self.radius));
square(oi.x()) + square(oi.y()) + square(oi.z()) - square(Interval::new(self.radius));
let f = b / (2. * a); let f = b / (2. * a);
let vx: Interval = oi.x() - f * di.x(); let vx: Interval = oi.x() - f * di.x();
let vy: Interval = oi.y() - f * di.y(); let vy: Interval = oi.y() - f * di.y();

View file

@ -6,9 +6,10 @@ use crate::core::interaction::{Interaction, InteractionTrait, SurfaceInteraction
use crate::core::shape::{ use crate::core::shape::{
QuadricIntersection, ShapeIntersection, ShapeSample, ShapeSampleContext, ShapeTrait, QuadricIntersection, ShapeIntersection, ShapeSample, ShapeSampleContext, ShapeTrait,
}; };
use crate::utils::Transform; use crate::utils::interval::Interval;
use crate::utils::math::square; use crate::utils::math::square;
use crate::utils::sampling::sample_uniform_disk_concentric; use crate::utils::sampling::sample_uniform_disk_concentric;
use crate::utils::Transform;
use crate::{Float, PI}; use crate::{Float, PI};
use num_traits::Float as NumFloat; use num_traits::Float as NumFloat;
@ -48,25 +49,30 @@ impl DiskShape {
} }
fn basic_intersect(&self, r: &Ray, t_max: Float) -> Option<QuadricIntersection> { fn basic_intersect(&self, r: &Ray, t_max: Float) -> Option<QuadricIntersection> {
let oi = self.object_from_render.apply_to_point(r.o); let oi = self
let di = self.object_from_render.apply_to_vector(r.d); .object_from_render
// Reject disk intersections for rays parallel to the disks plane .apply_to_interval(&Point3fi::new_from_point(r.o));
if di.z() == 0. { let di = self
.object_from_render
.apply_to_vector_interval(&Vector3fi::new_from_vector(r.d));
if Float::from(di.z()) == 0. {
return None;
}
let t_shape_hit: Interval = (self.height - oi.z()) / di.z();
if t_shape_hit.high <= 0. || t_shape_hit.low >= t_max {
return None; return None;
} }
let t_shape_hit = (self.height - oi.z()) / di.z(); let oi_f = Point3f::from(oi);
if t_shape_hit == 0. || t_shape_hit >= t_max { let di_f = Vector3f::from(di);
return None; let t = Float::from(t_shape_hit);
} let p_hit: Point3f = oi_f + di_f * t;
// See if hit point is inside disk radii and phi_max
let p_hit: Point3f = oi + t_shape_hit * di;
let dist2 = square(p_hit.x()) + square(p_hit.y()); let dist2 = square(p_hit.x()) + square(p_hit.y());
if dist2 > square(self.radius) || dist2 < square(self.inner_radius) { if dist2 > square(self.radius) || dist2 < square(self.inner_radius) {
return None; return None;
} }
let mut phi = p_hit.y().atan2(p_hit.x()); let mut phi = p_hit.y().atan2(p_hit.x());
if phi < 0. { if phi < 0. {
phi += 2. * PI; phi += 2. * PI;
@ -76,7 +82,7 @@ impl DiskShape {
} }
Some(QuadricIntersection { Some(QuadricIntersection {
t_hit: t_shape_hit, t_hit: t,
p_obj: p_hit, p_obj: p_hit,
phi, phi,
}) })
@ -105,7 +111,7 @@ impl DiskShape {
let p_error = Vector3f::zero(); let p_error = Vector3f::zero();
let flip_normal = self.reverse_orientation ^ self.transform_swap_handedness; let flip_normal = self.reverse_orientation ^ self.transform_swap_handedness;
let wo_object = self.object_from_render.apply_to_vector(wo); let wo_object = self.object_from_render.apply_to_vector(wo);
SurfaceInteraction::new( let intr = SurfaceInteraction::new(
Point3fi::new_with_error(p_hit, p_error), Point3fi::new_with_error(p_hit, p_error),
Point2f::new(u, v), Point2f::new(u, v),
wo_object, wo_object,
@ -115,7 +121,15 @@ impl DiskShape {
dndv, dndv,
time, time,
flip_normal, flip_normal,
) );
match self
.render_from_object
.apply_to_interaction(&Interaction::Surface(intr))
{
Interaction::Surface(si) => si,
_ => unreachable!("Only surfaces need apply"),
}
} }
} }
@ -197,7 +211,7 @@ impl ShapeTrait for DiskShape {
} }
wi = wi.normalize(); wi = wi.normalize();
ss.pdf = Vector3f::from(ss.intr.n()).dot(-wi).abs() / ctx.p().distance_squared(ss.intr.p()); ss.pdf /= Vector3f::from(ss.intr.n()).abs_dot(-wi) / ctx.p().distance_squared(ss.intr.p());
if ss.pdf.is_infinite() { if ss.pdf.is_infinite() {
return None; return None;
} }

View file

@ -1,17 +1,17 @@
use crate::core::geometry::{spherical_direction, Frame, SqrtExt};
use crate::core::geometry::{ use crate::core::geometry::{
Bounds3f, DirectionCone, Normal3f, Point2f, Point3f, Point3fi, Ray, Vector2f, Vector3f, Bounds3f, DirectionCone, Normal3f, Point2f, Point3f, Point3fi, Ray, Vector2f, Vector3f,
Vector3fi, VectorLike, Vector3fi, VectorLike,
}; };
use crate::core::geometry::{Frame, SqrtExt, spherical_direction};
use crate::core::interaction::{Interaction, InteractionTrait, SurfaceInteraction}; use crate::core::interaction::{Interaction, InteractionTrait, SurfaceInteraction};
use crate::core::pbrt::gamma; use crate::core::pbrt::gamma;
use crate::core::shape::{ use crate::core::shape::{
QuadricIntersection, ShapeIntersection, ShapeSample, ShapeSampleContext, ShapeTrait, QuadricIntersection, ShapeIntersection, ShapeSample, ShapeSampleContext, ShapeTrait,
}; };
use crate::utils::Transform;
use crate::utils::interval::Interval; use crate::utils::interval::Interval;
use crate::utils::math::{clamp, difference_of_products, radians, safe_acos, safe_sqrt, square}; use crate::utils::math::{clamp, difference_of_products, radians, safe_acos, safe_sqrt, square};
use crate::utils::sampling::sample_uniform_sphere; use crate::utils::sampling::sample_uniform_sphere;
use crate::utils::Transform;
use crate::{Float, PI}; use crate::{Float, PI};
use num_traits::Float as NumFloat; use num_traits::Float as NumFloat;
@ -86,7 +86,7 @@ impl SphereShape {
let c: Interval = let c: Interval =
square(oi.x()) + square(oi.y()) + square(oi.z()) - square(Interval::new(self.radius)); square(oi.x()) + square(oi.y()) + square(oi.z()) - square(Interval::new(self.radius));
let v: Vector3fi = (oi - b / Vector3fi::from((2. * a) * di)).into(); let v: Vector3fi = (oi - b / 2. * a * di).into();
let length: Interval = v.norm(); let length: Interval = v.norm();
let discrim = let discrim =
4. * a * (Interval::new(self.radius) + length) * (Interval::new(self.radius) - length); 4. * a * (Interval::new(self.radius) + length) * (Interval::new(self.radius) - length);
@ -108,7 +108,7 @@ impl SphereShape {
mem::swap(&mut t0, &mut t1); mem::swap(&mut t0, &mut t1);
} }
if t0.high > t_max || t1.low < 0. { if t0.high >= t_max || t1.low <= 0. {
return None; return None;
} }
let mut t_shape_hit = t0; let mut t_shape_hit = t0;
@ -120,6 +120,9 @@ impl SphereShape {
} }
let mut p_hit = Point3f::from(oi) + Float::from(t_shape_hit) * Vector3f::from(di); let mut p_hit = Point3f::from(oi) + Float::from(t_shape_hit) * Vector3f::from(di);
let scale = self.radius / p_hit.distance(Point3f::new(0., 0., 0.));
p_hit = Point3f::from(Vector3f::from(p_hit) * scale);
if p_hit.x() == 0. && p_hit.y() == 0. { if p_hit.x() == 0. && p_hit.y() == 0. {
p_hit[0] = 1e-5 * self.radius; p_hit[0] = 1e-5 * self.radius;
} }

View file

@ -81,7 +81,7 @@ impl TriangleShape {
fn get_uvs(&self) -> Option<[Point2f; 3]> { fn get_uvs(&self) -> Option<[Point2f; 3]> {
let mesh = self.mesh(); let mesh = self.mesh();
if mesh.s.is_empty() { if mesh.uv.is_empty() {
return None; return None;
} }
let [v0, v1, v2] = self.get_vertex_indices(); let [v0, v1, v2] = self.get_vertex_indices();

View file

@ -1,5 +1,5 @@
use crate::Float;
use crate::core::color::{RGB, XYZ}; use crate::core::color::{RGB, XYZ};
use crate::core::image::Image;
use crate::core::spectrum::SpectrumTrait; use crate::core::spectrum::SpectrumTrait;
use crate::core::texture::{SpectrumType, TextureEvalContext, TextureMapping2D}; use crate::core::texture::{SpectrumType, TextureEvalContext, TextureMapping2D};
use crate::spectra::{ use crate::spectra::{
@ -7,6 +7,7 @@ use crate::spectra::{
SampledWavelengths, SampledWavelengths,
}; };
use crate::utils::Ptr; use crate::utils::Ptr;
use crate::Float;
/* GPU heavy code, dont know if this will ever work the way Im doing things. /* GPU heavy code, dont know if this will ever work the way Im doing things.
* Leaving it here isolated, for careful handling */ * Leaving it here isolated, for careful handling */
@ -15,6 +16,7 @@ use crate::utils::Ptr;
pub struct GPUSpectrumImageTexture { pub struct GPUSpectrumImageTexture {
pub mapping: TextureMapping2D, pub mapping: TextureMapping2D,
pub tex_obj: u64, pub tex_obj: u64,
pub image: Ptr<Image>,
pub scale: Float, pub scale: Float,
pub invert: bool, pub invert: bool,
pub is_single_channel: bool, pub is_single_channel: bool,

View file

@ -6,7 +6,6 @@ pub mod fbm;
pub mod image; pub mod image;
pub mod marble; pub mod marble;
pub mod mix; pub mod mix;
pub mod ptex;
pub mod scaled; pub mod scaled;
pub mod windy; pub mod windy;
pub mod wrinkled; pub mod wrinkled;
@ -19,7 +18,6 @@ pub use fbm::*;
pub use image::*; pub use image::*;
pub use marble::*; pub use marble::*;
pub use mix::*; pub use mix::*;
pub use ptex::*;
pub use scaled::*; pub use scaled::*;
pub use windy::*; pub use windy::*;
pub use wrinkled::*; pub use wrinkled::*;

View file

@ -1,51 +0,0 @@
use crate::Float;
use crate::core::color::{ColorEncoding, RGB};
use crate::core::spectrum::{SpectrumTrait, StandardSpectra};
use crate::core::texture::{SpectrumType, TextureEvalContext};
use crate::spectra::{
DeviceStandardColorSpaces, RGBAlbedoSpectrum, RGBColorSpace, RGBIlluminantSpectrum,
RGBUnboundedSpectrum, SampledSpectrum, SampledWavelengths,
};
use crate::utils::Ptr;
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct GPUFloatPtexTexture {
pub face_values: *const Float,
}
impl GPUFloatPtexTexture {
pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
unsafe { *self.face_values.add(ctx.face_index as usize) }
}
}
#[repr(C)]
#[derive(Clone, Debug, Copy)]
pub struct GPUSpectrumPtexTexture {
pub face_values: Ptr<RGB>,
pub n_faces: u32,
pub spectrum_type: SpectrumType,
pub colorspaces: DeviceStandardColorSpaces,
}
impl GPUSpectrumPtexTexture {
pub fn evaluate(
&self,
ctx: &TextureEvalContext,
lambda: &SampledWavelengths,
) -> SampledSpectrum {
let index = (ctx.face_index as u32).clamp(0, self.n_faces.saturating_sub(1));
let rgb = unsafe { &*self.face_values.add(index as usize) };
let s_rgb = self.colorspaces.srgb;
match self.spectrum_type {
SpectrumType::Unbounded => RGBUnboundedSpectrum::new(&s_rgb, *rgb).sample(lambda),
SpectrumType::Albedo => {
let clamped_rgb = rgb.clamp(0.0, 1.0);
RGBAlbedoSpectrum::new(&s_rgb, clamped_rgb).sample(lambda)
}
SpectrumType::Illuminant => RGBIlluminantSpectrum::new(&s_rgb, *rgb).sample(lambda),
}
}
}

View file

@ -71,15 +71,16 @@ pub fn evaluate_polynomial(t: Float, coeffs: &[Float]) -> Float {
result result
} }
pub fn difference_of_products<T>(a: Float, b: T, c: Float, d: T) -> T pub fn difference_of_products<T, U, V>(a: T, b: U, c: T, d: U) -> V
where where
T: Copy + Neg<Output = T> + Mul<Float, Output = T> + Add<Output = T>, T: Copy + Neg<Output = T>,
T: MulAdd<Float, T, Output = T>, U: Copy + MulAdd<T, V, Output = V>,
V: Copy + Zero + Neg<Output = V> + Add<Output = V>,
{ {
let cd = d * c; let cd: V = d.mul_add(c, V::zero());
let diff = b.mul_add(a, -cd); let diff: V = b.mul_add(a, -cd);
let error = d.mul_add(-c, cd); let err: V = d.mul_add(-c, cd);
diff + error diff + err
} }
#[inline] #[inline]

View file

@ -94,46 +94,44 @@ impl<T: NumFloat + Sum + Product> Default for TransformGeneric<T> {
} }
} }
pub type Transform = TransformGeneric<Float>; impl<T: NumFloat> TransformGeneric<T> {
#[inline]
impl TransformGeneric<Float> { pub fn apply_to_point(&self, p: Point<T, 3>) -> Point<T, 3> {
pub fn apply_to_point(&self, p: Point3f) -> Point3f { let x = self.m[0][0]*p.x() + self.m[0][1]*p.y() + self.m[0][2]*p.z() + self.m[0][3];
let x = p.x(); let y = self.m[1][0]*p.x() + self.m[1][1]*p.y() + self.m[1][2]*p.z() + self.m[1][3];
let y = p.y(); let z = self.m[2][0]*p.x() + self.m[2][1]*p.y() + self.m[2][2]*p.z() + self.m[2][3];
let z = p.z(); let w = self.m[3][0]*p.x() + self.m[3][1]*p.y() + self.m[3][2]*p.z() + self.m[3][3];
let xp = self.m[0][0] * x + self.m[0][1] * y + self.m[0][2] * z + self.m[0][3]; if w == T::one() {
let yp = self.m[1][0] * x + self.m[1][1] * y + self.m[1][2] * z + self.m[1][3]; Point([x, y, z])
let zp = self.m[2][0] * x + self.m[2][1] * y + self.m[2][2] * z + self.m[2][3];
let wp = self.m[3][0] * x + self.m[3][1] * y + self.m[3][2] * z + self.m[3][3];
if wp == 1. {
Point3f::new(xp, yp, zp)
} else { } else {
Point3f::new(xp / wp, yp / wp, zp / wp) Point([x / w, y / w, z / w])
} }
} }
pub fn apply_to_vector(&self, v: Vector3f) -> Vector3f { #[inline]
let x = v.x(); pub fn apply_to_vector(&self, v: Vector<T, 3>) -> Vector<T, 3> {
let y = v.y(); // directions: linear part only, no translation, no w-divide
let z = v.z(); Vector([
let xv = self.m[0][0] * x + self.m[0][1] * y + self.m[0][2] * z; self.m[0][0]*v.x() + self.m[0][1]*v.y() + self.m[0][2]*v.z(),
let yv = self.m[1][0] * x + self.m[1][1] * y + self.m[1][2] * z; self.m[1][0]*v.x() + self.m[1][1]*v.y() + self.m[1][2]*v.z(),
let zv = self.m[2][0] * x + self.m[2][1] * y + self.m[2][2] * z; self.m[2][0]*v.x() + self.m[2][1]*v.y() + self.m[2][2]*v.z(),
Vector3f::new(xv, yv, zv) ])
} }
pub fn apply_to_normal(&self, p: Normal3f) -> Normal3f { #[inline]
let x = p.x(); pub fn apply_to_normal(&self, n: Normal<T, 3>) -> Normal<T, 3> {
let y = p.y(); // normals: inverse-transpose, no translation, no w-divide
let z = p.z(); Normal([
let xn = self.m_inv[0][0] * x + self.m_inv[1][1] * y + self.m_inv[2][0] * z; self.m_inv[0][0]*n.x() + self.m_inv[1][0]*n.y() + self.m_inv[2][0]*n.z(),
let yn = self.m_inv[0][1] * x + self.m_inv[1][1] * y + self.m_inv[2][1] * z; self.m_inv[0][1]*n.x() + self.m_inv[1][1]*n.y() + self.m_inv[2][1]*n.z(),
let zn = self.m_inv[0][2] * x + self.m_inv[1][2] * y + self.m_inv[2][2] * z; self.m_inv[0][2]*n.x() + self.m_inv[1][2]*n.y() + self.m_inv[2][2]*n.z(),
Normal3f::new(xn, yn, zn) ])
} }
}
pub type Transform = TransformGeneric<Float>;
impl Transform {
pub fn apply_to_bounds(&self, b: Bounds3f) -> Bounds3f { pub fn apply_to_bounds(&self, b: Bounds3f) -> Bounds3f {
let mut new_bounds = Bounds3f::default(); let mut new_bounds = Bounds3f::default();
for i in 0..8 { for i in 0..8 {
@ -669,60 +667,48 @@ impl Mul<TransformGeneric<Float>> for Float {
} }
} }
impl<T> Mul<Point<T, 3>> for TransformGeneric<T> impl<T: NumFloat> Mul<Point<T, 3>> for TransformGeneric<T> {
where
T: NumFloat,
{
type Output = Point<T, 3>; type Output = Point<T, 3>;
fn mul(self, p: Point<T, 3>) -> Self::Output { #[inline]
let xp = self.m[0][0] * p.x() + self.m[0][1] * p.y() + self.m[0][2] * p.z() + self.m[0][3]; fn mul(self, p: Point<T, 3>) -> Point<T, 3> {
let yp = self.m[1][0] * p.x() + self.m[1][1] * p.y() + self.m[1][2] * p.z() + self.m[1][3]; self.apply_to_point(p)
let zp = self.m[2][0] * p.x() + self.m[2][1] * p.y() + self.m[2][2] * p.z() + self.m[2][3];
let wp = self.m[3][0] * p.x() + self.m[3][1] * p.y() + self.m[3][2] * p.z() + self.m[3][3];
if wp == T::one() {
Point([xp, yp, zp])
} else {
Point([xp / wp, yp / wp, zp / wp])
}
} }
} }
impl<T: NumFloat> Mul<Vector<T, 3>> for TransformGeneric<T> {
impl<T> Mul<Vector<T, 3>> for TransformGeneric<T>
where
T: NumFloat,
{
type Output = Vector<T, 3>; type Output = Vector<T, 3>;
fn mul(self, p: Vector<T, 3>) -> Self::Output { #[inline]
let xp = self.m[0][0] * p.x() + self.m[0][1] * p.y() + self.m[0][2] * p.z() + self.m[0][3]; fn mul(self, v: Vector<T, 3>) -> Vector<T, 3> {
let yp = self.m[1][0] * p.x() + self.m[1][1] * p.y() + self.m[1][2] * p.z() + self.m[1][3]; self.apply_to_vector(v)
let zp = self.m[2][0] * p.x() + self.m[2][1] * p.y() + self.m[2][2] * p.z() + self.m[2][3]; }
let wp = self.m[3][0] * p.x() + self.m[3][1] * p.y() + self.m[3][2] * p.z() + self.m[3][3]; }
impl<T: NumFloat> Mul<Normal<T, 3>> for TransformGeneric<T> {
if wp == T::one() { type Output = Normal<T, 3>;
Vector([xp, yp, zp]) #[inline]
} else { fn mul(self, n: Normal<T, 3>) -> Normal<T, 3> {
Vector([xp / wp, yp / wp, zp / wp]) self.apply_to_normal(n)
}
} }
} }
impl<T> Mul<Normal<T, 3>> for TransformGeneric<T> // reference versions, so `&t * v` works without consuming the transform
where impl<T: NumFloat> Mul<Point<T, 3>> for &TransformGeneric<T> {
T: NumFloat, type Output = Point<T, 3>;
{ #[inline]
fn mul(self, p: Point<T, 3>) -> Point<T, 3> {
self.apply_to_point(p)
}
}
impl<T: NumFloat> Mul<Vector<T, 3>> for &TransformGeneric<T> {
type Output = Vector<T, 3>;
#[inline]
fn mul(self, v: Vector<T, 3>) -> Vector<T, 3> {
self.apply_to_vector(v)
}
}
impl<T: NumFloat> Mul<Normal<T, 3>> for &TransformGeneric<T> {
type Output = Normal<T, 3>; type Output = Normal<T, 3>;
fn mul(self, p: Normal<T, 3>) -> Self::Output { #[inline]
let xp = self.m[0][0] * p.x() + self.m[0][1] * p.y() + self.m[0][2] * p.z() + self.m[0][3]; fn mul(self, n: Normal<T, 3>) -> Normal<T, 3> {
let yp = self.m[1][0] * p.x() + self.m[1][1] * p.y() + self.m[1][2] * p.z() + self.m[1][3]; self.apply_to_normal(n)
let zp = self.m[2][0] * p.x() + self.m[2][1] * p.y() + self.m[2][2] * p.z() + self.m[2][3];
let wp = self.m[3][0] * p.x() + self.m[3][1] * p.y() + self.m[3][2] * p.z() + self.m[3][3];
if wp == T::one() {
Normal([xp, yp, zp])
} else {
Normal([xp / wp, yp / wp, zp / wp])
}
} }
} }

View file

@ -1,4 +1,5 @@
use crate::core::bxdf::BxDFFlags; use crate::core::bxdf::BxDFFlags;
use crate::core::film::VisibleSurface;
use crate::core::geometry::{ use crate::core::geometry::{
Normal3f, Point2f, Point2i, Point3f, Point3fi, Ray, RayDifferential, Vector3f, Normal3f, Point2f, Point2i, Point3f, Point3fi, Ray, RayDifferential, Vector3f,
}; };
@ -15,22 +16,13 @@ use crate::{Float, Ptr};
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct PixelSampleState { pub struct PixelSampleState {
pub filter_weight: SoABuffer<Float>, pub pixel: SoABuffer<Point2i>,
pub p_film: SoABuffer<Point2f>,
pub l: SoABuffer<SampledSpectrum>, pub l: SoABuffer<SampledSpectrum>,
pub lambda: SoABuffer<SampledWavelengths>, pub lambda: SoABuffer<SampledWavelengths>,
pub r_u: SoABuffer<SampledSpectrum>, pub filter_weight: SoABuffer<Float>,
pub r_l: SoABuffer<SampledSpectrum>,
pub prev_intr_ctx: SoABuffer<LightSampleContext>,
pub beta: SoABuffer<SampledSpectrum>,
pub depth: SoABuffer<u32>,
pub specular_bounce: SoABuffer<u8>,
pub any_non_specular_bounces: SoABuffer<u8>,
pub eta_scale: SoABuffer<Float>,
pub camera_ray_weight: SoABuffer<SampledSpectrum>, pub camera_ray_weight: SoABuffer<SampledSpectrum>,
pub visible_surface_idx: SoABuffer<u32>, pub visible_surface: SoABuffer<VisibleSurface>,
pub samples: SoABuffer<RaySamples>, pub samples: SoABuffer<RaySamples>,
pub p_pixel: SoABuffer<Point2i>,
} }
impl SoA for PixelSampleState { impl SoA for PixelSampleState {
@ -38,22 +30,13 @@ impl SoA for PixelSampleState {
fn allocate(n: u32, alloc: &dyn SoAAllocator) -> Self { fn allocate(n: u32, alloc: &dyn SoAAllocator) -> Self {
Self { Self {
filter_weight: alloc_soa_buffer(n, alloc), pixel: alloc_soa_buffer(n, alloc),
p_film: alloc_soa_buffer(n, alloc),
l: alloc_soa_buffer(n, alloc), l: alloc_soa_buffer(n, alloc),
lambda: alloc_soa_buffer(n, alloc), lambda: alloc_soa_buffer(n, alloc),
r_u: alloc_soa_buffer(n, alloc), filter_weight: alloc_soa_buffer(n, alloc),
r_l: alloc_soa_buffer(n, alloc),
prev_intr_ctx: alloc_soa_buffer(n, alloc),
beta: alloc_soa_buffer(n, alloc),
depth: alloc_soa_buffer(n, alloc),
specular_bounce: alloc_soa_buffer(n, alloc),
any_non_specular_bounces: alloc_soa_buffer(n, alloc),
eta_scale: alloc_soa_buffer(n, alloc),
camera_ray_weight: alloc_soa_buffer(n, alloc), camera_ray_weight: alloc_soa_buffer(n, alloc),
visible_surface_idx: alloc_soa_buffer(n, alloc), visible_surface: alloc_soa_buffer(n, alloc),
samples: alloc_soa_buffer(n, alloc), samples: alloc_soa_buffer(n, alloc),
p_pixel: alloc_soa_buffer(n, alloc),
} }
} }
@ -73,8 +56,8 @@ pub struct RayWorkItem {
pub r_l: SampledSpectrum, pub r_l: SampledSpectrum,
pub prev_intr_ctx: LightSampleContext, pub prev_intr_ctx: LightSampleContext,
pub eta_scale: Float, pub eta_scale: Float,
pub specular_bounce: u8, pub specular_bounce: bool,
pub any_non_specular_bounces: u8, pub any_non_specular_bounces: bool,
} }
#[repr(C)] #[repr(C)]
@ -89,8 +72,8 @@ pub struct RayWorkItemSoA {
pub r_l: SoABuffer<SampledSpectrum>, pub r_l: SoABuffer<SampledSpectrum>,
pub prev_intr_ctx: SoABuffer<LightSampleContext>, pub prev_intr_ctx: SoABuffer<LightSampleContext>,
pub eta_scale: SoABuffer<Float>, pub eta_scale: SoABuffer<Float>,
pub specular_bounce: SoABuffer<u8>, pub specular_bounce: SoABuffer<bool>,
pub any_non_specular_bounces: SoABuffer<u8>, pub any_non_specular_bounces: SoABuffer<bool>,
} }
impl SoA for RayWorkItemSoA { impl SoA for RayWorkItemSoA {
@ -332,14 +315,13 @@ pub struct MaterialEvalWorkItem {
pub lambda: SampledWavelengths, pub lambda: SampledWavelengths,
pub beta: SampledSpectrum, pub beta: SampledSpectrum,
pub r_u: SampledSpectrum, pub r_u: SampledSpectrum,
// pub r_l: SampledSpectrum,
pub any_non_specular_bounces: bool, pub any_non_specular_bounces: bool,
pub depth: u32, pub depth: u32,
pub eta_scale: Float, pub eta_scale: Float,
pub dpdus: Vector3f , pub dpdus: Vector3f,
pub dpdvs: Vector3f, pub dpdvs: Vector3f,
pub dndus: Normal3f, pub dndus: Normal3f,
pub dndvs: Normal3f pub dndvs: Normal3f,
} }
#[repr(C)] #[repr(C)]
@ -361,15 +343,13 @@ pub struct MaterialEvalWorkItemSoA {
pub lambda: SoABuffer<SampledWavelengths>, pub lambda: SoABuffer<SampledWavelengths>,
pub beta: SoABuffer<SampledSpectrum>, pub beta: SoABuffer<SampledSpectrum>,
pub r_u: SoABuffer<SampledSpectrum>, pub r_u: SoABuffer<SampledSpectrum>,
// pub r_l: SoABuffer<SampledSpectrum>,
pub any_non_specular_bounces: SoABuffer<u8>, pub any_non_specular_bounces: SoABuffer<u8>,
pub depth: SoABuffer<u32>, pub depth: SoABuffer<u32>,
pub eta_scale: SoABuffer<Float>, pub eta_scale: SoABuffer<Float>,
pub dpdus: SoABuffer<Vector3f>, pub dpdus: SoABuffer<Vector3f>,
pub dpdvs: SoABuffer<Vector3f>, pub dpdvs: SoABuffer<Vector3f>,
pub dndus: SoABuffer<Normal3f>, pub dndus: SoABuffer<Normal3f>,
pub dndvs: SoABuffer<Normal3f> pub dndvs: SoABuffer<Normal3f>,
} }
impl SoA for MaterialEvalWorkItemSoA { impl SoA for MaterialEvalWorkItemSoA {
@ -393,7 +373,6 @@ impl SoA for MaterialEvalWorkItemSoA {
lambda: alloc_soa_buffer(n, alloc), lambda: alloc_soa_buffer(n, alloc),
beta: alloc_soa_buffer(n, alloc), beta: alloc_soa_buffer(n, alloc),
r_u: alloc_soa_buffer(n, alloc), r_u: alloc_soa_buffer(n, alloc),
// r_l: alloc_soa_buffer(n, alloc),
any_non_specular_bounces: alloc_soa_buffer(n, alloc), any_non_specular_bounces: alloc_soa_buffer(n, alloc),
depth: alloc_soa_buffer(n, alloc), depth: alloc_soa_buffer(n, alloc),
eta_scale: alloc_soa_buffer(n, alloc), eta_scale: alloc_soa_buffer(n, alloc),
@ -422,7 +401,6 @@ impl SoA for MaterialEvalWorkItemSoA {
lambda: self.lambda.get(i), lambda: self.lambda.get(i),
beta: self.beta.get(i), beta: self.beta.get(i),
r_u: self.r_u.get(i), r_u: self.r_u.get(i),
// r_l: self.r_l.get(i),
any_non_specular_bounces: self.any_non_specular_bounces.get(i) != 0, any_non_specular_bounces: self.any_non_specular_bounces.get(i) != 0,
depth: self.depth.get(i), depth: self.depth.get(i),
eta_scale: self.eta_scale.get(i), eta_scale: self.eta_scale.get(i),
@ -430,7 +408,6 @@ impl SoA for MaterialEvalWorkItemSoA {
dpdvs: self.dpdvs.get(i), dpdvs: self.dpdvs.get(i),
dndus: self.dndus.get(i), dndus: self.dndus.get(i),
dndvs: self.dndvs.get(i), dndvs: self.dndvs.get(i),
} }
} }
@ -451,7 +428,6 @@ impl SoA for MaterialEvalWorkItemSoA {
self.lambda.set(i, v.lambda); self.lambda.set(i, v.lambda);
self.beta.set(i, v.beta); self.beta.set(i, v.beta);
self.r_u.set(i, v.r_u); self.r_u.set(i, v.r_u);
// self.r_l.set(i, v.r_l);
self.any_non_specular_bounces self.any_non_specular_bounces
.set(i, v.any_non_specular_bounces as u8); .set(i, v.any_non_specular_bounces as u8);
self.depth.set(i, v.depth); self.depth.set(i, v.depth);

View file

@ -15,7 +15,7 @@ use shared::spectra::{
cie::SWATCHES_RAW, DenselySampledSpectrum, PiecewiseLinearSpectrum, RGBColorSpace, cie::SWATCHES_RAW, DenselySampledSpectrum, PiecewiseLinearSpectrum, RGBColorSpace,
}; };
use shared::utils::math::{linear_least_squares, SquareMatrix}; use shared::utils::math::{linear_least_squares, SquareMatrix};
use shared::{Float, Ptr, leak}; use shared::{leak, Float, Ptr};
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, LazyLock}; use std::sync::{Arc, LazyLock};
@ -100,7 +100,7 @@ impl CreatePixelSensor for PixelSensor {
output_colorspace.as_ref(), output_colorspace.as_ref(),
sensor_illum.as_deref(), sensor_illum.as_deref(),
imaging_ratio, imaging_ratio,
arena arena,
)) ))
} else { } else {
let r_opt = get_named_spectrum(&format!("{}_r", sensor_name)); let r_opt = get_named_spectrum(&format!("{}_r", sensor_name));
@ -129,7 +129,7 @@ impl CreatePixelSensor for PixelSensor {
.expect("Sensor must have illuminant"), .expect("Sensor must have illuminant"),
), ),
imaging_ratio, imaging_ratio,
arena arena,
)) ))
} }
} }
@ -205,7 +205,7 @@ impl CreatePixelSensor for PixelSensor {
output_colorspace: &RGBColorSpace, output_colorspace: &RGBColorSpace,
sensor_illum: Option<&Spectrum>, sensor_illum: Option<&Spectrum>,
imaging_ratio: Float, imaging_ratio: Float,
arena: &Arena arena: &Arena,
) -> Self { ) -> Self {
let spectra = get_spectra_context(); let spectra = get_spectra_context();
let r_bar = CIE_X_DATA.clone(); let r_bar = CIE_X_DATA.clone();
@ -229,7 +229,6 @@ impl CreatePixelSensor for PixelSensor {
imaging_ratio, imaging_ratio,
} }
} }
} }
pub trait CreateFilmBase { pub trait CreateFilmBase {

View file

@ -3,16 +3,16 @@ use crate::core::texture::{
CreateFloatTexture, CreateSpectrumTexture, FloatTexture, FloatTextureTrait, SpectrumTexture, CreateFloatTexture, CreateSpectrumTexture, FloatTexture, FloatTextureTrait, SpectrumTexture,
SpectrumTextureTrait, SpectrumTextureTrait,
}; };
use crate::utils::mipmap::{MIPMap, MIPMapFilterOptions}; use crate::utils::mipmap::{FilterFunction, MIPMap, MIPMapFilterOptions};
use crate::utils::{FileLoc, TextureParameterDictionary}; use crate::utils::{FileLoc, TextureParameterDictionary, resolve_filename};
use crate::Arena; use crate::{Arena};
use anyhow::Result; use anyhow::Result;
use shared::core::color::RGB; use shared::core::color::RGB;
use shared::core::color::{ColorEncoding, SRGBEncoding}; use shared::core::color::{ColorEncoding, SRGBEncoding};
use shared::core::geometry::Vector2f; use shared::core::geometry::Vector2f;
use shared::core::image::WrapMode; use shared::core::image::WrapMode;
use shared::core::spectrum::SpectrumTrait; use shared::core::spectrum::SpectrumTrait;
use shared::core::texture::{SpectrumType, TextureEvalContext, TextureMapping2D}; use shared::core::texture::{SpectrumType, TexCoord2D, TextureEvalContext, TextureMapping2D};
use shared::spectra::{ use shared::spectra::{
RGBAlbedoSpectrum, RGBIlluminantSpectrum, RGBUnboundedSpectrum, SampledSpectrum, RGBAlbedoSpectrum, RGBIlluminantSpectrum, RGBUnboundedSpectrum, SampledSpectrum,
SampledWavelengths, SampledWavelengths,
@ -168,6 +168,7 @@ impl CreateSpectrumTexture for SpectrumImageTexture {
let filter_options = MIPMapFilterOptions::default(); let filter_options = MIPMapFilterOptions::default();
let wrap_str = parameters.get_one_string("wrap", "repeat")?; let wrap_str = parameters.get_one_string("wrap", "repeat")?;
// let wrap_mode = WrapMode::parse(wrap_str)?;
let wrap_mode = match wrap_str.as_str() { let wrap_mode = match wrap_str.as_str() {
"repeat" => WrapMode::Repeat, "repeat" => WrapMode::Repeat,
"clamp" => WrapMode::Clamp, "clamp" => WrapMode::Clamp,
@ -222,18 +223,65 @@ impl FloatImageTexture {
} }
impl FloatTextureTrait for FloatImageTexture { impl FloatTextureTrait for FloatImageTexture {
fn evaluate(&self, _ctx: &TextureEvalContext) -> Float { fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
todo!() let mut c: TexCoord2D = self.base.mapping.map(ctx);
c.st[1] = 1. - c.st[1];
let v: Float = self.base.scale
* self.base.mipmap.filter::<Float>(
c.st,
Vector2f::new(c.dsdx, c.dtdx),
Vector2f::new(c.dsdy, c.dtdy),
);
if self.base.invert {
(1. - v).max(0.)
} else {
v
}
} }
} }
impl CreateFloatTexture for FloatImageTexture { impl CreateFloatTexture for FloatImageTexture {
fn create( fn create(
_render_from_texture: Transform, render_from_texture: Transform,
_parameters: TextureParameterDictionary, parameters: TextureParameterDictionary,
_loc: FileLoc, loc: FileLoc,
_arena: &Arena, _arena: &Arena,
) -> Result<FloatTexture> { ) -> Result<FloatTexture> {
todo!() let mapping = TextureMapping2D::create(&parameters, &render_from_texture, &loc)?;
let max_aniso = parameters.get_one_float("maxanisotropy", 8.)?;
let filter = parameters.get_one_string("filter", "bilinear")?;
let mut filter_options = MIPMapFilterOptions::default();
filter_options.max_anisotropy = max_aniso;
let ff = FilterFunction::parse(&filter)?;
filter_options.filter = ff;
let wrap_string = parameters.get_one_string("wrap", "repeat")?;
let wrap_mode = WrapMode::parse(&wrap_string)?;
let scale = parameters.get_one_float("scale", 1.)?;
let invert = parameters.get_one_bool("invert", false)?;
let filename = resolve_filename(&parameters.get_one_string("filename", "")?);
let default_encoding = if Path::new(&filename)
.extension()
.map_or(false, |ext| ext == "png")
{
"sRGB"
} else {
"linear"
};
let encoding_str = parameters.get_one_string("encoding", default_encoding)?;
let encoding = ColorEncoding::from_name(&encoding_str)?;
let tex = FloatImageTexture::new(
mapping,
filename,
filter_options,
wrap_mode,
scale,
invert,
encoding,
);
Ok(FloatTexture::Image(tex))
} }
} }

View file

@ -3,6 +3,7 @@ use shared::core::color::{ColorEncoding, RGB};
use shared::core::geometry::{Point2f, Point2i, Vector2f, VectorLike}; use shared::core::geometry::{Point2f, Point2i, Vector2f, VectorLike};
use shared::core::image::{WrapMode, WrapMode2D}; use shared::core::image::{WrapMode, WrapMode2D};
use shared::spectra::RGBColorSpace; use shared::spectra::RGBColorSpace;
use anyhow::{bail, Result};
use shared::utils::math::{lerp, safe_sqrt, square}; use shared::utils::math::{lerp, safe_sqrt, square};
use shared::Float; use shared::Float;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
@ -21,6 +22,19 @@ pub enum FilterFunction {
Ewa, Ewa,
} }
impl FilterFunction {
pub fn parse(name: &str) -> Result<FilterFunction> {
match name {
"ewa" | "EWA" => Ok(FilterFunction::Ewa),
"trilinear" => Ok(FilterFunction::Trilinear),
"bilinear" => Ok(FilterFunction::Bilinear),
"point" => Ok(FilterFunction::Point),
_ => bail!("Filter function unknown")
}
}
}
impl std::fmt::Display for FilterFunction { impl std::fmt::Display for FilterFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self { let s = match self {

View file

@ -1,4 +1,5 @@
use crate::globals::get_options; use crate::globals::get_options;
use log::debug;
use rayon::prelude::*; use rayon::prelude::*;
use shared::core::geometry::{Bounds3f, Ray, VectorLike}; use shared::core::geometry::{Bounds3f, Ray, VectorLike};
use shared::core::interaction::{InteractionTrait, SurfaceInteraction}; use shared::core::interaction::{InteractionTrait, SurfaceInteraction};
@ -60,7 +61,7 @@ impl WavefrontAggregate for CpuAggregate {
r_u: r.r_u, r_u: r.r_u,
r_l: r.r_l, r_l: r.r_l,
depth: r.depth, depth: r.depth,
specular_bounce: r.specular_bounce != 0, specular_bounce: r.specular_bounce,
prev_intr_ctx: r.prev_intr_ctx, prev_intr_ctx: r.prev_intr_ctx,
}); });
return; return;
@ -83,14 +84,14 @@ impl WavefrontAggregate for CpuAggregate {
p: intr.p(), p: intr.p(),
n: intr.n(), n: intr.n(),
uv: intr.common.uv, uv: intr.common.uv,
wo: -r.ray.d, wo: intr.wo(),
lambda: r.lambda, lambda: r.lambda,
pixel_index: r.pixel_index, pixel_index: r.pixel_index,
beta: r.beta, beta: r.beta,
r_u: r.r_u, r_u: r.r_u,
r_l: r.r_l, r_l: r.r_l,
depth: r.depth, depth: r.depth,
specular_bounce: r.specular_bounce != 0, specular_bounce: r.specular_bounce,
prev_intr_ctx: r.prev_intr_ctx, prev_intr_ctx: r.prev_intr_ctx,
}); });
} }
@ -103,12 +104,20 @@ impl WavefrontAggregate for CpuAggregate {
universal_eval_mtl_q universal_eval_mtl_q
}; };
if material.is_conductor() {
debug!("shading frame: {:?}", intr.shading.dpdu);
debug!(
"dot product: {:?}",
intr.shading.dpdu.normalize().dot(intr.n().into())
);
}
eval_q.push(MaterialEvalWorkItem { eval_q.push(MaterialEvalWorkItem {
p: intr.pi(), p: intr.pi(),
n: intr.n(), n: intr.n(),
ns: intr.shading.n, ns: intr.shading.n,
dpdu: intr.shading.dpdu, dpdu: intr.dpdu,
dpdv: intr.shading.dpdv, dpdv: intr.dpdv,
uv: intr.common.uv, uv: intr.common.uv,
wo: intr.wo(), wo: intr.wo(),
time: r.ray.time, time: r.ray.time,
@ -120,7 +129,7 @@ impl WavefrontAggregate for CpuAggregate {
lambda: r.lambda, lambda: r.lambda,
beta: r.beta, beta: r.beta,
r_u: r.r_u, r_u: r.r_u,
any_non_specular_bounces: r.any_non_specular_bounces != 0, any_non_specular_bounces: r.any_non_specular_bounces,
depth: r.depth, depth: r.depth,
eta_scale: r.eta_scale, eta_scale: r.eta_scale,
dpdus: intr.shading.dpdu, dpdus: intr.shading.dpdu,

View file

@ -75,8 +75,6 @@ where
let scanlines_per_pass = (max_samples / res_x).max(1); let scanlines_per_pass = (max_samples / res_x).max(1);
let max_queue_size = res_x * scanlines_per_pass; let max_queue_size = res_x * scanlines_per_pass;
eprintln!("wavefront got {} lights", lights.len());
let mut infinite_lights = gvec(); let mut infinite_lights = gvec();
for light in &lights { for light in &lights {
if light.light_type().is_infinite() { if light.light_type().is_infinite() {
@ -84,10 +82,6 @@ where
} }
} }
eprintln!("infinite_lights len = {}", infinite_lights.len());
// for light in
let cpu_aggregate = CpuAggregate::new(*aggregate); let cpu_aggregate = CpuAggregate::new(*aggregate);
CpuWavefrontRenderer(WavefrontPathIntegrator { CpuWavefrontRenderer(WavefrontPathIntegrator {
@ -241,7 +235,7 @@ impl CpuWavefrontRenderer {
pixel_bounds.p_min.x() + (pixel_index as i32 % x_resolution), pixel_bounds.p_min.x() + (pixel_index as i32 % x_resolution),
y0 + (pixel_index as i32 / x_resolution), y0 + (pixel_index as i32 / x_resolution),
); );
pixel_sample_state.p_pixel.set(pixel_index, p_pixel); pixel_sample_state.pixel.set(pixel_index, p_pixel);
// Skipped pixels contribute nothing; their slots are simply never // Skipped pixels contribute nothing; their slots are simply never
// populated, and update_film filters them by the same bounds test. // populated, and update_film filters them by the same bounds test.
@ -263,9 +257,6 @@ impl CpuWavefrontRenderer {
pixel_sample_state pixel_sample_state
.filter_weight .filter_weight
.set(pixel_index, camera_sample.filter_weight); .set(pixel_index, camera_sample.filter_weight);
pixel_sample_state
.p_film
.set(pixel_index, camera_sample.p_film);
let Some(camera_ray) = camera.generate_ray(camera_sample, &lambda) else { let Some(camera_ray) = camera.generate_ray(camera_sample, &lambda) else {
pixel_sample_state pixel_sample_state
@ -288,8 +279,8 @@ impl CpuWavefrontRenderer {
r_l: SampledSpectrum::new(1.0), r_l: SampledSpectrum::new(1.0),
prev_intr_ctx: LightSampleContext::default(), prev_intr_ctx: LightSampleContext::default(),
eta_scale: 1.0, eta_scale: 1.0,
specular_bounce: 0, specular_bounce: false,
any_non_specular_bounces: 0, any_non_specular_bounces: false,
}); });
}); });
} }
@ -318,7 +309,7 @@ impl CpuWavefrontRenderer {
if w.depth == 0 || w.specular_bounce { if w.depth == 0 || w.specular_bounce {
l_contrib += w.beta * le / w.r_u.average(); l_contrib += w.beta * le / w.r_u.average();
} else { } else {
// MIS: combine BSDF and light sampling weights via ratio tracking // Compute MIS-weighted radiance contribution from infinite light
let ctx = w.prev_intr_ctx; let ctx = w.prev_intr_ctx;
let light_choice_pdf = light_sampler.pmf_with_context(&ctx, light); let light_choice_pdf = light_sampler.pmf_with_context(&ctx, light);
let r_l = w.r_l * light_choice_pdf * light.pdf_li(&ctx, w.ray_d, true); let r_l = w.r_l * light_choice_pdf * light.pdf_li(&ctx, w.ray_d, true);
@ -353,13 +344,14 @@ impl CpuWavefrontRenderer {
let l_contrib = if w.depth == 0 || w.specular_bounce { let l_contrib = if w.depth == 0 || w.specular_bounce {
w.beta * le / w.r_u.average() w.beta * le / w.r_u.average()
} else { } else {
let wi = -w.wo;
let ctx = w.prev_intr_ctx; let ctx = w.prev_intr_ctx;
let light_choice_pdf = light_sampler.pmf_with_context(&ctx, light); let light_choice_pdf = light_sampler.pmf_with_context(&ctx, light);
// wi from previous interaction to this light hit // wi from previous interaction to this light hit
let wi = (w.p - Point3f::from(ctx.pi)).normalize();
let light_pdf = light_choice_pdf * light.pdf_li(&ctx, wi, true); let light_pdf = light_choice_pdf * light.pdf_li(&ctx, wi, true);
let r_u = w.r_u;
let r_l = w.r_l * light_pdf; let r_l = w.r_l * light_pdf;
w.beta * le / (w.r_u + r_l).average() w.beta * le / (r_u + r_l).average()
}; };
if !l_contrib.is_black() { if !l_contrib.is_black() {
@ -382,6 +374,9 @@ impl CpuWavefrontRenderer {
} else { } else {
&self.basic_eval_material_queue &self.basic_eval_material_queue
}; };
if self.regularize {
eprintln!("regularize=true");
}
let n = queue.size(); let n = queue.size();
let next = ((depth + 1) % 2) as usize; let next = ((depth + 1) % 2) as usize;
@ -459,8 +454,14 @@ impl CpuWavefrontRenderer {
eta_scale *= square(bs.eta); eta_scale *= square(bs.eta);
} }
if material.is_conductor() {
if bs.is_specular() {
eprintln!("NON SPECULAR");
}
}
let rr_beta = (beta * eta_scale / r_u.average()).max_component_value(); let rr_beta = (beta * eta_scale / r_u.average()).max_component_value();
if rr_beta < 1.0 && w.depth > 1 { if rr_beta < 1.0 && w.depth >= 1 {
let q = (1.0 - rr_beta).max(0.0_f32); let q = (1.0 - rr_beta).max(0.0_f32);
if rs.indirect.rr < q { if rs.indirect.rr < q {
beta = SampledSpectrum::new(0.0); beta = SampledSpectrum::new(0.0);
@ -489,8 +490,8 @@ impl CpuWavefrontRenderer {
r_l, r_l,
prev_intr_ctx: ctx, prev_intr_ctx: ctx,
eta_scale, eta_scale,
specular_bounce: bs.is_specular() as u8, specular_bounce: bs.is_specular(),
any_non_specular_bounces: any_non_specular as u8, any_non_specular_bounces: any_non_specular,
}); });
} }
} }
@ -505,7 +506,7 @@ impl CpuWavefrontRenderer {
}; };
if flags.is_reflective() && !flags.is_transmissive() { if flags.is_reflective() && !flags.is_transmissive() {
light_ctx.pi = Point3fi::new_from_point(Ray::offset_origin(&w.p, &w.n, &(-wo))); light_ctx.pi = Point3fi::new_from_point(Ray::offset_origin(&w.p, &w.n, &wo));
} else if flags.is_transmissive() && flags.is_reflective() { } else if flags.is_transmissive() && flags.is_reflective() {
light_ctx.pi = Point3fi::new_from_point(Ray::offset_origin(&w.p, &w.n, &(-wo))); light_ctx.pi = Point3fi::new_from_point(Ray::offset_origin(&w.p, &w.n, &(-wo)));
} }
@ -573,7 +574,7 @@ impl CpuWavefrontRenderer {
(0..self.max_queue_size as usize) (0..self.max_queue_size as usize)
.into_par_iter() .into_par_iter()
.for_each(|pixel_index| { .for_each(|pixel_index| {
let p_pixel = self.pixel_sample_state.p_pixel.get(pixel_index); let p_pixel = self.pixel_sample_state.pixel.get(pixel_index);
if !pixel_bounds.contains_exclusive(p_pixel) { if !pixel_bounds.contains_exclusive(p_pixel) {
return; return;
} }
@ -600,7 +601,7 @@ impl CpuWavefrontRenderer {
let w = unsafe { ray_queue.storage.get(i) }; let w = unsafe { ray_queue.storage.get(i) };
let dimension = 6 + 7 * w.depth; let dimension = 6 + 7 * w.depth;
let pi = w.pixel_index as usize; let pi = w.pixel_index as usize;
let p_pixel = pixel_sample_state.p_pixel.get(pi); let p_pixel = pixel_sample_state.pixel.get(pi);
let mut sampler = sampler_proto.clone(); let mut sampler = sampler_proto.clone();
sampler.start_pixel_sample(p_pixel, sample_index as i32, Some(dimension)); sampler.start_pixel_sample(p_pixel, sample_index as i32, Some(dimension));