Compare commits
2 commits
050698c1d0
...
a14960562c
| Author | SHA1 | Date | |
|---|---|---|---|
| a14960562c | |||
| 44099dffa9 |
36 changed files with 1123 additions and 530 deletions
|
|
@ -1045,7 +1045,7 @@ const SRGB_TO_LINEAR_LUT: [Float; 256] = [
|
|||
pub const RES: u32 = 64;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
pub struct Coeffs {
|
||||
pub c0: Float,
|
||||
pub c1: Float,
|
||||
|
|
@ -1099,10 +1099,10 @@ impl Mul<Float> for Coeffs {
|
|||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct RGBToSpectrumTable {
|
||||
pub z_nodes: Ptr<Float>,
|
||||
pub coeffs: Ptr<Coeffs>,
|
||||
pub z_nodes: GVec<Float>,
|
||||
pub coeffs: GVec<Coeffs>,
|
||||
pub n_nodes: u32,
|
||||
}
|
||||
|
||||
|
|
@ -1113,7 +1113,7 @@ impl RGBToSpectrumTable {
|
|||
#[inline(always)]
|
||||
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;
|
||||
unsafe { *self.coeffs.add(offset as usize) }
|
||||
unsafe { *self.coeffs.as_ptr().add(offset as usize) }
|
||||
}
|
||||
|
||||
pub fn evaluate(&self, rgb: RGB) -> RGBSigmoidPolynomial {
|
||||
|
|
@ -1153,7 +1153,7 @@ impl RGBToSpectrumTable {
|
|||
let x = coord_a / z;
|
||||
let y = coord_b / z;
|
||||
|
||||
let z_nodes = unsafe { core::slice::from_raw_parts(self.z_nodes.as_raw(), RES as usize) };
|
||||
let z_nodes = &self.z_nodes;
|
||||
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 x_float = x * (RES - 1) as Float;
|
||||
|
|
|
|||
|
|
@ -18,5 +18,5 @@ pub mod textures;
|
|||
pub mod utils;
|
||||
|
||||
pub use core::pbrt::*;
|
||||
pub use utils::alloc::{gbox, gvec, gvec_from_slice, gvec_with_capacity, GVec, GBox};
|
||||
pub use utils::alloc::{gbox, gvec, gvec_from_slice, gvec_with_capacity, leak, GBox, GVec};
|
||||
pub use utils::{Array2D, PBRTOptions, Ptr, Transform};
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ impl CoatedDiffuseMaterial {
|
|||
thickness: Ptr<GPUFloatTexture>,
|
||||
albedo: Ptr<GPUSpectrumTexture>,
|
||||
g: Ptr<GPUFloatTexture>,
|
||||
eta: Ptr<Spectrum>,
|
||||
displacement: Ptr<GPUFloatTexture>,
|
||||
eta: Ptr<Spectrum>,
|
||||
normal_map: Ptr<Image>,
|
||||
remap_roughness: bool,
|
||||
max_depth: u32,
|
||||
|
|
@ -153,7 +153,6 @@ impl MaterialTrait for CoatedDiffuseMaterial {
|
|||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct CoatedConductorMaterial {
|
||||
normal_map: Ptr<Image>,
|
||||
displacement: Ptr<GPUFloatTexture>,
|
||||
interface_uroughness: Ptr<GPUFloatTexture>,
|
||||
interface_vroughness: Ptr<GPUFloatTexture>,
|
||||
|
|
@ -166,6 +165,7 @@ pub struct CoatedConductorMaterial {
|
|||
conductor_eta: Ptr<GPUSpectrumTexture>,
|
||||
k: Ptr<GPUSpectrumTexture>,
|
||||
reflectance: Ptr<GPUSpectrumTexture>,
|
||||
normal_map: Ptr<Image>,
|
||||
max_depth: u32,
|
||||
n_samples: u32,
|
||||
remap_roughness: bool,
|
||||
|
|
@ -175,12 +175,10 @@ pub struct CoatedConductorMaterial {
|
|||
impl CoatedConductorMaterial {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
normal_map: Ptr<Image>,
|
||||
displacement: Ptr<GPUFloatTexture>,
|
||||
interface_uroughness: Ptr<GPUFloatTexture>,
|
||||
interface_vroughness: Ptr<GPUFloatTexture>,
|
||||
thickness: Ptr<GPUFloatTexture>,
|
||||
interface_eta: Ptr<Spectrum>,
|
||||
g: Ptr<GPUFloatTexture>,
|
||||
albedo: Ptr<GPUSpectrumTexture>,
|
||||
conductor_uroughness: Ptr<GPUFloatTexture>,
|
||||
|
|
@ -188,6 +186,8 @@ impl CoatedConductorMaterial {
|
|||
conductor_eta: Ptr<GPUSpectrumTexture>,
|
||||
k: Ptr<GPUSpectrumTexture>,
|
||||
reflectance: Ptr<GPUSpectrumTexture>,
|
||||
normal_map: Ptr<Image>,
|
||||
interface_eta: Ptr<Spectrum>,
|
||||
max_depth: u32,
|
||||
n_samples: u32,
|
||||
remap_roughness: bool,
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ impl ColorSpaceId {
|
|||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct RGBColorSpace {
|
||||
pub r: Point2f,
|
||||
pub g: Point2f,
|
||||
|
|
@ -71,7 +71,7 @@ pub struct RGBColorSpace {
|
|||
pub xyz_from_rgb: SquareMatrix3f,
|
||||
pub rgb_from_xyz: SquareMatrix3f,
|
||||
pub illuminant: Ptr<DenselySampledSpectrum>,
|
||||
pub rgb_to_spectrum_table: RGBToSpectrumTable,
|
||||
pub rgb_to_spectrum_table: Ptr<RGBToSpectrumTable>,
|
||||
}
|
||||
|
||||
unsafe impl Send for RGBColorSpace {}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
extern crate alloc;
|
||||
|
||||
use crate::utils::ptr::Ptr;
|
||||
use alloc::alloc::Global;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
use core::alloc::{AllocError, Allocator, Layout};
|
||||
use core::ptr::NonNull;
|
||||
|
||||
|
|
@ -200,3 +201,9 @@ pub fn gvec_from_slice<T: Clone>(slice: &[T]) -> GVec<T> {
|
|||
pub fn gbox<T>(value: T) -> GBox<T> {
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -827,6 +827,12 @@ 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 {
|
||||
let base = PRIMES[base_index as usize] as u64;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use std::ops::Deref;
|
|||
use std::path::Path;
|
||||
|
||||
pub trait CreateRGBToSpectrumTable {
|
||||
fn from_data(z_nodes: &[Float], coeffs: &[Float]) -> Self;
|
||||
fn new(z_nodes: &[Float], coeffs: &[Float]) -> Self;
|
||||
fn load(base_dir: &Path, name: &str) -> Result<Self> where Self: Sized;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -349,4 +349,20 @@ impl HostImage {
|
|||
pub fn into_image(self) -> Image {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,14 +102,13 @@ pub fn create_light(
|
|||
arena,
|
||||
),
|
||||
"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
|
||||
)),
|
||||
_ => Err(anyhow!("{}: unknown light type \"{}\"", loc, name)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a diffuse area light bound to a specific shape
|
||||
pub fn create_area_light(
|
||||
render_from_light: Transform,
|
||||
medium: Option<Medium>,
|
||||
|
|
@ -119,15 +118,10 @@ pub fn create_area_light(
|
|||
alpha_tex: &FloatTexture,
|
||||
colorspace: Option<&RGBColorSpace>,
|
||||
arena: &mut Arena,
|
||||
) -> Result<Light> {
|
||||
crate::lights::diffuse::create(
|
||||
render_from_light,
|
||||
medium,
|
||||
parameters,
|
||||
loc,
|
||||
shape,
|
||||
alpha_tex,
|
||||
colorspace,
|
||||
arena,
|
||||
)
|
||||
) -> Result<Ptr<Light>> {
|
||||
let light = crate::lights::diffuse::create(
|
||||
render_from_light, medium, parameters, loc,
|
||||
shape, alpha_tex, colorspace, arena,
|
||||
)?;
|
||||
Ok(arena.alloc(light))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,164 +1,610 @@
|
|||
use shared::core::geometry::{Bounds3f, Point3i};
|
||||
use shared::core::medium::{GridMedium, HGPhaseFunction, HomogeneousMedium, RGBGridMedium};
|
||||
use crate::core::spectrum::spectrum_to_photometric;
|
||||
use crate::spectra::SRGB;
|
||||
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::spectra::{RGBIlluminantSpectrum, RGBUnboundedSpectrum, DenselySampledSpectrum};
|
||||
use shared::core::texture::SpectrumType;
|
||||
use shared::spectra::{
|
||||
ConstantSpectrum, DenselySampledSpectrum, RGBIlluminantSpectrum, RGBUnboundedSpectrum,
|
||||
};
|
||||
use shared::utils::containers::SampledGrid;
|
||||
use shared::{Float, Transform, core::medium::MajorantGrid};
|
||||
use shared::{Float, Ptr, Transform};
|
||||
|
||||
pub trait RGBGridMediumCreator {
|
||||
fn new(
|
||||
bounds: &Bounds3f,
|
||||
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;
|
||||
struct MeasuredSS {
|
||||
name: &'static str,
|
||||
sigma_prime_s: [Float; 3],
|
||||
sigma_a: [Float; 3],
|
||||
}
|
||||
|
||||
impl RGBGridMediumCreator for RGBGridMedium {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn new(
|
||||
bounds: &Bounds3f,
|
||||
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 {
|
||||
let mut majorant_grid = MajorantGrid::new(*bounds, Point3i::new(16, 16, 16));
|
||||
for z in 0..majorant_grid.res.x() {
|
||||
for y in 0..majorant_grid.res.y() {
|
||||
for x in 0..majorant_grid.res.x() {
|
||||
let bounds = majorant_grid.voxel_bounds(x, y, z);
|
||||
let convert = |s: &RGBUnboundedSpectrum| s.max_value();
|
||||
let max_sigma_t = sigma_a_grid.max_value_convert(bounds, convert)
|
||||
+ sigma_s_grid.max_value_convert(bounds, convert);
|
||||
majorant_grid.set(x, y, z, sigma_scale * max_sigma_t);
|
||||
}
|
||||
}
|
||||
}
|
||||
// From "A Practical Model for Subsurface Light Transport"
|
||||
// Jensen, Marschner, Levoy, Hanrahan — SIGGRAPH 2001
|
||||
// and "Acquiring Scattering Properties of Participating Media by Dilution"
|
||||
// Narasimhan, Gupta, Donner, Ramamoorthi, Nayar, Jensen — SIGGRAPH 2006
|
||||
static SUBSURFACE_TABLE: &[MeasuredSS] = &[
|
||||
MeasuredSS {
|
||||
name: "Apple",
|
||||
sigma_prime_s: [2.29, 2.39, 1.97],
|
||||
sigma_a: [0.0030, 0.0034, 0.046],
|
||||
},
|
||||
MeasuredSS {
|
||||
name: "Chicken1",
|
||||
sigma_prime_s: [0.15, 0.21, 0.38],
|
||||
sigma_a: [0.015, 0.077, 0.19],
|
||||
},
|
||||
MeasuredSS {
|
||||
name: "Chicken2",
|
||||
sigma_prime_s: [0.19, 0.25, 0.32],
|
||||
sigma_a: [0.018, 0.088, 0.20],
|
||||
},
|
||||
MeasuredSS {
|
||||
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],
|
||||
},
|
||||
];
|
||||
|
||||
Self {
|
||||
bounds: *bounds,
|
||||
render_from_medium: *render_from_medium,
|
||||
le_grid,
|
||||
le_scale,
|
||||
phase: HGPhaseFunction::new(g),
|
||||
sigma_a_grid,
|
||||
sigma_s_grid,
|
||||
sigma_scale,
|
||||
majorant_grid,
|
||||
}
|
||||
}
|
||||
fn get_medium_scattering_properties(name: &str) -> Option<(Spectrum, Spectrum)> {
|
||||
SUBSURFACE_TABLE.iter().find(|m| m.name == name).map(|m| {
|
||||
let sigma_a = Spectrum::RGBUnbounded(RGBUnboundedSpectrum::new(&SRGB, m.sigma_a.into()));
|
||||
let sigma_s =
|
||||
Spectrum::RGBUnbounded(RGBUnboundedSpectrum::new(&SRGB, m.sigma_prime_s.into()));
|
||||
(sigma_a, sigma_s)
|
||||
})
|
||||
}
|
||||
|
||||
pub trait GridMediumCreator {
|
||||
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;
|
||||
}
|
||||
|
||||
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.
|
||||
pub trait CreateMedium {
|
||||
fn create(
|
||||
name: &str,
|
||||
parameters: &ParameterDictionary,
|
||||
render_from_medium: Transform,
|
||||
loc: &FileLoc,
|
||||
arena: &Arena,
|
||||
) -> Result<Ptr<Medium>> {
|
||||
let medium = match name {
|
||||
"homogeneous" => create_homogeneous(parameters, loc, arena)?,
|
||||
"uniformgrid" => create_grid(parameters, render_from_medium, loc, arena)?,
|
||||
"rgbgrid" => create_rgb_grid(parameters, render_from_medium, loc, arena)?,
|
||||
// "cloud" => create_cloud(parameters, render_from_medium, loc, arena)?,
|
||||
// "nanovdb" => create_nanovdb(parameters, render_from_medium, loc, arena)?,
|
||||
_ => bail!("{}: unknown medium \"{}\"", loc, name),
|
||||
};
|
||||
Ok(arena.alloc(medium))
|
||||
}
|
||||
}
|
||||
|
||||
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 bounds = majorant_grid.voxel_bounds(x, y, z);
|
||||
majorant_grid.set(x, y, z, density_grid.max_value(bounds));
|
||||
}
|
||||
fn create_homogeneous(
|
||||
parameters: &ParameterDictionary,
|
||||
loc: &FileLoc,
|
||||
arena: &Arena,
|
||||
) -> Result<Medium> {
|
||||
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)
|
||||
};
|
||||
|
||||
Self {
|
||||
bounds: *bounds,
|
||||
render_from_medium: *render_from_medium,
|
||||
sigma_a_spec: sigma_a_spec.device(),
|
||||
sigma_s_spec: sigma_s_spec.device(),
|
||||
density_grid,
|
||||
phase: HGPhaseFunction::new(g),
|
||||
temperature_grid,
|
||||
le_spec: le_spec.device(),
|
||||
le_scale,
|
||||
is_emissive,
|
||||
majorant_grid,
|
||||
if sigma_a.is_none() {
|
||||
sigma_a = parameters
|
||||
.get_one_spectrum("sigma_a", None, SpectrumType::Unbounded)
|
||||
.or(Some(Spectrum::Constant(ConstantSpectrum::new(1.0))));
|
||||
}
|
||||
|
||||
if sigma_s.is_none() {
|
||||
sigma_s = parameters
|
||||
.get_one_spectrum("sigma_s", None, SpectrumType::Unbounded)
|
||||
.or(Some(Spectrum::Constant(ConstantSpectrum::new(1.0))));
|
||||
}
|
||||
|
||||
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(
|
||||
parameters: &ParameterDictionary,
|
||||
render_from_medium: shared::Transform,
|
||||
loc: &FileLoc,
|
||||
arena: &Arena,
|
||||
) -> Result<Medium> {
|
||||
let density = parameters.get_float_array("density")?;
|
||||
if density.is_empty() {
|
||||
bail!("{}: no \"density\" value provided for grid medium", loc);
|
||||
}
|
||||
|
||||
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),
|
||||
}))
|
||||
}
|
||||
|
||||
pub trait HomogeneousMediumCreator {
|
||||
fn new(
|
||||
sigma_a: Spectrum,
|
||||
sigma_s: Spectrum,
|
||||
sigma_scale: Float,
|
||||
le: Spectrum,
|
||||
le_scale: Float,
|
||||
g: Float,
|
||||
) -> Self;
|
||||
}
|
||||
fn create_rgb_grid(
|
||||
parameters: &ParameterDictionary,
|
||||
render_from_medium: shared::Transform,
|
||||
loc: &FileLoc,
|
||||
arena: &Arena,
|
||||
) -> Result<Medium> {
|
||||
let sigma_a_rgb = parameters.get_rgb_array("sigma_a")?;
|
||||
let sigma_s_rgb = parameters.get_rgb_array("sigma_s")?;
|
||||
|
||||
impl HomogeneousMediumCreator for HomogeneousMedium {
|
||||
fn new(
|
||||
sigma_a: Spectrum,
|
||||
sigma_s: Spectrum,
|
||||
sigma_scale: Float,
|
||||
le: Spectrum,
|
||||
le_scale: Float,
|
||||
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() {
|
||||
bail!("{}: RGB grid requires \"sigma_a\" and/or \"sigma_s\"", loc);
|
||||
}
|
||||
|
||||
sigma_a_spec.scale(sigma_scale);
|
||||
sigma_s_spec.scale(sigma_scale);
|
||||
le_spec.scale(le_scale);
|
||||
let n_density = if !sigma_a_rgb.is_empty() {
|
||||
if !sigma_s_rgb.is_empty() && sigma_a_rgb.len() != sigma_s_rgb.len() {
|
||||
bail!(
|
||||
"{}: different number of samples ({} vs {}) for \"sigma_a\" and \"sigma_s\"",
|
||||
loc,
|
||||
sigma_a_rgb.len(),
|
||||
sigma_s_rgb.len()
|
||||
);
|
||||
}
|
||||
sigma_a_rgb.len()
|
||||
} else {
|
||||
sigma_s_rgb.len()
|
||||
};
|
||||
|
||||
Self {
|
||||
sigma_a_spec: sigma_a_spec.device(),
|
||||
sigma_s_spec: sigma_s_spec.device(),
|
||||
le_spec: le_spec.device(),
|
||||
phase: HGPhaseFunction::new(g),
|
||||
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),
|
||||
}))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -556,7 +556,7 @@ impl ParserTarget for BasicSceneBuilder {
|
|||
self.current_accelerator
|
||||
.take()
|
||||
.expect("Accelerator not set before WorldBegin"),
|
||||
arena,
|
||||
&arena,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use crate::core::texture::{FloatTexture, SpectrumTexture};
|
|||
use crate::utils::parallel::{run_async, AsyncJob};
|
||||
use crate::utils::parameters::{NamedTextures, ParameterDictionary, TextureParameterDictionary};
|
||||
use crate::utils::resolve_filename;
|
||||
use crate::{Arena, FileLoc};
|
||||
use crate::{Arena, ArenaUpload, FileLoc, Upload};
|
||||
use anyhow::{anyhow, Result};
|
||||
use parking_lot::Mutex;
|
||||
use shared::core::camera::{Camera, CameraTransform};
|
||||
|
|
@ -556,7 +556,7 @@ impl BasicScene {
|
|||
loaded_shapes: &[Vec<Shape>],
|
||||
shape_entities: &[ShapeSceneEntity],
|
||||
textures: &NamedTextures,
|
||||
arena: &mut Arena,
|
||||
arena: &Arena,
|
||||
) -> HashMap<usize, Vec<Light>> {
|
||||
let light_state = self.light_state.lock();
|
||||
let mut shape_lights: HashMap<usize, Vec<Light>> = HashMap::new();
|
||||
|
|
@ -672,6 +672,53 @@ impl BasicScene {
|
|||
(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(
|
||||
entity: &ShapeSceneEntity,
|
||||
textures: &NamedTextures,
|
||||
|
|
@ -727,52 +774,30 @@ impl BasicScene {
|
|||
|
||||
let al_params = entity.light_index.map(|idx| &light_state.area_lights[idx]);
|
||||
|
||||
for shape in shapes {
|
||||
let area_light = al_params.and_then(|al_entity| {
|
||||
let colorspace_ref = 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(
|
||||
*entity.render_from_object,
|
||||
None,
|
||||
&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()),
|
||||
))
|
||||
};
|
||||
let built = Self::build_primitives_inner(
|
||||
shapes,
|
||||
mtl,
|
||||
&alpha_tex,
|
||||
mi.clone(),
|
||||
al_params,
|
||||
*entity.render_from_object,
|
||||
film_cs,
|
||||
arena,
|
||||
area_lights,
|
||||
);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -839,56 +864,33 @@ impl BasicScene {
|
|||
|
||||
let al_params = entity.light_index.map(|idx| &light_state.area_lights[idx]);
|
||||
|
||||
for shape in shapes {
|
||||
let area_light = al_params.and_then(|al_entity| {
|
||||
let colorspace_ref = 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(
|
||||
entity.transformed_base.render_from_object.start_transform,
|
||||
None,
|
||||
&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 built = Self::build_primitives_inner(
|
||||
shapes,
|
||||
mtl,
|
||||
&alpha_tex,
|
||||
mi.clone(),
|
||||
al_params,
|
||||
entity.transformed_base.render_from_object.start_transform,
|
||||
film_cs,
|
||||
arena,
|
||||
area_lights,
|
||||
);
|
||||
|
||||
for (shape, light_ptr, alpha_ptr) in built {
|
||||
let base_prim =
|
||||
if uploaded_light.is_null() && !mi.is_medium_transition() && alpha_tex.is_none() {
|
||||
Primitive::Simple(SimplePrimitive::new(shape_ptr, Ptr::from(&mtl)))
|
||||
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_ptr,
|
||||
mtl.upload(arena),
|
||||
uploaded_light,
|
||||
shape,
|
||||
arena.alloc(mtl),
|
||||
light_ptr,
|
||||
mi.clone(),
|
||||
alpha_tex
|
||||
.as_ref()
|
||||
.map(|t| t.upload(arena))
|
||||
.unwrap_or(Ptr::null()),
|
||||
alpha_ptr,
|
||||
))
|
||||
};
|
||||
|
||||
let base_ptr = arena.alloc(base_prim);
|
||||
|
||||
primitives.push(Primitive::Animated(AnimatedPrimitive {
|
||||
primitive: base_ptr,
|
||||
primitive: arena.alloc(base_prim),
|
||||
render_from_primitive: arena.alloc(entity.transformed_base.render_from_object),
|
||||
}));
|
||||
}
|
||||
|
|
@ -937,10 +939,7 @@ impl BasicScene {
|
|||
.get(&shape_ctx.entity_index)
|
||||
.and_then(|lights| lights.get(shape_ctx.shape_index));
|
||||
|
||||
let area_light = shape_lights_opt
|
||||
.map(|l| l.upload(arena))
|
||||
.unwrap_or(Ptr::null());
|
||||
|
||||
let light_ptr = arena.alloc_opt(shape_lights_opt);
|
||||
let shape_ptr = shape_ctx.shape;
|
||||
let prim = if area_light.is_null() && !mi.is_medium_transition() && alpha_tex.is_none()
|
||||
{
|
||||
|
|
@ -948,10 +947,10 @@ impl BasicScene {
|
|||
} else {
|
||||
Primitive::Geometric(GeometricPrimitive::new(
|
||||
shape_ptr,
|
||||
mtl.upload(arena),
|
||||
arena.alloc(mtl),
|
||||
area_light,
|
||||
mi.clone(),
|
||||
alpha_tex.upload(arena),
|
||||
arena.upload(alpha_tex),
|
||||
))
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
use crate::core::texture::FloatTexture;
|
||||
use crate::shapes::{BilinearPatchMesh, TriangleMesh};
|
||||
use crate::utils::{Arena, FileLoc, ParameterDictionary, resolve_filename};
|
||||
use crate::shapes::*;
|
||||
use crate::utils::resolve_filename;
|
||||
use crate::{Arena, FileLoc, ParameterDictionary};
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use parking_lot::Mutex;
|
||||
use shared::core::shape::*;
|
||||
use shared::Ptr;
|
||||
use shared::shapes::*;
|
||||
use shared::utils::Transform;
|
||||
use shared::{Transform, Ptr};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
|
@ -116,8 +116,8 @@ impl ShapeFactory for Shape {
|
|||
global_store.push(host_arc.clone());
|
||||
drop(global_store);
|
||||
|
||||
let n_tris = host_arc.device.n_triangles;
|
||||
let mesh_ptr = Ptr::from(&host_arc.device);
|
||||
let n_tris = host_arc..n_triangles;
|
||||
let mesh_ptr = Ptr::from(&host_arc);
|
||||
let shapes: Vec<Ptr<Shape>> = (0..n_tris)
|
||||
.map(|i| {
|
||||
let tri_shape = Shape::Triangle(TriangleShape {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ pub fn get_spectrum_cache() -> &'static InternCache<DenselySampledSpectrum> {
|
|||
|
||||
pub fn spectrum_to_photometric(s: Spectrum) -> Float {
|
||||
let effective_spectrum = match s {
|
||||
Spectrum::RGBIlluminant(ill) => &Spectrum::Dense(*ill.illuminant),
|
||||
Spectrum::RGBIlluminant(ill) => &Spectrum::Dense(ill.illuminant),
|
||||
_ => &s,
|
||||
};
|
||||
effective_spectrum.inner_product(&cie_y())
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
use crate::textures::*;
|
||||
use crate::utils::mipmap::{MIPMapFilterOptions, MIPMap};
|
||||
use crate::utils::TextureParameterDictionary;
|
||||
use crate::{Arena, FileLoc};
|
||||
use anyhow::{anyhow, Result};
|
||||
|
|
@ -9,8 +8,8 @@ use shared::core::geometry::Vector3f;
|
|||
use shared::core::image::WrapMode;
|
||||
use shared::core::texture::SpectrumType;
|
||||
use shared::core::texture::{
|
||||
CylindricalMapping, PlanarMapping, SphericalMapping, TextureEvalContext, TextureMapping2D,
|
||||
UVMapping, GPUFloatTexture, GPUSpectrumTexture
|
||||
CylindricalMapping, GPUFloatTexture, GPUSpectrumTexture, PlanarMapping, SphericalMapping,
|
||||
TextureEvalContext, TextureMapping2D, UVMapping,
|
||||
};
|
||||
use shared::spectra::{SampledSpectrum, SampledWavelengths};
|
||||
use shared::textures::*;
|
||||
|
|
@ -30,6 +29,7 @@ pub trait SpectrumTextureTrait {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[enum_dispatch(FloatTextureTrait)]
|
||||
pub enum FloatTexture {
|
||||
Constant(FloatConstantTexture),
|
||||
Checkerboard(FloatCheckerboardTexture),
|
||||
|
|
@ -40,6 +40,7 @@ pub enum FloatTexture {
|
|||
Mix(FloatMixTexture),
|
||||
DirectionMix(FloatDirectionMixTexture),
|
||||
// #[device(custom = "upload_image", variant_type = "GPUFloatImageTexture")]
|
||||
Scaled(FloatScaledTexture),
|
||||
Image(FloatImageTexture),
|
||||
Bilerp(FloatBilerpTexture),
|
||||
}
|
||||
|
|
@ -105,14 +106,12 @@ impl FloatTexture {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[enum_dispatch(SpectrumTextureTrait)]
|
||||
pub enum SpectrumTexture {
|
||||
Constant(SpectrumConstantTexture),
|
||||
Checkerboard(SpectrumCheckerboardTexture),
|
||||
Dots(SpectrumDotsTexture),
|
||||
// #[device(
|
||||
// custom = "upload_spectrum_image",
|
||||
// variant_type = "GPUSpectrumImageTexture"
|
||||
// )]
|
||||
Image(SpectrumImageTexture),
|
||||
Bilerp(SpectrumBilerpTexture),
|
||||
Scaled(SpectrumScaledTexture),
|
||||
|
|
@ -133,12 +132,11 @@ impl SpectrumTexture {
|
|||
scale: inner.base.scale,
|
||||
invert: inner.base.invert,
|
||||
is_single_channel: inner.base.mipmap.is_single_channel(),
|
||||
color_space: inner
|
||||
.base
|
||||
.mipmap
|
||||
.color_space
|
||||
.clone()
|
||||
.unwrap_or_else(crate::spectra::default_colorspace),
|
||||
color_space: arena.alloc(
|
||||
inner.base.mipmap.color_space
|
||||
.clone()
|
||||
.unwrap_or_else(crate::spectra::default_colorspace),
|
||||
),
|
||||
spectrum_type: inner.spectrum_type,
|
||||
}
|
||||
}
|
||||
|
|
@ -264,3 +262,4 @@ pub struct TexInfo {
|
|||
pub wrap_mode: WrapMode,
|
||||
pub encoding: ColorEncoding,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,10 +65,10 @@ pub static REC2020_COEFFS: Lazy<&[Float]> =
|
|||
Lazy::new(|| strip_to_len(REC2020_COEFFS_BYTES, COEFFS_LEN));
|
||||
|
||||
pub static SRGB_TABLE: Lazy<RGBToSpectrumTable> =
|
||||
Lazy::new(|| RGBToSpectrumTableData::new(SRGB_SCALE, SRGB_COEFFS));
|
||||
Lazy::new(|| RGBToSpectrumTable::new(&SRGB_SCALE, &SRGB_COEFFS));
|
||||
pub static DCI_P3_TABLE: Lazy<RGBToSpectrumTable> =
|
||||
Lazy::new(|| RGBToSpectrumTableData::new(DCI_P3_SCALE.to_vec(), DCI_P3_COEFFS.to_vec()));
|
||||
Lazy::new(|| RGBToSpectrumTable::new(&DCI_P3_SCALE, &DCI_P3_COEFFS));
|
||||
pub static REC2020_TABLE: Lazy<RGBToSpectrumTable> =
|
||||
Lazy::new(|| RGBToSpectrumTableData::new(REC2020_SCALE.to_vec(), REC2020_COEFFS.to_vec()));
|
||||
Lazy::new(|| RGBToSpectrumTable::new(&REC2020_SCALE, &REC2020_COEFFS));
|
||||
pub static ACES_TABLE: Lazy<RGBToSpectrumTable> =
|
||||
Lazy::new(|| RGBToSpectrumTableData::new(ACES_SCALE.to_vec(), ACES_COEFFS.to_vec()));
|
||||
Lazy::new(|| RGBToSpectrumTable::new(&ACES_SCALE, &ACES_COEFFS));
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
use super::RayIntegratorTrait;
|
||||
use super::base::IntegratorBase;
|
||||
use crate::Arena;
|
||||
use super::RayIntegratorTrait;
|
||||
use crate::core::camera::InitMetadata;
|
||||
use crate::core::film::FilmTrait;
|
||||
use crate::core::image::{Image, ImageIO, ImageMetadata};
|
||||
use crate::globals::get_options;
|
||||
use crate::spectra::get_spectra_context;
|
||||
use crate::Arena;
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||
use shared::Float;
|
||||
use shared::core::camera::{Camera, CameraTrait};
|
||||
use shared::core::geometry::{Bounds2i, Point2i, VectorLike};
|
||||
use shared::core::sampler::get_camera_sample;
|
||||
use shared::core::sampler::{Sampler, SamplerTrait};
|
||||
use shared::spectra::SampledSpectrum;
|
||||
use shared::Float;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
|
@ -213,7 +213,7 @@ pub fn render<T>(
|
|||
let film_image = film.get_image(&film_metadata, splat_scale);
|
||||
|
||||
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();
|
||||
|
||||
if let Some(file) = &mut mse_out_file {
|
||||
|
|
|
|||
|
|
@ -15,4 +15,4 @@ pub mod utils;
|
|||
|
||||
#[cfg(feature = "cuda")]
|
||||
pub mod gpu;
|
||||
pub use utils::{Arena, FileLoc, ParameterDictionary};
|
||||
pub use utils::{Arena, FileLoc, ParameterDictionary, Upload, ArenaUpload};
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use crate::core::light::lookup_spectrum;
|
|||
use crate::core::spectrum::spectrum_to_photometric;
|
||||
use crate::core::texture::FloatTexture;
|
||||
use crate::utils::resolve_filename;
|
||||
use crate::utils::upload::ArenaUpload;
|
||||
use crate::{Arena, FileLoc, ParameterDictionary};
|
||||
use anyhow::{anyhow, Result};
|
||||
use shared::core::geometry::Point2i;
|
||||
|
|
@ -12,11 +13,10 @@ use shared::core::light::{Light, LightBase, LightType};
|
|||
use shared::core::medium::{Medium, MediumInterface};
|
||||
use shared::core::shape::{Shape, ShapeTrait};
|
||||
use shared::core::spectrum::Spectrum;
|
||||
use shared::core::texture::GPUFloatTexture;
|
||||
use shared::core::texture::{SpectrumType, TextureEvalContext};
|
||||
use shared::lights::DiffuseAreaLight;
|
||||
use shared::spectra::RGBColorSpace;
|
||||
use shared::utils::{Ptr, Transform};
|
||||
use shared::utils::Transform;
|
||||
use shared::{Float, PI};
|
||||
|
||||
pub fn create(
|
||||
|
|
@ -37,51 +37,49 @@ pub fn create(
|
|||
let two_sided = params.get_one_bool("twosided", false)?;
|
||||
|
||||
let filename = resolve_filename(¶ms.get_one_string("filename", "")?);
|
||||
let (image, image_color_space) = if !filename.is_empty() {
|
||||
if l.is_some() {
|
||||
return Err(anyhow!("{}: both \"L\" and \"filename\" specified", loc));
|
||||
}
|
||||
let (image, image_color_space): (Option<Image>, Option<RGBColorSpace>) =
|
||||
if !filename.is_empty() {
|
||||
if l.is_some() {
|
||||
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() {
|
||||
return Err(anyhow!("{}: image has infinite pixel values", loc));
|
||||
}
|
||||
if im.image.has_any_nan_pixels() {
|
||||
return Err(anyhow!("{}: image has NaN pixel values", loc));
|
||||
}
|
||||
if im.image.has_any_infinite_pixels() {
|
||||
return Err(anyhow!("{}: image has infinite pixel values", loc));
|
||||
}
|
||||
if im.image.has_any_nan_pixels() {
|
||||
return Err(anyhow!("{}: image has NaN pixel values", loc));
|
||||
}
|
||||
|
||||
let channel_desc = im
|
||||
.image
|
||||
.get_channel_desc(&["R", "G", "B"])
|
||||
.map_err(|_| anyhow!("{}: image must have R, G, B channels", loc))?;
|
||||
let channel_desc = im
|
||||
.image
|
||||
.get_channel_desc(&["R", "G", "B"])
|
||||
.map_err(|_| anyhow!("{}: image must have R, G, B channels", loc))?;
|
||||
|
||||
let image = im.image.select_channels(&channel_desc);
|
||||
let cs = im.metadata.get_colorspace();
|
||||
let image = im.image.select_channels(&channel_desc);
|
||||
let cs = im.metadata.get_colorspace();
|
||||
|
||||
(Some(image), cs)
|
||||
} else {
|
||||
if l.is_none() {
|
||||
l = Some(illum_spec);
|
||||
}
|
||||
(None, None)
|
||||
};
|
||||
(Some(image), cs)
|
||||
} else {
|
||||
if l.is_none() {
|
||||
l = Some(illum_spec);
|
||||
}
|
||||
(None, None)
|
||||
};
|
||||
|
||||
let l_for_scale = l.as_ref().unwrap_or(&illum_spec);
|
||||
scale /= spectrum_to_photometric(*l_for_scale);
|
||||
|
||||
let phi_v = params.get_one_float("power", -1.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;
|
||||
|
||||
if let Some(ref img) = image {
|
||||
// Get the appropriate luminance vector from the image colour space
|
||||
let lum_vec = image_color_space.unwrap().luminance_vector();
|
||||
let lum_vec = image_color_space
|
||||
.as_ref()
|
||||
.expect("image present but no color space")
|
||||
.luminance_vector();
|
||||
|
||||
let mut sum_k_e = 0.0;
|
||||
let res = img.resolution();
|
||||
|
|
@ -91,7 +89,6 @@ pub fn create(
|
|||
let r = img.get_channel(Point2i::new(x, y), 0);
|
||||
let g = img.get_channel(Point2i::new(x, y), 1);
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
|
@ -100,21 +97,15 @@ pub fn create(
|
|||
|
||||
let side_factor = if two_sided { 2.0 } else { 1.0 };
|
||||
k_e *= side_factor * shape.area() * PI;
|
||||
|
||||
// now multiply up scale to hit the target power
|
||||
scale *= phi_v / k_e;
|
||||
}
|
||||
|
||||
let alpha_ptr = alpha.upload(arena);
|
||||
let is_constant_zero = match &*alpha_ptr {
|
||||
GPUFloatTexture::Constant(tex) => tex.evaluate(&TextureEvalContext::default()) == 0.0,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let (light_type, _) = if is_constant_zero {
|
||||
(LightType::DeltaPosition, None)
|
||||
// Upload alpha texture to GPU and check for constant-zero
|
||||
let alpha_ptr = arena.upload(alpha);
|
||||
let light_type = if alpha_ptr.is_constant_zero() {
|
||||
LightType::DeltaPosition
|
||||
} else {
|
||||
(LightType::Area, Some(alpha))
|
||||
LightType::Area
|
||||
};
|
||||
|
||||
let mi = match medium {
|
||||
|
|
@ -127,6 +118,7 @@ pub fn create(
|
|||
}
|
||||
None => MediumInterface::default(),
|
||||
};
|
||||
|
||||
let base = LightBase::new(light_type, render_from_light, mi);
|
||||
|
||||
if let Some(ref img) = image {
|
||||
|
|
@ -143,30 +135,20 @@ pub fn create(
|
|||
|
||||
let is_triangle_or_bilinear = matches!(*shape, Shape::Triangle(_) | Shape::BilinearPatch(_));
|
||||
if render_from_light.has_scale(None) && !is_triangle_or_bilinear {
|
||||
println!(
|
||||
"Scaling detected in rendering to light space transformation! \
|
||||
Proceed at your own risk; your image may have errors."
|
||||
eprintln!(
|
||||
"Warning: scaling detected in rendering-to-light transform; \
|
||||
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 {
|
||||
base,
|
||||
area: shape.area(),
|
||||
shape: shape_ptr,
|
||||
shape: arena.alloc(*shape),
|
||||
alpha: alpha_ptr,
|
||||
image: image_ptr,
|
||||
colorspace: colorspace_ptr,
|
||||
lemit: lemit_ptr,
|
||||
image: arena.alloc(image),
|
||||
colorspace: arena.alloc_opt(image_color_space),
|
||||
lemit: arena.alloc((*lookup_spectrum(l_for_scale)).clone()),
|
||||
two_sided,
|
||||
scale,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ impl CreateDistantLight for DistantLight {
|
|||
let lemit = lookup_spectrum(&le);
|
||||
Self {
|
||||
base,
|
||||
lemit: Ptr::from(&lemit.device()),
|
||||
lemit: Ptr::from(&*lemit),
|
||||
scale,
|
||||
scene_center: Point3f::default(),
|
||||
scene_radius: 0.,
|
||||
|
|
|
|||
|
|
@ -99,8 +99,8 @@ pub fn create(
|
|||
|
||||
let image_ptr = if !image.is_null() {
|
||||
let distrib = PiecewiseConstant2D::from_image(&image);
|
||||
let distrib_ptr = distrib.upload(arena);
|
||||
let img_ptr = image.upload(arena);
|
||||
let distrib_ptr = arena.alloc(distrib);
|
||||
let img_ptr = arena.alloc(image);
|
||||
(img_ptr, distrib_ptr)
|
||||
} else {
|
||||
(Ptr::null(), Ptr::null())
|
||||
|
|
@ -108,7 +108,7 @@ pub fn create(
|
|||
|
||||
let specific = GoniometricLight {
|
||||
base,
|
||||
iemit: arena.alloc(iemit.device()),
|
||||
iemit: arena.alloc(*iemit),
|
||||
scale,
|
||||
image: image_ptr.0,
|
||||
distrib: image_ptr.1,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use crate::core::spectrum::spectrum_to_photometric;
|
|||
use crate::spectra::get_spectra_context;
|
||||
use crate::utils::resolve_filename;
|
||||
use crate::utils::sampling::{PiecewiseConstant2D, WindowedPiecewiseConstant2D};
|
||||
use crate::{Arena, FileLoc, ParameterDictionary};
|
||||
use crate::{Arena, FileLoc, ParameterDictionary, ArenaUpload, Upload};
|
||||
use anyhow::{anyhow, Result};
|
||||
use rayon::prelude::*;
|
||||
use shared::core::camera::CameraTransform;
|
||||
|
|
@ -63,7 +63,7 @@ pub fn create(
|
|||
}
|
||||
|
||||
let lemit = lookup_spectrum(&spectrum);
|
||||
let light = UniformInfiniteLight::new(render_from_light, scale, lemit.upload(arena));
|
||||
let light = UniformInfiniteLight::new(render_from_light, scale, arena.alloc(*lemit));
|
||||
return Ok(Light::InfiniteUniform(light));
|
||||
}
|
||||
|
||||
|
|
@ -140,9 +140,9 @@ fn create_image_light(
|
|||
render_from_light,
|
||||
scale,
|
||||
image_ptr,
|
||||
image_cs.upload(arena),
|
||||
distrib.upload(arena),
|
||||
compensated_distrib.upload(arena),
|
||||
arena.alloc(image_cs),
|
||||
arena.alloc(distrib),
|
||||
arena.alloc(compensated_distrib),
|
||||
);
|
||||
|
||||
Ok(Light::InfiniteImage(light))
|
||||
|
|
@ -198,11 +198,11 @@ fn create_portal_light(
|
|||
let light = PortalInfiniteLight::new(
|
||||
render_from_light,
|
||||
scale,
|
||||
remapped.upload(arena),
|
||||
image_cs.upload(arena),
|
||||
arena.alloc(remapped),
|
||||
arena.alloc(image_cs),
|
||||
portal,
|
||||
portal_frame,
|
||||
distribution.upload(arena),
|
||||
arena.alloc(distribution),
|
||||
);
|
||||
|
||||
Ok(Light::InfinitePortal(light))
|
||||
|
|
|
|||
|
|
@ -86,6 +86,6 @@ pub fn create(
|
|||
None => MediumInterface::default(),
|
||||
};
|
||||
|
||||
let specific = PointLight::new(final_render, mi, l, scale);
|
||||
let specific = PointLight::new(final_render, mi, l, scale, arena);
|
||||
Ok(Light::Point(specific))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ impl CreateSpotLight for SpotLight {
|
|||
);
|
||||
|
||||
let i = lookup_spectrum(&le);
|
||||
let iemit = Ptr::from(&i.device());
|
||||
let iemit = arena.alloc(i);
|
||||
Self {
|
||||
base,
|
||||
iemit,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use crate::core::texture::SpectrumTexture;
|
|||
use crate::globals::get_options;
|
||||
use crate::spectra::data::get_named_spectrum;
|
||||
use crate::utils::TextureParameterDictionary;
|
||||
use crate::{Arena, FileLoc};
|
||||
use crate::{Arena, FileLoc, Upload, ArenaUpload};
|
||||
use anyhow::{bail, Result};
|
||||
use shared::core::material::Material;
|
||||
use shared::core::spectrum::Spectrum;
|
||||
|
|
@ -56,15 +56,15 @@ impl CreateMaterial for CoatedDiffuseMaterial {
|
|||
let remap_roughness = parameters.get_one_bool("remaproughness", true)?;
|
||||
|
||||
let specific = CoatedDiffuseMaterial::new(
|
||||
reflectance.upload(arena),
|
||||
u_roughness.upload(arena),
|
||||
v_roughness.upload(arena),
|
||||
thickness.upload(arena),
|
||||
albedo.upload(arena),
|
||||
g.upload(arena),
|
||||
eta.upload(arena),
|
||||
displacement.upload(arena),
|
||||
normal_map.upload(arena),
|
||||
arena.upload(reflectance),
|
||||
arena.upload(u_roughness),
|
||||
arena.upload(v_roughness),
|
||||
arena.upload(thickness),
|
||||
arena.upload(albedo),
|
||||
arena.upload(g),
|
||||
arena.upload(displacement),
|
||||
arena.alloc(eta),
|
||||
arena.alloc(normal_map),
|
||||
remap_roughness,
|
||||
max_depth as u32,
|
||||
n_samples as u32,
|
||||
|
|
@ -154,19 +154,19 @@ impl CreateMaterial for CoatedConductorMaterial {
|
|||
let remap_roughness = parameters.get_one_bool("remaproughness", true)?;
|
||||
|
||||
let material = Self::new(
|
||||
normal_map.upload(arena),
|
||||
displacement.upload(arena),
|
||||
interface_u_roughness.upload(arena),
|
||||
interface_v_roughness.upload(arena),
|
||||
thickness.upload(arena),
|
||||
interface_eta.upload(arena),
|
||||
g.upload(arena),
|
||||
albedo.upload(arena),
|
||||
conductor_u_roughness.upload(arena),
|
||||
conductor_v_roughness.upload(arena),
|
||||
conductor_eta.upload(arena),
|
||||
k.upload(arena),
|
||||
reflectance.upload(arena),
|
||||
arena.upload(displacement)
|
||||
arena.upload(interface_u_roughness),
|
||||
arena.upload(interface_v_roughness),
|
||||
arena.upload(thickness),
|
||||
arena.upload(g),
|
||||
arena.upload(albedo),
|
||||
arena.upload(conductor_u_roughness),
|
||||
arena.upload(conductor_v_roughness),
|
||||
arena.upload(conductor_eta),
|
||||
arena.upload(k),
|
||||
arena.upload(reflectance),
|
||||
arena.alloc(normal_map),
|
||||
arena.alloc(interface_eta),
|
||||
max_depth as u32,
|
||||
n_samples as u32,
|
||||
remap_roughness,
|
||||
|
|
|
|||
|
|
@ -2,15 +2,15 @@ use crate::core::image::Image;
|
|||
use crate::core::material::CreateMaterial;
|
||||
use crate::core::texture::SpectrumTexture;
|
||||
use crate::spectra::get_colorspace_device;
|
||||
use crate::{Arena, FileLoc};
|
||||
use crate::utils::TextureParameterDictionary;
|
||||
use crate::{Arena, FileLoc, Upload, ArenaUpload};
|
||||
use anyhow::Result;
|
||||
use shared::bxdfs::HairBxDF;
|
||||
use shared::core::material::Material;
|
||||
use shared::core::spectrum::Spectrum;
|
||||
use shared::core::texture::SpectrumType;
|
||||
use shared::materials::complex::*;
|
||||
use shared::textures::SpectrumConstantTexture;
|
||||
use anyhow::Result;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
|
@ -30,8 +30,7 @@ impl CreateMaterial for HairMaterial {
|
|||
let pheomelanin = parameters.get_float_texture_or_null("pheomelanin")?;
|
||||
let has_melanin = eumelanin.is_some() || pheomelanin.is_some();
|
||||
|
||||
// Default distribution if nothing is spceified
|
||||
let sigma_a = if sigma_a.is_none() && !reflectance.is_none() && !has_melanin {
|
||||
let sigma_a = if sigma_a.is_none() && reflectance.is_none() && !has_melanin {
|
||||
let stdcs = get_colorspace_device();
|
||||
let default_rgb = HairBxDF::sigma_a_from_concentration(1.3, 0.0, stdcs);
|
||||
let spectrum = Spectrum::RGBUnbounded(default_rgb);
|
||||
|
|
@ -45,21 +44,23 @@ impl CreateMaterial for HairMaterial {
|
|||
let beta_m = parameters.get_float_texture("beta_m", 0.3)?;
|
||||
let beta_n = parameters.get_float_texture("beta_n", 0.3)?;
|
||||
let alpha = parameters.get_float_texture("alpha", 2.)?;
|
||||
|
||||
let material = HairMaterial::new(
|
||||
sigma_a.upload(arena),
|
||||
reflectance.upload(arena),
|
||||
eumelanin.upload(arena),
|
||||
pheomelanin.upload(arena),
|
||||
eta.upload(arena),
|
||||
beta_m.upload(arena),
|
||||
beta_n.upload(arena),
|
||||
alpha.upload(arena),
|
||||
arena.upload(sigma_a),
|
||||
arena.upload(reflectance),
|
||||
arena.upload(eumelanin),
|
||||
arena.upload(pheomelanin),
|
||||
arena.upload(eta),
|
||||
arena.upload(beta_m),
|
||||
arena.upload(beta_n),
|
||||
arena.upload(alpha),
|
||||
);
|
||||
|
||||
Ok(Material::Hair(material))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl CreateMaterial for SubsurfaceMaterial {
|
||||
fn create(
|
||||
_parameters: &TextureParameterDictionary,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use super::*;
|
||||
use crate::globals::get_options;
|
||||
use crate::utils::math::compute_radical_inverse_permutations;
|
||||
use shared::utils::math::compute_radical_inverse_permutations;
|
||||
use anyhow::{Result, anyhow};
|
||||
use shared::core::geometry::Point2i;
|
||||
use shared::core::sampler::{HaltonSampler, MAX_HALTON_RESOLUTION, RandomizeStrategy};
|
||||
|
|
@ -21,7 +21,7 @@ impl CreateHaltonSampler for HaltonSampler {
|
|||
randomize: RandomizeStrategy,
|
||||
seed: u64,
|
||||
) -> 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_exponents = [0u64; 2];
|
||||
let bases = [2, 3];
|
||||
|
|
|
|||
|
|
@ -258,21 +258,29 @@ impl TriQuadMesh {
|
|||
TriangleMesh::new(
|
||||
render_from_object,
|
||||
reverse_orientation,
|
||||
self.tri_indices,
|
||||
self.p,
|
||||
self.n,
|
||||
Vec::new(),
|
||||
self.uv,
|
||||
self.face_indices,
|
||||
&self.tri_indices,
|
||||
&self.p,
|
||||
&self.n,
|
||||
&Vec::new(),
|
||||
&self.uv,
|
||||
&self.face_indices,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl TriangleMesh {
|
||||
pub trait ReadTriangleMesh {
|
||||
pub fn from_ply<P: AsRef<Path>>(
|
||||
filename: P,
|
||||
render_from_object: &Transform,
|
||||
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> {
|
||||
let mesh = TriQuadMesh::read_ply(filename)?;
|
||||
Ok(mesh.into_triangle_mesh(render_from_object, reverse_orientation))
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ pub trait CreateRGBColorSpace {
|
|||
r: Point2f,
|
||||
g: Point2f,
|
||||
b: Point2f,
|
||||
illuminant: Arc<DenselySampledSpectrum>,
|
||||
rgb_to_spectrum_table: Ptr<RGBToSpectrumTable>,
|
||||
illuminant: &DenselySampledSpectrum,
|
||||
rgb_to_spectrum_table: &RGBToSpectrumTable,
|
||||
) -> Self;
|
||||
}
|
||||
|
||||
|
|
@ -22,11 +22,12 @@ impl CreateRGBColorSpace for RGBColorSpace {
|
|||
r: Point2f,
|
||||
g: Point2f,
|
||||
b: Point2f,
|
||||
illuminant: Arc<DenselySampledSpectrum>,
|
||||
rgb_to_spectrum_table: Ptr<RGBToSpectrumTable>,
|
||||
illuminant: &DenselySampledSpectrum,
|
||||
rgb_to_spectrum_table: &RGBToSpectrumTable,
|
||||
) -> Self {
|
||||
let stdspec = get_spectra_context();
|
||||
let illum_spectrum = Spectrum::Dense(illuminant.as_ref().clone());
|
||||
let illum_ptr = Ptr::from(illuminant);
|
||||
let illum_spectrum = Spectrum::Dense(illum_ptr);
|
||||
let w_xyz: XYZ = illum_spectrum.to_xyz(&stdspec);
|
||||
let w = w_xyz.xy();
|
||||
|
||||
|
|
@ -49,8 +50,8 @@ impl CreateRGBColorSpace for RGBColorSpace {
|
|||
g,
|
||||
b,
|
||||
w,
|
||||
illuminant: Ptr::from(illuminant.as_ref()),
|
||||
rgb_to_spectrum_table,
|
||||
illuminant: illum_ptr,
|
||||
rgb_to_spectrum_table: Ptr::from(rgb_to_spectrum_table),
|
||||
xyz_from_rgb,
|
||||
rgb_from_xyz,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use shared::Float;
|
||||
use shared::core::spectrum::Spectrum;
|
||||
use shared::spectra::cie::*;
|
||||
use shared::spectra::{PiecewiseLinearSpectrum, DenselySampledSpectrum};
|
||||
use shared::spectra::{DenselySampledSpectrum, PiecewiseLinearSpectrum};
|
||||
use shared::{gbox, leak, Float, Ptr};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ pub fn create_cie(data: &[Float]) -> DenselySampledSpectrum {
|
|||
.collect();
|
||||
|
||||
let buffer = PiecewiseLinearSpectrum::new(lambdas, data.to_vec());
|
||||
let spec = Spectrum::Piecewise(buffer);
|
||||
let spec = Spectrum::Piecewise(Ptr::from(&*buffer));
|
||||
DenselySampledSpectrum::from_spectrum(&spec)
|
||||
}
|
||||
|
||||
|
|
@ -27,7 +27,7 @@ pub static NAMED_SPECTRA: LazyLock<HashMap<String, Spectrum>> = LazyLock::new(||
|
|||
macro_rules! add {
|
||||
($name:expr, $data:expr, $norm:expr) => {
|
||||
let buffer = PiecewiseLinearSpectrum::from_interleaved($data, $norm);
|
||||
let spectrum = Spectrum::Piecewise(buffer);
|
||||
let spectrum = Spectrum::Piecewise(leak(buffer));
|
||||
m.insert($name.to_string(), spectrum);
|
||||
};
|
||||
}
|
||||
|
|
@ -49,7 +49,6 @@ pub static NAMED_SPECTRA: LazyLock<HashMap<String, Spectrum>> = LazyLock::new(||
|
|||
add!("stdillum-F12", &CIE_ILLUM_F12, true);
|
||||
add!("illum-acesD60", &ACES_ILLUM_D60, true);
|
||||
|
||||
// --- Glasses ---
|
||||
add!("glass-BK7", &GLASS_BK7_ETA, false);
|
||||
add!("glass-BAF10", &GLASS_BAF10_ETA, false);
|
||||
add!("glass-FK51A", &GLASS_FK51A_ETA, false);
|
||||
|
|
@ -58,7 +57,6 @@ pub static NAMED_SPECTRA: LazyLock<HashMap<String, Spectrum>> = LazyLock::new(||
|
|||
add!("glass-F10", &GLASS_SF10_ETA, false);
|
||||
add!("glass-F11", &GLASS_SF11_ETA, false);
|
||||
|
||||
// --- Metals ---
|
||||
add!("metal-Ag-eta", &AG_ETA, false);
|
||||
add!("metal-Ag-k", &AG_K, false);
|
||||
add!("metal-Al-eta", &AL_ETA, false);
|
||||
|
|
@ -74,87 +72,70 @@ pub static NAMED_SPECTRA: LazyLock<HashMap<String, Spectrum>> = LazyLock::new(||
|
|||
add!("metal-TiO2-eta", &TIO2_ETA, 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_g", &CANON_EOS_100D_G, 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_g", &CANON_EOS_1DX_MKII_G, 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_g", &CANON_EOS_200D_G, 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_g", &CANON_EOS_200D_MKII_G, 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_g", &CANON_EOS_5D_G, 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_g", &CANON_EOS_5D_MKII_G, 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_g", &CANON_EOS_5D_MKIII_G, 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_g", &CANON_EOS_5D_MKIV_G, 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_g", &CANON_EOS_5DS_G, 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_g", &CANON_EOS_M_G, 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_g", &HASSELBLAD_L1D_20C_G, false);
|
||||
add!("hasselblad_l1d_20c_b", &HASSELBLAD_L1D_20C_B, false);
|
||||
|
||||
// --- Nikon D810 ---
|
||||
add!("nikon_d810_r", &NIKON_D810_R, false);
|
||||
add!("nikon_d810_g", &NIKON_D810_G, false);
|
||||
add!("nikon_d810_b", &NIKON_D810_B, false);
|
||||
|
||||
// --- Nikon D850 ---
|
||||
add!("nikon_d850_r", &NIKON_D850_R, false);
|
||||
add!("nikon_d850_g", &NIKON_D850_G, 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_g", &SONY_ILCE_6400_G, 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_g", &SONY_ILCE_7M3_G, 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_g", &SONY_ILCE_7RM3_G, 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_g", &SONY_ILCE_9_G, false);
|
||||
add!("sony_ilce_9_b", &SONY_ILCE_9_B, false);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ use std::sync::LazyLock;
|
|||
|
||||
pub mod colorspace;
|
||||
pub mod data;
|
||||
pub mod dense;
|
||||
pub mod piecewise;
|
||||
|
||||
pub static CIE_X_DATA: LazyLock<DenselySampledSpectrum> =
|
||||
|
|
@ -28,19 +27,16 @@ fn get_d65_illuminant_buffer() -> Arc<DenselySampledSpectrum> {
|
|||
}
|
||||
|
||||
pub fn cie_x() -> Spectrum {
|
||||
Spectrum::Dense(CIE_X_DATA)
|
||||
Spectrum::Dense(Ptr::from(&*CIE_X_DATA))
|
||||
}
|
||||
|
||||
pub fn cie_y() -> Spectrum {
|
||||
Spectrum::Dense(CIE_Y_DATA)
|
||||
Spectrum::Dense(Ptr::from(&*CIE_Y_DATA))
|
||||
}
|
||||
|
||||
pub fn cie_z() -> Spectrum {
|
||||
Spectrum::Dense(CIE_Z_DATA)
|
||||
Spectrum::Dense(Ptr::from(&*CIE_Z_DATA))
|
||||
}
|
||||
|
||||
pub fn cie_d65() -> Spectrum {
|
||||
Spectrum::Dense(CIE_D65_DATA)
|
||||
Spectrum::Dense(Ptr::from(&*CIE_D65_DATA))
|
||||
}
|
||||
|
||||
pub fn get_spectra_context() -> StandardSpectra {
|
||||
|
|
@ -52,6 +48,7 @@ pub fn get_spectra_context() -> StandardSpectra {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
pub static SRGB: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| {
|
||||
let illum = get_d65_illuminant_buffer();
|
||||
let r = Point2f::new(0.64, 0.33);
|
||||
|
|
@ -59,7 +56,7 @@ pub static SRGB: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| {
|
|||
let b = Point2f::new(0.15, 0.06);
|
||||
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(|| {
|
||||
|
|
@ -68,7 +65,7 @@ pub static DCI_P3: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| {
|
|||
let g = Point2f::new(0.265, 0.690);
|
||||
let b = Point2f::new(0.150, 0.060);
|
||||
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(|| {
|
||||
|
|
@ -77,7 +74,7 @@ pub static REC2020: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
|
|||
let g = Point2f::new(0.170, 0.797);
|
||||
let b = Point2f::new(0.131, 0.046);
|
||||
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(|| {
|
||||
|
|
@ -85,8 +82,8 @@ pub static ACES: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
|
|||
let r = Point2f::new(0.7347, 0.2653);
|
||||
let g = Point2f::new(0.0000, 1.0000);
|
||||
let b = Point2f::new(0.0001, -0.0770);
|
||||
let table_ptr = Ptr::from(&ACES_TABLE.view);
|
||||
Arc::new(RGBColorSpace::new(r, g, b, illum, table_ptr))
|
||||
let table_ptr = Ptr::from(&ACES_TABLE);
|
||||
Arc::new(RGBColorSpace::new(r, g, b, &illum, &table_ptr))
|
||||
});
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -120,10 +117,10 @@ pub fn get_colorspace_context() -> StandardColorSpaces {
|
|||
|
||||
pub fn get_colorspace_device() -> DeviceStandardColorSpaces {
|
||||
DeviceStandardColorSpaces {
|
||||
srgb: Ptr::from(&*SRGB),
|
||||
dci_p3: Ptr::from(&*DCI_P3),
|
||||
rec2020: Ptr::from(&*REC2020),
|
||||
aces2065_1: Ptr::from(&*ACES),
|
||||
srgb: Ptr::from(&**SRGB),
|
||||
dci_p3: Ptr::from(&**DCI_P3),
|
||||
rec2020: Ptr::from(&**REC2020),
|
||||
aces2065_1: Ptr::from(&**ACES),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -136,6 +133,11 @@ pub fn default_colorspace_arc() -> Arc<RGBColorSpace> {
|
|||
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 {
|
||||
Spectrum::Dense(default_colorspace().illuminant)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ use shared::utils::Transform;
|
|||
use shared::Float;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
// use crate::utils::{FileLoc, TextureParameterDictionary};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ImageTextureBase {
|
||||
|
|
|
|||
|
|
@ -1,59 +0,0 @@
|
|||
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)
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@ pub mod containers;
|
|||
pub mod error;
|
||||
pub mod file;
|
||||
pub mod io;
|
||||
pub mod math;
|
||||
pub mod mipmap;
|
||||
pub mod parallel;
|
||||
pub mod parameters;
|
||||
|
|
@ -19,6 +18,7 @@ pub use parameters::{
|
|||
ParameterDictionary, ParsedParameter, ParsedParameterVector, TextureParameterDictionary,
|
||||
};
|
||||
pub use strings::*;
|
||||
pub use upload::{Upload, ArenaUpload};
|
||||
|
||||
#[cfg(feature = "vulkan")]
|
||||
pub type Arena = arena::Arena<backend::vulkan::VulkanAllocator>;
|
||||
|
|
@ -28,3 +28,28 @@ pub type Arena = arena::Arena<backend::cuda::CudaAllocator>;
|
|||
|
||||
#[cfg(not(any(feature = "cuda", feature = "vulkan")))]
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::core::spectrum::SPECTRUM_FILE_CACHE;
|
||||
use crate::spectra::piecewise::ReadFromFile;
|
||||
use crate::core::texture::{FloatTexture, SpectrumTexture};
|
||||
use crate::spectra::data::get_named_spectrum;
|
||||
use crate::spectra::piecewise::ReadFromFile;
|
||||
use crate::utils::FileLoc;
|
||||
use anyhow::{bail, Result};
|
||||
use shared::core::color::RGB;
|
||||
|
|
@ -136,6 +136,18 @@ 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] {
|
||||
¶m.floats
|
||||
}
|
||||
}
|
||||
|
||||
impl PBRTParameter for Float {
|
||||
type Raw = Float;
|
||||
const TYPE_NAME: &'static str = "float";
|
||||
|
|
@ -281,6 +293,12 @@ impl ParameterDictionary {
|
|||
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<()> {
|
||||
for p in &self.params {
|
||||
match p.type_name.as_str() {
|
||||
|
|
@ -444,6 +462,10 @@ impl ParameterDictionary {
|
|||
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>> {
|
||||
self.lookup_array(name)
|
||||
}
|
||||
|
|
@ -476,6 +498,10 @@ impl ParameterDictionary {
|
|||
self.lookup_array(name)
|
||||
}
|
||||
|
||||
pub fn get_rgb_array(&self, name: &str) -> Result<Vec<RGB>> {
|
||||
self.lookup_array(name)
|
||||
}
|
||||
|
||||
pub fn get_one_spectrum(
|
||||
&self,
|
||||
name: &str,
|
||||
|
|
@ -655,11 +681,11 @@ impl ParameterDictionary {
|
|||
})
|
||||
.unzip();
|
||||
|
||||
vec![Spectrum::Piecewise(PiecewiseLinearSpectrum {
|
||||
lambdas: lambdas.as_ptr().into(),
|
||||
values: values.as_ptr().into(),
|
||||
vec![Spectrum::Piecewise(leak(PiecewiseLinearSpectrum {
|
||||
lambdas: gvec_from_slice(lambdas),
|
||||
values: gvec_from_slice(values),
|
||||
count: lambdas.len() as u32,
|
||||
})]
|
||||
}))]
|
||||
}
|
||||
|
||||
fn extract_file_spectrum(&self, param: &ParsedParameter) -> Vec<Spectrum> {
|
||||
|
|
@ -681,18 +707,18 @@ fn read_spectrum_from_file(filename: &str) -> Result<Spectrum, String> {
|
|||
{
|
||||
let cache = SPECTRUM_FILE_CACHE.lock();
|
||||
if let Some(s) = cache.get(&fn_key) {
|
||||
return Ok(s.clone());
|
||||
return Ok(*s); // Spectrum is Copy, so just copy it
|
||||
}
|
||||
}
|
||||
|
||||
let pls = PiecewiseLinearSpectrum::read(&fn_key)
|
||||
.ok_or_else(|| format!("unable to read or parse spectrum file '{}'", fn_key))?;
|
||||
|
||||
let spectrum = Spectrum::Piecewise(*pls);
|
||||
let spectrum = Spectrum::Piecewise(leak(pls));
|
||||
|
||||
{
|
||||
let mut cache = SPECTRUM_FILE_CACHE.lock();
|
||||
cache.insert(fn_key, spectrum.clone());
|
||||
cache.insert(fn_key, spectrum);
|
||||
}
|
||||
|
||||
Ok(spectrum)
|
||||
|
|
|
|||
160
src/utils/upload.rs
Normal file
160
src/utils/upload.rs
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue