Compare commits

..

No commits in common. "a14960562c5984322db3abac318f2e41afb21863" and "050698c1d040806cd93ce650271d6f320c250839" have entirely different histories.

36 changed files with 530 additions and 1123 deletions

View file

@ -1045,7 +1045,7 @@ const SRGB_TO_LINEAR_LUT: [Float; 256] = [
pub const RES: u32 = 64; pub const RES: u32 = 64;
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq)] #[derive(Clone, Copy, Debug, Default)]
pub struct Coeffs { pub struct Coeffs {
pub c0: Float, pub c0: Float,
pub c1: Float, pub c1: Float,
@ -1099,10 +1099,10 @@ impl Mul<Float> for Coeffs {
} }
#[repr(C)] #[repr(C)]
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct RGBToSpectrumTable { pub struct RGBToSpectrumTable {
pub z_nodes: GVec<Float>, pub z_nodes: Ptr<Float>,
pub coeffs: GVec<Coeffs>, pub coeffs: Ptr<Coeffs>,
pub n_nodes: u32, pub n_nodes: u32,
} }
@ -1113,7 +1113,7 @@ impl RGBToSpectrumTable {
#[inline(always)] #[inline(always)]
fn get_coeffs(&self, bucket: u32, z: u32, y: u32, x: u32) -> Coeffs { fn get_coeffs(&self, bucket: u32, z: u32, y: u32, x: u32) -> Coeffs {
let offset = bucket * (RES * RES * RES) + z * (RES * RES) + y * (RES) + x; let offset = bucket * (RES * RES * RES) + z * (RES * RES) + y * (RES) + x;
unsafe { *self.coeffs.as_ptr().add(offset as usize) } unsafe { *self.coeffs.add(offset as usize) }
} }
pub fn evaluate(&self, rgb: RGB) -> RGBSigmoidPolynomial { pub fn evaluate(&self, rgb: RGB) -> RGBSigmoidPolynomial {
@ -1153,7 +1153,7 @@ impl RGBToSpectrumTable {
let x = coord_a / z; let x = coord_a / z;
let y = coord_b / z; let y = coord_b / z;
let z_nodes = &self.z_nodes; let z_nodes = unsafe { core::slice::from_raw_parts(self.z_nodes.as_raw(), RES as usize) };
let zi = find_interval(RES, |i| z_nodes[i as usize] < z) as usize; let zi = find_interval(RES, |i| z_nodes[i as usize] < z) as usize;
let dz = (z - z_nodes[zi]) / (z_nodes[zi + 1] - z_nodes[zi]); let dz = (z - z_nodes[zi]) / (z_nodes[zi + 1] - z_nodes[zi]);
let x_float = x * (RES - 1) as Float; let x_float = x * (RES - 1) as Float;

View file

@ -18,5 +18,5 @@ pub mod textures;
pub mod utils; pub mod utils;
pub use core::pbrt::*; pub use core::pbrt::*;
pub use utils::alloc::{gbox, gvec, gvec_from_slice, gvec_with_capacity, leak, GBox, GVec}; pub use utils::alloc::{gbox, gvec, gvec_from_slice, gvec_with_capacity, GVec, GBox};
pub use utils::{Array2D, PBRTOptions, Ptr, Transform}; pub use utils::{Array2D, PBRTOptions, Ptr, Transform};

View file

@ -40,8 +40,8 @@ impl CoatedDiffuseMaterial {
thickness: Ptr<GPUFloatTexture>, thickness: Ptr<GPUFloatTexture>,
albedo: Ptr<GPUSpectrumTexture>, albedo: Ptr<GPUSpectrumTexture>,
g: Ptr<GPUFloatTexture>, g: Ptr<GPUFloatTexture>,
displacement: Ptr<GPUFloatTexture>,
eta: Ptr<Spectrum>, eta: Ptr<Spectrum>,
displacement: Ptr<GPUFloatTexture>,
normal_map: Ptr<Image>, normal_map: Ptr<Image>,
remap_roughness: bool, remap_roughness: bool,
max_depth: u32, max_depth: u32,
@ -153,6 +153,7 @@ impl MaterialTrait for CoatedDiffuseMaterial {
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct CoatedConductorMaterial { pub struct CoatedConductorMaterial {
normal_map: Ptr<Image>,
displacement: Ptr<GPUFloatTexture>, displacement: Ptr<GPUFloatTexture>,
interface_uroughness: Ptr<GPUFloatTexture>, interface_uroughness: Ptr<GPUFloatTexture>,
interface_vroughness: Ptr<GPUFloatTexture>, interface_vroughness: Ptr<GPUFloatTexture>,
@ -165,7 +166,6 @@ pub struct CoatedConductorMaterial {
conductor_eta: Ptr<GPUSpectrumTexture>, conductor_eta: Ptr<GPUSpectrumTexture>,
k: Ptr<GPUSpectrumTexture>, k: Ptr<GPUSpectrumTexture>,
reflectance: Ptr<GPUSpectrumTexture>, reflectance: Ptr<GPUSpectrumTexture>,
normal_map: Ptr<Image>,
max_depth: u32, max_depth: u32,
n_samples: u32, n_samples: u32,
remap_roughness: bool, remap_roughness: bool,
@ -175,10 +175,12 @@ pub struct CoatedConductorMaterial {
impl CoatedConductorMaterial { impl CoatedConductorMaterial {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
normal_map: Ptr<Image>,
displacement: Ptr<GPUFloatTexture>, displacement: Ptr<GPUFloatTexture>,
interface_uroughness: Ptr<GPUFloatTexture>, interface_uroughness: Ptr<GPUFloatTexture>,
interface_vroughness: Ptr<GPUFloatTexture>, interface_vroughness: Ptr<GPUFloatTexture>,
thickness: Ptr<GPUFloatTexture>, thickness: Ptr<GPUFloatTexture>,
interface_eta: Ptr<Spectrum>,
g: Ptr<GPUFloatTexture>, g: Ptr<GPUFloatTexture>,
albedo: Ptr<GPUSpectrumTexture>, albedo: Ptr<GPUSpectrumTexture>,
conductor_uroughness: Ptr<GPUFloatTexture>, conductor_uroughness: Ptr<GPUFloatTexture>,
@ -186,8 +188,6 @@ impl CoatedConductorMaterial {
conductor_eta: Ptr<GPUSpectrumTexture>, conductor_eta: Ptr<GPUSpectrumTexture>,
k: Ptr<GPUSpectrumTexture>, k: Ptr<GPUSpectrumTexture>,
reflectance: Ptr<GPUSpectrumTexture>, reflectance: Ptr<GPUSpectrumTexture>,
normal_map: Ptr<Image>,
interface_eta: Ptr<Spectrum>,
max_depth: u32, max_depth: u32,
n_samples: u32, n_samples: u32,
remap_roughness: bool, remap_roughness: bool,

View file

@ -62,7 +62,7 @@ impl ColorSpaceId {
} }
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone)]
pub struct RGBColorSpace { pub struct RGBColorSpace {
pub r: Point2f, pub r: Point2f,
pub g: Point2f, pub g: Point2f,
@ -71,7 +71,7 @@ pub struct RGBColorSpace {
pub xyz_from_rgb: SquareMatrix3f, pub xyz_from_rgb: SquareMatrix3f,
pub rgb_from_xyz: SquareMatrix3f, pub rgb_from_xyz: SquareMatrix3f,
pub illuminant: Ptr<DenselySampledSpectrum>, pub illuminant: Ptr<DenselySampledSpectrum>,
pub rgb_to_spectrum_table: Ptr<RGBToSpectrumTable>, pub rgb_to_spectrum_table: RGBToSpectrumTable,
} }
unsafe impl Send for RGBColorSpace {} unsafe impl Send for RGBColorSpace {}

View file

@ -1,9 +1,8 @@
extern crate alloc; extern crate alloc;
use crate::utils::ptr::Ptr;
use alloc::alloc::Global; use alloc::alloc::Global;
use alloc::boxed::Box;
use alloc::vec::Vec; use alloc::vec::Vec;
use alloc::boxed::Box;
use core::alloc::{AllocError, Allocator, Layout}; use core::alloc::{AllocError, Allocator, Layout};
use core::ptr::NonNull; use core::ptr::NonNull;
@ -201,9 +200,3 @@ pub fn gvec_from_slice<T: Clone>(slice: &[T]) -> GVec<T> {
pub fn gbox<T>(value: T) -> GBox<T> { pub fn gbox<T>(value: T) -> GBox<T> {
Box::new_in(value, GpuAlloc::default()) Box::new_in(value, GpuAlloc::default())
} }
pub fn leak<T: 'static>(val: T) -> Ptr<T> {
let b = gbox(val);
let leaked: &'static T = Box::leak(b);
Ptr::from(leaked)
}

View file

@ -827,12 +827,6 @@ impl DigitPermutation {
} }
} }
pub fn compute_radical_inverse_permutations(seed: u64) -> GVec<DigitPermutation> {
let mut result = gvec();
result.extend(PRIMES.iter().map(|&base| DigitPermutation::new(base as i32, seed)));
result
}
pub fn scrambled_radical_inverse(base_index: u32, mut a: u64, perm: &DigitPermutation) -> Float { pub fn scrambled_radical_inverse(base_index: u32, mut a: u64, perm: &DigitPermutation) -> Float {
let base = PRIMES[base_index as usize] as u64; let base = PRIMES[base_index as usize] as u64;

View file

@ -6,7 +6,7 @@ use std::ops::Deref;
use std::path::Path; use std::path::Path;
pub trait CreateRGBToSpectrumTable { pub trait CreateRGBToSpectrumTable {
fn new(z_nodes: &[Float], coeffs: &[Float]) -> Self; fn from_data(z_nodes: &[Float], coeffs: &[Float]) -> Self;
fn load(base_dir: &Path, name: &str) -> Result<Self> where Self: Sized; fn load(base_dir: &Path, name: &str) -> Result<Self> where Self: Sized;
} }

View file

@ -349,20 +349,4 @@ impl HostImage {
pub fn into_image(self) -> Image { pub fn into_image(self) -> Image {
self.inner self.inner
} }
pub fn resolution(&self) -> Point2i {
self.inner.resolution()
}
pub fn n_channels(&self) -> i32 {
self.inner.n_channels()
}
pub fn encoding(&self) -> ColorEncoding {
self.inner.encoding
}
pub fn format(&self) -> PixelFormat {
self.inner.format()
}
} }

View file

@ -102,13 +102,14 @@ pub fn create_light(
arena, arena,
), ),
"diffuse" => Err(anyhow!( "diffuse" => Err(anyhow!(
"{}: \"diffuse\" is an area light. Use create_area_light with a shape", "{}: \"diffuse\" is an area light; use create_area_light with a shape",
loc loc
)), )),
_ => Err(anyhow!("{}: unknown light type \"{}\"", loc, name)), _ => Err(anyhow!("{}: unknown light type \"{}\"", loc, name)),
} }
} }
/// Create a diffuse area light bound to a specific shape
pub fn create_area_light( pub fn create_area_light(
render_from_light: Transform, render_from_light: Transform,
medium: Option<Medium>, medium: Option<Medium>,
@ -118,10 +119,15 @@ pub fn create_area_light(
alpha_tex: &FloatTexture, alpha_tex: &FloatTexture,
colorspace: Option<&RGBColorSpace>, colorspace: Option<&RGBColorSpace>,
arena: &mut Arena, arena: &mut Arena,
) -> Result<Ptr<Light>> { ) -> Result<Light> {
let light = crate::lights::diffuse::create( crate::lights::diffuse::create(
render_from_light, medium, parameters, loc, render_from_light,
shape, alpha_tex, colorspace, arena, medium,
)?; parameters,
Ok(arena.alloc(light)) loc,
shape,
alpha_tex,
colorspace,
arena,
)
} }

View file

@ -1,610 +1,164 @@
use crate::core::spectrum::spectrum_to_photometric; use shared::core::geometry::{Bounds3f, Point3i};
use crate::spectra::SRGB; use shared::core::medium::{GridMedium, HGPhaseFunction, HomogeneousMedium, RGBGridMedium};
use crate::{Arena, FileLoc, ParameterDictionary};
use anyhow::{bail, Result};
use shared::core::geometry::{Bounds3f, Point3f, Point3i};
use shared::core::medium::{
GridMedium, HGPhaseFunction, HomogeneousMedium, MajorantGrid, Medium, RGBGridMedium,
};
use shared::core::spectrum::{Spectrum, SpectrumTrait}; use shared::core::spectrum::{Spectrum, SpectrumTrait};
use shared::core::texture::SpectrumType; use shared::spectra::{RGBIlluminantSpectrum, RGBUnboundedSpectrum, DenselySampledSpectrum};
use shared::spectra::{
ConstantSpectrum, DenselySampledSpectrum, RGBIlluminantSpectrum, RGBUnboundedSpectrum,
};
use shared::utils::containers::SampledGrid; use shared::utils::containers::SampledGrid;
use shared::{Float, Ptr, Transform}; use shared::{Float, Transform, core::medium::MajorantGrid};
struct MeasuredSS { pub trait RGBGridMediumCreator {
name: &'static str, fn new(
sigma_prime_s: [Float; 3], bounds: &Bounds3f,
sigma_a: [Float; 3], render_from_medium: &Transform,
g: Float,
sigma_a_grid: SampledGrid<RGBUnboundedSpectrum>,
sigma_s_grid: SampledGrid<RGBUnboundedSpectrum>,
sigma_scale: Float,
le_grid: SampledGrid<RGBIlluminantSpectrum>,
le_scale: Float,
) -> Self;
} }
// From "A Practical Model for Subsurface Light Transport" impl RGBGridMediumCreator for RGBGridMedium {
// Jensen, Marschner, Levoy, Hanrahan — SIGGRAPH 2001 #[allow(clippy::too_many_arguments)]
// and "Acquiring Scattering Properties of Participating Media by Dilution" fn new(
// Narasimhan, Gupta, Donner, Ramamoorthi, Nayar, Jensen — SIGGRAPH 2006 bounds: &Bounds3f,
static SUBSURFACE_TABLE: &[MeasuredSS] = &[ render_from_medium: &Transform,
MeasuredSS { g: Float,
name: "Apple", sigma_a_grid: SampledGrid<RGBUnboundedSpectrum>,
sigma_prime_s: [2.29, 2.39, 1.97], sigma_s_grid: SampledGrid<RGBUnboundedSpectrum>,
sigma_a: [0.0030, 0.0034, 0.046], sigma_scale: Float,
}, le_grid: SampledGrid<RGBIlluminantSpectrum>,
MeasuredSS { le_scale: Float,
name: "Chicken1", ) -> Self {
sigma_prime_s: [0.15, 0.21, 0.38], let mut majorant_grid = MajorantGrid::new(*bounds, Point3i::new(16, 16, 16));
sigma_a: [0.015, 0.077, 0.19], for z in 0..majorant_grid.res.x() {
}, for y in 0..majorant_grid.res.y() {
MeasuredSS { for x in 0..majorant_grid.res.x() {
name: "Chicken2", let bounds = majorant_grid.voxel_bounds(x, y, z);
sigma_prime_s: [0.19, 0.25, 0.32], let convert = |s: &RGBUnboundedSpectrum| s.max_value();
sigma_a: [0.018, 0.088, 0.20], let max_sigma_t = sigma_a_grid.max_value_convert(bounds, convert)
}, + sigma_s_grid.max_value_convert(bounds, convert);
MeasuredSS { majorant_grid.set(x, y, z, sigma_scale * max_sigma_t);
name: "Cream", }
sigma_prime_s: [7.38, 5.47, 3.15], }
sigma_a: [0.0002, 0.0028, 0.0163], }
},
MeasuredSS {
name: "Ketchup",
sigma_prime_s: [0.18, 0.07, 0.03],
sigma_a: [0.061, 0.97, 1.45],
},
MeasuredSS {
name: "Marble",
sigma_prime_s: [2.19, 2.62, 3.00],
sigma_a: [0.0021, 0.0041, 0.0071],
},
MeasuredSS {
name: "Potato",
sigma_prime_s: [0.68, 0.70, 0.55],
sigma_a: [0.0024, 0.0090, 0.12],
},
MeasuredSS {
name: "Skimmilk",
sigma_prime_s: [0.70, 1.22, 1.90],
sigma_a: [0.0014, 0.0025, 0.0142],
},
MeasuredSS {
name: "Skin1",
sigma_prime_s: [0.74, 0.88, 1.01],
sigma_a: [0.032, 0.17, 0.48],
},
MeasuredSS {
name: "Skin2",
sigma_prime_s: [1.09, 1.59, 1.79],
sigma_a: [0.013, 0.070, 0.145],
},
MeasuredSS {
name: "Spectralon",
sigma_prime_s: [11.6, 20.4, 14.9],
sigma_a: [0.00, 0.00, 0.00],
},
MeasuredSS {
name: "Wholemilk",
sigma_prime_s: [2.55, 3.21, 3.77],
sigma_a: [0.0011, 0.0024, 0.014],
},
MeasuredSS {
name: "Lowfat Milk",
sigma_prime_s: [0.89187, 1.5136, 2.532],
sigma_a: [0.002875, 0.00575, 0.0115],
},
MeasuredSS {
name: "Reduced Milk",
sigma_prime_s: [2.4858, 3.1669, 4.5214],
sigma_a: [0.0025556, 0.0051111, 0.012778],
},
MeasuredSS {
name: "Regular Milk",
sigma_prime_s: [4.5513, 5.8294, 7.136],
sigma_a: [0.0015333, 0.0046, 0.019933],
},
MeasuredSS {
name: "Espresso",
sigma_prime_s: [0.72378, 0.84557, 1.0247],
sigma_a: [4.7984, 6.5751, 8.8493],
},
MeasuredSS {
name: "Mint Mocha Coffee",
sigma_prime_s: [0.31602, 0.38538, 0.48131],
sigma_a: [3.772, 5.8228, 7.82],
},
MeasuredSS {
name: "Lowfat Soy Milk",
sigma_prime_s: [0.30576, 0.34233, 0.61664],
sigma_a: [0.0014375, 0.0071875, 0.035937],
},
MeasuredSS {
name: "Regular Soy Milk",
sigma_prime_s: [0.59223, 0.73866, 1.4693],
sigma_a: [0.0019167, 0.0095833, 0.065167],
},
MeasuredSS {
name: "Lowfat Chocolate Milk",
sigma_prime_s: [0.64925, 0.83916, 1.1057],
sigma_a: [0.0115, 0.0368, 0.1564],
},
MeasuredSS {
name: "Regular Chocolate Milk",
sigma_prime_s: [1.4585, 2.1289, 2.9527],
sigma_a: [0.010063, 0.043125, 0.14375],
},
MeasuredSS {
name: "Coke",
sigma_prime_s: [8.9053e-05, 8.372e-05, 0.0],
sigma_a: [0.10014, 0.16503, 0.2468],
},
MeasuredSS {
name: "Pepsi",
sigma_prime_s: [6.1697e-05, 4.2564e-05, 0.0],
sigma_a: [0.091641, 0.14158, 0.20729],
},
MeasuredSS {
name: "Sprite",
sigma_prime_s: [6.0306e-06, 6.4139e-06, 6.5504e-06],
sigma_a: [0.001886, 0.0018308, 0.0020025],
},
MeasuredSS {
name: "Gatorade",
sigma_prime_s: [0.0024574, 0.003007, 0.0037325],
sigma_a: [0.024794, 0.019289, 0.008878],
},
MeasuredSS {
name: "Chardonnay",
sigma_prime_s: [1.7982e-05, 1.3758e-05, 1.2023e-05],
sigma_a: [0.010782, 0.011855, 0.023997],
},
MeasuredSS {
name: "White Zinfandel",
sigma_prime_s: [1.7501e-05, 1.9069e-05, 1.288e-05],
sigma_a: [0.012072, 0.016184, 0.019843],
},
MeasuredSS {
name: "Merlot",
sigma_prime_s: [2.1129e-05, 0.0, 0.0],
sigma_a: [0.11632, 0.25191, 0.29434],
},
MeasuredSS {
name: "Budweiser Beer",
sigma_prime_s: [2.4356e-05, 2.4079e-05, 1.0564e-05],
sigma_a: [0.011492, 0.024911, 0.057786],
},
MeasuredSS {
name: "Coors Light Beer",
sigma_prime_s: [5.0922e-05, 4.301e-05, 0.0],
sigma_a: [0.006164, 0.013984, 0.034983],
},
MeasuredSS {
name: "Clorox",
sigma_prime_s: [0.0024035, 0.0031373, 0.003991],
sigma_a: [0.0033542, 0.014892, 0.026297],
},
MeasuredSS {
name: "Apple Juice",
sigma_prime_s: [0.00013612, 0.00015836, 0.000227],
sigma_a: [0.012957, 0.023741, 0.052184],
},
MeasuredSS {
name: "Cranberry Juice",
sigma_prime_s: [0.00010402, 0.00011646, 7.8139e-05],
sigma_a: [0.039437, 0.094223, 0.12426],
},
MeasuredSS {
name: "Grape Juice",
sigma_prime_s: [5.382e-05, 0.0, 0.0],
sigma_a: [0.10404, 0.23958, 0.29325],
},
MeasuredSS {
name: "Ruby Grapefruit Juice",
sigma_prime_s: [0.011002, 0.010927, 0.011036],
sigma_a: [0.085867, 0.18314, 0.25262],
},
MeasuredSS {
name: "White Grapefruit Juice",
sigma_prime_s: [0.22826, 0.23998, 0.32748],
sigma_a: [0.0138, 0.018831, 0.056781],
},
MeasuredSS {
name: "Shampoo",
sigma_prime_s: [0.0007176, 0.0008303, 0.0009016],
sigma_a: [0.014107, 0.045693, 0.061717],
},
MeasuredSS {
name: "Strawberry Shampoo",
sigma_prime_s: [0.00015671, 0.00015947, 1.518e-05],
sigma_a: [0.01449, 0.05796, 0.075823],
},
MeasuredSS {
name: "Head & Shoulders Shampoo",
sigma_prime_s: [0.023805, 0.028804, 0.034306],
sigma_a: [0.084621, 0.15688, 0.20365],
},
MeasuredSS {
name: "Lemon Tea Powder",
sigma_prime_s: [0.040224, 0.045264, 0.051081],
sigma_a: [2.4288, 4.5757, 7.2127],
},
MeasuredSS {
name: "Orange Powder",
sigma_prime_s: [0.00015617, 0.00017482, 0.0001762],
sigma_a: [0.001449, 0.003441, 0.007863],
},
MeasuredSS {
name: "Pink Lemonade Powder",
sigma_prime_s: [0.00012103, 0.00013073, 0.00012528],
sigma_a: [0.001165, 0.002366, 0.003195],
},
MeasuredSS {
name: "Cappuccino Powder",
sigma_prime_s: [1.8436, 2.5851, 2.1662],
sigma_a: [35.844, 49.547, 61.084],
},
MeasuredSS {
name: "Salt Powder",
sigma_prime_s: [0.027333, 0.032451, 0.031979],
sigma_a: [0.28415, 0.3257, 0.34148],
},
MeasuredSS {
name: "Sugar Powder",
sigma_prime_s: [0.00022272, 0.00025513, 0.000271],
sigma_a: [0.012638, 0.031051, 0.050124],
},
MeasuredSS {
name: "Suisse Mocha Powder",
sigma_prime_s: [2.7979, 3.5452, 4.3365],
sigma_a: [17.502, 27.004, 35.433],
},
MeasuredSS {
name: "Pacific Ocean Surface Water",
sigma_prime_s: [0.0001764, 0.00032095, 0.00019617],
sigma_a: [0.031845, 0.031324, 0.030147],
},
];
fn get_medium_scattering_properties(name: &str) -> Option<(Spectrum, Spectrum)> { Self {
SUBSURFACE_TABLE.iter().find(|m| m.name == name).map(|m| { bounds: *bounds,
let sigma_a = Spectrum::RGBUnbounded(RGBUnboundedSpectrum::new(&SRGB, m.sigma_a.into())); render_from_medium: *render_from_medium,
let sigma_s = le_grid,
Spectrum::RGBUnbounded(RGBUnboundedSpectrum::new(&SRGB, m.sigma_prime_s.into())); le_scale,
(sigma_a, sigma_s) phase: HGPhaseFunction::new(g),
}) sigma_a_grid,
sigma_s_grid,
sigma_scale,
majorant_grid,
}
}
} }
pub trait CreateMedium { pub trait GridMediumCreator {
fn create( fn new(
name: &str, bounds: &Bounds3f,
parameters: &ParameterDictionary, render_from_medium: &Transform,
render_from_medium: Transform, sigma_a: &Spectrum,
loc: &FileLoc, sigma_s: &Spectrum,
arena: &Arena, sigma_scale: Float,
) -> Result<Ptr<Medium>> { g: Float,
let medium = match name { density_grid: SampledGrid<Float>,
"homogeneous" => create_homogeneous(parameters, loc, arena)?, temperature_grid: Option<SampledGrid<Float>>,
"uniformgrid" => create_grid(parameters, render_from_medium, loc, arena)?, le: &Spectrum,
"rgbgrid" => create_rgb_grid(parameters, render_from_medium, loc, arena)?, le_scale: SampledGrid<Float>,
// "cloud" => create_cloud(parameters, render_from_medium, loc, arena)?, ) -> Self;
// "nanovdb" => create_nanovdb(parameters, render_from_medium, loc, arena)?, }
_ => bail!("{}: unknown medium \"{}\"", loc, name),
impl GridMediumCreator for GridMedium {
#[allow(clippy::too_many_arguments)]
fn new(
bounds: &Bounds3f,
render_from_medium: &Transform,
sigma_a: &Spectrum,
sigma_s: &Spectrum,
sigma_scale: Float,
g: Float,
density_grid: SampledGrid<Float>,
temperature_grid: Option<SampledGrid<Float>>,
le: &Spectrum,
le_scale: SampledGrid<Float>,
) -> Self {
let mut sigma_a_spec = DenselySampledSpectrum::from_spectrum(sigma_a);
let mut sigma_s_spec = DenselySampledSpectrum::from_spectrum(sigma_s);
sigma_a_spec.scale(sigma_scale);
sigma_s_spec.scale(sigma_scale);
let le_spec = DenselySampledSpectrum::from_spectrum(le);
let mut majorant_grid = MajorantGrid::new(*bounds, Point3i::new(16, 16, 16)).device;
let is_emissive = if temperature_grid.is_some() {
true
} else {
Spectrum::Dense(le_spec.device()).max_value() > 0.
}; };
Ok(arena.alloc(medium))
}
}
fn create_homogeneous( for z in 0..majorant_grid.res.z() {
parameters: &ParameterDictionary, for y in 0..majorant_grid.res.y() {
loc: &FileLoc, for x in 0..majorant_grid.res.x() {
arena: &Arena, let bounds = majorant_grid.voxel_bounds(x, y, z);
) -> Result<Medium> { majorant_grid.set(x, y, z, density_grid.max_value(bounds));
let preset = parameters.get_one_string("preset", "")?; }
let (mut sigma_a, mut sigma_s) = if !preset.is_empty() {
match get_medium_scattering_properties(&preset) {
Some(props) => (Some(props.0), Some(props.1)),
None => {
log::warn!("{}: material preset \"{}\" not found", loc, preset);
(None, None)
} }
} }
} else {
(None, None)
};
if sigma_a.is_none() { Self {
sigma_a = parameters bounds: *bounds,
.get_one_spectrum("sigma_a", None, SpectrumType::Unbounded) render_from_medium: *render_from_medium,
.or(Some(Spectrum::Constant(ConstantSpectrum::new(1.0)))); sigma_a_spec: sigma_a_spec.device(),
} sigma_s_spec: sigma_s_spec.device(),
density_grid,
if sigma_s.is_none() { phase: HGPhaseFunction::new(g),
sigma_s = parameters temperature_grid,
.get_one_spectrum("sigma_s", None, SpectrumType::Unbounded) le_spec: le_spec.device(),
.or(Some(Spectrum::Constant(ConstantSpectrum::new(1.0)))); le_scale,
} is_emissive,
majorant_grid,
let sigma_a = sigma_a.unwrap();
let sigma_s = sigma_s.unwrap();
let le = parameters
.get_one_spectrum("Le", None, SpectrumType::Illuminant)
.filter(|s| s.max_value() > 0.0);
let mut le_scale = parameters.get_one_float("Lescale", 1.0)?;
let le = match le {
Some(s) => {
le_scale /= spectrum_to_photometric(s);
s
} }
None => Spectrum::Constant(ConstantSpectrum::new(0.0)), }
};
let sigma_scale = parameters.get_one_float("scale", 1.0)?;
let g = parameters.get_one_float("g", 0.0)?;
let mut sigma_a_spec = DenselySampledSpectrum::from_spectrum(&sigma_a);
let mut sigma_s_spec = DenselySampledSpectrum::from_spectrum(&sigma_s);
let mut le_spec = DenselySampledSpectrum::from_spectrum(&le);
sigma_a_spec.scale(sigma_scale);
sigma_s_spec.scale(sigma_scale);
le_spec.scale(le_scale);
Ok(Medium::Homogeneous(HomogeneousMedium {
sigma_a_spec: arena.alloc(sigma_a_spec),
sigma_s_spec: arena.alloc(sigma_s_spec),
le_spec: arena.alloc(le_spec),
phase: HGPhaseFunction::new(g),
}))
} }
fn create_grid( pub trait HomogeneousMediumCreator {
parameters: &ParameterDictionary, fn new(
render_from_medium: shared::Transform, sigma_a: Spectrum,
loc: &FileLoc, sigma_s: Spectrum,
arena: &Arena, sigma_scale: Float,
) -> Result<Medium> { le: Spectrum,
let density = parameters.get_float_array("density")?; le_scale: Float,
if density.is_empty() { g: Float,
bail!("{}: no \"density\" value provided for grid medium", loc); ) -> Self;
}
let temperature = parameters.get_float_array("temperature")?;
if !temperature.is_empty() && temperature.len() != density.len() {
bail!(
"{}: different number of samples ({} vs {}) for \"density\" and \"temperature\"",
loc,
density.len(),
temperature.len()
);
}
let nx = parameters.get_one_int("nx", 1)?;
let ny = parameters.get_one_int("ny", 1)?;
let nz = parameters.get_one_int("nz", 1)?;
if density.len() as i32 != nx * ny * nz {
bail!(
"{}: grid medium has {} density values; expected nx*ny*nz = {}",
loc,
density.len(),
nx * ny * nz
);
}
let density_grid = SampledGrid::new(&density, nx, ny, nz);
let temperature_grid = if !temperature.is_empty() {
Some(SampledGrid::new(&temperature, nx, ny, nz))
} else {
None
};
let le = parameters.get_one_spectrum("Le", None, SpectrumType::Illuminant);
if le.is_some() && !temperature.is_empty() {
bail!(
"{}: both \"Le\" and \"temperature\" values were provided",
loc
);
}
let (le, le_norm) = match le.filter(|s| s.max_value() > 0.0) {
Some(s) => (s, 1.0 / spectrum_to_photometric(s)),
None => (Spectrum::Constant(ConstantSpectrum::new(0.0)), 1.0),
};
let le_scale_values = parameters.get_float_array("Lescale")?;
let le_grid = if le_scale_values.is_empty() {
SampledGrid::new(&[le_norm], 1, 1, 1)
} else {
if le_scale_values.len() as i32 != nx * ny * nz {
bail!(
"{}: expected {} values for \"Lescale\" but got {}",
loc,
nx * ny * nz,
le_scale_values.len()
);
}
let scaled: Vec<Float> = le_scale_values.iter().map(|v| v * le_norm).collect();
SampledGrid::new(&scaled, nx, ny, nz)
};
let p0 = parameters.get_one_point3f("p0", Point3f::new(0.0, 0.0, 0.0))?;
let p1 = parameters.get_one_point3f("p1", Point3f::new(1.0, 1.0, 1.0))?;
let bounds = Bounds3f::from_points(p0, p1);
let g = parameters.get_one_float("g", 0.0)?;
let sigma_a = parameters
.get_one_spectrum("sigma_a", None, SpectrumType::Unbounded)
.unwrap_or(Spectrum::Constant(ConstantSpectrum::new(1.0)));
let sigma_s = parameters
.get_one_spectrum("sigma_s", None, SpectrumType::Unbounded)
.unwrap_or(Spectrum::Constant(ConstantSpectrum::new(1.0)));
let sigma_scale = parameters.get_one_float("scale", 1.0)?;
let mut sigma_a_spec = DenselySampledSpectrum::from_spectrum(&sigma_a);
let mut sigma_s_spec = DenselySampledSpectrum::from_spectrum(&sigma_s);
sigma_a_spec.scale(sigma_scale);
sigma_s_spec.scale(sigma_scale);
let le_spec = DenselySampledSpectrum::from_spectrum(&le);
let is_emissive = if temperature_grid.is_some() {
true
} else {
le.max_value() > 0.0
};
let mut majorant_grid = MajorantGrid::new(bounds, Point3i::new(16, 16, 16));
for z in 0..majorant_grid.res.z() {
for y in 0..majorant_grid.res.y() {
for x in 0..majorant_grid.res.x() {
let voxel_bounds = majorant_grid.voxel_bounds(x, y, z);
majorant_grid.set(x, y, z, density_grid.max_value(voxel_bounds));
}
}
}
Ok(Medium::Grid(GridMedium {
bounds,
render_from_medium,
sigma_a_spec: arena.alloc(sigma_a_spec),
sigma_s_spec: arena.alloc(sigma_s_spec),
density_grid: arena.alloc(density_grid),
phase: HGPhaseFunction::new(g),
temperature_grid: arena.alloc_opt(temperature_grid),
le_spec: arena.alloc(le_spec),
le_scale: arena.alloc(le_grid),
is_emissive,
majorant_grid: arena.alloc(majorant_grid),
}))
} }
fn create_rgb_grid( impl HomogeneousMediumCreator for HomogeneousMedium {
parameters: &ParameterDictionary, fn new(
render_from_medium: shared::Transform, sigma_a: Spectrum,
loc: &FileLoc, sigma_s: Spectrum,
arena: &Arena, sigma_scale: Float,
) -> Result<Medium> { le: Spectrum,
let sigma_a_rgb = parameters.get_rgb_array("sigma_a")?; le_scale: Float,
let sigma_s_rgb = parameters.get_rgb_array("sigma_s")?; g: Float,
) -> Self {
let mut sigma_a_spec = DenselySampledSpectrum::from_spectrum(&sigma_a);
let mut sigma_s_spec = DenselySampledSpectrum::from_spectrum(&sigma_s);
let mut le_spec = DenselySampledSpectrum::from_spectrum(&le);
if sigma_a_rgb.is_empty() && sigma_s_rgb.is_empty() { sigma_a_spec.scale(sigma_scale);
bail!("{}: RGB grid requires \"sigma_a\" and/or \"sigma_s\"", loc); sigma_s_spec.scale(sigma_scale);
} le_spec.scale(le_scale);
let n_density = if !sigma_a_rgb.is_empty() { Self {
if !sigma_s_rgb.is_empty() && sigma_a_rgb.len() != sigma_s_rgb.len() { sigma_a_spec: sigma_a_spec.device(),
bail!( sigma_s_spec: sigma_s_spec.device(),
"{}: different number of samples ({} vs {}) for \"sigma_a\" and \"sigma_s\"", le_spec: le_spec.device(),
loc, phase: HGPhaseFunction::new(g),
sigma_a_rgb.len(),
sigma_s_rgb.len()
);
}
sigma_a_rgb.len()
} else {
sigma_s_rgb.len()
};
let le_rgb = parameters.get_rgb_array("Le")?;
if !le_rgb.is_empty() && sigma_a_rgb.is_empty() {
bail!(
"{}: RGB grid requires \"sigma_a\" if \"Le\" is provided",
loc
);
}
if !le_rgb.is_empty() && le_rgb.len() != n_density {
bail!(
"{}: expected {} values for \"Le\" but got {}",
loc,
n_density,
le_rgb.len()
);
}
let nx = parameters.get_one_int("nx", 1)?;
let ny = parameters.get_one_int("ny", 1)?;
let nz = parameters.get_one_int("nz", 1)?;
if n_density as i32 != nx * ny * nz {
bail!(
"{}: RGB grid medium has {} values; expected nx*ny*nz = {}",
loc,
n_density,
nx * ny * nz
);
}
let cs = parameters.colorspace();
let sigma_a_grid = if !sigma_a_rgb.is_empty() {
let spectra: Vec<RGBUnboundedSpectrum> = sigma_a_rgb
.iter()
.map(|rgb| RGBUnboundedSpectrum::new(cs, *rgb))
.collect();
Some(SampledGrid::new(&spectra, nx, ny, nz))
} else {
None
};
let sigma_s_grid = if !sigma_s_rgb.is_empty() {
let spectra: Vec<RGBUnboundedSpectrum> = sigma_s_rgb
.iter()
.map(|rgb| RGBUnboundedSpectrum::new(cs, *rgb))
.collect();
Some(SampledGrid::new(&spectra, nx, ny, nz))
} else {
None
};
let le_grid = if !le_rgb.is_empty() {
let spectra: Vec<RGBIlluminantSpectrum> = le_rgb
.iter()
.map(|rgb| RGBIlluminantSpectrum::new(cs, *rgb))
.collect();
Some(SampledGrid::new(&spectra, nx, ny, nz))
} else {
None
};
let p0 = parameters.get_one_point3f("p0", Point3f::new(0.0, 0.0, 0.0))?;
let p1 = parameters.get_one_point3f("p1", Point3f::new(1.0, 1.0, 1.0))?;
let bounds = Bounds3f::from_points(p0, p1);
let le_scale = parameters.get_one_float("Lescale", 1.0)?;
let g = parameters.get_one_float("g", 0.0)?;
let sigma_scale = parameters.get_one_float("scale", 1.0)?;
// Build majorant grid from combined sigma_a + sigma_s maxima
let mut majorant_grid = MajorantGrid::new(bounds, Point3i::new(16, 16, 16));
let convert = |s: &RGBUnboundedSpectrum| s.max_value();
for z in 0..majorant_grid.res.z() {
for y in 0..majorant_grid.res.y() {
for x in 0..majorant_grid.res.x() {
let voxel_bounds = majorant_grid.voxel_bounds(x, y, z);
let max_a = sigma_a_grid
.as_ref()
.map(|g| g.max_value_convert(voxel_bounds, convert))
.unwrap_or(1.0);
let max_s = sigma_s_grid
.as_ref()
.map(|g| g.max_value_convert(voxel_bounds, convert))
.unwrap_or(1.0);
majorant_grid.set(x, y, z, sigma_scale * (max_a + max_s));
}
} }
} }
Ok(Medium::RGBGrid(RGBGridMedium {
bounds,
render_from_medium,
phase: HGPhaseFunction::new(g),
le_scale,
sigma_scale,
sigma_a_grid: arena.alloc_opt(sigma_a_grid),
sigma_s_grid: arena.alloc_opt(sigma_s_grid),
le_grid: arena.alloc_opt(le_grid),
majorant_grid: arena.alloc(majorant_grid),
}))
} }

View file

@ -556,7 +556,7 @@ impl ParserTarget for BasicSceneBuilder {
self.current_accelerator self.current_accelerator
.take() .take()
.expect("Accelerator not set before WorldBegin"), .expect("Accelerator not set before WorldBegin"),
&arena, arena,
); );
Ok(()) Ok(())
} }

View file

@ -12,7 +12,7 @@ use crate::core::texture::{FloatTexture, SpectrumTexture};
use crate::utils::parallel::{run_async, AsyncJob}; use crate::utils::parallel::{run_async, AsyncJob};
use crate::utils::parameters::{NamedTextures, ParameterDictionary, TextureParameterDictionary}; use crate::utils::parameters::{NamedTextures, ParameterDictionary, TextureParameterDictionary};
use crate::utils::resolve_filename; use crate::utils::resolve_filename;
use crate::{Arena, ArenaUpload, FileLoc, Upload}; use crate::{Arena, FileLoc};
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use parking_lot::Mutex; use parking_lot::Mutex;
use shared::core::camera::{Camera, CameraTransform}; use shared::core::camera::{Camera, CameraTransform};
@ -556,7 +556,7 @@ impl BasicScene {
loaded_shapes: &[Vec<Shape>], loaded_shapes: &[Vec<Shape>],
shape_entities: &[ShapeSceneEntity], shape_entities: &[ShapeSceneEntity],
textures: &NamedTextures, textures: &NamedTextures,
arena: &Arena, arena: &mut Arena,
) -> HashMap<usize, Vec<Light>> { ) -> HashMap<usize, Vec<Light>> {
let light_state = self.light_state.lock(); let light_state = self.light_state.lock();
let mut shape_lights: HashMap<usize, Vec<Light>> = HashMap::new(); let mut shape_lights: HashMap<usize, Vec<Light>> = HashMap::new();
@ -672,53 +672,6 @@ impl BasicScene {
(primitives, area_lights) (primitives, area_lights)
} }
fn build_primitives_inner(
shapes: Vec<Ptr<Shape>>,
mtl: Material,
alpha_tex: &Option<Arc<FloatTexture>>,
mi: MediumInterface,
al_params: Option<&AreaLightEntity>,
render_from_light: Transform,
film_cs: Option<&RGBColorSpace>,
arena: &mut Arena,
area_lights: &mut Vec<Arc<Light>>,
) -> Vec<(Ptr<Shape>, Ptr<Light>, Ptr<GPUFloatTexture>)> {
shapes
.into_iter()
.map(|shape| {
let area_light_ptr = al_params
.and_then(|al_entity| {
let cs = al_entity.parameters.color_space.as_deref().or(film_cs);
let default_alpha = Arc::new(FloatTexture::default());
let alpha_ref = alpha_tex.as_ref().unwrap_or(&default_alpha);
crate::core::light::create_area_light(
render_from_light,
None,
&al_entity.parameters,
&al_entity.loc,
&shape,
alpha_ref,
cs,
arena,
)
.ok()
})
.unwrap_or(Ptr::null());
if !area_light_ptr.is_null() {
area_lights.push(Arc::new(unsafe { &*area_light_ptr.as_raw() }.clone()));
}
let alpha_ptr = alpha_tex
.as_ref()
.map(|t| arena.upload(t.as_ref()))
.unwrap_or(Ptr::null());
(shape, area_light_ptr, alpha_ptr)
})
.collect()
}
fn build_primitives_for_entity( fn build_primitives_for_entity(
entity: &ShapeSceneEntity, entity: &ShapeSceneEntity,
textures: &NamedTextures, textures: &NamedTextures,
@ -774,30 +727,52 @@ impl BasicScene {
let al_params = entity.light_index.map(|idx| &light_state.area_lights[idx]); let al_params = entity.light_index.map(|idx| &light_state.area_lights[idx]);
let built = Self::build_primitives_inner( for shape in shapes {
shapes, let area_light = al_params.and_then(|al_entity| {
mtl, let colorspace_ref = al_entity.parameters.color_space.as_deref().or(film_cs);
&alpha_tex, let default_alpha = Arc::new(FloatTexture::default());
mi.clone(), let alpha_ref = alpha_tex.as_ref().unwrap_or(&default_alpha);
al_params,
*entity.render_from_object, crate::core::light::create_area_light(
film_cs, *entity.render_from_object,
arena, None,
area_lights, &al_entity.parameters,
); &al_entity.loc,
&shape,
alpha_ref,
colorspace_ref,
arena,
)
.ok()
});
let uploaded_light = area_light
.as_ref()
.map(|l| l.upload(arena))
.unwrap_or(Ptr::null());
if let Some(ref light) = area_light {
area_lights.push(Arc::new(light.clone()));
}
let shape_ptr = shape.upload(arena);
let prim =
if uploaded_light.is_null() && !mi.is_medium_transition() && alpha_tex.is_none() {
Primitive::Simple(SimplePrimitive::new(shape_ptr, Ptr::from(&mtl)))
} else {
Primitive::Geometric(GeometricPrimitive::new(
shape_ptr,
mtl.upload(arena),
uploaded_light,
mi.clone(),
alpha_tex
.as_ref()
.map(|t| t.upload(arena))
.unwrap_or(Ptr::null()),
))
};
for (shape, light_ptr, alpha_ptr) in built {
let prim = if light_ptr.is_null() && !mi.is_medium_transition() && alpha_tex.is_none() {
Primitive::Simple(SimplePrimitive::new(shape, Ptr::from(&mtl)))
} else {
Primitive::Geometric(GeometricPrimitive::new(
shape,
arena.alloc(mtl),
light_ptr,
mi.clone(),
alpha_ptr,
))
};
primitives.push(prim); primitives.push(prim);
} }
} }
@ -864,33 +839,56 @@ impl BasicScene {
let al_params = entity.light_index.map(|idx| &light_state.area_lights[idx]); let al_params = entity.light_index.map(|idx| &light_state.area_lights[idx]);
let built = Self::build_primitives_inner( for shape in shapes {
shapes, let area_light = al_params.and_then(|al_entity| {
mtl, let colorspace_ref = al_entity.parameters.color_space.as_deref().or(film_cs);
&alpha_tex, let default_alpha = Arc::new(FloatTexture::default());
mi.clone(), let alpha_ref = alpha_tex.as_ref().unwrap_or(&default_alpha);
al_params,
entity.transformed_base.render_from_object.start_transform, crate::core::light::create_area_light(
film_cs, entity.transformed_base.render_from_object.start_transform,
arena, None,
area_lights, &al_entity.parameters,
); &al_entity.loc,
&shape,
alpha_ref,
colorspace_ref,
arena,
)
.ok()
});
let uploaded_light = area_light
.as_ref()
.map(|l| l.upload(arena))
.unwrap_or(Ptr::null());
if let Some(ref light) = area_light {
area_lights.push(Arc::new(light.clone()));
}
let shape_ptr = shape.upload(arena);
for (shape, light_ptr, alpha_ptr) in built {
let base_prim = let base_prim =
if light_ptr.is_null() && !mi.is_medium_transition() && alpha_tex.is_none() { if uploaded_light.is_null() && !mi.is_medium_transition() && alpha_tex.is_none() {
Primitive::Simple(SimplePrimitive::new(shape, Ptr::from(&mtl))) Primitive::Simple(SimplePrimitive::new(shape_ptr, Ptr::from(&mtl)))
} else { } else {
Primitive::Geometric(GeometricPrimitive::new( Primitive::Geometric(GeometricPrimitive::new(
shape, shape_ptr,
arena.alloc(mtl), mtl.upload(arena),
light_ptr, uploaded_light,
mi.clone(), mi.clone(),
alpha_ptr, alpha_tex
.as_ref()
.map(|t| t.upload(arena))
.unwrap_or(Ptr::null()),
)) ))
}; };
let base_ptr = arena.alloc(base_prim);
primitives.push(Primitive::Animated(AnimatedPrimitive { primitives.push(Primitive::Animated(AnimatedPrimitive {
primitive: arena.alloc(base_prim), primitive: base_ptr,
render_from_primitive: arena.alloc(entity.transformed_base.render_from_object), render_from_primitive: arena.alloc(entity.transformed_base.render_from_object),
})); }));
} }
@ -939,7 +937,10 @@ impl BasicScene {
.get(&shape_ctx.entity_index) .get(&shape_ctx.entity_index)
.and_then(|lights| lights.get(shape_ctx.shape_index)); .and_then(|lights| lights.get(shape_ctx.shape_index));
let light_ptr = arena.alloc_opt(shape_lights_opt); let area_light = shape_lights_opt
.map(|l| l.upload(arena))
.unwrap_or(Ptr::null());
let shape_ptr = shape_ctx.shape; let shape_ptr = shape_ctx.shape;
let prim = if area_light.is_null() && !mi.is_medium_transition() && alpha_tex.is_none() let prim = if area_light.is_null() && !mi.is_medium_transition() && alpha_tex.is_none()
{ {
@ -947,10 +948,10 @@ impl BasicScene {
} else { } else {
Primitive::Geometric(GeometricPrimitive::new( Primitive::Geometric(GeometricPrimitive::new(
shape_ptr, shape_ptr,
arena.alloc(mtl), mtl.upload(arena),
area_light, area_light,
mi.clone(), mi.clone(),
arena.upload(alpha_tex), alpha_tex.upload(arena),
)) ))
}; };

View file

@ -1,12 +1,12 @@
use crate::core::texture::FloatTexture; use crate::core::texture::FloatTexture;
use crate::shapes::*; use crate::shapes::{BilinearPatchMesh, TriangleMesh};
use crate::utils::resolve_filename; use crate::utils::{Arena, FileLoc, ParameterDictionary, resolve_filename};
use crate::{Arena, FileLoc, ParameterDictionary};
use anyhow::{anyhow, bail, Result}; use anyhow::{anyhow, bail, Result};
use parking_lot::Mutex; use parking_lot::Mutex;
use shared::core::shape::*; use shared::core::shape::*;
use shared::Ptr;
use shared::shapes::*; use shared::shapes::*;
use shared::{Transform, Ptr}; use shared::utils::Transform;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
@ -116,8 +116,8 @@ impl ShapeFactory for Shape {
global_store.push(host_arc.clone()); global_store.push(host_arc.clone());
drop(global_store); drop(global_store);
let n_tris = host_arc..n_triangles; let n_tris = host_arc.device.n_triangles;
let mesh_ptr = Ptr::from(&host_arc); let mesh_ptr = Ptr::from(&host_arc.device);
let shapes: Vec<Ptr<Shape>> = (0..n_tris) let shapes: Vec<Ptr<Shape>> = (0..n_tris)
.map(|i| { .map(|i| {
let tri_shape = Shape::Triangle(TriangleShape { let tri_shape = Shape::Triangle(TriangleShape {

View file

@ -19,7 +19,7 @@ pub fn get_spectrum_cache() -> &'static InternCache<DenselySampledSpectrum> {
pub fn spectrum_to_photometric(s: Spectrum) -> Float { pub fn spectrum_to_photometric(s: Spectrum) -> Float {
let effective_spectrum = match s { let effective_spectrum = match s {
Spectrum::RGBIlluminant(ill) => &Spectrum::Dense(ill.illuminant), Spectrum::RGBIlluminant(ill) => &Spectrum::Dense(*ill.illuminant),
_ => &s, _ => &s,
}; };
effective_spectrum.inner_product(&cie_y()) effective_spectrum.inner_product(&cie_y())

View file

@ -1,4 +1,5 @@
use crate::textures::*; use crate::textures::*;
use crate::utils::mipmap::{MIPMapFilterOptions, MIPMap};
use crate::utils::TextureParameterDictionary; use crate::utils::TextureParameterDictionary;
use crate::{Arena, FileLoc}; use crate::{Arena, FileLoc};
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
@ -8,8 +9,8 @@ use shared::core::geometry::Vector3f;
use shared::core::image::WrapMode; use shared::core::image::WrapMode;
use shared::core::texture::SpectrumType; use shared::core::texture::SpectrumType;
use shared::core::texture::{ use shared::core::texture::{
CylindricalMapping, GPUFloatTexture, GPUSpectrumTexture, PlanarMapping, SphericalMapping, CylindricalMapping, PlanarMapping, SphericalMapping, TextureEvalContext, TextureMapping2D,
TextureEvalContext, TextureMapping2D, UVMapping, UVMapping, GPUFloatTexture, GPUSpectrumTexture
}; };
use shared::spectra::{SampledSpectrum, SampledWavelengths}; use shared::spectra::{SampledSpectrum, SampledWavelengths};
use shared::textures::*; use shared::textures::*;
@ -29,7 +30,6 @@ pub trait SpectrumTextureTrait {
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
#[enum_dispatch(FloatTextureTrait)]
pub enum FloatTexture { pub enum FloatTexture {
Constant(FloatConstantTexture), Constant(FloatConstantTexture),
Checkerboard(FloatCheckerboardTexture), Checkerboard(FloatCheckerboardTexture),
@ -40,7 +40,6 @@ pub enum FloatTexture {
Mix(FloatMixTexture), Mix(FloatMixTexture),
DirectionMix(FloatDirectionMixTexture), DirectionMix(FloatDirectionMixTexture),
// #[device(custom = "upload_image", variant_type = "GPUFloatImageTexture")] // #[device(custom = "upload_image", variant_type = "GPUFloatImageTexture")]
Scaled(FloatScaledTexture),
Image(FloatImageTexture), Image(FloatImageTexture),
Bilerp(FloatBilerpTexture), Bilerp(FloatBilerpTexture),
} }
@ -106,12 +105,14 @@ impl FloatTexture {
} }
} }
#[derive(Clone, Debug)]
#[enum_dispatch(SpectrumTextureTrait)]
pub enum SpectrumTexture { pub enum SpectrumTexture {
Constant(SpectrumConstantTexture), Constant(SpectrumConstantTexture),
Checkerboard(SpectrumCheckerboardTexture), Checkerboard(SpectrumCheckerboardTexture),
Dots(SpectrumDotsTexture), Dots(SpectrumDotsTexture),
// #[device(
// custom = "upload_spectrum_image",
// variant_type = "GPUSpectrumImageTexture"
// )]
Image(SpectrumImageTexture), Image(SpectrumImageTexture),
Bilerp(SpectrumBilerpTexture), Bilerp(SpectrumBilerpTexture),
Scaled(SpectrumScaledTexture), Scaled(SpectrumScaledTexture),
@ -132,11 +133,12 @@ impl SpectrumTexture {
scale: inner.base.scale, scale: inner.base.scale,
invert: inner.base.invert, invert: inner.base.invert,
is_single_channel: inner.base.mipmap.is_single_channel(), is_single_channel: inner.base.mipmap.is_single_channel(),
color_space: arena.alloc( color_space: inner
inner.base.mipmap.color_space .base
.clone() .mipmap
.unwrap_or_else(crate::spectra::default_colorspace), .color_space
), .clone()
.unwrap_or_else(crate::spectra::default_colorspace),
spectrum_type: inner.spectrum_type, spectrum_type: inner.spectrum_type,
} }
} }
@ -262,4 +264,3 @@ pub struct TexInfo {
pub wrap_mode: WrapMode, pub wrap_mode: WrapMode,
pub encoding: ColorEncoding, pub encoding: ColorEncoding,
} }

View file

@ -65,10 +65,10 @@ pub static REC2020_COEFFS: Lazy<&[Float]> =
Lazy::new(|| strip_to_len(REC2020_COEFFS_BYTES, COEFFS_LEN)); Lazy::new(|| strip_to_len(REC2020_COEFFS_BYTES, COEFFS_LEN));
pub static SRGB_TABLE: Lazy<RGBToSpectrumTable> = pub static SRGB_TABLE: Lazy<RGBToSpectrumTable> =
Lazy::new(|| RGBToSpectrumTable::new(&SRGB_SCALE, &SRGB_COEFFS)); Lazy::new(|| RGBToSpectrumTableData::new(SRGB_SCALE, SRGB_COEFFS));
pub static DCI_P3_TABLE: Lazy<RGBToSpectrumTable> = pub static DCI_P3_TABLE: Lazy<RGBToSpectrumTable> =
Lazy::new(|| RGBToSpectrumTable::new(&DCI_P3_SCALE, &DCI_P3_COEFFS)); Lazy::new(|| RGBToSpectrumTableData::new(DCI_P3_SCALE.to_vec(), DCI_P3_COEFFS.to_vec()));
pub static REC2020_TABLE: Lazy<RGBToSpectrumTable> = pub static REC2020_TABLE: Lazy<RGBToSpectrumTable> =
Lazy::new(|| RGBToSpectrumTable::new(&REC2020_SCALE, &REC2020_COEFFS)); Lazy::new(|| RGBToSpectrumTableData::new(REC2020_SCALE.to_vec(), REC2020_COEFFS.to_vec()));
pub static ACES_TABLE: Lazy<RGBToSpectrumTable> = pub static ACES_TABLE: Lazy<RGBToSpectrumTable> =
Lazy::new(|| RGBToSpectrumTable::new(&ACES_SCALE, &ACES_COEFFS)); Lazy::new(|| RGBToSpectrumTableData::new(ACES_SCALE.to_vec(), ACES_COEFFS.to_vec()));

View file

@ -1,19 +1,19 @@
use super::base::IntegratorBase;
use super::RayIntegratorTrait; use super::RayIntegratorTrait;
use super::base::IntegratorBase;
use crate::Arena;
use crate::core::camera::InitMetadata; use crate::core::camera::InitMetadata;
use crate::core::film::FilmTrait; use crate::core::film::FilmTrait;
use crate::core::image::{Image, ImageIO, ImageMetadata}; use crate::core::image::{Image, ImageIO, ImageMetadata};
use crate::globals::get_options; use crate::globals::get_options;
use crate::spectra::get_spectra_context; use crate::spectra::get_spectra_context;
use crate::Arena;
use indicatif::{ProgressBar, ProgressStyle}; use indicatif::{ProgressBar, ProgressStyle};
use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use shared::Float;
use shared::core::camera::{Camera, CameraTrait}; use shared::core::camera::{Camera, CameraTrait};
use shared::core::geometry::{Bounds2i, Point2i, VectorLike}; use shared::core::geometry::{Bounds2i, Point2i, VectorLike};
use shared::core::sampler::get_camera_sample; use shared::core::sampler::get_camera_sample;
use shared::core::sampler::{Sampler, SamplerTrait}; use shared::core::sampler::{Sampler, SamplerTrait};
use shared::spectra::SampledSpectrum; use shared::spectra::SampledSpectrum;
use shared::Float;
use std::io::Write; use std::io::Write;
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
@ -213,7 +213,7 @@ pub fn render<T>(
let film_image = film.get_image(&film_metadata, splat_scale); let film_image = film.get_image(&film_metadata, splat_scale);
let (mse_values, _mse_debug_img) = let (mse_values, _mse_debug_img) =
film_image.mse(&film_image.all_channels_desc(), ref_img, false); film_image.mse(film_image.all_channels_desc(), ref_img, false);
let mse_avg = mse_values.average(); let mse_avg = mse_values.average();
if let Some(file) = &mut mse_out_file { if let Some(file) = &mut mse_out_file {

View file

@ -15,4 +15,4 @@ pub mod utils;
#[cfg(feature = "cuda")] #[cfg(feature = "cuda")]
pub mod gpu; pub mod gpu;
pub use utils::{Arena, FileLoc, ParameterDictionary, Upload, ArenaUpload}; pub use utils::{Arena, FileLoc, ParameterDictionary};

View file

@ -5,7 +5,6 @@ use crate::core::light::lookup_spectrum;
use crate::core::spectrum::spectrum_to_photometric; use crate::core::spectrum::spectrum_to_photometric;
use crate::core::texture::FloatTexture; use crate::core::texture::FloatTexture;
use crate::utils::resolve_filename; use crate::utils::resolve_filename;
use crate::utils::upload::ArenaUpload;
use crate::{Arena, FileLoc, ParameterDictionary}; use crate::{Arena, FileLoc, ParameterDictionary};
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use shared::core::geometry::Point2i; use shared::core::geometry::Point2i;
@ -13,10 +12,11 @@ use shared::core::light::{Light, LightBase, LightType};
use shared::core::medium::{Medium, MediumInterface}; use shared::core::medium::{Medium, MediumInterface};
use shared::core::shape::{Shape, ShapeTrait}; use shared::core::shape::{Shape, ShapeTrait};
use shared::core::spectrum::Spectrum; use shared::core::spectrum::Spectrum;
use shared::core::texture::GPUFloatTexture;
use shared::core::texture::{SpectrumType, TextureEvalContext}; use shared::core::texture::{SpectrumType, TextureEvalContext};
use shared::lights::DiffuseAreaLight; use shared::lights::DiffuseAreaLight;
use shared::spectra::RGBColorSpace; use shared::spectra::RGBColorSpace;
use shared::utils::Transform; use shared::utils::{Ptr, Transform};
use shared::{Float, PI}; use shared::{Float, PI};
pub fn create( pub fn create(
@ -37,49 +37,51 @@ pub fn create(
let two_sided = params.get_one_bool("twosided", false)?; let two_sided = params.get_one_bool("twosided", false)?;
let filename = resolve_filename(&params.get_one_string("filename", "")?); let filename = resolve_filename(&params.get_one_string("filename", "")?);
let (image, image_color_space): (Option<Image>, Option<RGBColorSpace>) = let (image, image_color_space) = if !filename.is_empty() {
if !filename.is_empty() { if l.is_some() {
if l.is_some() { return Err(anyhow!("{}: both \"L\" and \"filename\" specified", loc));
return Err(anyhow!("{}: both \"L\" and \"filename\" specified", loc)); }
}
let im = Image::read(Path::new(&filename), None)?; let im = Image::read(Path::new(&filename), None)?;
if im.image.has_any_infinite_pixels() { if im.image.has_any_infinite_pixels() {
return Err(anyhow!("{}: image has infinite pixel values", loc)); return Err(anyhow!("{}: image has infinite pixel values", loc));
} }
if im.image.has_any_nan_pixels() { if im.image.has_any_nan_pixels() {
return Err(anyhow!("{}: image has NaN pixel values", loc)); return Err(anyhow!("{}: image has NaN pixel values", loc));
} }
let channel_desc = im let channel_desc = im
.image .image
.get_channel_desc(&["R", "G", "B"]) .get_channel_desc(&["R", "G", "B"])
.map_err(|_| anyhow!("{}: image must have R, G, B channels", loc))?; .map_err(|_| anyhow!("{}: image must have R, G, B channels", loc))?;
let image = im.image.select_channels(&channel_desc); let image = im.image.select_channels(&channel_desc);
let cs = im.metadata.get_colorspace(); let cs = im.metadata.get_colorspace();
(Some(image), cs) (Some(image), cs)
} else { } else {
if l.is_none() { if l.is_none() {
l = Some(illum_spec); l = Some(illum_spec);
} }
(None, None) (None, None)
}; };
let l_for_scale = l.as_ref().unwrap_or(&illum_spec); let l_for_scale = l.as_ref().unwrap_or(&illum_spec);
scale /= spectrum_to_photometric(*l_for_scale); scale /= spectrum_to_photometric(*l_for_scale);
let phi_v = params.get_one_float("power", -1.0)?; let phi_v = params.get_one_float("power", -1.0)?;
if phi_v > 0.0 { if phi_v > 0.0 {
// k_e is the emissive power of the light as defined by the spectral
// distribution and texture and is used to normalize the emitted
// radiance such that the user-defined power will be the actual power
// emitted by the light.
let mut k_e: Float = 1.0; let mut k_e: Float = 1.0;
if let Some(ref img) = image { if let Some(ref img) = image {
let lum_vec = image_color_space // Get the appropriate luminance vector from the image colour space
.as_ref() let lum_vec = image_color_space.unwrap().luminance_vector();
.expect("image present but no color space")
.luminance_vector();
let mut sum_k_e = 0.0; let mut sum_k_e = 0.0;
let res = img.resolution(); let res = img.resolution();
@ -89,6 +91,7 @@ pub fn create(
let r = img.get_channel(Point2i::new(x, y), 0); let r = img.get_channel(Point2i::new(x, y), 0);
let g = img.get_channel(Point2i::new(x, y), 1); let g = img.get_channel(Point2i::new(x, y), 1);
let b = img.get_channel(Point2i::new(x, y), 2); let b = img.get_channel(Point2i::new(x, y), 2);
sum_k_e += r * lum_vec[0] + g * lum_vec[1] + b * lum_vec[2]; sum_k_e += r * lum_vec[0] + g * lum_vec[1] + b * lum_vec[2];
} }
} }
@ -97,15 +100,21 @@ pub fn create(
let side_factor = if two_sided { 2.0 } else { 1.0 }; let side_factor = if two_sided { 2.0 } else { 1.0 };
k_e *= side_factor * shape.area() * PI; k_e *= side_factor * shape.area() * PI;
// now multiply up scale to hit the target power
scale *= phi_v / k_e; scale *= phi_v / k_e;
} }
// Upload alpha texture to GPU and check for constant-zero let alpha_ptr = alpha.upload(arena);
let alpha_ptr = arena.upload(alpha); let is_constant_zero = match &*alpha_ptr {
let light_type = if alpha_ptr.is_constant_zero() { GPUFloatTexture::Constant(tex) => tex.evaluate(&TextureEvalContext::default()) == 0.0,
LightType::DeltaPosition _ => false,
};
let (light_type, _) = if is_constant_zero {
(LightType::DeltaPosition, None)
} else { } else {
LightType::Area (LightType::Area, Some(alpha))
}; };
let mi = match medium { let mi = match medium {
@ -118,7 +127,6 @@ pub fn create(
} }
None => MediumInterface::default(), None => MediumInterface::default(),
}; };
let base = LightBase::new(light_type, render_from_light, mi); let base = LightBase::new(light_type, render_from_light, mi);
if let Some(ref img) = image { if let Some(ref img) = image {
@ -135,20 +143,30 @@ pub fn create(
let is_triangle_or_bilinear = matches!(*shape, Shape::Triangle(_) | Shape::BilinearPatch(_)); let is_triangle_or_bilinear = matches!(*shape, Shape::Triangle(_) | Shape::BilinearPatch(_));
if render_from_light.has_scale(None) && !is_triangle_or_bilinear { if render_from_light.has_scale(None) && !is_triangle_or_bilinear {
eprintln!( println!(
"Warning: scaling detected in rendering-to-light transform; \ "Scaling detected in rendering to light space transformation! \
image may have errors." Proceed at your own risk; your image may have errors."
); );
} }
let shape_ptr = shape.upload(arena);
let image_ptr = image
.as_ref()
.map(|img| arena.alloc(*img.device()))
.unwrap_or(Ptr::null());
let colorspace_ptr = image_color_space
.map(|cs| cs.upload(arena))
.unwrap_or(Ptr::null());
let lemit_ptr = arena.alloc(lookup_spectrum(l_for_scale).device());
let specific = DiffuseAreaLight { let specific = DiffuseAreaLight {
base, base,
area: shape.area(), area: shape.area(),
shape: arena.alloc(*shape), shape: shape_ptr,
alpha: alpha_ptr, alpha: alpha_ptr,
image: arena.alloc(image), image: image_ptr,
colorspace: arena.alloc_opt(image_color_space), colorspace: colorspace_ptr,
lemit: arena.alloc((*lookup_spectrum(l_for_scale)).clone()), lemit: lemit_ptr,
two_sided, two_sided,
scale, scale,
}; };

View file

@ -28,7 +28,7 @@ impl CreateDistantLight for DistantLight {
let lemit = lookup_spectrum(&le); let lemit = lookup_spectrum(&le);
Self { Self {
base, base,
lemit: Ptr::from(&*lemit), lemit: Ptr::from(&lemit.device()),
scale, scale,
scene_center: Point3f::default(), scene_center: Point3f::default(),
scene_radius: 0., scene_radius: 0.,

View file

@ -99,8 +99,8 @@ pub fn create(
let image_ptr = if !image.is_null() { let image_ptr = if !image.is_null() {
let distrib = PiecewiseConstant2D::from_image(&image); let distrib = PiecewiseConstant2D::from_image(&image);
let distrib_ptr = arena.alloc(distrib); let distrib_ptr = distrib.upload(arena);
let img_ptr = arena.alloc(image); let img_ptr = image.upload(arena);
(img_ptr, distrib_ptr) (img_ptr, distrib_ptr)
} else { } else {
(Ptr::null(), Ptr::null()) (Ptr::null(), Ptr::null())
@ -108,7 +108,7 @@ pub fn create(
let specific = GoniometricLight { let specific = GoniometricLight {
base, base,
iemit: arena.alloc(*iemit), iemit: arena.alloc(iemit.device()),
scale, scale,
image: image_ptr.0, image: image_ptr.0,
distrib: image_ptr.1, distrib: image_ptr.1,

View file

@ -4,7 +4,7 @@ use crate::core::spectrum::spectrum_to_photometric;
use crate::spectra::get_spectra_context; use crate::spectra::get_spectra_context;
use crate::utils::resolve_filename; use crate::utils::resolve_filename;
use crate::utils::sampling::{PiecewiseConstant2D, WindowedPiecewiseConstant2D}; use crate::utils::sampling::{PiecewiseConstant2D, WindowedPiecewiseConstant2D};
use crate::{Arena, FileLoc, ParameterDictionary, ArenaUpload, Upload}; use crate::{Arena, FileLoc, ParameterDictionary};
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use rayon::prelude::*; use rayon::prelude::*;
use shared::core::camera::CameraTransform; use shared::core::camera::CameraTransform;
@ -63,7 +63,7 @@ pub fn create(
} }
let lemit = lookup_spectrum(&spectrum); let lemit = lookup_spectrum(&spectrum);
let light = UniformInfiniteLight::new(render_from_light, scale, arena.alloc(*lemit)); let light = UniformInfiniteLight::new(render_from_light, scale, lemit.upload(arena));
return Ok(Light::InfiniteUniform(light)); return Ok(Light::InfiniteUniform(light));
} }
@ -140,9 +140,9 @@ fn create_image_light(
render_from_light, render_from_light,
scale, scale,
image_ptr, image_ptr,
arena.alloc(image_cs), image_cs.upload(arena),
arena.alloc(distrib), distrib.upload(arena),
arena.alloc(compensated_distrib), compensated_distrib.upload(arena),
); );
Ok(Light::InfiniteImage(light)) Ok(Light::InfiniteImage(light))
@ -198,11 +198,11 @@ fn create_portal_light(
let light = PortalInfiniteLight::new( let light = PortalInfiniteLight::new(
render_from_light, render_from_light,
scale, scale,
arena.alloc(remapped), remapped.upload(arena),
arena.alloc(image_cs), image_cs.upload(arena),
portal, portal,
portal_frame, portal_frame,
arena.alloc(distribution), distribution.upload(arena),
); );
Ok(Light::InfinitePortal(light)) Ok(Light::InfinitePortal(light))

View file

@ -86,6 +86,6 @@ pub fn create(
None => MediumInterface::default(), None => MediumInterface::default(),
}; };
let specific = PointLight::new(final_render, mi, l, scale, arena); let specific = PointLight::new(final_render, mi, l, scale);
Ok(Light::Point(specific)) Ok(Light::Point(specific))
} }

View file

@ -42,7 +42,7 @@ impl CreateSpotLight for SpotLight {
); );
let i = lookup_spectrum(&le); let i = lookup_spectrum(&le);
let iemit = arena.alloc(i); let iemit = Ptr::from(&i.device());
Self { Self {
base, base,
iemit, iemit,

View file

@ -4,7 +4,7 @@ use crate::core::texture::SpectrumTexture;
use crate::globals::get_options; use crate::globals::get_options;
use crate::spectra::data::get_named_spectrum; use crate::spectra::data::get_named_spectrum;
use crate::utils::TextureParameterDictionary; use crate::utils::TextureParameterDictionary;
use crate::{Arena, FileLoc, Upload, ArenaUpload}; use crate::{Arena, FileLoc};
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use shared::core::material::Material; use shared::core::material::Material;
use shared::core::spectrum::Spectrum; use shared::core::spectrum::Spectrum;
@ -56,15 +56,15 @@ impl CreateMaterial for CoatedDiffuseMaterial {
let remap_roughness = parameters.get_one_bool("remaproughness", true)?; let remap_roughness = parameters.get_one_bool("remaproughness", true)?;
let specific = CoatedDiffuseMaterial::new( let specific = CoatedDiffuseMaterial::new(
arena.upload(reflectance), reflectance.upload(arena),
arena.upload(u_roughness), u_roughness.upload(arena),
arena.upload(v_roughness), v_roughness.upload(arena),
arena.upload(thickness), thickness.upload(arena),
arena.upload(albedo), albedo.upload(arena),
arena.upload(g), g.upload(arena),
arena.upload(displacement), eta.upload(arena),
arena.alloc(eta), displacement.upload(arena),
arena.alloc(normal_map), normal_map.upload(arena),
remap_roughness, remap_roughness,
max_depth as u32, max_depth as u32,
n_samples as u32, n_samples as u32,
@ -154,19 +154,19 @@ impl CreateMaterial for CoatedConductorMaterial {
let remap_roughness = parameters.get_one_bool("remaproughness", true)?; let remap_roughness = parameters.get_one_bool("remaproughness", true)?;
let material = Self::new( let material = Self::new(
arena.upload(displacement) normal_map.upload(arena),
arena.upload(interface_u_roughness), displacement.upload(arena),
arena.upload(interface_v_roughness), interface_u_roughness.upload(arena),
arena.upload(thickness), interface_v_roughness.upload(arena),
arena.upload(g), thickness.upload(arena),
arena.upload(albedo), interface_eta.upload(arena),
arena.upload(conductor_u_roughness), g.upload(arena),
arena.upload(conductor_v_roughness), albedo.upload(arena),
arena.upload(conductor_eta), conductor_u_roughness.upload(arena),
arena.upload(k), conductor_v_roughness.upload(arena),
arena.upload(reflectance), conductor_eta.upload(arena),
arena.alloc(normal_map), k.upload(arena),
arena.alloc(interface_eta), reflectance.upload(arena),
max_depth as u32, max_depth as u32,
n_samples as u32, n_samples as u32,
remap_roughness, remap_roughness,

View file

@ -2,15 +2,15 @@ use crate::core::image::Image;
use crate::core::material::CreateMaterial; use crate::core::material::CreateMaterial;
use crate::core::texture::SpectrumTexture; use crate::core::texture::SpectrumTexture;
use crate::spectra::get_colorspace_device; use crate::spectra::get_colorspace_device;
use crate::{Arena, FileLoc};
use crate::utils::TextureParameterDictionary; use crate::utils::TextureParameterDictionary;
use crate::{Arena, FileLoc, Upload, ArenaUpload};
use anyhow::Result;
use shared::bxdfs::HairBxDF; use shared::bxdfs::HairBxDF;
use shared::core::material::Material; use shared::core::material::Material;
use shared::core::spectrum::Spectrum; use shared::core::spectrum::Spectrum;
use shared::core::texture::SpectrumType; use shared::core::texture::SpectrumType;
use shared::materials::complex::*; use shared::materials::complex::*;
use shared::textures::SpectrumConstantTexture; use shared::textures::SpectrumConstantTexture;
use anyhow::Result;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
@ -30,7 +30,8 @@ impl CreateMaterial for HairMaterial {
let pheomelanin = parameters.get_float_texture_or_null("pheomelanin")?; let pheomelanin = parameters.get_float_texture_or_null("pheomelanin")?;
let has_melanin = eumelanin.is_some() || pheomelanin.is_some(); let has_melanin = eumelanin.is_some() || pheomelanin.is_some();
let sigma_a = if sigma_a.is_none() && reflectance.is_none() && !has_melanin { // Default distribution if nothing is spceified
let sigma_a = if sigma_a.is_none() && !reflectance.is_none() && !has_melanin {
let stdcs = get_colorspace_device(); let stdcs = get_colorspace_device();
let default_rgb = HairBxDF::sigma_a_from_concentration(1.3, 0.0, stdcs); let default_rgb = HairBxDF::sigma_a_from_concentration(1.3, 0.0, stdcs);
let spectrum = Spectrum::RGBUnbounded(default_rgb); let spectrum = Spectrum::RGBUnbounded(default_rgb);
@ -44,23 +45,21 @@ impl CreateMaterial for HairMaterial {
let beta_m = parameters.get_float_texture("beta_m", 0.3)?; let beta_m = parameters.get_float_texture("beta_m", 0.3)?;
let beta_n = parameters.get_float_texture("beta_n", 0.3)?; let beta_n = parameters.get_float_texture("beta_n", 0.3)?;
let alpha = parameters.get_float_texture("alpha", 2.)?; let alpha = parameters.get_float_texture("alpha", 2.)?;
let material = HairMaterial::new( let material = HairMaterial::new(
arena.upload(sigma_a), sigma_a.upload(arena),
arena.upload(reflectance), reflectance.upload(arena),
arena.upload(eumelanin), eumelanin.upload(arena),
arena.upload(pheomelanin), pheomelanin.upload(arena),
arena.upload(eta), eta.upload(arena),
arena.upload(beta_m), beta_m.upload(arena),
arena.upload(beta_n), beta_n.upload(arena),
arena.upload(alpha), alpha.upload(arena),
); );
Ok(Material::Hair(material)) Ok(Material::Hair(material))
} }
} }
impl CreateMaterial for SubsurfaceMaterial { impl CreateMaterial for SubsurfaceMaterial {
fn create( fn create(
_parameters: &TextureParameterDictionary, _parameters: &TextureParameterDictionary,

View file

@ -1,6 +1,6 @@
use super::*; use super::*;
use crate::globals::get_options; use crate::globals::get_options;
use shared::utils::math::compute_radical_inverse_permutations; use crate::utils::math::compute_radical_inverse_permutations;
use anyhow::{Result, anyhow}; use anyhow::{Result, anyhow};
use shared::core::geometry::Point2i; use shared::core::geometry::Point2i;
use shared::core::sampler::{HaltonSampler, MAX_HALTON_RESOLUTION, RandomizeStrategy}; use shared::core::sampler::{HaltonSampler, MAX_HALTON_RESOLUTION, RandomizeStrategy};
@ -21,7 +21,7 @@ impl CreateHaltonSampler for HaltonSampler {
randomize: RandomizeStrategy, randomize: RandomizeStrategy,
seed: u64, seed: u64,
) -> Self { ) -> Self {
let digit_permutations = compute_radical_inverse_permutations(seed); let (_, digit_permutations) = compute_radical_inverse_permutations(seed);
let mut base_scales = [0u64; 2]; let mut base_scales = [0u64; 2];
let mut base_exponents = [0u64; 2]; let mut base_exponents = [0u64; 2];
let bases = [2, 3]; let bases = [2, 3];

View file

@ -258,29 +258,21 @@ impl TriQuadMesh {
TriangleMesh::new( TriangleMesh::new(
render_from_object, render_from_object,
reverse_orientation, reverse_orientation,
&self.tri_indices, self.tri_indices,
&self.p, self.p,
&self.n, self.n,
&Vec::new(), Vec::new(),
&self.uv, self.uv,
&self.face_indices, self.face_indices,
) )
} }
} }
pub trait ReadTriangleMesh { impl TriangleMesh {
pub fn from_ply<P: AsRef<Path>>( pub fn from_ply<P: AsRef<Path>>(
filename: P, filename: P,
render_from_object: &Transform, render_from_object: &Transform,
reverse_orientation: bool, reverse_orientation: bool,
) -> AnyResult<Self>;
}
impl ReadTriangleMesh for TriangleMesh {
fn from_ply<P: AsRef<Path>>(
filename: P,
render_from_object: &Transform,
reverse_orientation: bool,
) -> AnyResult<Self> { ) -> AnyResult<Self> {
let mesh = TriQuadMesh::read_ply(filename)?; let mesh = TriQuadMesh::read_ply(filename)?;
Ok(mesh.into_triangle_mesh(render_from_object, reverse_orientation)) Ok(mesh.into_triangle_mesh(render_from_object, reverse_orientation))

View file

@ -12,8 +12,8 @@ pub trait CreateRGBColorSpace {
r: Point2f, r: Point2f,
g: Point2f, g: Point2f,
b: Point2f, b: Point2f,
illuminant: &DenselySampledSpectrum, illuminant: Arc<DenselySampledSpectrum>,
rgb_to_spectrum_table: &RGBToSpectrumTable, rgb_to_spectrum_table: Ptr<RGBToSpectrumTable>,
) -> Self; ) -> Self;
} }
@ -22,12 +22,11 @@ impl CreateRGBColorSpace for RGBColorSpace {
r: Point2f, r: Point2f,
g: Point2f, g: Point2f,
b: Point2f, b: Point2f,
illuminant: &DenselySampledSpectrum, illuminant: Arc<DenselySampledSpectrum>,
rgb_to_spectrum_table: &RGBToSpectrumTable, rgb_to_spectrum_table: Ptr<RGBToSpectrumTable>,
) -> Self { ) -> Self {
let stdspec = get_spectra_context(); let stdspec = get_spectra_context();
let illum_ptr = Ptr::from(illuminant); let illum_spectrum = Spectrum::Dense(illuminant.as_ref().clone());
let illum_spectrum = Spectrum::Dense(illum_ptr);
let w_xyz: XYZ = illum_spectrum.to_xyz(&stdspec); let w_xyz: XYZ = illum_spectrum.to_xyz(&stdspec);
let w = w_xyz.xy(); let w = w_xyz.xy();
@ -50,8 +49,8 @@ impl CreateRGBColorSpace for RGBColorSpace {
g, g,
b, b,
w, w,
illuminant: illum_ptr, illuminant: Ptr::from(illuminant.as_ref()),
rgb_to_spectrum_table: Ptr::from(rgb_to_spectrum_table), rgb_to_spectrum_table,
xyz_from_rgb, xyz_from_rgb,
rgb_from_xyz, rgb_from_xyz,
} }

View file

@ -1,7 +1,7 @@
use shared::Float;
use shared::core::spectrum::Spectrum; use shared::core::spectrum::Spectrum;
use shared::spectra::cie::*; use shared::spectra::cie::*;
use shared::spectra::{DenselySampledSpectrum, PiecewiseLinearSpectrum}; use shared::spectra::{PiecewiseLinearSpectrum, DenselySampledSpectrum};
use shared::{gbox, leak, Float, Ptr};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::LazyLock; use std::sync::LazyLock;
@ -17,7 +17,7 @@ pub fn create_cie(data: &[Float]) -> DenselySampledSpectrum {
.collect(); .collect();
let buffer = PiecewiseLinearSpectrum::new(lambdas, data.to_vec()); let buffer = PiecewiseLinearSpectrum::new(lambdas, data.to_vec());
let spec = Spectrum::Piecewise(Ptr::from(&*buffer)); let spec = Spectrum::Piecewise(buffer);
DenselySampledSpectrum::from_spectrum(&spec) DenselySampledSpectrum::from_spectrum(&spec)
} }
@ -27,7 +27,7 @@ pub static NAMED_SPECTRA: LazyLock<HashMap<String, Spectrum>> = LazyLock::new(||
macro_rules! add { macro_rules! add {
($name:expr, $data:expr, $norm:expr) => { ($name:expr, $data:expr, $norm:expr) => {
let buffer = PiecewiseLinearSpectrum::from_interleaved($data, $norm); let buffer = PiecewiseLinearSpectrum::from_interleaved($data, $norm);
let spectrum = Spectrum::Piecewise(leak(buffer)); let spectrum = Spectrum::Piecewise(buffer);
m.insert($name.to_string(), spectrum); m.insert($name.to_string(), spectrum);
}; };
} }
@ -49,6 +49,7 @@ pub static NAMED_SPECTRA: LazyLock<HashMap<String, Spectrum>> = LazyLock::new(||
add!("stdillum-F12", &CIE_ILLUM_F12, true); add!("stdillum-F12", &CIE_ILLUM_F12, true);
add!("illum-acesD60", &ACES_ILLUM_D60, true); add!("illum-acesD60", &ACES_ILLUM_D60, true);
// --- Glasses ---
add!("glass-BK7", &GLASS_BK7_ETA, false); add!("glass-BK7", &GLASS_BK7_ETA, false);
add!("glass-BAF10", &GLASS_BAF10_ETA, false); add!("glass-BAF10", &GLASS_BAF10_ETA, false);
add!("glass-FK51A", &GLASS_FK51A_ETA, false); add!("glass-FK51A", &GLASS_FK51A_ETA, false);
@ -57,6 +58,7 @@ pub static NAMED_SPECTRA: LazyLock<HashMap<String, Spectrum>> = LazyLock::new(||
add!("glass-F10", &GLASS_SF10_ETA, false); add!("glass-F10", &GLASS_SF10_ETA, false);
add!("glass-F11", &GLASS_SF11_ETA, false); add!("glass-F11", &GLASS_SF11_ETA, false);
// --- Metals ---
add!("metal-Ag-eta", &AG_ETA, false); add!("metal-Ag-eta", &AG_ETA, false);
add!("metal-Ag-k", &AG_K, false); add!("metal-Ag-k", &AG_K, false);
add!("metal-Al-eta", &AL_ETA, false); add!("metal-Al-eta", &AL_ETA, false);
@ -72,70 +74,87 @@ pub static NAMED_SPECTRA: LazyLock<HashMap<String, Spectrum>> = LazyLock::new(||
add!("metal-TiO2-eta", &TIO2_ETA, false); add!("metal-TiO2-eta", &TIO2_ETA, false);
add!("metal-TiO2-k", &TIO2_K, false); add!("metal-TiO2-k", &TIO2_K, false);
// --- Canon EOS 100D ---
add!("canon_eos_100d_r", &CANON_EOS_100D_R, false); add!("canon_eos_100d_r", &CANON_EOS_100D_R, false);
add!("canon_eos_100d_g", &CANON_EOS_100D_G, false); add!("canon_eos_100d_g", &CANON_EOS_100D_G, false);
add!("canon_eos_100d_b", &CANON_EOS_100D_B, false); add!("canon_eos_100d_b", &CANON_EOS_100D_B, false);
// --- Canon EOS 1DX MkII ---
add!("canon_eos_1dx_mkii_r", &CANON_EOS_1DX_MKII_R, false); add!("canon_eos_1dx_mkii_r", &CANON_EOS_1DX_MKII_R, false);
add!("canon_eos_1dx_mkii_g", &CANON_EOS_1DX_MKII_G, false); add!("canon_eos_1dx_mkii_g", &CANON_EOS_1DX_MKII_G, false);
add!("canon_eos_1dx_mkii_b", &CANON_EOS_1DX_MKII_B, false); add!("canon_eos_1dx_mkii_b", &CANON_EOS_1DX_MKII_B, false);
// --- Canon EOS 200D ---
add!("canon_eos_200d_r", &CANON_EOS_200D_R, false); add!("canon_eos_200d_r", &CANON_EOS_200D_R, false);
add!("canon_eos_200d_g", &CANON_EOS_200D_G, false); add!("canon_eos_200d_g", &CANON_EOS_200D_G, false);
add!("canon_eos_200d_b", &CANON_EOS_200D_B, false); add!("canon_eos_200d_b", &CANON_EOS_200D_B, false);
// --- Canon EOS 200D MkII ---
add!("canon_eos_200d_mkii_r", &CANON_EOS_200D_MKII_R, false); add!("canon_eos_200d_mkii_r", &CANON_EOS_200D_MKII_R, false);
add!("canon_eos_200d_mkii_g", &CANON_EOS_200D_MKII_G, false); add!("canon_eos_200d_mkii_g", &CANON_EOS_200D_MKII_G, false);
add!("canon_eos_200d_mkii_b", &CANON_EOS_200D_MKII_B, false); add!("canon_eos_200d_mkii_b", &CANON_EOS_200D_MKII_B, false);
// --- Canon EOS 5D ---
add!("canon_eos_5d_r", &CANON_EOS_5D_R, false); add!("canon_eos_5d_r", &CANON_EOS_5D_R, false);
add!("canon_eos_5d_g", &CANON_EOS_5D_G, false); add!("canon_eos_5d_g", &CANON_EOS_5D_G, false);
add!("canon_eos_5d_b", &CANON_EOS_5D_B, false); add!("canon_eos_5d_b", &CANON_EOS_5D_B, false);
// --- Canon EOS 5D MkII ---
add!("canon_eos_5d_mkii_r", &CANON_EOS_5D_MKII_R, false); add!("canon_eos_5d_mkii_r", &CANON_EOS_5D_MKII_R, false);
add!("canon_eos_5d_mkii_g", &CANON_EOS_5D_MKII_G, false); add!("canon_eos_5d_mkii_g", &CANON_EOS_5D_MKII_G, false);
add!("canon_eos_5d_mkii_b", &CANON_EOS_5D_MKII_B, false); add!("canon_eos_5d_mkii_b", &CANON_EOS_5D_MKII_B, false);
// --- Canon EOS 5D MkIII ---
add!("canon_eos_5d_mkiii_r", &CANON_EOS_5D_MKIII_R, false); add!("canon_eos_5d_mkiii_r", &CANON_EOS_5D_MKIII_R, false);
add!("canon_eos_5d_mkiii_g", &CANON_EOS_5D_MKIII_G, false); add!("canon_eos_5d_mkiii_g", &CANON_EOS_5D_MKIII_G, false);
add!("canon_eos_5d_mkiii_b", &CANON_EOS_5D_MKIII_B, false); add!("canon_eos_5d_mkiii_b", &CANON_EOS_5D_MKIII_B, false);
// --- Canon EOS 5D MkIV ---
add!("canon_eos_5d_mkiv_r", &CANON_EOS_5D_MKIV_R, false); add!("canon_eos_5d_mkiv_r", &CANON_EOS_5D_MKIV_R, false);
add!("canon_eos_5d_mkiv_g", &CANON_EOS_5D_MKIV_G, false); add!("canon_eos_5d_mkiv_g", &CANON_EOS_5D_MKIV_G, false);
add!("canon_eos_5d_mkiv_b", &CANON_EOS_5D_MKIV_B, false); add!("canon_eos_5d_mkiv_b", &CANON_EOS_5D_MKIV_B, false);
// --- Canon EOS 5DS ---
add!("canon_eos_5ds_r", &CANON_EOS_5DS_R, false); add!("canon_eos_5ds_r", &CANON_EOS_5DS_R, false);
add!("canon_eos_5ds_g", &CANON_EOS_5DS_G, false); add!("canon_eos_5ds_g", &CANON_EOS_5DS_G, false);
add!("canon_eos_5ds_b", &CANON_EOS_5DS_B, false); add!("canon_eos_5ds_b", &CANON_EOS_5DS_B, false);
// --- Canon EOS M ---
add!("canon_eos_m_r", &CANON_EOS_M_R, false); add!("canon_eos_m_r", &CANON_EOS_M_R, false);
add!("canon_eos_m_g", &CANON_EOS_M_G, false); add!("canon_eos_m_g", &CANON_EOS_M_G, false);
add!("canon_eos_m_b", &CANON_EOS_M_B, false); add!("canon_eos_m_b", &CANON_EOS_M_B, false);
// --- Hasselblad L1D 20C ---
add!("hasselblad_l1d_20c_r", &HASSELBLAD_L1D_20C_R, false); add!("hasselblad_l1d_20c_r", &HASSELBLAD_L1D_20C_R, false);
add!("hasselblad_l1d_20c_g", &HASSELBLAD_L1D_20C_G, false); add!("hasselblad_l1d_20c_g", &HASSELBLAD_L1D_20C_G, false);
add!("hasselblad_l1d_20c_b", &HASSELBLAD_L1D_20C_B, false); add!("hasselblad_l1d_20c_b", &HASSELBLAD_L1D_20C_B, false);
// --- Nikon D810 ---
add!("nikon_d810_r", &NIKON_D810_R, false); add!("nikon_d810_r", &NIKON_D810_R, false);
add!("nikon_d810_g", &NIKON_D810_G, false); add!("nikon_d810_g", &NIKON_D810_G, false);
add!("nikon_d810_b", &NIKON_D810_B, false); add!("nikon_d810_b", &NIKON_D810_B, false);
// --- Nikon D850 ---
add!("nikon_d850_r", &NIKON_D850_R, false); add!("nikon_d850_r", &NIKON_D850_R, false);
add!("nikon_d850_g", &NIKON_D850_G, false); add!("nikon_d850_g", &NIKON_D850_G, false);
add!("nikon_d850_b", &NIKON_D850_B, false); add!("nikon_d850_b", &NIKON_D850_B, false);
// --- Sony ILCE 6400 ---
add!("sony_ilce_6400_r", &SONY_ILCE_6400_R, false); add!("sony_ilce_6400_r", &SONY_ILCE_6400_R, false);
add!("sony_ilce_6400_g", &SONY_ILCE_6400_G, false); add!("sony_ilce_6400_g", &SONY_ILCE_6400_G, false);
add!("sony_ilce_6400_b", &SONY_ILCE_6400_B, false); add!("sony_ilce_6400_b", &SONY_ILCE_6400_B, false);
// --- Sony ILCE 7M3 ---
add!("sony_ilce_7m3_r", &SONY_ILCE_7M3_R, false); add!("sony_ilce_7m3_r", &SONY_ILCE_7M3_R, false);
add!("sony_ilce_7m3_g", &SONY_ILCE_7M3_G, false); add!("sony_ilce_7m3_g", &SONY_ILCE_7M3_G, false);
add!("sony_ilce_7m3_b", &SONY_ILCE_7M3_B, false); add!("sony_ilce_7m3_b", &SONY_ILCE_7M3_B, false);
// --- Sony ILCE 7RM3 ---
add!("sony_ilce_7rm3_r", &SONY_ILCE_7RM3_R, false); add!("sony_ilce_7rm3_r", &SONY_ILCE_7RM3_R, false);
add!("sony_ilce_7rm3_g", &SONY_ILCE_7RM3_G, false); add!("sony_ilce_7rm3_g", &SONY_ILCE_7RM3_G, false);
add!("sony_ilce_7rm3_b", &SONY_ILCE_7RM3_B, false); add!("sony_ilce_7rm3_b", &SONY_ILCE_7RM3_B, false);
// --- Sony ILCE 9 ---
add!("sony_ilce_9_r", &SONY_ILCE_9_R, false); add!("sony_ilce_9_r", &SONY_ILCE_9_R, false);
add!("sony_ilce_9_g", &SONY_ILCE_9_G, false); add!("sony_ilce_9_g", &SONY_ILCE_9_G, false);
add!("sony_ilce_9_b", &SONY_ILCE_9_B, false); add!("sony_ilce_9_b", &SONY_ILCE_9_B, false);

View file

@ -11,6 +11,7 @@ use std::sync::LazyLock;
pub mod colorspace; pub mod colorspace;
pub mod data; pub mod data;
pub mod dense;
pub mod piecewise; pub mod piecewise;
pub static CIE_X_DATA: LazyLock<DenselySampledSpectrum> = pub static CIE_X_DATA: LazyLock<DenselySampledSpectrum> =
@ -27,16 +28,19 @@ fn get_d65_illuminant_buffer() -> Arc<DenselySampledSpectrum> {
} }
pub fn cie_x() -> Spectrum { pub fn cie_x() -> Spectrum {
Spectrum::Dense(Ptr::from(&*CIE_X_DATA)) Spectrum::Dense(CIE_X_DATA)
} }
pub fn cie_y() -> Spectrum { pub fn cie_y() -> Spectrum {
Spectrum::Dense(Ptr::from(&*CIE_Y_DATA)) Spectrum::Dense(CIE_Y_DATA)
} }
pub fn cie_z() -> Spectrum { pub fn cie_z() -> Spectrum {
Spectrum::Dense(Ptr::from(&*CIE_Z_DATA)) Spectrum::Dense(CIE_Z_DATA)
} }
pub fn cie_d65() -> Spectrum { pub fn cie_d65() -> Spectrum {
Spectrum::Dense(Ptr::from(&*CIE_D65_DATA)) Spectrum::Dense(CIE_D65_DATA)
} }
pub fn get_spectra_context() -> StandardSpectra { pub fn get_spectra_context() -> StandardSpectra {
@ -48,7 +52,6 @@ pub fn get_spectra_context() -> StandardSpectra {
} }
} }
pub static SRGB: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| { pub static SRGB: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| {
let illum = get_d65_illuminant_buffer(); let illum = get_d65_illuminant_buffer();
let r = Point2f::new(0.64, 0.33); let r = Point2f::new(0.64, 0.33);
@ -56,7 +59,7 @@ pub static SRGB: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| {
let b = Point2f::new(0.15, 0.06); let b = Point2f::new(0.15, 0.06);
let table_ptr = Ptr::from(&*SRGB_TABLE); let table_ptr = Ptr::from(&*SRGB_TABLE);
Arc::new(RGBColorSpace::new(r, g, b, &illum, &table_ptr)) Arc::new(RGBColorSpace::new(r, g, b, illum, table_ptr))
}); });
pub static DCI_P3: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| { pub static DCI_P3: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| {
@ -65,7 +68,7 @@ pub static DCI_P3: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| {
let g = Point2f::new(0.265, 0.690); let g = Point2f::new(0.265, 0.690);
let b = Point2f::new(0.150, 0.060); let b = Point2f::new(0.150, 0.060);
let table_ptr = Ptr::from(&*DCI_P3_TABLE); let table_ptr = Ptr::from(&*DCI_P3_TABLE);
Arc::new(RGBColorSpace::new(r, g, b, &illum, &table_ptr)) Arc::new(RGBColorSpace::new(r, g, b, illum, table_ptr))
}); });
pub static REC2020: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| { pub static REC2020: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
@ -74,7 +77,7 @@ pub static REC2020: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
let g = Point2f::new(0.170, 0.797); let g = Point2f::new(0.170, 0.797);
let b = Point2f::new(0.131, 0.046); let b = Point2f::new(0.131, 0.046);
let table_ptr = Ptr::from(&*REC2020_TABLE); let table_ptr = Ptr::from(&*REC2020_TABLE);
Arc::new(RGBColorSpace::new(r, g, b, &illum, &table_ptr)) Arc::new(RGBColorSpace::new(r, g, b, illum, table_ptr))
}); });
pub static ACES: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| { pub static ACES: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
@ -82,8 +85,8 @@ pub static ACES: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
let r = Point2f::new(0.7347, 0.2653); let r = Point2f::new(0.7347, 0.2653);
let g = Point2f::new(0.0000, 1.0000); let g = Point2f::new(0.0000, 1.0000);
let b = Point2f::new(0.0001, -0.0770); let b = Point2f::new(0.0001, -0.0770);
let table_ptr = Ptr::from(&ACES_TABLE); let table_ptr = Ptr::from(&ACES_TABLE.view);
Arc::new(RGBColorSpace::new(r, g, b, &illum, &table_ptr)) Arc::new(RGBColorSpace::new(r, g, b, illum, table_ptr))
}); });
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -117,10 +120,10 @@ pub fn get_colorspace_context() -> StandardColorSpaces {
pub fn get_colorspace_device() -> DeviceStandardColorSpaces { pub fn get_colorspace_device() -> DeviceStandardColorSpaces {
DeviceStandardColorSpaces { DeviceStandardColorSpaces {
srgb: Ptr::from(&**SRGB), srgb: Ptr::from(&*SRGB),
dci_p3: Ptr::from(&**DCI_P3), dci_p3: Ptr::from(&*DCI_P3),
rec2020: Ptr::from(&**REC2020), rec2020: Ptr::from(&*REC2020),
aces2065_1: Ptr::from(&**ACES), aces2065_1: Ptr::from(&*ACES),
} }
} }
@ -133,11 +136,6 @@ pub fn default_colorspace_arc() -> Arc<RGBColorSpace> {
Arc::new(default_colorspace()) Arc::new(default_colorspace())
} }
pub fn default_colorspace_ref() -> &'static RGBColorSpace {
static CS: OnceLock<RGBColorSpace> = OnceLock::new();
CS.get_or_init(|| stdcs.srgb)
}
pub fn default_illuminant() -> Spectrum { pub fn default_illuminant() -> Spectrum {
Spectrum::Dense(default_colorspace().illuminant) Spectrum::Dense(default_colorspace().illuminant)
} }

View file

@ -21,6 +21,7 @@ use shared::utils::Transform;
use shared::Float; use shared::Float;
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
// use crate::utils::{FileLoc, TextureParameterDictionary};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct ImageTextureBase { pub struct ImageTextureBase {

59
src/utils/math.rs Normal file
View file

@ -0,0 +1,59 @@
use half::f16;
use shared::utils::hash::hash_buffer;
use shared::utils::math::{permutation_element, DigitPermutation, PRIMES};
use shared::Float;
#[inline(always)]
pub fn f16_to_f32(bits: u16) -> f32 {
#[cfg(feature = "cuda")]
{
// Use hardware intrinsic on CUDA
// Cast bits to cuda_f16, then cast to f32
let half_val = unsafe { core::mem::transmute::<u16, cuda_std::f16>(bits) };
half_val.to_f32()
}
#[cfg(feature = "vulkan")]
{
// Use shared logic or spirv-std intrinsics if available.
// Sadly, f16 support in rust-gpu is still maturing.
// A manual bit-conversion function is often safest here.
f16_to_f32_software(bits)
}
#[cfg(not(any(feature = "cuda", feature = "vulkan")))]
{
f16::from_bits(bits).to_f32()
}
}
pub fn compute_radical_inverse_permutations(seed: u64) -> (Vec<u16>, Vec<DigitPermutation>) {
let temp_data: Vec<Vec<u16>> = PRIMES
.iter()
.map(|&base| DigitPermutation::new(base as i32, seed).permutations)
.collect();
let mut storage: Vec<u16> = Vec::with_capacity(temp_data.iter().map(|v| v.len()).sum());
for vec in &temp_data {
storage.extend_from_slice(vec);
}
let mut views = Vec::with_capacity(PRIMES.len());
// let mut current_offset = 0;
// let storage_base_ptr = storage.as_ptr();
for (i, &base) in PRIMES.iter().enumerate() {
let len = temp_data[i].len();
let n_digits = len as u32 / base as u32;
// let ptr_to_data = storage_base_ptr.add(current_offset);
views.push(DigitPermutation::new(base as i32, n_digits as u64));
// current_offset += len;
}
(storage, views)
}

View file

@ -4,6 +4,7 @@ pub mod containers;
pub mod error; pub mod error;
pub mod file; pub mod file;
pub mod io; pub mod io;
pub mod math;
pub mod mipmap; pub mod mipmap;
pub mod parallel; pub mod parallel;
pub mod parameters; pub mod parameters;
@ -18,7 +19,6 @@ pub use parameters::{
ParameterDictionary, ParsedParameter, ParsedParameterVector, TextureParameterDictionary, ParameterDictionary, ParsedParameter, ParsedParameterVector, TextureParameterDictionary,
}; };
pub use strings::*; pub use strings::*;
pub use upload::{Upload, ArenaUpload};
#[cfg(feature = "vulkan")] #[cfg(feature = "vulkan")]
pub type Arena = arena::Arena<backend::vulkan::VulkanAllocator>; pub type Arena = arena::Arena<backend::vulkan::VulkanAllocator>;
@ -28,28 +28,3 @@ pub type Arena = arena::Arena<backend::cuda::CudaAllocator>;
#[cfg(not(any(feature = "cuda", feature = "vulkan")))] #[cfg(not(any(feature = "cuda", feature = "vulkan")))]
pub type Arena = arena::Arena<backend::SystemAllocator>; pub type Arena = arena::Arena<backend::SystemAllocator>;
use half::f16;
#[inline(always)]
pub fn f16_to_f32(bits: u16) -> f32 {
#[cfg(feature = "cuda")]
{
// Use hardware intrinsic on CUDA
// Cast bits to cuda_f16, then cast to f32
let half_val = unsafe { core::mem::transmute::<u16, cuda_std::f16>(bits) };
half_val.to_f32()
}
#[cfg(feature = "vulkan")]
{
// Use shared logic or spirv-std intrinsics if available.
// Keep an eye on rust-gpu
f16_to_f32_software(bits)
}
#[cfg(not(any(feature = "cuda", feature = "vulkan")))]
{
f16::from_bits(bits).to_f32()
}
}

View file

@ -1,7 +1,7 @@
use crate::core::spectrum::SPECTRUM_FILE_CACHE; use crate::core::spectrum::SPECTRUM_FILE_CACHE;
use crate::spectra::piecewise::ReadFromFile;
use crate::core::texture::{FloatTexture, SpectrumTexture}; use crate::core::texture::{FloatTexture, SpectrumTexture};
use crate::spectra::data::get_named_spectrum; use crate::spectra::data::get_named_spectrum;
use crate::spectra::piecewise::ReadFromFile;
use crate::utils::FileLoc; use crate::utils::FileLoc;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use shared::core::color::RGB; use shared::core::color::RGB;
@ -136,18 +136,6 @@ impl PBRTParameter for bool {
} }
} }
impl PBRTParameter for RGB {
type Raw = Float;
const TYPE_NAME: &'static str = "rgb";
const N_PER_ITEM: usize = 3;
fn convert(v: &[Self::Raw]) -> Self {
RGB::new(v[0], v[1], v[2])
}
fn get_values(param: &ParsedParameter) -> &[Self::Raw] {
&param.floats
}
}
impl PBRTParameter for Float { impl PBRTParameter for Float {
type Raw = Float; type Raw = Float;
const TYPE_NAME: &'static str = "float"; const TYPE_NAME: &'static str = "float";
@ -293,12 +281,6 @@ impl ParameterDictionary {
Ok(dict) Ok(dict)
} }
pub fn colorspace(&self) -> &RGBColorSpace {
self.color_space
.as_deref()
.unwrap_or_else(|| crate::spectra::default_colorspace_ref())
}
fn check_parameter_types(&self) -> Result<()> { fn check_parameter_types(&self) -> Result<()> {
for p in &self.params { for p in &self.params {
match p.type_name.as_str() { match p.type_name.as_str() {
@ -462,10 +444,6 @@ impl ParameterDictionary {
self.lookup_single(name, def) self.lookup_single(name, def)
} }
pub fn get_one_rgb(&self, name: &str, def: RGB) -> Result<RGB> {
self.lookup_single(name, def)
}
pub fn get_float_array(&self, name: &str) -> Result<Vec<Float>> { pub fn get_float_array(&self, name: &str) -> Result<Vec<Float>> {
self.lookup_array(name) self.lookup_array(name)
} }
@ -498,10 +476,6 @@ impl ParameterDictionary {
self.lookup_array(name) self.lookup_array(name)
} }
pub fn get_rgb_array(&self, name: &str) -> Result<Vec<RGB>> {
self.lookup_array(name)
}
pub fn get_one_spectrum( pub fn get_one_spectrum(
&self, &self,
name: &str, name: &str,
@ -681,11 +655,11 @@ impl ParameterDictionary {
}) })
.unzip(); .unzip();
vec![Spectrum::Piecewise(leak(PiecewiseLinearSpectrum { vec![Spectrum::Piecewise(PiecewiseLinearSpectrum {
lambdas: gvec_from_slice(lambdas), lambdas: lambdas.as_ptr().into(),
values: gvec_from_slice(values), values: values.as_ptr().into(),
count: lambdas.len() as u32, count: lambdas.len() as u32,
}))] })]
} }
fn extract_file_spectrum(&self, param: &ParsedParameter) -> Vec<Spectrum> { fn extract_file_spectrum(&self, param: &ParsedParameter) -> Vec<Spectrum> {
@ -707,18 +681,18 @@ fn read_spectrum_from_file(filename: &str) -> Result<Spectrum, String> {
{ {
let cache = SPECTRUM_FILE_CACHE.lock(); let cache = SPECTRUM_FILE_CACHE.lock();
if let Some(s) = cache.get(&fn_key) { if let Some(s) = cache.get(&fn_key) {
return Ok(*s); // Spectrum is Copy, so just copy it return Ok(s.clone());
} }
} }
let pls = PiecewiseLinearSpectrum::read(&fn_key) let pls = PiecewiseLinearSpectrum::read(&fn_key)
.ok_or_else(|| format!("unable to read or parse spectrum file '{}'", fn_key))?; .ok_or_else(|| format!("unable to read or parse spectrum file '{}'", fn_key))?;
let spectrum = Spectrum::Piecewise(leak(pls)); let spectrum = Spectrum::Piecewise(*pls);
{ {
let mut cache = SPECTRUM_FILE_CACHE.lock(); let mut cache = SPECTRUM_FILE_CACHE.lock();
cache.insert(fn_key, spectrum); cache.insert(fn_key, spectrum.clone());
} }
Ok(spectrum) Ok(spectrum)

View file

@ -1,160 +0,0 @@
use crate::core::texture::{FloatTexture, SpectrumTexture};
use crate::Arena;
use shared::core::texture::{GPUFloatTexture, GPUSpectrumTexture};
use shared::textures::*;
use shared::Ptr;
use std::sync::Arc;
pub trait Upload {
type Target;
fn upload(self, arena: &Arena) -> Self::Target;
}
fn convert_float(tex: &FloatTexture, arena: &Arena) -> GPUFloatTexture {
match tex {
FloatTexture::Constant(t) => GPUFloatTexture::Constant(*t),
FloatTexture::Bilerp(t) => GPUFloatTexture::Bilerp(*t),
FloatTexture::Checkerboard(t) => GPUFloatTexture::Checkerboard(*t),
FloatTexture::Dots(t) => GPUFloatTexture::Dots(*t),
FloatTexture::FBm(t) => GPUFloatTexture::FBm(*t),
FloatTexture::Windy(t) => GPUFloatTexture::Windy(*t),
FloatTexture::Wrinkled(t) => GPUFloatTexture::Wrinkled(*t),
FloatTexture::Scaled(t) => {
let tex = arena.alloc(convert_float(&t.tex, arena));
let scale = arena.alloc(convert_float(&t.scale, arena));
GPUFloatTexture::Scaled(GPUFloatScaledTexture { tex, scale })
}
FloatTexture::Mix(t) => {
let tex1 = arena.alloc(convert_float(&t.tex1, arena));
let tex2 = arena.alloc(convert_float(&t.tex2, arena));
let amount = arena.alloc(convert_float(&t.amount, arena));
GPUFloatTexture::Mix(GPUFloatMixTexture { tex1, tex2, amount })
}
FloatTexture::DirectionMix(t) => {
let tex1 = arena.alloc(convert_float(&t.tex1, arena));
let tex2 = arena.alloc(convert_float(&t.tex2, arena));
GPUFloatTexture::DirectionMix(GPUFloatDirectionMixTexture {
tex1,
tex2,
dir: t.dir,
})
}
FloatTexture::Image(t) => {
let tex_obj = arena.get_texture_object(&t.base.mipmap);
GPUFloatTexture::Image(GPUFloatImageTexture {
mapping: t.base.mapping,
tex_obj,
scale: t.base.scale,
invert: t.base.invert,
})
}
}
}
fn convert_spectrum(tex: &SpectrumTexture, arena: &Arena) -> GPUSpectrumTexture {
match tex {
SpectrumTexture::Constant(t) => GPUSpectrumTexture::Constant(*t),
SpectrumTexture::Bilerp(t) => GPUSpectrumTexture::Bilerp(*t),
SpectrumTexture::Checkerboard(t) => GPUSpectrumTexture::Checkerboard(*t),
SpectrumTexture::Dots(t) => GPUSpectrumTexture::Dots(*t),
SpectrumTexture::Marble(t) => GPUSpectrumTexture::Marble(*t),
SpectrumTexture::Scaled(t) => {
let tex = arena.alloc(convert_spectrum(&t.tex, arena));
let scale = arena.alloc(convert_float(&t.scale, arena));
GPUSpectrumTexture::Scaled(GPUSpectrumScaledTexture { tex, scale })
}
SpectrumTexture::Mix(t) => {
let tex1 = arena.alloc(convert_spectrum(&t.tex1, arena));
let tex2 = arena.alloc(convert_spectrum(&t.tex2, arena));
let amount = arena.alloc(convert_float(&t.amount, arena));
GPUSpectrumTexture::Mix(GPUSpectrumMixTexture { tex1, tex2, amount })
}
SpectrumTexture::DirectionMix(t) => {
let tex1 = arena.alloc(convert_spectrum(&t.tex1, arena));
let tex2 = arena.alloc(convert_spectrum(&t.tex2, arena));
GPUSpectrumTexture::DirectionMix(GPUSpectrumDirectionMixTexture {
tex1,
tex2,
dir: t.dir,
})
}
SpectrumTexture::Image(t) => {
let tex_obj = arena.get_texture_object(&t.base.mipmap);
GPUSpectrumTexture::Image(GPUSpectrumImageTexture {
mapping: t.base.mapping,
tex_obj,
scale: t.base.scale,
invert: t.base.invert,
is_single_channel: t.base.mipmap.is_single_channel(),
spectrum_type: t.spectrum_type,
color_space: arena.alloc(
t.base
.mipmap
.color_space
.clone()
.unwrap_or_else(crate::spectra::default_colorspace),
),
})
}
}
}
impl Upload for Arc<FloatTexture> {
type Target = Ptr<GPUFloatTexture>;
fn upload(self, arena: &Arena) -> Self::Target {
arena.alloc(convert_float(&self, arena))
}
}
impl Upload for Arc<SpectrumTexture> {
type Target = Ptr<GPUSpectrumTexture>;
fn upload(self, arena: &Arena) -> Self::Target {
arena.alloc(convert_spectrum(&self, arena))
}
}
impl Upload for &FloatTexture {
type Target = Ptr<GPUFloatTexture>;
fn upload(self, arena: &Arena) -> Self::Target {
arena.alloc(convert_float(&self, arena))
}
}
impl Upload for &SpectrumTexture {
type Target = Ptr<GPUSpectrumTexture>;
fn upload(self, arena: &Arena) -> Self::Target {
arena.alloc(convert_spectrum(&self, arena))
}
}
impl Upload for Option<Arc<FloatTexture>> {
type Target = Ptr<GPUFloatTexture>;
fn upload(self, arena: &Arena) -> Self::Target {
arena.alloc_opt(self.map(|v| convert_float(&v, arena)))
}
}
impl Upload for Option<Arc<SpectrumTexture>> {
type Target = Ptr<GPUSpectrumTexture>;
fn upload(self, arena: &Arena) -> Self::Target {
arena.alloc_opt(self.map(|v| convert_spectrum(&v, arena)))
}
}
pub trait ArenaUpload {
fn upload<T: Upload>(&self, value: T) -> T::Target;
}
impl ArenaUpload for Arena {
fn upload<T: Upload>(&self, value: T) -> T::Target {
value.upload(self)
}
}