Bunch of logic errors in port of math and sampling functions
This commit is contained in:
parent
28bb963268
commit
34ea80c030
17 changed files with 162 additions and 123 deletions
|
|
@ -251,7 +251,7 @@ impl TabulatedBSSRDF {
|
|||
sr += weight
|
||||
* self
|
||||
.table
|
||||
.eval_profile(rho_offset + j as u32, radius_offset + k as u32);
|
||||
.eval_profile((rho_offset + j as i32) as u32, (radius_offset + k as i32) as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -302,14 +302,14 @@ impl TabulatedBSSRDF {
|
|||
for (j, rho_weight) in rho_weights.iter().enumerate() {
|
||||
if *rho_weight != 0. {
|
||||
// Update _rhoEff_ and _sr_ for wavelength
|
||||
rho_eff += rhoeff_samples[rho_offset as usize + j] * rho_weight;
|
||||
rho_eff += rhoeff_samples[(rho_offset + j as i32) as usize] * rho_weight;
|
||||
|
||||
// Fix: Use .iter().enumerate() for 'k'
|
||||
for (k, radius_weight) in radius_weights.iter().enumerate() {
|
||||
if *radius_weight != 0. {
|
||||
sr += self
|
||||
.table
|
||||
.eval_profile(rho_offset + j as u32, radius_offset + k as u32)
|
||||
.eval_profile((rho_offset + j as i32) as u32, (radius_offset + k as i32) as u32)
|
||||
* rho_weight
|
||||
* radius_weight;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -625,7 +625,8 @@ impl RGBSigmoidPolynomial {
|
|||
}
|
||||
|
||||
pub fn evaluate(&self, lambda: Float) -> Float {
|
||||
let eval = evaluate_polynomial(lambda, &[self.c0, self.c1, self.c2]);
|
||||
// pbrt: `s(EvaluatePolynomial(lambda, c2, c1, c0))` -- c2 is the constant term.
|
||||
let eval = evaluate_polynomial(lambda, &[self.c2, self.c1, self.c0]);
|
||||
Self::s(eval)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -192,15 +192,18 @@ impl SamplerTrait for HaltonSampler {
|
|||
}
|
||||
|
||||
fn get1d(&mut self) -> Float {
|
||||
if self.dim + 1 >= PRIME_TABLE_SIZE as u32 {
|
||||
// pbrt: `SampleDimension(dimension++)` -- POST-increment. Pre-incrementing makes
|
||||
// the next Get2D() reuse the dimension this call just consumed.
|
||||
if self.dim >= PRIME_TABLE_SIZE as u32 {
|
||||
self.dim = 2;
|
||||
}
|
||||
let dim = self.dim;
|
||||
self.dim += 1;
|
||||
self.sample_dimension(self.dim)
|
||||
self.sample_dimension(dim)
|
||||
}
|
||||
|
||||
fn get2d(&mut self) -> Point2f {
|
||||
if self.dim > PRIME_TABLE_SIZE as u32 {
|
||||
if self.dim + 1 >= PRIME_TABLE_SIZE as u32 {
|
||||
self.dim = 2;
|
||||
}
|
||||
let dim = self.dim;
|
||||
|
|
@ -301,8 +304,9 @@ impl SamplerTrait for StratifiedSampler {
|
|||
hash as u32,
|
||||
);
|
||||
self.dim += 2;
|
||||
// pbrt: both the modulus and the divisor are xPixelSamples.
|
||||
let x = stratum % self.x_pixel_samples as u32;
|
||||
let y = stratum / self.y_pixel_samples as u32;
|
||||
let y = stratum / self.x_pixel_samples as u32;
|
||||
let dx = if self.jitter {
|
||||
self.rng.uniform::<Float>()
|
||||
} else {
|
||||
|
|
@ -387,6 +391,7 @@ impl SamplerTrait for PaddedSobolSampler {
|
|||
self.samples_per_pixel as u32,
|
||||
hash as u32,
|
||||
);
|
||||
self.dim += 1;
|
||||
self.sample_dimension(0, index, (hash >> 32) as u32)
|
||||
}
|
||||
fn get2d(&mut self) -> Point2f {
|
||||
|
|
@ -512,7 +517,7 @@ impl SamplerTrait for SobolSampler {
|
|||
) as Float;
|
||||
u[1] = clamp(
|
||||
u[1] * self.scale as Float - self.pixel[1] as Float,
|
||||
1.,
|
||||
0.,
|
||||
ONE_MINUS_EPSILON,
|
||||
) as Float;
|
||||
u
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use crate::core::geometry::{spherical_triangle_area, SqrtExt, Tuple, VectorLike};
|
||||
use crate::core::geometry::{
|
||||
Bounds3f, DirectionCone, Normal, Normal3f, Point2f, Point3f, Point3fi, Ray, Vector2f, Vector3,
|
||||
Vector3f,
|
||||
};
|
||||
use crate::core::geometry::{SqrtExt, Tuple, VectorLike, spherical_triangle_area};
|
||||
use crate::core::interaction::{
|
||||
Interaction, InteractionBase, InteractionTrait, SimpleInteraction, SurfaceInteraction,
|
||||
};
|
||||
|
|
@ -13,7 +13,7 @@ use crate::utils::sampling::{
|
|||
bilinear_pdf, invert_spherical_triangle_sample, sample_bilinear, sample_spherical_triangle,
|
||||
sample_uniform_triangle,
|
||||
};
|
||||
use crate::{gamma, Float, GVec, Ptr};
|
||||
use crate::{Float, GVec, Ptr, gamma};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
@ -198,34 +198,34 @@ impl TriangleShape {
|
|||
|
||||
// Ensure that computed triangle $t$ is conservatively greater than zero
|
||||
// Compute $\delta_z$ term for triangle $t$ error bounds
|
||||
let maxZt = Vector3f::new(p0t.z(), p1t.z(), p2t.z())
|
||||
let max_zt = Vector3f::new(p0t.z(), p1t.z(), p2t.z())
|
||||
.abs()
|
||||
.max_component_value();
|
||||
let deltaZ = gamma(3) * maxZt;
|
||||
let delta_z = gamma(3) * max_zt;
|
||||
|
||||
// Compute $\delta_x$ and $\delta_y$ terms for triangle $t$ error bounds
|
||||
let maxXt = Vector3f::new(p0t.x(), p1t.x(), p2t.x())
|
||||
let max_xt = Vector3f::new(p0t.x(), p1t.x(), p2t.x())
|
||||
.abs()
|
||||
.max_component_value();
|
||||
let maxYt = Vector3f::new(p0t.y(), p1t.y(), p2t.y())
|
||||
let max_yt = Vector3f::new(p0t.y(), p1t.y(), p2t.y())
|
||||
.abs()
|
||||
.max_component_value();
|
||||
let deltaX = gamma(5) * (maxXt + maxZt);
|
||||
let deltaY = gamma(5) * (maxYt + maxZt);
|
||||
let delta_x = gamma(5) * (max_xt + max_zt);
|
||||
let delta_y = gamma(5) * (max_yt + max_zt);
|
||||
|
||||
// Compute $\delta_e$ term for triangle $t$ error bounds
|
||||
let deltaE = 2. * (gamma(2) * maxXt * maxYt + deltaY * maxXt + deltaX * maxYt);
|
||||
let delta_e = 2. * (gamma(2) * max_xt * max_yt + delta_y * max_xt + delta_x * max_yt);
|
||||
|
||||
// Compute $\delta_t$ term for triangle $t$ error bounds and check _t_
|
||||
let maxE = Vector3f::new(e0, e1, e2).abs().max_component_value();
|
||||
let deltaT =
|
||||
3. * (gamma(3) * maxE * maxZt + deltaE * maxZt + deltaZ * maxE) * inv_det.abs();
|
||||
if t <= deltaT {
|
||||
let max_e = Vector3f::new(e0, e1, e2).abs().max_component_value();
|
||||
let delta_t =
|
||||
3. * (gamma(3) * max_e * max_zt + delta_e * max_zt + delta_z * max_e) * inv_det.abs();
|
||||
if t <= delta_t {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Return _TriangleIntersection_ for intersection
|
||||
return Some(TriangleIntersection { b0, b1, b2, t });
|
||||
Some(TriangleIntersection { b0, b1, b2, t })
|
||||
}
|
||||
|
||||
fn interaction_from_intersection(
|
||||
|
|
@ -345,11 +345,11 @@ impl TriangleShape {
|
|||
|
||||
let mut ts = ns.cross(ss.into());
|
||||
if ts.norm_squared() > 0.0 {
|
||||
ss = ts.cross(ns.into()).into();
|
||||
ss = ts.cross(ns).into();
|
||||
} else {
|
||||
let (s, t) = ns.coordinate_system();
|
||||
ss = s.into();
|
||||
ts = t.into();
|
||||
ts = t;
|
||||
}
|
||||
|
||||
let (dndu, dndv) = if let Some(normals) = self.get_shading_normals() {
|
||||
|
|
@ -539,7 +539,8 @@ impl ShapeTrait for TriangleShape {
|
|||
|
||||
fn intersect(&self, ray: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection> {
|
||||
let [p0, p1, p2] = self.get_points();
|
||||
let tri_isect = self.intersect_triangle(ray, t_max.unwrap_or(Float::INFINITY), p0, p1, p2)?;
|
||||
let tri_isect =
|
||||
self.intersect_triangle(ray, t_max.unwrap_or(Float::INFINITY), p0, p1, p2)?;
|
||||
let intr = self.interaction_from_intersection(tri_isect, ray.time, -ray.d);
|
||||
Some(ShapeIntersection::new(intr, tri_isect.t))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,11 +61,14 @@ where
|
|||
T::lerp(t, a, b)
|
||||
}
|
||||
|
||||
/// pbrt `EvaluatePolynomial(t, c, cRemaining...) = FMA(t, EvaluatePolynomial(t, cRemaining...), c)`.
|
||||
/// `coeffs[0]` is the *constant* term and `coeffs[n-1]` the highest-degree one, so
|
||||
/// evaluation folds from the back.
|
||||
#[inline]
|
||||
pub fn evaluate_polynomial(t: Float, coeffs: &[Float]) -> Float {
|
||||
assert!(!coeffs.is_empty());
|
||||
let mut result = coeffs[0];
|
||||
for &c in &coeffs[1..] {
|
||||
let mut result = coeffs[coeffs.len() - 1];
|
||||
for &c in coeffs[..coeffs.len() - 1].iter().rev() {
|
||||
result = num_traits::Float::mul_add(t, result, c);
|
||||
}
|
||||
result
|
||||
|
|
@ -96,7 +99,7 @@ where
|
|||
|
||||
#[inline]
|
||||
pub fn safe_sqrt(x: Float) -> Float {
|
||||
assert!(x > -1e-3);
|
||||
debug_assert!(x > -1e-3);
|
||||
0.0_f32.max(x).sqrt()
|
||||
}
|
||||
|
||||
|
|
@ -104,20 +107,14 @@ pub fn safe_sqrt(x: Float) -> Float {
|
|||
pub fn safe_asin<T: NumFloat>(x: T) -> T {
|
||||
let epsilon = T::from(0.0001).unwrap();
|
||||
let one = T::one();
|
||||
if x >= -(one + epsilon) && x <= one + epsilon {
|
||||
clamp(x, -one, one).asin()
|
||||
} else {
|
||||
panic!("Not valid value for asin")
|
||||
}
|
||||
debug_assert!(x >= -(one + epsilon) && x <= one + epsilon);
|
||||
clamp(x, -one, one).asin()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn safe_acos(x: Float) -> Float {
|
||||
if (-1.001..1.001).contains(&x) {
|
||||
clamp(x, -1., 1.).acos()
|
||||
} else {
|
||||
panic!("Not valid value for acos")
|
||||
}
|
||||
debug_assert!((-1.001..1.001).contains(&x));
|
||||
clamp(x, -1., 1.).acos()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -351,9 +348,10 @@ pub fn wrap_equal_area_square(uv: &mut Point2f) -> Point2f {
|
|||
|
||||
pub fn integrate_catmull_rom(nodes: &[Float], f: &[Float], cdf: &mut [Float]) -> Float {
|
||||
debug_assert_eq!(nodes.len(), f.len());
|
||||
debug_assert_eq!(cdf.len(), nodes.len());
|
||||
let mut sum = 0.;
|
||||
cdf[0] = 0.;
|
||||
for i in 0..nodes.len() {
|
||||
for i in 0..nodes.len() - 1 {
|
||||
let x0 = nodes[i];
|
||||
let x1 = nodes[i + 1];
|
||||
let f0 = f[i];
|
||||
|
|
@ -434,7 +432,7 @@ pub fn invert_catmull_rom(nodes: &[Float], f: &[Float], u: Float) -> Float {
|
|||
return x0 + t * width;
|
||||
}
|
||||
|
||||
pub fn catmull_rom_weights(nodes: &[Float], x: Float) -> Option<(u32, [Float; 4])> {
|
||||
pub fn catmull_rom_weights(nodes: &[Float], x: Float) -> Option<(i32, [Float; 4])> {
|
||||
if nodes.len() < 4 {
|
||||
return None;
|
||||
}
|
||||
|
|
@ -452,7 +450,10 @@ pub fn catmull_rom_weights(nodes: &[Float], x: Float) -> Option<(u32, [Float; 4]
|
|||
// Safety clamp (though bounds check above handles most cases)
|
||||
let idx = idx.min(nodes.len() - 2);
|
||||
|
||||
let offset = idx.saturating_sub(1); // The C++ code uses idx - 1 for the offset
|
||||
// pbrt: `*offset = idx - 1`, which is legitimately -1 when idx == 0. weights[0] is
|
||||
// then 0, so callers never dereference the out-of-range slot -- but the remaining
|
||||
// three weights must still line up with nodes[idx-1+i].
|
||||
let offset = idx as i32 - 1;
|
||||
let x0 = nodes[idx];
|
||||
let x1 = nodes[idx + 1];
|
||||
|
||||
|
|
@ -491,7 +492,7 @@ pub fn catmull_rom_weights(nodes: &[Float], x: Float) -> Option<(u32, [Float; 4]
|
|||
weights[3] = 0.0;
|
||||
}
|
||||
|
||||
Some((offset as u32, weights))
|
||||
Some((offset, weights))
|
||||
}
|
||||
|
||||
pub fn equal_area_sphere_to_square(d: Vector3f) -> Point2f {
|
||||
|
|
@ -546,7 +547,7 @@ pub fn equal_area_square_to_sphere(p: Point2f) -> Vector3f {
|
|||
|
||||
// Compute angle \phi for square to sphere mapping
|
||||
let mut phi = if r == 0. { 1. } else { (vp - up) / r + 1. };
|
||||
phi /= PI_OVER_4;
|
||||
phi *= PI_OVER_4;
|
||||
|
||||
// Find z for spherical direction
|
||||
let z = (1. - square(r)).copysign(signed_distance);
|
||||
|
|
@ -585,12 +586,13 @@ pub fn gaussian_integral(x0: Float, x1: Float, mu: Float, sigma: Float) -> Float
|
|||
0.5 * (erf((mu - x0) / sigma_root2) - erf((mu - x1) / sigma_root2))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sample_linear(u: Float, a: Float, b: Float) -> Float {
|
||||
assert!(a >= 0. && b >= 0.);
|
||||
if u == 0. && a == 0. {
|
||||
return 0.;
|
||||
}
|
||||
let x = u * (a + b) / (a + (lerp(u, square(a), square(b))));
|
||||
let x = u * (a + b) / (a + lerp(u, square(a), square(b)).sqrt());
|
||||
x.min(ONE_MINUS_EPSILON)
|
||||
}
|
||||
|
||||
|
|
@ -700,12 +702,13 @@ pub fn sample_discrete(
|
|||
}
|
||||
|
||||
pub fn sample_tent(u: Float, r: Float) -> Float {
|
||||
// pbrt rewrites `u` in place with the remapped sample and feeds *that* to SampleLinear.
|
||||
let mut u_remapped = 0.0;
|
||||
let offset = sample_discrete(&[0.5, 0.5], u, None, Some(&mut u_remapped));
|
||||
if offset == 0 {
|
||||
-r + r * sample_linear(u, 0., 1.)
|
||||
-r + r * sample_linear(u_remapped, 0., 1.)
|
||||
} else {
|
||||
r * sample_linear(u, 1., 0.)
|
||||
r * sample_linear(u_remapped, 1., 0.)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -735,7 +738,7 @@ pub fn encode_morton_2(x: u32, y: u32) -> u64 {
|
|||
}
|
||||
|
||||
pub fn encode_morton_3(x: Float, y: Float, z: Float) -> u32 {
|
||||
(left_shift3(x as u32) << 2) | (left_shift3(y as u32) << 1) | left_shift3(z as u32)
|
||||
(left_shift3(z as u32) << 2) | (left_shift3(y as u32) << 1) | left_shift3(x as u32)
|
||||
}
|
||||
|
||||
pub fn round_up_pow2(mut n: i32) -> i32 {
|
||||
|
|
@ -1590,7 +1593,7 @@ where
|
|||
}
|
||||
|
||||
let det: T = (0..N).map(|i| lum[i][i]).product();
|
||||
if parity < 0 { -det } else { det }
|
||||
if parity % 2 == 1 { -det } else { det }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1723,7 +1726,9 @@ pub fn f16_to_f32_software(h: u16) -> f32 {
|
|||
m <<= 1;
|
||||
e += 1;
|
||||
}
|
||||
(sign << 31) | ((112 - e) << 23) | ((m & 0x3FF) << 13)
|
||||
// half subnormals have exponent -14; after `e` normalising shifts the
|
||||
// f32 biased exponent is (-14 - e) + 127 = 113 - e.
|
||||
(sign << 31) | ((113 - e) << 23) | ((m & 0x3FF) << 13)
|
||||
}
|
||||
} else if exp == 0x1F {
|
||||
(sign << 31) | (0xFF << 23) | (mant << 13)
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ pub fn invert_linear_sample(x: Float, a: Float, b: Float) -> Float {
|
|||
|
||||
pub fn sample_bilinear(u: Point2f, w: &[Float]) -> Point2f {
|
||||
let y = sample_linear(u[1], w[0] + w[1], w[2] + w[3]);
|
||||
let x = sample_linear(u[1], lerp(y, w[0], w[2]), lerp(y, w[1], w[3]));
|
||||
let x = sample_linear(u[0], lerp(y, w[0], w[2]), lerp(y, w[1], w[3]));
|
||||
Point2f::new(x, y)
|
||||
}
|
||||
|
||||
|
|
@ -333,7 +333,8 @@ pub fn invert_spherical_rectangle_sample(
|
|||
}
|
||||
|
||||
pub fn sample_exponential(u: Float, a: Float) -> Float {
|
||||
(1. - u).ln() / a
|
||||
debug_assert!(a > 0.);
|
||||
-(1. - u).ln() / a
|
||||
}
|
||||
|
||||
pub fn sample_spherical_triangle(
|
||||
|
|
@ -550,7 +551,9 @@ pub fn sample_catmull_rom_2d(
|
|||
let mut v = 0.;
|
||||
for i in 0..4 {
|
||||
if weights[i] != 0. {
|
||||
let ind = (offset + i as u32) * n2 + idx;
|
||||
// `offset` is pbrt's `idx - 1` and may be -1; the matching weight is
|
||||
// then zero, so the slot is never actually read.
|
||||
let ind = (offset + i as i32) * n2 as i32 + idx as i32;
|
||||
v += array[ind as usize] * weights[i];
|
||||
}
|
||||
}
|
||||
|
|
@ -658,10 +661,10 @@ pub struct VarianceEstimator {
|
|||
impl VarianceEstimator {
|
||||
pub fn add(&mut self, x: Float) {
|
||||
self.n += 1;
|
||||
let delta = x / self.mean;
|
||||
let delta = x - self.mean;
|
||||
self.mean += delta / self.n as Float;
|
||||
let delta2 = x - self.mean;
|
||||
self.s *= delta * delta2;
|
||||
self.s += delta * delta2;
|
||||
}
|
||||
|
||||
pub fn mean(&self) -> Float {
|
||||
|
|
@ -688,7 +691,7 @@ impl VarianceEstimator {
|
|||
if ve.n != 0 {
|
||||
self.s += ve.s
|
||||
+ square(ve.mean - self.mean) * (self.n * ve.n) as Float / (self.n + ve.n) as Float;
|
||||
self.mean +=
|
||||
self.mean =
|
||||
(self.n as Float * self.mean + ve.n as Float * ve.mean) / (self.n + ve.n) as Float;
|
||||
self.n += ve.n;
|
||||
}
|
||||
|
|
@ -719,24 +722,37 @@ impl PiecewiseConstant1D {
|
|||
}
|
||||
|
||||
pub fn new_with_bounds(f: &[Float], min: Float, max: Float) -> Self {
|
||||
debug_assert!(max > min);
|
||||
let n = f.len();
|
||||
|
||||
// pbrt takes the absolute value of the function before integrating, so filters
|
||||
// with negative lobes (Mitchell, LanczosSinc) still give a usable CDF.
|
||||
let mut func: GVec<Float> = gvec_with_capacity(n);
|
||||
for &v in f {
|
||||
func.push(v.abs());
|
||||
}
|
||||
|
||||
let mut cdf = gvec_with_capacity(n + 1);
|
||||
cdf.push(0.0);
|
||||
|
||||
let delta = (max - min) / n as Float;
|
||||
for i in 0..n {
|
||||
cdf.push(cdf[i] + f[i] * delta);
|
||||
cdf.push(cdf[i] + func[i] * delta);
|
||||
}
|
||||
|
||||
let func_integral = cdf[n];
|
||||
if func_integral > 0.0 {
|
||||
for c in &mut cdf {
|
||||
if func_integral == 0.0 {
|
||||
for (i, c) in cdf.iter_mut().enumerate().skip(1) {
|
||||
*c = i as Float / n as Float;
|
||||
}
|
||||
} else {
|
||||
for c in cdf.iter_mut().skip(1) {
|
||||
*c /= func_integral;
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
func: gvec_from_slice(f),
|
||||
func,
|
||||
cdf,
|
||||
min,
|
||||
max,
|
||||
|
|
@ -748,7 +764,6 @@ impl PiecewiseConstant1D {
|
|||
where
|
||||
F: Fn(Float) -> Float,
|
||||
{
|
||||
let delta = (max - min) / n as Float;
|
||||
let delta = (max - min) / n as Float;
|
||||
let mut values = gvec_with_capacity(n);
|
||||
for i in 0..n {
|
||||
|
|
@ -816,6 +831,7 @@ impl PiecewiseConstant1D {
|
|||
pub struct PiecewiseConstant2D {
|
||||
pub conditionals: GVec<PiecewiseConstant1D>,
|
||||
pub marginal: PiecewiseConstant1D,
|
||||
pub domain: Bounds2f,
|
||||
pub n_u: u32,
|
||||
pub n_v: u32,
|
||||
}
|
||||
|
|
@ -852,6 +868,7 @@ impl PiecewiseConstant2D {
|
|||
Self {
|
||||
conditionals,
|
||||
marginal,
|
||||
domain,
|
||||
n_u: n_u.try_into().unwrap(),
|
||||
n_v: n_v.try_into().unwrap(),
|
||||
}
|
||||
|
|
@ -876,7 +893,10 @@ impl PiecewiseConstant2D {
|
|||
self.marginal.integral()
|
||||
}
|
||||
|
||||
pub fn pdf(&self, p: Point2f) -> Float {
|
||||
pub fn pdf(&self, pr: Point2f) -> Float {
|
||||
// pbrt maps the query point into the unit domain first; the distribution's
|
||||
// domain is only [0,1]^2 for image distributions, not e.g. filter samplers.
|
||||
let p = Point2f::from(self.domain.offset(&pr));
|
||||
let u_offset = ((p.x() * self.n_u as Float) as usize).min(self.n_u as usize - 1);
|
||||
let v_offset = ((p.y() * self.n_v as Float) as usize).min(self.n_v as usize - 1);
|
||||
let conditional = unsafe { &*self.conditionals.as_ptr().add(v_offset) };
|
||||
|
|
|
|||
|
|
@ -81,16 +81,15 @@ impl MaterialFactory for Material {
|
|||
SubsurfaceMaterial::create(parameters, normal_map, named_materials, &loc, arena)
|
||||
}
|
||||
"mix" => MixMaterial::create(parameters, normal_map, named_materials, &loc, arena),
|
||||
|
||||
_ => Err(anyhow!("Material type '{}' unknown at {}", name, &loc)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_diffuse_material(arena: &Arena) -> Material {
|
||||
use shared::core::texture::SpectrumTexture;
|
||||
use shared::core::texture::SpectrumConstantTexture;
|
||||
use shared::core::spectrum::{ConstantSpectrum, Spectrum};
|
||||
use shared::core::texture::SpectrumConstantTexture;
|
||||
use shared::core::texture::SpectrumTexture;
|
||||
use shared::materials::DiffuseMaterial;
|
||||
use shared::utils::Ptr;
|
||||
|
||||
|
|
|
|||
|
|
@ -24,10 +24,7 @@ impl CreateMaterial for CoatedDiffuseMaterial {
|
|||
arena: &Arena,
|
||||
) -> Result<Material> {
|
||||
let reflectance = parameters
|
||||
.get_spectrum_texture("reflectance", None, SpectrumType::Albedo)
|
||||
.unwrap_or(Arc::new(SpectrumTexture::Constant(
|
||||
SpectrumConstantTexture::new(Spectrum::Constant(ConstantSpectrum::new(0.5))),
|
||||
)));
|
||||
.get_spectrum_texture("reflectance", 0.5, SpectrumType::Albedo);
|
||||
|
||||
let u_roughness =
|
||||
parameters.get_float_texture_with_fallback("uroughness", "roughness", 0.5)?;
|
||||
|
|
@ -48,11 +45,7 @@ impl CreateMaterial for CoatedDiffuseMaterial {
|
|||
let n_samples = parameters.get_one_int("nsamples", 1)?;
|
||||
let g = parameters.get_float_texture("g", 0.)?;
|
||||
let albedo = parameters
|
||||
.get_spectrum_texture("albedo", None, SpectrumType::Albedo)
|
||||
.unwrap_or_else(|| {
|
||||
let default_spectrum = Spectrum::Constant(ConstantSpectrum::new(0.));
|
||||
SpectrumTexture::Constant(SpectrumConstantTexture::new(default_spectrum)).into()
|
||||
});
|
||||
.get_spectrum_texture("albedo", 0., SpectrumType::Albedo);
|
||||
let displacement = parameters.get_float_texture("displacement", 0.)?;
|
||||
let remap_roughness = parameters.get_one_bool("remaproughness", true)?;
|
||||
|
||||
|
|
@ -146,11 +139,7 @@ impl CreateMaterial for CoatedConductorMaterial {
|
|||
let n_samples = parameters.get_one_int("nsamples", 1)?;
|
||||
let g = parameters.get_float_texture("g", 0.)?;
|
||||
let albedo = parameters
|
||||
.get_spectrum_texture("albedo", None, SpectrumType::Albedo)
|
||||
.unwrap_or_else(|| {
|
||||
let spectrum = Spectrum::Constant(ConstantSpectrum::new(0.));
|
||||
SpectrumTexture::Constant(SpectrumConstantTexture::new(spectrum)).into()
|
||||
});
|
||||
.get_spectrum_texture("albedo", 0., SpectrumType::Albedo);
|
||||
let displacement = parameters.get_float_texture_or_null("displacement")?;
|
||||
let remap_roughness = parameters.get_one_bool("remaproughness", true)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -240,8 +240,7 @@ impl CreateMaterial for SubsurfaceMaterial {
|
|||
Some(r) => {
|
||||
let one = Spectrum::Constant(ConstantSpectrum::new(1.));
|
||||
let mfp = parameters
|
||||
.get_spectrum_texture("mfp", Some(one), SpectrumType::Unbounded)
|
||||
.expect("default supplied");
|
||||
.get_spectrum_texture("mfp", one, SpectrumType::Unbounded);
|
||||
SubsurfaceScattering::Reflectance {
|
||||
reflectance: arena.upload(r),
|
||||
mfp: arena.upload(mfp),
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ impl CreateMaterial for ThinDielectricMaterial {
|
|||
parameters: &TextureParameterDictionary,
|
||||
normal_map: Option<Arc<HostImage>>,
|
||||
_named_materials: &HashMap<String, Material>,
|
||||
loc: &FileLoc,
|
||||
_loc: &FileLoc,
|
||||
arena: &Arena,
|
||||
) -> Result<Material> {
|
||||
let eta = if let Some(&v) = parameters.get_float_array("eta")?.first() {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use crate::Arena;
|
||||
use crate::core::image::HostImage;
|
||||
use crate::core::material::CreateMaterial;
|
||||
use crate::core::texture::SpectrumTexture;
|
||||
use crate::utils::upload::ArenaUpload;
|
||||
use crate::utils::{FileLoc, TextureParameterDictionary};
|
||||
use crate::Arena;
|
||||
use anyhow::Result;
|
||||
use shared::core::material::Material;
|
||||
use shared::core::spectrum::Spectrum;
|
||||
|
|
@ -22,13 +22,7 @@ impl CreateMaterial for DiffuseMaterial {
|
|||
_loc: &FileLoc,
|
||||
arena: &Arena,
|
||||
) -> Result<Material> {
|
||||
let reflectance = parameters
|
||||
.get_spectrum_texture("reflectance", None, SpectrumType::Albedo)
|
||||
.unwrap_or_else(|| {
|
||||
Arc::new(SpectrumTexture::Constant(
|
||||
SpectrumConstantTexture::new(Spectrum::Constant(ConstantSpectrum::new(0.5))),
|
||||
))
|
||||
});
|
||||
let reflectance = parameters.get_spectrum_texture("reflectance", 0.5, SpectrumType::Albedo);
|
||||
let displacement = parameters.get_float_texture_or_null("displacement")?;
|
||||
|
||||
let specific = DiffuseMaterial {
|
||||
|
|
@ -42,12 +36,26 @@ impl CreateMaterial for DiffuseMaterial {
|
|||
|
||||
impl CreateMaterial for DiffuseTransmissionMaterial {
|
||||
fn create(
|
||||
_parameters: &TextureParameterDictionary,
|
||||
_normal_map: Option<Arc<HostImage>>,
|
||||
parameters: &TextureParameterDictionary,
|
||||
normal_map: Option<Arc<HostImage>>,
|
||||
_named_materials: &HashMap<String, Material>,
|
||||
_loc: &FileLoc,
|
||||
_arena: &Arena,
|
||||
arena: &Arena,
|
||||
) -> Result<Material> {
|
||||
todo!()
|
||||
let reflectance =
|
||||
parameters.get_spectrum_texture("reflectance", 0.25, SpectrumType::Albedo);
|
||||
let transmittance =
|
||||
parameters.get_spectrum_texture("transmittance", 0.25, SpectrumType::Albedo);
|
||||
|
||||
let displacement = parameters.get_float_texture_or_null("displacement")?;
|
||||
let scale = parameters.get_one_float("scale", 1.)?;
|
||||
let mat = DiffuseTransmissionMaterial {
|
||||
normal_map: arena.upload(normal_map),
|
||||
displacement: arena.upload(displacement),
|
||||
reflectance: arena.upload(reflectance),
|
||||
transmittance: arena.upload(transmittance),
|
||||
scale,
|
||||
};
|
||||
Ok(Material::DiffuseTransmission(mat))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
use crate::core::image::HostImage;
|
||||
use crate::core::material::CreateMaterial;
|
||||
use crate::utils::{Arena, FileLoc, TextureParameterDictionary};
|
||||
use crate::{Arena, ArenaUpload, FileLoc, utils::TextureParameterDictionary};
|
||||
use anyhow::Result;
|
||||
use anyhow::anyhow;
|
||||
use shared::Ptr;
|
||||
use shared::core::material::Material;
|
||||
use shared::materials::MixMaterial;
|
||||
use std::collections::HashMap;
|
||||
|
|
@ -9,12 +11,31 @@ use std::sync::Arc;
|
|||
|
||||
impl CreateMaterial for MixMaterial {
|
||||
fn create(
|
||||
_parameters: &TextureParameterDictionary,
|
||||
parameters: &TextureParameterDictionary,
|
||||
_normal_map: Option<Arc<HostImage>>,
|
||||
_named_materials: &HashMap<String, Material>,
|
||||
_loc: &FileLoc,
|
||||
_arena: &Arena,
|
||||
named_materials: &HashMap<String, Material>,
|
||||
loc: &FileLoc,
|
||||
arena: &Arena,
|
||||
) -> Result<Material> {
|
||||
todo!()
|
||||
let names = parameters.get_string_array("materials")?;
|
||||
let [a, b]: [String; 2] = names
|
||||
.try_into()
|
||||
.map_err(|_| anyhow!("{loc}: must provide two values for \"string materials\""))?;
|
||||
|
||||
let mut resolve = |n: &str| -> Result<Ptr<Material>> {
|
||||
let m = named_materials
|
||||
.get(n)
|
||||
.ok_or_else(|| anyhow!("{loc}: {n}: named material not found"))?;
|
||||
Ok(arena.alloc(*m))
|
||||
};
|
||||
|
||||
let materials = [resolve(&a)?, resolve(&b)?];
|
||||
let amount = parameters.get_float_texture("amount", 0.5)?;
|
||||
|
||||
let mix_mat = MixMaterial {
|
||||
materials,
|
||||
amount: arena.upload(amount),
|
||||
};
|
||||
Ok(Material::Mix(mix_mat))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,8 +64,7 @@ impl CreateSpectrumTexture for SpectrumCheckerboardTexture {
|
|||
let tex = |name: &str, def: Spectrum| {
|
||||
arena.upload(
|
||||
parameters
|
||||
.get_spectrum_texture(name, Some(def), spectrum_type)
|
||||
.expect("default supplied"),
|
||||
.get_spectrum_texture(name, def, spectrum_type),
|
||||
)
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -47,8 +47,7 @@ impl CreateSpectrumTexture for SpectrumDotsTexture {
|
|||
|
||||
let get = |name: &str, def: Spectrum| {
|
||||
let t = parameters
|
||||
.get_spectrum_texture(name, Some(def), spectrum_type)
|
||||
.expect("default supplied");
|
||||
.get_spectrum_texture(name, def, spectrum_type);
|
||||
arena.upload(t)
|
||||
};
|
||||
let tex = SpectrumDotsTexture::new(map, get("inside", one), get("outside", zero));
|
||||
|
|
|
|||
|
|
@ -87,8 +87,7 @@ impl CreateSpectrumTexture for SpectrumMixTexture {
|
|||
|
||||
let tex = |name: &str, def: Spectrum| {
|
||||
parameters
|
||||
.get_spectrum_texture(name, Some(def), spectrum_type)
|
||||
.expect("default supplied")
|
||||
.get_spectrum_texture(name, def, spectrum_type)
|
||||
};
|
||||
|
||||
let tex = SpectrumMixTexture {
|
||||
|
|
@ -119,8 +118,7 @@ impl CreateSpectrumTexture for SpectrumDirectionMixTexture {
|
|||
let dir = render_from_texture.apply_to_vector(dir_raw).normalize();
|
||||
let tex = |name: &str, def: Spectrum| {
|
||||
parameters
|
||||
.get_spectrum_texture(name, Some(def), spectrum_type)
|
||||
.expect("default supplied")
|
||||
.get_spectrum_texture(name, def, spectrum_type)
|
||||
};
|
||||
|
||||
let zero = Spectrum::Constant(ConstantSpectrum::new(0.));
|
||||
|
|
|
|||
|
|
@ -66,9 +66,8 @@ impl CreateSpectrumTexture for SpectrumScaledTexture {
|
|||
_arena: &Arena,
|
||||
) -> Result<SpectrumTexture> {
|
||||
let one = Spectrum::Constant(ConstantSpectrum::new(1.0));
|
||||
let tex = parameters
|
||||
.get_spectrum_texture("tex", Some(one), spectrum_type)
|
||||
.ok_or_else(|| anyhow::anyhow!("{}: missing \"tex\" parameter", _loc))?;
|
||||
// pbrt defaults "tex" to 1.0 rather than erroring (textures.cpp).
|
||||
let tex = parameters.get_spectrum_texture("tex", one, spectrum_type);
|
||||
let scale = parameters.get_float_texture("scale", 1.0)?;
|
||||
|
||||
if let FloatTexture::Constant(ref cscale) = *scale {
|
||||
|
|
|
|||
|
|
@ -835,22 +835,18 @@ impl TextureParameterDictionary {
|
|||
self.dict.report_unused()
|
||||
}
|
||||
|
||||
/// Fetch a spectrum texture, falling back to a constant built from `def`.
|
||||
/// A default is always supplied, so this cannot fail -- mirrors
|
||||
/// `get_float_texture`. Use `get_spectrum_texture_or_null` when absence is
|
||||
/// meaningful.
|
||||
pub fn get_spectrum_texture(
|
||||
&self,
|
||||
name: &str,
|
||||
val: Option<Spectrum>,
|
||||
def: impl Into<Spectrum>,
|
||||
stype: SpectrumType,
|
||||
) -> Option<Arc<SpectrumTexture>> {
|
||||
let tex = self.get_spectrum_texture_or_null(name, stype);
|
||||
if tex.is_some() {
|
||||
tex
|
||||
} else if val.is_some() {
|
||||
Some(Arc::new(SpectrumTexture::Constant(
|
||||
SpectrumConstantTexture::new(val.unwrap()),
|
||||
)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
) -> Arc<SpectrumTexture> {
|
||||
self.get_spectrum_texture_or_null(name, stype)
|
||||
.unwrap_or_else(|| Arc::new(SpectrumTexture::from(def.into())))
|
||||
}
|
||||
|
||||
pub fn get_float_texture(&self, name: &str, val: Float) -> Result<Arc<FloatTexture>> {
|
||||
|
|
|
|||
Loading…
Reference in a new issue