Compare commits
No commits in common. "c412b6d6681d9028d29937913d81495901656bef" and "0ef563d1a551767d183b243b56c0375ccc3f773a" have entirely different histories.
c412b6d668
...
0ef563d1a5
90 changed files with 1454 additions and 2081 deletions
|
|
@ -19,7 +19,7 @@ use crate::spectra::{
|
||||||
N_SPECTRUM_SAMPLES, RGBColorSpace, RGBUnboundedSpectrum, SampledSpectrum, SampledWavelengths,
|
N_SPECTRUM_SAMPLES, RGBColorSpace, RGBUnboundedSpectrum, SampledSpectrum, SampledWavelengths,
|
||||||
StandardColorSpaces,
|
StandardColorSpaces,
|
||||||
};
|
};
|
||||||
use crate::utils::DevicePtr;
|
use crate::utils::Ptr;
|
||||||
use crate::utils::hash::hash_buffer;
|
use crate::utils::hash::hash_buffer;
|
||||||
use crate::utils::math::{
|
use crate::utils::math::{
|
||||||
clamp, fast_exp, i0, lerp, log_i0, radians, safe_acos, safe_asin, safe_sqrt, sample_discrete,
|
clamp, fast_exp, i0, lerp, log_i0, radians, safe_acos, safe_asin, safe_sqrt, sample_discrete,
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use crate::core::geometry::{
|
||||||
use crate::core::scattering::reflect;
|
use crate::core::scattering::reflect;
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::math::square;
|
use crate::utils::math::square;
|
||||||
use crate::utils::ptr::{DevicePtr, Slice};
|
use crate::utils::ptr::{Ptr, Slice};
|
||||||
use crate::utils::sampling::{PiecewiseLinear2D, cosine_hemisphere_pdf, sample_cosine_hemisphere};
|
use crate::utils::sampling::{PiecewiseLinear2D, cosine_hemisphere_pdf, sample_cosine_hemisphere};
|
||||||
use crate::{Float, INV_PI, PI, PI_OVER_2};
|
use crate::{Float, INV_PI, PI, PI_OVER_2};
|
||||||
use core::any::Any;
|
use core::any::Any;
|
||||||
|
|
@ -28,7 +28,7 @@ pub struct MeasuredBxDFData {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct MeasuredBxDF {
|
pub struct MeasuredBxDF {
|
||||||
pub brdf: DevicePtr<MeasuredBxDFData>,
|
pub brdf: Ptr<MeasuredBxDFData>,
|
||||||
pub lambda: SampledWavelengths,
|
pub lambda: SampledWavelengths,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,7 +38,7 @@ unsafe impl Sync for MeasuredBxDF {}
|
||||||
impl MeasuredBxDF {
|
impl MeasuredBxDF {
|
||||||
pub fn new(brdf: &MeasuredBxDFData, lambda: &SampledWavelengths) -> Self {
|
pub fn new(brdf: &MeasuredBxDFData, lambda: &SampledWavelengths) -> Self {
|
||||||
Self {
|
Self {
|
||||||
brdf: DevicePtr::from(brdf),
|
brdf: Ptr::from(brdf),
|
||||||
lambda: *lambda,
|
lambda: *lambda,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use crate::core::film::Film;
|
||||||
use crate::core::geometry::{
|
use crate::core::geometry::{
|
||||||
Bounds2f, Normal3f, Point2f, Point2i, Point3f, Ray, Vector2f, Vector2i, Vector3f, VectorLike,
|
Bounds2f, Normal3f, Point2f, Point2i, Point3f, Ray, Vector2f, Vector2i, Vector3f, VectorLike,
|
||||||
};
|
};
|
||||||
use crate::core::image::{DeviceImage, PixelFormat};
|
use crate::core::image::{Image, PixelFormat};
|
||||||
use crate::core::medium::Medium;
|
use crate::core::medium::Medium;
|
||||||
use crate::core::pbrt::Float;
|
use crate::core::pbrt::Float;
|
||||||
use crate::core::sampler::CameraSample;
|
use crate::core::sampler::CameraSample;
|
||||||
|
|
@ -37,7 +37,7 @@ pub struct RealisticCamera {
|
||||||
base: CameraBase,
|
base: CameraBase,
|
||||||
focus_distance: Float,
|
focus_distance: Float,
|
||||||
set_aperture_diameter: Float,
|
set_aperture_diameter: Float,
|
||||||
aperture_image: *const DeviceImage,
|
aperture_image: *const Image,
|
||||||
element_interfaces: *const LensElementInterface,
|
element_interfaces: *const LensElementInterface,
|
||||||
n_elements: usize,
|
n_elements: usize,
|
||||||
physical_extent: Bounds2f,
|
physical_extent: Bounds2f,
|
||||||
|
|
@ -51,7 +51,7 @@ impl RealisticCamera {
|
||||||
lens_params: &[Float],
|
lens_params: &[Float],
|
||||||
focus_distance: Float,
|
focus_distance: Float,
|
||||||
set_aperture_diameter: Float,
|
set_aperture_diameter: Float,
|
||||||
aperture_image: Option<DeviceImage>,
|
aperture_image: Option<Image>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let film_ptr = base.film;
|
let film_ptr = base.film;
|
||||||
if film_ptr.is_null() {
|
if film_ptr.is_null() {
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,17 @@ use crate::Float;
|
||||||
use crate::core::bxdf::{BSDFSample, BxDF, BxDFFlags, BxDFTrait, FArgs, TransportMode};
|
use crate::core::bxdf::{BSDFSample, BxDF, BxDFFlags, BxDFTrait, FArgs, TransportMode};
|
||||||
use crate::core::geometry::{Frame, Normal3f, Point2f, Vector3f, VectorLike};
|
use crate::core::geometry::{Frame, Normal3f, Point2f, Vector3f, VectorLike};
|
||||||
use crate::spectra::SampledSpectrum;
|
use crate::spectra::SampledSpectrum;
|
||||||
use crate::utils::DevicePtr;
|
use crate::utils::Ptr;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Debug, Default)]
|
#[derive(Copy, Debug, Default)]
|
||||||
pub struct BSDF {
|
pub struct BSDF {
|
||||||
bxdf: DevicePtr<BxDF>,
|
bxdf: Ptr<BxDF>,
|
||||||
shading_frame: Frame,
|
shading_frame: Frame,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BSDF {
|
impl BSDF {
|
||||||
pub fn new(ns: Normal3f, dpdus: Vector3f, bxdf: DevicePtr<BxDF>) -> Self {
|
pub fn new(ns: Normal3f, dpdus: Vector3f, bxdf: Ptr<BxDF>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bxdf,
|
bxdf,
|
||||||
shading_frame: Frame::new(dpdus.normalize(), Vector3f::from(ns)),
|
shading_frame: Frame::new(dpdus.normalize(), Vector3f::from(ns)),
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use crate::core::geometry::{Frame, Normal3f, Point2f, Point3f, Point3fi, Vector3
|
||||||
use crate::core::interaction::{InteractionBase, ShadingGeom, SurfaceInteraction};
|
use crate::core::interaction::{InteractionBase, ShadingGeom, SurfaceInteraction};
|
||||||
use crate::core::shape::Shape;
|
use crate::core::shape::Shape;
|
||||||
use crate::spectra::{N_SPECTRUM_SAMPLES, SampledSpectrum};
|
use crate::spectra::{N_SPECTRUM_SAMPLES, SampledSpectrum};
|
||||||
use crate::utils::Ptr;
|
use crate::utils::ArenaPtr;
|
||||||
use crate::utils::math::{catmull_rom_weights, square};
|
use crate::utils::math::{catmull_rom_weights, square};
|
||||||
use crate::utils::sampling::sample_catmull_rom_2d;
|
use crate::utils::sampling::sample_catmull_rom_2d;
|
||||||
use crate::utils::{Ptr, ptr::Slice};
|
use crate::utils::{Ptr, ptr::Slice};
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use crate::core::pbrt::Float;
|
||||||
use crate::core::sampler::CameraSample;
|
use crate::core::sampler::CameraSample;
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::math::lerp;
|
use crate::utils::math::lerp;
|
||||||
use crate::utils::ptr::DevicePtr;
|
use crate::utils::ptr::Ptr;
|
||||||
use crate::utils::transform::{AnimatedTransform, Transform};
|
use crate::utils::transform::{AnimatedTransform, Transform};
|
||||||
|
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
|
|
@ -113,8 +113,8 @@ pub struct CameraBase {
|
||||||
pub min_pos_differential_y: Vector3f,
|
pub min_pos_differential_y: Vector3f,
|
||||||
pub min_dir_differential_x: Vector3f,
|
pub min_dir_differential_x: Vector3f,
|
||||||
pub min_dir_differential_y: Vector3f,
|
pub min_dir_differential_y: Vector3f,
|
||||||
pub film: DevicePtr<Film>,
|
pub film: Ptr<Film>,
|
||||||
pub medium: DevicePtr<Medium>,
|
pub medium: Ptr<Medium>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch(CameraTrait)]
|
#[enum_dispatch(CameraTrait)]
|
||||||
|
|
@ -232,13 +232,13 @@ pub trait CameraTrait {
|
||||||
Point3f::new(0., 0., 0.) + self.base().min_pos_differential_x,
|
Point3f::new(0., 0., 0.) + self.base().min_pos_differential_x,
|
||||||
Vector3f::new(0., 0., 1.) + self.base().min_dir_differential_x,
|
Vector3f::new(0., 0., 1.) + self.base().min_dir_differential_x,
|
||||||
None,
|
None,
|
||||||
&DevicePtr::default(),
|
&Ptr::default(),
|
||||||
);
|
);
|
||||||
let y_ray = Ray::new(
|
let y_ray = Ray::new(
|
||||||
Point3f::new(0., 0., 0.) + self.base().min_pos_differential_y,
|
Point3f::new(0., 0., 0.) + self.base().min_pos_differential_y,
|
||||||
Vector3f::new(0., 0., 1.) + self.base().min_dir_differential_y,
|
Vector3f::new(0., 0., 1.) + self.base().min_dir_differential_y,
|
||||||
None,
|
None,
|
||||||
&DevicePtr::default(),
|
&Ptr::default(),
|
||||||
);
|
);
|
||||||
let n_down = Vector3f::from(n_down_z);
|
let n_down = Vector3f::from(n_down_z);
|
||||||
let tx = -(n_down.dot(y_ray.o.into())) / n_down.dot(x_ray.d);
|
let tx = -(n_down.dot(y_ray.o.into())) / n_down.dot(x_ray.d);
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,14 @@ use std::ops::{
|
||||||
Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
|
Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::Float;
|
|
||||||
use crate::core::geometry::Point2f;
|
use crate::core::geometry::Point2f;
|
||||||
|
use crate::core::pbrt::{Float, find_interval};
|
||||||
use crate::core::spectrum::Spectrum;
|
use crate::core::spectrum::Spectrum;
|
||||||
use crate::utils::find_interval;
|
|
||||||
use crate::utils::math::{SquareMatrix, SquareMatrix3f, clamp, evaluate_polynomial, lerp};
|
use crate::utils::math::{SquareMatrix, SquareMatrix3f, clamp, evaluate_polynomial, lerp};
|
||||||
|
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
|
|
||||||
#[repr(C)]
|
#[derive(Debug, Clone)]
|
||||||
#[derive(Debug, Default, Clone, Copy)]
|
|
||||||
pub struct XYZ {
|
pub struct XYZ {
|
||||||
pub x: Float,
|
pub x: Float,
|
||||||
pub y: Float,
|
pub y: Float,
|
||||||
|
|
@ -26,12 +24,6 @@ impl From<(Float, Float, Float)> for XYZ {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<[Float; 3]> for XYZ {
|
|
||||||
fn from(triplet: (Float, Float, Float)) -> Self {
|
|
||||||
XYZ::new(triplet.0, triplet.1, triplet.2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> IntoIterator for &'a XYZ {
|
impl<'a> IntoIterator for &'a XYZ {
|
||||||
type Item = &'a Float;
|
type Item = &'a Float;
|
||||||
type IntoIter = std::array::IntoIter<&'a Float, 3>;
|
type IntoIter = std::array::IntoIter<&'a Float, 3>;
|
||||||
|
|
@ -89,9 +81,9 @@ impl XYZ {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<u32> for XYZ {
|
impl Index<usize> for XYZ {
|
||||||
type Output = Float;
|
type Output = Float;
|
||||||
fn index(&self, index: u32) -> &Self::Output {
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
debug_assert!(index < 3);
|
debug_assert!(index < 3);
|
||||||
match index {
|
match index {
|
||||||
0 => &self.x,
|
0 => &self.x,
|
||||||
|
|
@ -101,8 +93,8 @@ impl Index<u32> for XYZ {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndexMut<u32> for XYZ {
|
impl IndexMut<usize> for XYZ {
|
||||||
fn index_mut(&mut self, index: u32) -> &mut Self::Output {
|
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||||
debug_assert!(index < 3);
|
debug_assert!(index < 3);
|
||||||
match index {
|
match index {
|
||||||
0 => &mut self.x,
|
0 => &mut self.x,
|
||||||
|
|
@ -255,8 +247,7 @@ impl fmt::Display for XYZ {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[derive(Debug, Default, Copy, Clone)]
|
||||||
#[derive(Debug, Default, Clone, Copy)]
|
|
||||||
pub struct RGB {
|
pub struct RGB {
|
||||||
pub r: Float,
|
pub r: Float,
|
||||||
pub g: Float,
|
pub g: Float,
|
||||||
|
|
@ -295,7 +286,7 @@ impl RGB {
|
||||||
self.r.min(self.g).min(self.b)
|
self.r.min(self.g).min(self.b)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn min_component_index(&self) -> u32 {
|
pub fn min_component_index(&self) -> usize {
|
||||||
if self.r < self.g {
|
if self.r < self.g {
|
||||||
if self.r < self.b { 0 } else { 2 }
|
if self.r < self.b { 0 } else { 2 }
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -303,7 +294,7 @@ impl RGB {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn max_component_index(&self) -> u32 {
|
pub fn max_component_index(&self) -> usize {
|
||||||
if self.r > self.g {
|
if self.r > self.g {
|
||||||
if self.r > self.b { 0 } else { 2 }
|
if self.r > self.b { 0 } else { 2 }
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -324,9 +315,9 @@ impl RGB {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<u32> for RGB {
|
impl Index<usize> for RGB {
|
||||||
type Output = Float;
|
type Output = Float;
|
||||||
fn index(&self, index: u32) -> &Self::Output {
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
debug_assert!(index < 3);
|
debug_assert!(index < 3);
|
||||||
match index {
|
match index {
|
||||||
0 => &self.r,
|
0 => &self.r,
|
||||||
|
|
@ -336,8 +327,8 @@ impl Index<u32> for RGB {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndexMut<u32> for RGB {
|
impl IndexMut<usize> for RGB {
|
||||||
fn index_mut(&mut self, index: u32) -> &mut Self::Output {
|
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||||
debug_assert!(index < 3);
|
debug_assert!(index < 3);
|
||||||
match index {
|
match index {
|
||||||
0 => &mut self.r,
|
0 => &mut self.r,
|
||||||
|
|
@ -667,7 +658,7 @@ impl ColorEncodingTrait for SRGBEncoding {
|
||||||
|
|
||||||
fn to_linear(&self, vin: &[u8], vout: &mut [Float]) {
|
fn to_linear(&self, vin: &[u8], vout: &mut [Float]) {
|
||||||
for (i, &v) in vin.iter().enumerate() {
|
for (i, &v) in vin.iter().enumerate() {
|
||||||
vout[i] = SRGB_TO_LINEAR_LUT[v as u32];
|
vout[i] = SRGB_TO_LINEAR_LUT[v as usize];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -970,7 +961,7 @@ const SRGB_TO_LINEAR_LUT: [Float; 256] = [
|
||||||
1.0000000000,
|
1.0000000000,
|
||||||
];
|
];
|
||||||
|
|
||||||
pub const RES: u32 = 64;
|
pub const RES: usize = 64;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
|
@ -1016,9 +1007,9 @@ unsafe impl Sync for RGBToSpectrumTable {}
|
||||||
|
|
||||||
impl RGBToSpectrumTable {
|
impl RGBToSpectrumTable {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn get_coeffs(&self, bucket: u32, z: u32, y: u32, x: u32) -> Coeffs {
|
fn get_coeffs(&self, bucket: usize, z: usize, y: usize, x: usize) -> 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.add(offset as usize) }
|
unsafe { *self.coeffs.add(offset) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn evaluate(&self, rgb: RGB) -> RGBSigmoidPolynomial {
|
pub fn evaluate(&self, rgb: RGB) -> RGBSigmoidPolynomial {
|
||||||
|
|
@ -1054,25 +1045,25 @@ 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_slice = unsafe { core::slice::from_raw_parts(self.z_nodes, RES as usize) };
|
let z_nodes_slice = unsafe { core::slice::from_raw_parts(self.z_nodes, RES) };
|
||||||
let zi = find_interval(RES, |i| z_nodes_slice[i as usize] < z) as usize;
|
let zi = find_interval(RES, |i| z_nodes_slice[i] < z);
|
||||||
let dz = (z - z_nodes_slice[zi]) / (z_nodes_slice[zi + 1] - z_nodes_slice[zi]);
|
let dz = (z - z_nodes_slice[zi]) / (z_nodes_slice[zi + 1] - z_nodes_slice[zi]);
|
||||||
let x_float = x * (RES - 1) as Float;
|
let x_float = x * (RES - 1) as Float;
|
||||||
let xi = (x_float as u32).min(RES - 2);
|
let xi = (x_float as usize).min(RES - 2);
|
||||||
let dx = x_float - xi as Float;
|
let dx = x_float - xi as Float;
|
||||||
|
|
||||||
let y_float = y * (RES - 1) as Float;
|
let y_float = y * (RES - 1) as Float;
|
||||||
let yi = (y_float as u32).min(RES - 2);
|
let yi = (y_float as usize).min(RES - 2);
|
||||||
let dy = y_float - yi as Float;
|
let dy = y_float - yi as Float;
|
||||||
|
|
||||||
let c000 = self.get_coeffs(c_idx, zi as u32, yi, xi);
|
let c000 = self.get_coeffs(c_idx, zi, yi, xi);
|
||||||
let c001 = self.get_coeffs(c_idx, zi as u32, yi, xi + 1);
|
let c001 = self.get_coeffs(c_idx, zi, yi, xi + 1);
|
||||||
let c010 = self.get_coeffs(c_idx, zi as u32, yi + 1, xi);
|
let c010 = self.get_coeffs(c_idx, zi, yi + 1, xi);
|
||||||
let c011 = self.get_coeffs(c_idx, zi as u32, yi + 1, xi + 1);
|
let c011 = self.get_coeffs(c_idx, zi, yi + 1, xi + 1);
|
||||||
let c100 = self.get_coeffs(c_idx, zi as u32 + 1, yi, xi);
|
let c100 = self.get_coeffs(c_idx, zi + 1, yi, xi);
|
||||||
let c101 = self.get_coeffs(c_idx, zi as u32 + 1, yi, xi + 1);
|
let c101 = self.get_coeffs(c_idx, zi + 1, yi, xi + 1);
|
||||||
let c110 = self.get_coeffs(c_idx, zi as u32 + 1, yi + 1, xi);
|
let c110 = self.get_coeffs(c_idx, zi + 1, yi + 1, xi);
|
||||||
let c111 = self.get_coeffs(c_idx, zi as u32 + 1, yi + 1, xi + 1);
|
let c111 = self.get_coeffs(c_idx, zi + 1, yi + 1, xi + 1);
|
||||||
let c00 = lerp(dx, c000, c001);
|
let c00 = lerp(dx, c000, c001);
|
||||||
let c01 = lerp(dx, c010, c011);
|
let c01 = lerp(dx, c010, c011);
|
||||||
let c10 = lerp(dx, c100, c101);
|
let c10 = lerp(dx, c100, c101);
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use crate::core::geometry::{
|
||||||
Bounds2f, Bounds2fi, Bounds2i, Normal3f, Point2f, Point2i, Point3f, Tuple, Vector2f, Vector2fi,
|
Bounds2f, Bounds2fi, Bounds2i, Normal3f, Point2f, Point2i, Point3f, Tuple, Vector2f, Vector2fi,
|
||||||
Vector2i, Vector3f,
|
Vector2i, Vector3f,
|
||||||
};
|
};
|
||||||
use crate::core::image::{DeviceImage, PixelFormat};
|
use crate::core::image::{Image, PixelFormat};
|
||||||
use crate::core::interaction::SurfaceInteraction;
|
use crate::core::interaction::SurfaceInteraction;
|
||||||
use crate::core::pbrt::Float;
|
use crate::core::pbrt::Float;
|
||||||
use crate::core::spectrum::{Spectrum, SpectrumTrait, StandardSpectra};
|
use crate::core::spectrum::{Spectrum, SpectrumTrait, StandardSpectra};
|
||||||
|
|
@ -17,7 +17,7 @@ use crate::utils::AtomicFloat;
|
||||||
use crate::utils::containers::Array2D;
|
use crate::utils::containers::Array2D;
|
||||||
use crate::utils::math::linear_least_squares;
|
use crate::utils::math::linear_least_squares;
|
||||||
use crate::utils::math::{SquareMatrix, wrap_equal_area_square};
|
use crate::utils::math::{SquareMatrix, wrap_equal_area_square};
|
||||||
use crate::utils::ptr::DevicePtr;
|
use crate::utils::ptr::Ptr;
|
||||||
use crate::utils::sampling::VarianceEstimator;
|
use crate::utils::sampling::VarianceEstimator;
|
||||||
use crate::utils::transform::AnimatedTransform;
|
use crate::utils::transform::AnimatedTransform;
|
||||||
|
|
||||||
|
|
@ -472,7 +472,7 @@ impl PixelSensor {
|
||||||
|
|
||||||
pub fn new_with_white_balance(
|
pub fn new_with_white_balance(
|
||||||
output_colorspace: &RGBColorSpace,
|
output_colorspace: &RGBColorSpace,
|
||||||
sensor_illum: DevicePtr<Spectrum>,
|
sensor_illum: Ptr<Spectrum>,
|
||||||
imaging_ratio: Float,
|
imaging_ratio: Float,
|
||||||
spectra: *const StandardSpectra,
|
spectra: *const StandardSpectra,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use crate::core::pbrt::Float;
|
||||||
use crate::filters::*;
|
use crate::filters::*;
|
||||||
use crate::utils::containers::Array2D;
|
use crate::utils::containers::Array2D;
|
||||||
use crate::utils::math::{gaussian, gaussian_integral, lerp, sample_tent, windowed_sinc};
|
use crate::utils::math::{gaussian, gaussian_integral, lerp, sample_tent, windowed_sinc};
|
||||||
use crate::utils::sampling::DevicePiecewiseConstant2D;
|
use crate::utils::sampling::PiecewiseConstant2D;
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
|
|
||||||
pub struct FilterSample {
|
pub struct FilterSample {
|
||||||
|
|
@ -15,7 +15,7 @@ pub struct FilterSample {
|
||||||
#[derive(Clone, Debug, Copy)]
|
#[derive(Clone, Debug, Copy)]
|
||||||
pub struct FilterSampler {
|
pub struct FilterSampler {
|
||||||
pub domain: Bounds2f,
|
pub domain: Bounds2f,
|
||||||
pub distrib: DevicePiecewiseConstant2D,
|
pub distrib: PiecewiseConstant2D,
|
||||||
pub f: Array2D<Float>,
|
pub f: Array2D<Float>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,7 +43,7 @@ impl FilterSampler {
|
||||||
f[(x as i32, y as i32)] = func(p);
|
f[(x as i32, y as i32)] = func(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let distrib = DevicePiecewiseConstant2D::new_with_bounds(&f, domain);
|
let distrib = PiecewiseConstant2D::new_with_bounds(&f, domain);
|
||||||
Self { domain, f, distrib }
|
Self { domain, f, distrib }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use super::{Normal3f, Point3f, Point3fi, Vector3f, VectorLike};
|
||||||
use crate::core::medium::Medium;
|
use crate::core::medium::Medium;
|
||||||
use crate::core::pbrt::Float;
|
use crate::core::pbrt::Float;
|
||||||
use crate::utils::math::{next_float_down, next_float_up};
|
use crate::utils::math::{next_float_down, next_float_up};
|
||||||
use crate::utils::ptr::DevicePtr;
|
use crate::utils::ptr::Ptr;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
|
@ -10,7 +10,7 @@ pub struct Ray {
|
||||||
pub o: Point3f,
|
pub o: Point3f,
|
||||||
pub d: Vector3f,
|
pub d: Vector3f,
|
||||||
pub time: Float,
|
pub time: Float,
|
||||||
pub medium: DevicePtr<Medium>,
|
pub medium: Ptr<Medium>,
|
||||||
// We do this instead of creating a trait for Rayable or some gnarly thing like that
|
// We do this instead of creating a trait for Rayable or some gnarly thing like that
|
||||||
pub has_differentials: bool,
|
pub has_differentials: bool,
|
||||||
pub differential: RayDifferential,
|
pub differential: RayDifferential,
|
||||||
|
|
@ -21,7 +21,7 @@ impl Default for Ray {
|
||||||
Self {
|
Self {
|
||||||
o: Point3f::new(0.0, 0.0, 0.0),
|
o: Point3f::new(0.0, 0.0, 0.0),
|
||||||
d: Vector3f::new(0.0, 0.0, 0.0),
|
d: Vector3f::new(0.0, 0.0, 0.0),
|
||||||
medium: DevicePtr::null(),
|
medium: Ptr::null(),
|
||||||
time: 0.0,
|
time: 0.0,
|
||||||
has_differentials: false,
|
has_differentials: false,
|
||||||
differential: RayDifferential::default(),
|
differential: RayDifferential::default(),
|
||||||
|
|
@ -35,7 +35,7 @@ impl Ray {
|
||||||
o,
|
o,
|
||||||
d,
|
d,
|
||||||
time: time.unwrap_or_else(|| Self::default().time),
|
time: time.unwrap_or_else(|| Self::default().time),
|
||||||
medium: DevicePtr::from(medium),
|
medium: Ptr::from(medium),
|
||||||
..Self::default()
|
..Self::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -71,7 +71,7 @@ impl Ray {
|
||||||
o: origin,
|
o: origin,
|
||||||
d,
|
d,
|
||||||
time,
|
time,
|
||||||
medium: DevicePtr::null(),
|
medium: Ptr::null(),
|
||||||
has_differentials: false,
|
has_differentials: false,
|
||||||
differential: RayDifferential::default(),
|
differential: RayDifferential::default(),
|
||||||
}
|
}
|
||||||
|
|
@ -99,7 +99,7 @@ impl Ray {
|
||||||
o: pf,
|
o: pf,
|
||||||
d,
|
d,
|
||||||
time,
|
time,
|
||||||
medium: DevicePtr::null(),
|
medium: Ptr::null(),
|
||||||
has_differentials: false,
|
has_differentials: false,
|
||||||
differential: RayDifferential::default(),
|
differential: RayDifferential::default(),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,75 +66,87 @@ pub enum Pixels {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct ImageBase {
|
pub struct Image {
|
||||||
pub format: PixelFormat,
|
pub format: PixelFormat,
|
||||||
|
pub pixels: Pixels,
|
||||||
pub encoding: ColorEncoding,
|
pub encoding: ColorEncoding,
|
||||||
pub resolution: Point2i,
|
pub resolution: Point2i,
|
||||||
pub n_channels: i32,
|
pub n_channels: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImageBase {
|
impl Image {
|
||||||
|
pub fn resolution(&self) -> Point2i {
|
||||||
|
self.resolution
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_valid(&self) -> bool {
|
||||||
|
self.resolution.x() > 0. && self.resolution.y() > 0.
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format(&self) -> PixelFormat {
|
||||||
|
self.format
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn n_channels(&self) -> i32 {
|
||||||
|
self.n_channels
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pixel_offset(&self, p: Point2i) -> u32 {
|
||||||
|
let width = self.resolution.x() as u32;
|
||||||
|
let idx = p.y() as u32 * width + p.x() as u32;
|
||||||
|
idx * (self.n_channels as u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_channel_with_wrap(&self, p: Point2i, c: i32, wrap_mode: WrapMode2D) -> Float {
|
||||||
|
if !self.remap_pixel_coords(&mut p, wrap_mode) {
|
||||||
|
return 0.;
|
||||||
|
}
|
||||||
|
|
||||||
|
let offset = self.pixel_offset(p) + c;
|
||||||
|
unsafe {
|
||||||
|
match self.pixels {
|
||||||
|
Pixels::U8(ptr) => {
|
||||||
|
let raw_u8 = *ptr.add(offset);
|
||||||
|
self.encoding.to_linear_scalar(raw_u8)
|
||||||
|
}
|
||||||
|
Pixels::F16(ptr) => {
|
||||||
|
let half_bits = *ptr.add(offset);
|
||||||
|
f16_to_f32(f16::from_bits(half_bits))
|
||||||
|
}
|
||||||
|
Pixels::F32(ptr) => *ptr.add(offset),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_channel(&self, p: Point2i, c: i32) -> Float {
|
||||||
|
self.get_channel_with_wrap(p, c, WrapMode::Clamp.into())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn remap_pixel_coords(&self, p: &mut Point2i, wrap_mode: WrapMode2D) -> bool {
|
pub fn remap_pixel_coords(&self, p: &mut Point2i, wrap_mode: WrapMode2D) -> bool {
|
||||||
let resolution = self.resolution;
|
|
||||||
for i in 0..2 {
|
for i in 0..2 {
|
||||||
if p[i] >= 0 && p[i] < resolution[i] {
|
if p[i] >= 0 && p[i] < self.resolution[i] {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
match wrap_mode.uv[i] {
|
match wrap_mode.uv[i] {
|
||||||
WrapMode::Black => return false,
|
WrapMode::Black => return false,
|
||||||
WrapMode::Clamp => p[i] = p[i].clamp(0, resolution[i] - 1),
|
WrapMode::Clamp => p[i] = p[i].clamp(0, self.resolution[i] - 1),
|
||||||
WrapMode::Repeat => p[i] = p[i].rem_euclid(resolution[i]),
|
WrapMode::Repeat => p[i] = p[i].rem_euclid(self.resolution[i]),
|
||||||
WrapMode::OctahedralSphere => {
|
WrapMode::OctahedralSphere => {
|
||||||
p[i] = p[i].clamp(0, resolution[i] - 1);
|
p[i] = p[i].clamp(0, self.resolution[i] - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct DeviceImage {
|
|
||||||
pub base: ImageBase,
|
|
||||||
pub pixels: Pixels,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DeviceImage {
|
|
||||||
pub fn base(&self) -> ImageBase {
|
|
||||||
self.base
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resolution(&self) -> Point2i {
|
|
||||||
self.base.resolution
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_valid(&self) -> bool {
|
|
||||||
self.resolution().x() > 0 && self.resolution().y() > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn format(&self) -> PixelFormat {
|
|
||||||
self.base().format
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn n_channels(&self) -> i32 {
|
|
||||||
self.base().n_channels
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pixel_offset(&self, p: Point2i) -> u32 {
|
|
||||||
let width = self.resolution().x() as u32;
|
|
||||||
let idx = p.y() as u32 * width + p.x() as u32;
|
|
||||||
idx * (self.n_channels() as u32)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bilerp_channel(&self, p: Point2f, c: i32) -> Float {
|
pub fn bilerp_channel(&self, p: Point2f, c: i32) -> Float {
|
||||||
self.bilerp_channel_with_wrap(p, c, WrapMode::Clamp.into())
|
self.bilerp_channel_with_wrap(p, c, WrapMode::Clamp.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bilerp_channel_with_wrap(&self, p: Point2f, c: i32, wrap_mode: WrapMode2D) -> Float {
|
pub fn bilerp_channel_with_wrap(&self, p: Point2f, c: i32, wrap_mode: WrapMode2D) -> Float {
|
||||||
let x = p.x() * self.resolution().x() as Float - 0.5;
|
let x = p.x() * self.resolution.x() as Float - 0.5;
|
||||||
let y = p.y() * self.resolution().y() as Float - 0.5;
|
let y = p.y() * self.resolution.y() as Float - 0.5;
|
||||||
let xi = x.floor() as i32;
|
let xi = x.floor() as i32;
|
||||||
let yi = y.floor() as i32;
|
let yi = y.floor() as i32;
|
||||||
let dx = x - xi as Float;
|
let dx = x - xi as Float;
|
||||||
|
|
@ -145,51 +157,22 @@ impl DeviceImage {
|
||||||
let v11 = self.get_channel_with_wrap(Point2i::new(xi + 1, yi + 1), c, wrap_mode);
|
let v11 = self.get_channel_with_wrap(Point2i::new(xi + 1, yi + 1), c, wrap_mode);
|
||||||
lerp(dy, lerp(dx, v00, v10), lerp(dx, v01, v11))
|
lerp(dy, lerp(dx, v00, v10), lerp(dx, v01, v11))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ImageAccess {
|
pub fn lookup_nearest_channel_with_wrap(
|
||||||
fn get_channel_with_wrap(&self, p: Point2i, c: i32, wrap_mode: WrapMode2D) -> Float;
|
&self,
|
||||||
fn get_channel(&self, p: Point2i, c: i32) -> Float;
|
p: Point2f,
|
||||||
fn lookup_nearest_channel_with_wrap(&self, p: Point2f, c: i32, wrap_mode: WrapMode2D) -> Float;
|
c: i32,
|
||||||
fn lookup_nearest_channel(&self, p: Point2f, c: i32) -> Float;
|
wrap_mode: WrapMode2D,
|
||||||
}
|
) -> Float {
|
||||||
|
|
||||||
impl ImageAccess for DeviceImage {
|
|
||||||
fn get_channel_with_wrap(&self, mut p: Point2i, c: i32, wrap_mode: WrapMode2D) -> Float {
|
|
||||||
if !self.base.remap_pixel_coords(&mut p, wrap_mode) {
|
|
||||||
return 0.;
|
|
||||||
}
|
|
||||||
|
|
||||||
let offset = self.pixel_offset(p) + c as u32;
|
|
||||||
unsafe {
|
|
||||||
match self.pixels {
|
|
||||||
Pixels::U8(ptr) => {
|
|
||||||
let raw_u8 = *ptr.add(offset as usize);
|
|
||||||
self.base().encoding.to_linear_scalar(raw_u8)
|
|
||||||
}
|
|
||||||
Pixels::F16(ptr) => {
|
|
||||||
let half_bits: u16 = *ptr.add(offset as usize) as u16;
|
|
||||||
f16_to_f32(half_bits)
|
|
||||||
}
|
|
||||||
Pixels::F32(ptr) => *ptr.add(offset as usize),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_channel(&self, p: Point2i, c: i32) -> Float {
|
|
||||||
self.get_channel_with_wrap(p, c, WrapMode::Clamp.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lookup_nearest_channel_with_wrap(&self, p: Point2f, c: i32, wrap_mode: WrapMode2D) -> Float {
|
|
||||||
let pi = Point2i::new(
|
let pi = Point2i::new(
|
||||||
p.x() as i32 * self.resolution().x(),
|
p.x() as i32 * self.resolution.x(),
|
||||||
p.y() as i32 * self.resolution().y(),
|
p.y() as i32 * self.resolution.y(),
|
||||||
);
|
);
|
||||||
|
|
||||||
self.get_channel_with_wrap(pi, c, wrap_mode)
|
self.get_channel_with_wrap(pi, c, wrap_mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lookup_nearest_channel(&self, p: Point2f, c: i32) -> Float {
|
pub fn lookup_nearest_channel(&self, p: Point2f, c: i32) -> Float {
|
||||||
self.lookup_nearest_channel_with_wrap(p, c, WrapMode::Clamp.into())
|
self.lookup_nearest_channel_with_wrap(p, c, WrapMode::Clamp.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use crate::core::camera::{Camera, CameraTrait};
|
||||||
use crate::core::geometry::{
|
use crate::core::geometry::{
|
||||||
Normal3f, Point2f, Point3f, Point3fi, Ray, RayDifferential, Vector3f, VectorLike,
|
Normal3f, Point2f, Point3f, Point3fi, Ray, RayDifferential, Vector3f, VectorLike,
|
||||||
};
|
};
|
||||||
use crate::core::image::DeviceImage;
|
use crate::core::image::Image;
|
||||||
use crate::core::light::{Light, LightTrait};
|
use crate::core::light::{Light, LightTrait};
|
||||||
use crate::core::material::{
|
use crate::core::material::{
|
||||||
Material, MaterialEvalContext, MaterialTrait, NormalBumpEvalContext, bump_map, normal_map,
|
Material, MaterialEvalContext, MaterialTrait, NormalBumpEvalContext, bump_map, normal_map,
|
||||||
|
|
@ -413,7 +413,7 @@ impl SurfaceInteraction {
|
||||||
&mut self,
|
&mut self,
|
||||||
tex_eval: &UniversalTextureEvaluator,
|
tex_eval: &UniversalTextureEvaluator,
|
||||||
displacement: Ptr<GPUFloatTexture>,
|
displacement: Ptr<GPUFloatTexture>,
|
||||||
normal_image: Ptr<DeviceImage>,
|
normal_image: Ptr<Image>,
|
||||||
) {
|
) {
|
||||||
let ctx = NormalBumpEvalContext::from(&*self);
|
let ctx = NormalBumpEvalContext::from(&*self);
|
||||||
let (dpdu, dpdv) = if !displacement.is_null() {
|
let (dpdu, dpdv) = if !displacement.is_null() {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use crate::core::geometry::{
|
||||||
Bounds2f, Bounds3f, DirectionCone, Normal3f, Point2f, Point2i, Point3f, Point3fi, Ray,
|
Bounds2f, Bounds3f, DirectionCone, Normal3f, Point2f, Point2i, Point3f, Point3fi, Ray,
|
||||||
Vector3f, VectorLike, cos_theta,
|
Vector3f, VectorLike, cos_theta,
|
||||||
};
|
};
|
||||||
use crate::core::image::DeviceImage;
|
use crate::core::image::Image;
|
||||||
use crate::core::interaction::{
|
use crate::core::interaction::{
|
||||||
Interaction, InteractionBase, InteractionTrait, MediumInteraction, SimpleInteraction,
|
Interaction, InteractionBase, InteractionTrait, MediumInteraction, SimpleInteraction,
|
||||||
SurfaceInteraction,
|
SurfaceInteraction,
|
||||||
|
|
@ -17,8 +17,8 @@ use crate::spectra::{
|
||||||
};
|
};
|
||||||
use crate::utils::Transform;
|
use crate::utils::Transform;
|
||||||
use crate::utils::math::{equal_area_sphere_to_square, radians, safe_sqrt, smooth_step, square};
|
use crate::utils::math::{equal_area_sphere_to_square, radians, safe_sqrt, smooth_step, square};
|
||||||
use crate::utils::ptr::{DevicePtr, Ptr};
|
use crate::utils::ptr::Ptr;
|
||||||
use crate::utils::sampling::DevicePiecewiseConstant2D;
|
use crate::utils::sampling::PiecewiseConstant2D;
|
||||||
use crate::{Float, PI};
|
use crate::{Float, PI};
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
|
||||||
|
|
@ -340,9 +340,9 @@ pub enum Light {
|
||||||
DiffuseArea(DiffuseAreaLight),
|
DiffuseArea(DiffuseAreaLight),
|
||||||
Distant(DistantLight),
|
Distant(DistantLight),
|
||||||
Goniometric(GoniometricLight),
|
Goniometric(GoniometricLight),
|
||||||
InfiniteUniform(UniformInfiniteLight),
|
InfiniteUniform(InfiniteUniformLight),
|
||||||
InfiniteImage(ImageInfiniteLight),
|
InfiniteImage(InfiniteImageLight),
|
||||||
InfinitePortal(PortalInfiniteLight),
|
InfinitePortal(InfinitePortalLight),
|
||||||
Point(PointLight),
|
Point(PointLight),
|
||||||
Projection(ProjectionLight),
|
Projection(ProjectionLight),
|
||||||
Spot(SpotLight),
|
Spot(SpotLight),
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use crate::core::bsdf::BSDF;
|
||||||
use crate::core::bssrdf::BSSRDF;
|
use crate::core::bssrdf::BSSRDF;
|
||||||
use crate::core::bxdf::BxDF;
|
use crate::core::bxdf::BxDF;
|
||||||
use crate::core::geometry::{Frame, Normal3f, Point2f, Point3f, Vector2f, Vector3f, VectorLike};
|
use crate::core::geometry::{Frame, Normal3f, Point2f, Point3f, Vector2f, Vector3f, VectorLike};
|
||||||
use crate::core::image::{DeviceImage, WrapMode, WrapMode2D};
|
use crate::core::image::{Image, WrapMode, WrapMode2D};
|
||||||
use crate::core::interaction::{Interaction, InteractionTrait, ShadingGeom, SurfaceInteraction};
|
use crate::core::interaction::{Interaction, InteractionTrait, ShadingGeom, SurfaceInteraction};
|
||||||
use crate::core::scattering::TrowbridgeReitzDistribution;
|
use crate::core::scattering::TrowbridgeReitzDistribution;
|
||||||
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
||||||
|
|
@ -19,7 +19,7 @@ use crate::core::texture::{
|
||||||
};
|
};
|
||||||
use crate::materials::*;
|
use crate::materials::*;
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::DevicePtr;
|
use crate::utils::Ptr;
|
||||||
use crate::utils::hash::hash_float;
|
use crate::utils::hash::hash_float;
|
||||||
use crate::utils::math::clamp;
|
use crate::utils::math::clamp;
|
||||||
|
|
||||||
|
|
@ -103,7 +103,7 @@ impl From<&NormalBumpEvalContext> for TextureEvalContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn normal_map(normal_map: &DeviceImage, ctx: &NormalBumpEvalContext) -> (Vector3f, Vector3f) {
|
pub fn normal_map(normal_map: &Image, ctx: &NormalBumpEvalContext) -> (Vector3f, Vector3f) {
|
||||||
let wrap = WrapMode2D::from(WrapMode::Repeat);
|
let wrap = WrapMode2D::from(WrapMode::Repeat);
|
||||||
let uv = Point2f::new(ctx.uv[0], 1. - ctx.uv[1]);
|
let uv = Point2f::new(ctx.uv[0], 1. - ctx.uv[1]);
|
||||||
let r = normal_map.bilerp_channel_with_wrap(uv, 0, wrap);
|
let r = normal_map.bilerp_channel_with_wrap(uv, 0, wrap);
|
||||||
|
|
@ -125,7 +125,7 @@ pub fn bump_map<T: TextureEvaluator>(
|
||||||
displacement: &GPUFloatTexture,
|
displacement: &GPUFloatTexture,
|
||||||
ctx: &NormalBumpEvalContext,
|
ctx: &NormalBumpEvalContext,
|
||||||
) -> (Vector3f, Vector3f) {
|
) -> (Vector3f, Vector3f) {
|
||||||
debug_assert!(tex_eval.can_evaluate(&[DevicePtr::from(displacement)], &[]));
|
debug_assert!(tex_eval.can_evaluate(&[Ptr::from(displacement)], &[]));
|
||||||
let mut du = 0.5 * (ctx.dudx.abs() + ctx.dudy.abs());
|
let mut du = 0.5 * (ctx.dudx.abs() + ctx.dudy.abs());
|
||||||
if du == 0.0 {
|
if du == 0.0 {
|
||||||
du = 0.0005;
|
du = 0.0005;
|
||||||
|
|
@ -173,8 +173,8 @@ pub trait MaterialTrait {
|
||||||
) -> Option<BSSRDF>;
|
) -> Option<BSSRDF>;
|
||||||
|
|
||||||
fn can_evaluate_textures(&self, tex_eval: &dyn TextureEvaluator) -> bool;
|
fn can_evaluate_textures(&self, tex_eval: &dyn TextureEvaluator) -> bool;
|
||||||
fn get_normal_map(&self) -> Option<&DeviceImage>;
|
fn get_normal_map(&self) -> Option<&Image>;
|
||||||
fn get_displacement(&self) -> DevicePtr<GPUFloatTexture>;
|
fn get_displacement(&self) -> Ptr<GPUFloatTexture>;
|
||||||
fn has_subsurface_scattering(&self) -> bool;
|
fn has_subsurface_scattering(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use crate::spectra::{
|
||||||
};
|
};
|
||||||
use crate::utils::containers::SampledGrid;
|
use crate::utils::containers::SampledGrid;
|
||||||
use crate::utils::math::{clamp, square};
|
use crate::utils::math::{clamp, square};
|
||||||
use crate::utils::ptr::DevicePtr;
|
use crate::utils::ptr::Ptr;
|
||||||
use crate::utils::rng::Rng;
|
use crate::utils::rng::Rng;
|
||||||
use crate::utils::transform::Transform;
|
use crate::utils::transform::Transform;
|
||||||
|
|
||||||
|
|
@ -829,8 +829,8 @@ impl MediumTrait for NanoVDBMedium {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct MediumInterface {
|
pub struct MediumInterface {
|
||||||
pub inside: DevicePtr<Medium>,
|
pub inside: Ptr<Medium>,
|
||||||
pub outside: DevicePtr<Medium>,
|
pub outside: Ptr<Medium>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for MediumInterface {}
|
unsafe impl Send for MediumInterface {}
|
||||||
|
|
@ -839,32 +839,17 @@ unsafe impl Sync for MediumInterface {}
|
||||||
impl Default for MediumInterface {
|
impl Default for MediumInterface {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
inside: DevicePtr::null(),
|
inside: Ptr::null(),
|
||||||
outside: DevicePtr::null(),
|
outside: Ptr::null(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Medium> for MediumInterface {
|
|
||||||
fn from(medium: Medium) -> Self {
|
|
||||||
Self {
|
|
||||||
inside: DevicePtr::from(&medium),
|
|
||||||
outside: DevicePtr::from(&medium),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&Medium> for MediumInterface {
|
|
||||||
fn from(medium: &Medium) -> Self {
|
|
||||||
Self::from(medium.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MediumInterface {
|
impl MediumInterface {
|
||||||
pub fn new(inside: &Medium, outside: &Medium) -> Self {
|
pub fn new(inside: &Medium, outside: &Medium) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inside: DevicePtr::from(inside),
|
inside: Ptr::from(inside),
|
||||||
outside: DevicePtr::from(outside),
|
outside: Ptr::from(outside),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,50 +5,8 @@ use std::hash::Hash;
|
||||||
use std::ops::{Add, Mul};
|
use std::ops::{Add, Mul};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use crate::core::image::DeviceImage;
|
|
||||||
use crate::core::light::LightTrait;
|
|
||||||
use crate::core::shape::Shape;
|
|
||||||
use crate::core::texture::GPUFloatTexture;
|
|
||||||
use crate::lights::*;
|
|
||||||
use crate::spectra::{DenselySampledSpectrum, RGBColorSpace};
|
|
||||||
use crate::utils::Ptr;
|
|
||||||
|
|
||||||
pub type Float = f32;
|
pub type Float = f32;
|
||||||
|
|
||||||
// #[derive(Copy, Clone, Debug)]
|
|
||||||
// pub struct Host;
|
|
||||||
//
|
|
||||||
// #[derive(Copy, Clone, Debug)]
|
|
||||||
// pub struct Device;
|
|
||||||
//
|
|
||||||
// pub trait Backend: Copy + Clone + 'static {
|
|
||||||
// type ShapeRef: Copy + Clone;
|
|
||||||
// type TextureRef: Copy + Clone;
|
|
||||||
// type ImageRef: Copy + Clone;
|
|
||||||
// type DenseSpectrumRef = Ptr<DenselySampledSpectrum>;
|
|
||||||
// type ColorSpaceRef = Ptr<RGBColorSpace>;
|
|
||||||
// type DiffuseLight: LightTrait;
|
|
||||||
// type PointLight: LightTrait;
|
|
||||||
// type UniformInfiniteLight: LightTrait;
|
|
||||||
// type PortalInfiniteLight: LightTrait;
|
|
||||||
// type ImageInfiniteLight: LightTrait;
|
|
||||||
// type SpotLight: LightTrait;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// impl Backend for Device {
|
|
||||||
// type ShapeRef = Ptr<Shape>;
|
|
||||||
// type TextureRef = Ptr<GPUFloatTexture>;
|
|
||||||
// type ColorSpaceRef = Ptr<RGBColorSpace>;
|
|
||||||
// type DenseSpectrumRef = Ptr<DenselySampledSpectrum>;
|
|
||||||
// type ImageRef = Ptr<DeviceImage>;
|
|
||||||
// type DiffuseLight = Ptr<DiffuseAreaLight<Device>>;
|
|
||||||
// type PointLight = Ptr<PointLight<Device>>;
|
|
||||||
// type UniformInfiniteLight = Ptr<UniformInfiniteLight<Device>>;
|
|
||||||
// type PortalInfiniteLight = Ptr<PortalInfiniteLight<Device>>;
|
|
||||||
// type ImageInfiniteLight = Ptr<ImageInfiniteLight<Device>>;
|
|
||||||
// type SpotLight = Ptr<SpotLight<Device>>;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
#[cfg(not(feature = "use_f64"))]
|
#[cfg(not(feature = "use_f64"))]
|
||||||
pub type FloatBits = u32;
|
pub type FloatBits = u32;
|
||||||
|
|
||||||
|
|
@ -142,11 +100,36 @@ pub const PI_OVER_2: Float = 1.570_796_326_794_896_619_23;
|
||||||
pub const PI_OVER_4: Float = 0.785_398_163_397_448_309_61;
|
pub const PI_OVER_4: Float = 0.785_398_163_397_448_309_61;
|
||||||
pub const SQRT_2: Float = 1.414_213_562_373_095_048_80;
|
pub const SQRT_2: Float = 1.414_213_562_373_095_048_80;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn find_interval<F>(sz: u32, pred: F) -> u32
|
||||||
|
where
|
||||||
|
F: Fn(u32) -> bool,
|
||||||
|
{
|
||||||
|
let mut first = 0;
|
||||||
|
let mut len = sz;
|
||||||
|
|
||||||
|
while len > 0 {
|
||||||
|
let half = len >> 1;
|
||||||
|
let middle = first + half;
|
||||||
|
|
||||||
|
if pred(middle) {
|
||||||
|
first = middle + 1;
|
||||||
|
len -= half + 1;
|
||||||
|
} else {
|
||||||
|
len = half;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret = (first as i32 - 1).max(0) as u32;
|
||||||
|
ret.min(sz.saturating_sub(2))
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn gamma(n: i32) -> Float {
|
pub fn gamma(n: i32) -> Float {
|
||||||
n as Float * MACHINE_EPSILON / (1. - n as Float * MACHINE_EPSILON)
|
n as Float * MACHINE_EPSILON / (1. - n as Float * MACHINE_EPSILON)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Define the static counters. These are thread-safe.
|
||||||
pub static RARE_EVENT_TOTAL_CALLS: AtomicU64 = AtomicU64::new(0);
|
pub static RARE_EVENT_TOTAL_CALLS: AtomicU64 = AtomicU64::new(0);
|
||||||
pub static RARE_EVENT_CONDITION_MET: AtomicU64 = AtomicU64::new(0);
|
pub static RARE_EVENT_CONDITION_MET: AtomicU64 = AtomicU64::new(0);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use crate::core::medium::{Medium, MediumInterface};
|
||||||
use crate::core::pbrt::Float;
|
use crate::core::pbrt::Float;
|
||||||
use crate::core::shape::{Shape, ShapeIntersection, ShapeTrait};
|
use crate::core::shape::{Shape, ShapeIntersection, ShapeTrait};
|
||||||
use crate::core::texture::{GPUFloatTexture, TextureEvalContext};
|
use crate::core::texture::{GPUFloatTexture, TextureEvalContext};
|
||||||
use crate::utils::Ptr;
|
use crate::utils::ArenaPtr;
|
||||||
use crate::utils::hash::hash_float;
|
use crate::utils::hash::hash_float;
|
||||||
use crate::utils::transform::{AnimatedTransform, Transform};
|
use crate::utils::transform::{AnimatedTransform, Transform};
|
||||||
|
|
||||||
|
|
@ -88,13 +88,13 @@ impl PrimitiveTrait for GeometricPrimitive {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct SimplePrimitive {
|
pub struct SimplePrimitive {
|
||||||
shape: Ptr<Shape>,
|
shape: ArenaPtr<Shape>,
|
||||||
material: Ptr<Material>,
|
material: ArenaPtr<Material>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TransformedPrimitive {
|
pub struct TransformedPrimitive {
|
||||||
pub primitive: Ptr<Primitive>,
|
pub primitive: ArenaPtr<Primitive>,
|
||||||
pub render_from_primitive: Transform,
|
pub render_from_primitive: Transform,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -129,7 +129,7 @@ impl PrimitiveTrait for TransformedPrimitive {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct AnimatedPrimitive {
|
pub struct AnimatedPrimitive {
|
||||||
primitive: Ptr<Primitive>,
|
primitive: ArenaPtr<Primitive>,
|
||||||
render_from_primitive: AnimatedTransform,
|
render_from_primitive: AnimatedTransform,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -164,7 +164,7 @@ impl PrimitiveTrait for AnimatedPrimitive {
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct BVHAggregatePrimitive {
|
pub struct BVHAggregatePrimitive {
|
||||||
max_prims_in_node: u32,
|
max_prims_in_node: u32,
|
||||||
primitives: *const Ptr<Primitive>,
|
primitives: *const ArenaPtr<Primitive>,
|
||||||
nodes: *const LinearBVHNode,
|
nodes: *const LinearBVHNode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::core::filter::FilterTrait;
|
use crate::core::filter::FilterTrait;
|
||||||
use crate::core::geometry::{Bounds2f, Point2f, Point2i, Vector2f};
|
use crate::core::geometry::{Bounds2f, Point2f, Point2i, Vector2f};
|
||||||
use crate::core::options::{PBRTOptions, get_options};
|
use crate::core::options::{PBRTOptions, get_options};
|
||||||
use crate::core::pbrt::{Float, ONE_MINUS_EPSILON, PI, PI_OVER_2, PI_OVER_4};
|
use crate::core::pbrt::{Float, ONE_MINUS_EPSILON, PI, PI_OVER_2, PI_OVER_4, find_interval};
|
||||||
use crate::utils::Ptr;
|
use crate::utils::Ptr;
|
||||||
use crate::utils::containers::Array2D;
|
use crate::utils::containers::Array2D;
|
||||||
use crate::utils::math::{
|
use crate::utils::math::{
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use crate::core::material::Material;
|
||||||
use crate::core::medium::{Medium, MediumInterface};
|
use crate::core::medium::{Medium, MediumInterface};
|
||||||
use crate::shapes::*;
|
use crate::shapes::*;
|
||||||
use crate::utils::math::{next_float_down, next_float_up};
|
use crate::utils::math::{next_float_down, next_float_up};
|
||||||
use crate::utils::{DevicePtr, Transform};
|
use crate::utils::{Ptr, Transform};
|
||||||
use crate::{Float, PI};
|
use crate::{Float, PI};
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
|
|
||||||
|
|
@ -118,12 +118,7 @@ impl ShapeSampleContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn_ray(&self, w: Vector3f) -> Ray {
|
pub fn spawn_ray(&self, w: Vector3f) -> Ray {
|
||||||
Ray::new(
|
Ray::new(self.offset_ray_origin(w), w, Some(self.time), &Ptr::null())
|
||||||
self.offset_ray_origin(w),
|
|
||||||
w,
|
|
||||||
Some(self.time),
|
|
||||||
&DevicePtr::null(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use crate::spectra::{
|
||||||
SampledWavelengths,
|
SampledWavelengths,
|
||||||
};
|
};
|
||||||
use crate::textures::*;
|
use crate::textures::*;
|
||||||
use crate::utils::DevicePtr;
|
use crate::utils::Ptr;
|
||||||
use crate::utils::Transform;
|
use crate::utils::Transform;
|
||||||
use crate::utils::math::square;
|
use crate::utils::math::square;
|
||||||
use crate::{Float, INV_2_PI, INV_PI, PI};
|
use crate::{Float, INV_2_PI, INV_PI, PI};
|
||||||
|
|
@ -428,8 +428,8 @@ pub trait TextureEvaluator: Send + Sync {
|
||||||
|
|
||||||
fn can_evaluate(
|
fn can_evaluate(
|
||||||
&self,
|
&self,
|
||||||
_ftex: &[DevicePtr<GPUFloatTexture>],
|
_ftex: &[Ptr<GPUFloatTexture>],
|
||||||
_stex: &[DevicePtr<GPUSpectrumTexture>],
|
_stex: &[Ptr<GPUSpectrumTexture>],
|
||||||
) -> bool;
|
) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -453,8 +453,8 @@ impl TextureEvaluator for UniversalTextureEvaluator {
|
||||||
|
|
||||||
fn can_evaluate(
|
fn can_evaluate(
|
||||||
&self,
|
&self,
|
||||||
_float_textures: &[DevicePtr<GPUFloatTexture>],
|
_float_textures: &[Ptr<GPUFloatTexture>],
|
||||||
_spectrum_textures: &[DevicePtr<GPUSpectrumTexture>],
|
_spectrum_textures: &[Ptr<GPUSpectrumTexture>],
|
||||||
) -> bool {
|
) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#![allow(unused_imports, dead_code)]
|
#![allow(unused_imports, dead_code)]
|
||||||
#![feature(float_erf)]
|
#![feature(float_erf)]
|
||||||
#![feature(f16)]
|
#![feature(f16)]
|
||||||
#![feature(associated_type_defaults)]
|
|
||||||
|
|
||||||
pub mod bxdfs;
|
pub mod bxdfs;
|
||||||
pub mod cameras;
|
pub mod cameras;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
|
use crate::PI;
|
||||||
use crate::core::color::{RGB, XYZ};
|
use crate::core::color::{RGB, XYZ};
|
||||||
use crate::core::geometry::*;
|
use crate::core::geometry::*;
|
||||||
use crate::core::image::{DeviceImage, ImageAccess};
|
use crate::core::image::Image;
|
||||||
use crate::core::interaction::{
|
use crate::core::interaction::{
|
||||||
Interaction, InteractionTrait, MediumInteraction, SurfaceInteraction,
|
Interaction, InteractionTrait, MediumInteraction, SurfaceInteraction,
|
||||||
};
|
};
|
||||||
|
|
@ -8,6 +9,7 @@ use crate::core::light::{
|
||||||
LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait, LightType,
|
LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait, LightType,
|
||||||
};
|
};
|
||||||
use crate::core::medium::MediumInterface;
|
use crate::core::medium::MediumInterface;
|
||||||
|
use crate::core::pbrt::Float;
|
||||||
use crate::core::shape::{Shape, ShapeSampleContext, ShapeTrait};
|
use crate::core::shape::{Shape, ShapeSampleContext, ShapeTrait};
|
||||||
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
||||||
use crate::core::texture::{
|
use crate::core::texture::{
|
||||||
|
|
@ -16,7 +18,6 @@ use crate::core::texture::{
|
||||||
use crate::spectra::*;
|
use crate::spectra::*;
|
||||||
use crate::utils::hash::hash_float;
|
use crate::utils::hash::hash_float;
|
||||||
use crate::utils::{Ptr, Transform};
|
use crate::utils::{Ptr, Transform};
|
||||||
use crate::{Float, PI};
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Debug, Copy)]
|
#[derive(Clone, Debug, Copy)]
|
||||||
|
|
@ -24,9 +25,9 @@ pub struct DiffuseAreaLight {
|
||||||
pub base: LightBase,
|
pub base: LightBase,
|
||||||
pub shape: Ptr<Shape>,
|
pub shape: Ptr<Shape>,
|
||||||
pub alpha: Ptr<GPUFloatTexture>,
|
pub alpha: Ptr<GPUFloatTexture>,
|
||||||
pub colorspace: Ptr<RGBColorSpace>,
|
pub image_color_space: Ptr<RGBColorSpace>,
|
||||||
pub lemit: Ptr<DenselySampledSpectrum>,
|
pub lemit: Ptr<DenselySampledSpectrum>,
|
||||||
pub image: Ptr<DeviceImage>,
|
pub image: Ptr<Image>,
|
||||||
pub area: Float,
|
pub area: Float,
|
||||||
pub two_sided: bool,
|
pub two_sided: bool,
|
||||||
pub scale: Float,
|
pub scale: Float,
|
||||||
|
|
@ -174,8 +175,8 @@ impl LightTrait for DiffuseAreaLight {
|
||||||
fn bounds(&self) -> Option<LightBounds> {
|
fn bounds(&self) -> Option<LightBounds> {
|
||||||
let mut phi = 0.;
|
let mut phi = 0.;
|
||||||
if !self.image.is_null() {
|
if !self.image.is_null() {
|
||||||
for y in 0..self.image.base.resolution.y() {
|
for y in 0..self.image.resolution.y() {
|
||||||
for x in 0..self.image.base.resolution.x() {
|
for x in 0..self.image.resolution.x() {
|
||||||
for c in 0..3 {
|
for c in 0..3 {
|
||||||
phi += self.image.get_channel(Point2i::new(x, y), c);
|
phi += self.image.get_channel(Point2i::new(x, y), c);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use crate::core::interaction::{Interaction, InteractionBase, SimpleInteraction};
|
||||||
use crate::core::light::{LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait};
|
use crate::core::light::{LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait};
|
||||||
use crate::core::spectrum::SpectrumTrait;
|
use crate::core::spectrum::SpectrumTrait;
|
||||||
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::Ptr;
|
use crate::utils::{ArenaPtr, Ptr};
|
||||||
use crate::{Float, PI};
|
use crate::{Float, PI};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::core::geometry::{Bounds3f, Normal3f, Point2f, Point2i, Point3f, Ray, Vector3f};
|
use crate::core::geometry::{Bounds3f, Normal3f, Point2f, Point2i, Point3f, Ray, Vector3f};
|
||||||
use crate::core::image::{DeviceImage, ImageAccess};
|
use crate::core::image::Image;
|
||||||
use crate::core::light::{
|
use crate::core::light::{
|
||||||
LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait, LightType,
|
LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait, LightType,
|
||||||
};
|
};
|
||||||
|
|
@ -7,17 +7,17 @@ use crate::core::medium::MediumInterface;
|
||||||
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
||||||
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::math::equal_area_sphere_to_square;
|
use crate::utils::math::equal_area_sphere_to_square;
|
||||||
use crate::utils::sampling::DevicePiecewiseConstant2D;
|
use crate::utils::sampling::PiecewiseConstant2D;
|
||||||
use crate::utils::{Ptr, Transform};
|
use crate::utils::{Ptr, Transform};
|
||||||
use crate::{Float, PI};
|
use crate::{Float, PI};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct GoniometricLight {
|
pub struct GoniometricLight {
|
||||||
pub base: LightBase,
|
pub base: LightBase,
|
||||||
pub iemit: DenselySampledSpectrum,
|
iemit: DenselySampledSpectrum,
|
||||||
pub scale: Float,
|
scale: Float,
|
||||||
pub image: Ptr<DeviceImage>,
|
image: Ptr<Image>,
|
||||||
pub distrib: Ptr<DevicePiecewiseConstant2D>,
|
distrib: Ptr<PiecewiseConstant2D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GoniometricLight {
|
impl GoniometricLight {
|
||||||
|
|
@ -77,14 +77,13 @@ impl LightTrait for GoniometricLight {
|
||||||
|
|
||||||
#[cfg(not(target_os = "cuda"))]
|
#[cfg(not(target_os = "cuda"))]
|
||||||
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
|
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
|
||||||
let resolution = self.image.resolution();
|
|
||||||
let mut sum_y = 0.;
|
let mut sum_y = 0.;
|
||||||
for y in 0..resolution.y() {
|
for y in 0..self.image.resolution.y() {
|
||||||
for x in 0..resolution.x() {
|
for x in 0..self.image.resolution.x() {
|
||||||
sum_y += self.image.get_channel(Point2i::new(x, y), 0);
|
sum_y += self.image.get_channel(Point2i::new(x, y), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.scale * self.iemit.sample(&lambda) * 4. * PI * sum_y
|
self.scale * self.iemit.sample(&lambda) * 4. * PI * sum_y
|
||||||
/ (resolution.x() * resolution.y()) as Float
|
/ (self.image.resolution.x() * self.image.resolution.y()) as Float
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,23 @@
|
||||||
|
use crate::{
|
||||||
|
core::{
|
||||||
|
geometry::{Frame, VectorLike},
|
||||||
|
interaction::InteractionBase,
|
||||||
|
},
|
||||||
|
spectra::{RGBColorSpace, RGBIlluminantSpectrum},
|
||||||
|
utils::{
|
||||||
|
math::{clamp, equal_area_sphere_to_square, equal_area_square_to_sphere, square},
|
||||||
|
sampling::{
|
||||||
|
AliasTable, PiecewiseConstant2D, WindowedPiecewiseConstant2D, sample_uniform_sphere,
|
||||||
|
uniform_sphere_pdf,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::core::color::RGB;
|
use crate::core::color::RGB;
|
||||||
use crate::core::geometry::{
|
use crate::core::geometry::{
|
||||||
Bounds2f, Bounds3f, Normal3f, Point2f, Point2i, Point3f, Ray, Vector2f, Vector3f,
|
Bounds2f, Bounds3f, Normal3f, Point2f, Point2i, Point3f, Ray, Vector2f, Vector3f,
|
||||||
};
|
};
|
||||||
use crate::core::geometry::{Frame, VectorLike};
|
use crate::core::image::{Image, PixelFormat, WrapMode};
|
||||||
use crate::core::image::{DeviceImage, ImageAccess, PixelFormat, WrapMode};
|
|
||||||
use crate::core::interaction::InteractionBase;
|
|
||||||
use crate::core::interaction::{Interaction, SimpleInteraction};
|
use crate::core::interaction::{Interaction, SimpleInteraction};
|
||||||
use crate::core::light::{
|
use crate::core::light::{
|
||||||
LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait, LightType,
|
LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait, LightType,
|
||||||
|
|
@ -12,19 +25,14 @@ use crate::core::light::{
|
||||||
use crate::core::medium::{Medium, MediumInterface};
|
use crate::core::medium::{Medium, MediumInterface};
|
||||||
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
||||||
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
|
||||||
use crate::spectra::{RGBColorSpace, RGBIlluminantSpectrum};
|
use crate::utils::Transform;
|
||||||
use crate::utils::math::{clamp, equal_area_sphere_to_square, equal_area_square_to_sphere, square};
|
use crate::utils::ptr::Ptr;
|
||||||
use crate::utils::sampling::{
|
|
||||||
AliasTable, DevicePiecewiseConstant2D, WindowedPiecewiseConstant2D, sample_uniform_sphere,
|
|
||||||
uniform_sphere_pdf,
|
|
||||||
};
|
|
||||||
use crate::utils::{Ptr, Transform};
|
|
||||||
use crate::{Float, PI};
|
use crate::{Float, PI};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct UniformInfiniteLight {
|
pub struct InfiniteUniformLight {
|
||||||
pub base: LightBase,
|
pub base: LightBase,
|
||||||
pub lemit: Ptr<DenselySampledSpectrum>,
|
pub lemit: Ptr<DenselySampledSpectrum>,
|
||||||
pub scale: Float,
|
pub scale: Float,
|
||||||
|
|
@ -32,10 +40,10 @@ pub struct UniformInfiniteLight {
|
||||||
pub scene_radius: Float,
|
pub scene_radius: Float,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for UniformInfiniteLight {}
|
unsafe impl Send for InfiniteUniformLight {}
|
||||||
unsafe impl Sync for UniformInfiniteLight {}
|
unsafe impl Sync for InfiniteUniformLight {}
|
||||||
|
|
||||||
impl LightTrait for UniformInfiniteLight {
|
impl LightTrait for InfiniteUniformLight {
|
||||||
fn base(&self) -> &LightBase {
|
fn base(&self) -> &LightBase {
|
||||||
&self.base
|
&self.base
|
||||||
}
|
}
|
||||||
|
|
@ -112,21 +120,21 @@ impl LightTrait for UniformInfiniteLight {
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct ImageInfiniteLight {
|
pub struct InfiniteImageLight {
|
||||||
pub base: LightBase,
|
pub base: LightBase,
|
||||||
pub image: Ptr<DeviceImage>,
|
pub image: Ptr<Image>,
|
||||||
pub image_color_space: Ptr<RGBColorSpace>,
|
pub image_color_space: Ptr<RGBColorSpace>,
|
||||||
pub distrib: Ptr<DevicePiecewiseConstant2D>,
|
pub distrib: Ptr<PiecewiseConstant2D>,
|
||||||
pub compensated_distrib: Ptr<DevicePiecewiseConstant2D>,
|
pub compensated_distrib: Ptr<PiecewiseConstant2D>,
|
||||||
pub scale: Float,
|
pub scale: Float,
|
||||||
pub scene_radius: Float,
|
pub scene_radius: Float,
|
||||||
pub scene_center: Point3f,
|
pub scene_center: Point3f,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for ImageInfiniteLight {}
|
unsafe impl Send for InfiniteImageLight {}
|
||||||
unsafe impl Sync for ImageInfiniteLight {}
|
unsafe impl Sync for InfiniteImageLight {}
|
||||||
|
|
||||||
impl ImageInfiniteLight {
|
impl InfiniteImageLight {
|
||||||
fn image_le(&self, uv: Point2f, lambda: &SampledWavelengths) -> SampledSpectrum {
|
fn image_le(&self, uv: Point2f, lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
let mut rgb = RGB::default();
|
let mut rgb = RGB::default();
|
||||||
for c in 0..3 {
|
for c in 0..3 {
|
||||||
|
|
@ -141,7 +149,7 @@ impl ImageInfiniteLight {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LightTrait for ImageInfiniteLight {
|
impl LightTrait for InfiniteImageLight {
|
||||||
fn base(&self) -> &LightBase {
|
fn base(&self) -> &LightBase {
|
||||||
&self.base
|
&self.base
|
||||||
}
|
}
|
||||||
|
|
@ -248,9 +256,9 @@ impl LightTrait for ImageInfiniteLight {
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct PortalInfiniteLight {
|
pub struct InfinitePortalLight {
|
||||||
pub base: LightBase,
|
pub base: LightBase,
|
||||||
pub image: Ptr<DeviceImage>,
|
pub image: Ptr<Image>,
|
||||||
pub image_color_space: Ptr<RGBColorSpace>,
|
pub image_color_space: Ptr<RGBColorSpace>,
|
||||||
pub scale: Float,
|
pub scale: Float,
|
||||||
pub portal: [Point3f; 4],
|
pub portal: [Point3f; 4],
|
||||||
|
|
@ -260,7 +268,7 @@ pub struct PortalInfiniteLight {
|
||||||
pub scene_radius: Float,
|
pub scene_radius: Float,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PortalInfiniteLight {
|
impl InfinitePortalLight {
|
||||||
pub fn image_lookup(&self, uv: Point2f, lambda: &SampledWavelengths) -> SampledSpectrum {
|
pub fn image_lookup(&self, uv: Point2f, lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
let mut rgb = RGB::default();
|
let mut rgb = RGB::default();
|
||||||
for c in 0..3 {
|
for c in 0..3 {
|
||||||
|
|
@ -317,7 +325,7 @@ impl PortalInfiniteLight {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LightTrait for PortalInfiniteLight {
|
impl LightTrait for InfinitePortalLight {
|
||||||
fn base(&self) -> &LightBase {
|
fn base(&self) -> &LightBase {
|
||||||
&self.base
|
&self.base
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ pub mod spot;
|
||||||
pub use diffuse::DiffuseAreaLight;
|
pub use diffuse::DiffuseAreaLight;
|
||||||
pub use distant::DistantLight;
|
pub use distant::DistantLight;
|
||||||
pub use goniometric::GoniometricLight;
|
pub use goniometric::GoniometricLight;
|
||||||
pub use infinite::{ImageInfiniteLight, PortalInfiniteLight, UniformInfiniteLight};
|
pub use infinite::{InfiniteImageLight, InfinitePortalLight, InfiniteUniformLight};
|
||||||
pub use point::PointLight;
|
pub use point::PointLight;
|
||||||
pub use projection::ProjectionLight;
|
pub use projection::ProjectionLight;
|
||||||
pub use spot::SpotLight;
|
pub use spot::SpotLight;
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use crate::core::light::{
|
||||||
};
|
};
|
||||||
use crate::core::spectrum::SpectrumTrait;
|
use crate::core::spectrum::SpectrumTrait;
|
||||||
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::Ptr;
|
use crate::utils::ptr::Ptr;
|
||||||
use crate::{Float, PI};
|
use crate::{Float, PI};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use crate::core::color::RGB;
|
||||||
use crate::core::geometry::{
|
use crate::core::geometry::{
|
||||||
Bounds2f, Bounds3f, Normal3f, Point2f, Point2i, Point3f, Ray, Vector3f, VectorLike, cos_theta,
|
Bounds2f, Bounds3f, Normal3f, Point2f, Point2i, Point3f, Ray, Vector3f, VectorLike, cos_theta,
|
||||||
};
|
};
|
||||||
use crate::core::image::DeviceImage;
|
use crate::core::image::Image;
|
||||||
use crate::core::light::{
|
use crate::core::light::{
|
||||||
LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait, LightType,
|
LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait, LightType,
|
||||||
};
|
};
|
||||||
|
|
@ -11,9 +11,10 @@ use crate::core::medium::MediumInterface;
|
||||||
use crate::core::spectrum::SpectrumTrait;
|
use crate::core::spectrum::SpectrumTrait;
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::math::{radians, square};
|
use crate::utils::math::{radians, square};
|
||||||
|
use crate::utils::ptr::Ptr;
|
||||||
use crate::{
|
use crate::{
|
||||||
spectra::{RGBColorSpace, RGBIlluminantSpectrum},
|
spectra::{RGBColorSpace, RGBIlluminantSpectrum},
|
||||||
utils::{Ptr, Transform, sampling::DevicePiecewiseConstant2D},
|
utils::{Transform, sampling::PiecewiseConstant2D},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
@ -26,8 +27,8 @@ pub struct ProjectionLight {
|
||||||
pub screen_from_light: Transform,
|
pub screen_from_light: Transform,
|
||||||
pub light_from_screen: Transform,
|
pub light_from_screen: Transform,
|
||||||
pub a: Float,
|
pub a: Float,
|
||||||
pub image: Ptr<DeviceImage>,
|
pub image: Ptr<Image>,
|
||||||
pub distrib: Ptr<DevicePiecewiseConstant2D>,
|
pub distrib: Ptr<PiecewiseConstant2D>,
|
||||||
pub image_color_space: Ptr<RGBColorSpace>,
|
pub image_color_space: Ptr<RGBColorSpace>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use crate::core::light::{LightBounds, LightSampleContext};
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::math::{clamp, lerp, sample_discrete};
|
use crate::utils::math::{clamp, lerp, sample_discrete};
|
||||||
use crate::utils::math::{safe_sqrt, square};
|
use crate::utils::math::{safe_sqrt, square};
|
||||||
use crate::utils::ptr::{DevicePtr, Slice};
|
use crate::utils::ptr::{Ptr, Slice};
|
||||||
use crate::utils::sampling::AliasTable;
|
use crate::utils::sampling::AliasTable;
|
||||||
use crate::{Float, ONE_MINUS_EPSILON, PI};
|
use crate::{Float, ONE_MINUS_EPSILON, PI};
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
|
|
@ -154,14 +154,14 @@ impl CompactLightBounds {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SampledLight {
|
pub struct SampledLight {
|
||||||
pub light: DevicePtr<Light>,
|
pub light: Ptr<Light>,
|
||||||
pub p: Float,
|
pub p: Float,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SampledLight {
|
impl SampledLight {
|
||||||
pub fn new(light: Light, p: Float) -> Self {
|
pub fn new(light: Light, p: Float) -> Self {
|
||||||
Self {
|
Self {
|
||||||
light: DevicePtr::from(&light),
|
light: Ptr::from(&light),
|
||||||
p,
|
p,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -214,7 +214,7 @@ impl LightSamplerTrait for UniformLightSampler {
|
||||||
|
|
||||||
let light_index = (u as u32 * self.lights_len).min(self.lights_len - 1) as usize;
|
let light_index = (u as u32 * self.lights_len).min(self.lights_len - 1) as usize;
|
||||||
Some(SampledLight {
|
Some(SampledLight {
|
||||||
light: DevicePtr::from(&self.light(light_index)),
|
light: Ptr::from(&self.light(light_index)),
|
||||||
p: 1. / self.lights_len as Float,
|
p: 1. / self.lights_len as Float,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -262,7 +262,7 @@ impl LightSamplerTrait for PowerLightSampler {
|
||||||
|
|
||||||
let light_ref = &self.lights[light_index as usize];
|
let light_ref = &self.lights[light_index as usize];
|
||||||
Some(SampledLight {
|
Some(SampledLight {
|
||||||
light: DevicePtr::from(light_ref),
|
light: Ptr::from(light_ref),
|
||||||
p: pmf,
|
p: pmf,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use crate::bxdfs::{
|
||||||
use crate::core::bsdf::BSDF;
|
use crate::core::bsdf::BSDF;
|
||||||
use crate::core::bssrdf::BSSRDF;
|
use crate::core::bssrdf::BSSRDF;
|
||||||
use crate::core::bxdf::BxDF;
|
use crate::core::bxdf::BxDF;
|
||||||
use crate::core::image::DeviceImage;
|
use crate::core::image::Image;
|
||||||
use crate::core::material::{Material, MaterialEvalContext, MaterialTrait};
|
use crate::core::material::{Material, MaterialEvalContext, MaterialTrait};
|
||||||
use crate::core::scattering::TrowbridgeReitzDistribution;
|
use crate::core::scattering::TrowbridgeReitzDistribution;
|
||||||
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
||||||
|
|
@ -16,7 +16,7 @@ use crate::utils::math::clamp;
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct CoatedDiffuseMaterial {
|
pub struct CoatedDiffuseMaterial {
|
||||||
pub normal_map: Ptr<DeviceImage>,
|
pub normal_map: Ptr<Image>,
|
||||||
pub displacement: Ptr<GPUFloatTexture>,
|
pub displacement: Ptr<GPUFloatTexture>,
|
||||||
pub reflectance: Ptr<GPUSpectrumTexture>,
|
pub reflectance: Ptr<GPUSpectrumTexture>,
|
||||||
pub albedo: Ptr<GPUSpectrumTexture>,
|
pub albedo: Ptr<GPUSpectrumTexture>,
|
||||||
|
|
@ -42,7 +42,7 @@ impl CoatedDiffuseMaterial {
|
||||||
g: &GPUFloatTexture,
|
g: &GPUFloatTexture,
|
||||||
eta: &Spectrum,
|
eta: &Spectrum,
|
||||||
displacement: &GPUFloatTexture,
|
displacement: &GPUFloatTexture,
|
||||||
normal_map: &DeviceImage,
|
normal_map: &Image,
|
||||||
remap_roughness: bool,
|
remap_roughness: bool,
|
||||||
max_depth: usize,
|
max_depth: usize,
|
||||||
n_samples: usize,
|
n_samples: usize,
|
||||||
|
|
@ -134,7 +134,7 @@ impl MaterialTrait for CoatedDiffuseMaterial {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_normal_map(&self) -> Option<&DeviceImage> {
|
fn get_normal_map(&self) -> Option<&Image> {
|
||||||
Some(&*self.normal_map)
|
Some(&*self.normal_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -150,7 +150,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<DeviceImage>,
|
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>,
|
||||||
|
|
@ -172,7 +172,7 @@ impl CoatedConductorMaterial {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
#[cfg(not(target_os = "cuda"))]
|
#[cfg(not(target_os = "cuda"))]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
normal_map: &DeviceImage,
|
normal_map: &Image,
|
||||||
displacement: &GPUFloatTexture,
|
displacement: &GPUFloatTexture,
|
||||||
interface_uroughness: &GPUFloatTexture,
|
interface_uroughness: &GPUFloatTexture,
|
||||||
interface_vroughness: &GPUFloatTexture,
|
interface_vroughness: &GPUFloatTexture,
|
||||||
|
|
@ -326,7 +326,7 @@ impl MaterialTrait for CoatedConductorMaterial {
|
||||||
tex_eval.can_evaluate(&float_textures, &spectrum_textures)
|
tex_eval.can_evaluate(&float_textures, &spectrum_textures)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_normal_map(&self) -> Option<&DeviceImage> {
|
fn get_normal_map(&self) -> Option<&Image> {
|
||||||
Some(&*self.normal_map)
|
Some(&*self.normal_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,40 +6,40 @@ use crate::bxdfs::{
|
||||||
use crate::core::bsdf::BSDF;
|
use crate::core::bsdf::BSDF;
|
||||||
use crate::core::bssrdf::{BSSRDF, BSSRDFTable};
|
use crate::core::bssrdf::{BSSRDF, BSSRDFTable};
|
||||||
use crate::core::bxdf::BxDF;
|
use crate::core::bxdf::BxDF;
|
||||||
use crate::core::image::DeviceImage;
|
use crate::core::image::Image;
|
||||||
use crate::core::material::{Material, MaterialEvalContext, MaterialTrait};
|
use crate::core::material::{Material, MaterialEvalContext, MaterialTrait};
|
||||||
use crate::core::scattering::TrowbridgeReitzDistribution;
|
use crate::core::scattering::TrowbridgeReitzDistribution;
|
||||||
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
||||||
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator};
|
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator};
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use crate::textures::GPUSpectrumMixTexture;
|
use crate::textures::GPUSpectrumMixTexture;
|
||||||
use crate::utils::DevicePtr;
|
use crate::utils::Ptr;
|
||||||
use crate::utils::math::clamp;
|
use crate::utils::math::clamp;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct HairMaterial {
|
pub struct HairMaterial {
|
||||||
pub sigma_a: DevicePtr<GPUSpectrumTexture>,
|
pub sigma_a: Ptr<GPUSpectrumTexture>,
|
||||||
pub color: DevicePtr<GPUSpectrumTexture>,
|
pub color: Ptr<GPUSpectrumTexture>,
|
||||||
pub eumelanin: DevicePtr<GPUFloatTexture>,
|
pub eumelanin: Ptr<GPUFloatTexture>,
|
||||||
pub pheomelanin: DevicePtr<GPUFloatTexture>,
|
pub pheomelanin: Ptr<GPUFloatTexture>,
|
||||||
pub eta: DevicePtr<GPUFloatTexture>,
|
pub eta: Ptr<GPUFloatTexture>,
|
||||||
pub beta_m: DevicePtr<GPUFloatTexture>,
|
pub beta_m: Ptr<GPUFloatTexture>,
|
||||||
pub beta_n: DevicePtr<GPUFloatTexture>,
|
pub beta_n: Ptr<GPUFloatTexture>,
|
||||||
pub alpha: DevicePtr<GPUFloatTexture>,
|
pub alpha: Ptr<GPUFloatTexture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HairMaterial {
|
impl HairMaterial {
|
||||||
#[cfg(not(target_os = "cuda"))]
|
#[cfg(not(target_os = "cuda"))]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
sigma_a: DevicePtr<GPUSpectrumTexture>,
|
sigma_a: Ptr<GPUSpectrumTexture>,
|
||||||
color: DevicePtr<GPUSpectrumTexture>,
|
color: Ptr<GPUSpectrumTexture>,
|
||||||
eumelanin: DevicePtr<GPUFloatTexture>,
|
eumelanin: Ptr<GPUFloatTexture>,
|
||||||
pheomelanin: DevicePtr<GPUFloatTexture>,
|
pheomelanin: Ptr<GPUFloatTexture>,
|
||||||
eta: DevicePtr<GPUFloatTexture>,
|
eta: Ptr<GPUFloatTexture>,
|
||||||
beta_m: DevicePtr<GPUFloatTexture>,
|
beta_m: Ptr<GPUFloatTexture>,
|
||||||
beta_n: DevicePtr<GPUFloatTexture>,
|
beta_n: Ptr<GPUFloatTexture>,
|
||||||
alpha: DevicePtr<GPUFloatTexture>,
|
alpha: Ptr<GPUFloatTexture>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
sigma_a,
|
sigma_a,
|
||||||
|
|
@ -76,12 +76,12 @@ impl MaterialTrait for HairMaterial {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_normal_map(&self) -> Option<&DeviceImage> {
|
fn get_normal_map(&self) -> Option<&Image> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_displacement(&self) -> DevicePtr<GPUFloatTexture> {
|
fn get_displacement(&self) -> Ptr<GPUFloatTexture> {
|
||||||
DevicePtr::null()
|
Ptr::null()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_subsurface_scattering(&self) -> bool {
|
fn has_subsurface_scattering(&self) -> bool {
|
||||||
|
|
@ -92,9 +92,9 @@ impl MaterialTrait for HairMaterial {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct MeasuredMaterial {
|
pub struct MeasuredMaterial {
|
||||||
pub displacement: DevicePtr<GPUFloatTexture>,
|
pub displacement: Ptr<GPUFloatTexture>,
|
||||||
pub normal_map: DevicePtr<DeviceImage>,
|
pub normal_map: Ptr<Image>,
|
||||||
pub brdf: DevicePtr<MeasuredBxDFData>,
|
pub brdf: Ptr<MeasuredBxDFData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MaterialTrait for MeasuredMaterial {
|
impl MaterialTrait for MeasuredMaterial {
|
||||||
|
|
@ -121,11 +121,11 @@ impl MaterialTrait for MeasuredMaterial {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_normal_map(&self) -> Option<&DeviceImage> {
|
fn get_normal_map(&self) -> Option<&Image> {
|
||||||
Some(&*self.normal_map)
|
Some(&*self.normal_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_displacement(&self) -> DevicePtr<GPUFloatTexture> {
|
fn get_displacement(&self) -> Ptr<GPUFloatTexture> {
|
||||||
self.displacement
|
self.displacement
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,16 +137,16 @@ impl MaterialTrait for MeasuredMaterial {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct SubsurfaceMaterial {
|
pub struct SubsurfaceMaterial {
|
||||||
pub normal_map: DevicePtr<DeviceImage>,
|
pub normal_map: Ptr<Image>,
|
||||||
pub displacement: DevicePtr<GPUFloatTexture>,
|
pub displacement: Ptr<GPUFloatTexture>,
|
||||||
pub sigma_a: DevicePtr<GPUSpectrumTexture>,
|
pub sigma_a: Ptr<GPUSpectrumTexture>,
|
||||||
pub sigma_s: DevicePtr<GPUSpectrumMixTexture>,
|
pub sigma_s: Ptr<GPUSpectrumMixTexture>,
|
||||||
pub reflectance: DevicePtr<GPUSpectrumMixTexture>,
|
pub reflectance: Ptr<GPUSpectrumMixTexture>,
|
||||||
pub mfp: DevicePtr<GPUSpectrumMixTexture>,
|
pub mfp: Ptr<GPUSpectrumMixTexture>,
|
||||||
pub eta: Float,
|
pub eta: Float,
|
||||||
pub scale: Float,
|
pub scale: Float,
|
||||||
pub u_roughness: DevicePtr<GPUFloatTexture>,
|
pub u_roughness: Ptr<GPUFloatTexture>,
|
||||||
pub v_roughness: DevicePtr<GPUFloatTexture>,
|
pub v_roughness: Ptr<GPUFloatTexture>,
|
||||||
pub remap_roughness: bool,
|
pub remap_roughness: bool,
|
||||||
pub table: BSSRDFTable,
|
pub table: BSSRDFTable,
|
||||||
}
|
}
|
||||||
|
|
@ -173,11 +173,11 @@ impl MaterialTrait for SubsurfaceMaterial {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_normal_map(&self) -> Option<&DeviceImage> {
|
fn get_normal_map(&self) -> Option<&Image> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_displacement(&self) -> DevicePtr<GPUFloatTexture> {
|
fn get_displacement(&self) -> Ptr<GPUFloatTexture> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use crate::bxdfs::{
|
||||||
use crate::core::bsdf::BSDF;
|
use crate::core::bsdf::BSDF;
|
||||||
use crate::core::bssrdf::BSSRDF;
|
use crate::core::bssrdf::BSSRDF;
|
||||||
use crate::core::bxdf::BxDF;
|
use crate::core::bxdf::BxDF;
|
||||||
use crate::core::image::DeviceImage;
|
use crate::core::image::Image;
|
||||||
use crate::core::material::{Material, MaterialEvalContext, MaterialTrait};
|
use crate::core::material::{Material, MaterialEvalContext, MaterialTrait};
|
||||||
use crate::core::scattering::TrowbridgeReitzDistribution;
|
use crate::core::scattering::TrowbridgeReitzDistribution;
|
||||||
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
||||||
|
|
@ -23,7 +23,7 @@ pub struct ConductorMaterial {
|
||||||
pub u_roughness: Ptr<GPUFloatTexture>,
|
pub u_roughness: Ptr<GPUFloatTexture>,
|
||||||
pub v_roughness: Ptr<GPUFloatTexture>,
|
pub v_roughness: Ptr<GPUFloatTexture>,
|
||||||
pub remap_roughness: bool,
|
pub remap_roughness: bool,
|
||||||
pub normal_map: Ptr<DeviceImage>,
|
pub normal_map: Ptr<Image>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MaterialTrait for ConductorMaterial {
|
impl MaterialTrait for ConductorMaterial {
|
||||||
|
|
@ -50,7 +50,7 @@ impl MaterialTrait for ConductorMaterial {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_normal_map(&self) -> Option<&DeviceImage> {
|
fn get_normal_map(&self) -> Option<&Image> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,23 +4,23 @@ use crate::bxdfs::{
|
||||||
use crate::core::bsdf::BSDF;
|
use crate::core::bsdf::BSDF;
|
||||||
use crate::core::bssrdf::BSSRDF;
|
use crate::core::bssrdf::BSSRDF;
|
||||||
use crate::core::bxdf::BxDF;
|
use crate::core::bxdf::BxDF;
|
||||||
use crate::core::image::DeviceImage;
|
use crate::core::image::Image;
|
||||||
use crate::core::material::{Material, MaterialEvalContext, MaterialTrait};
|
use crate::core::material::{Material, MaterialEvalContext, MaterialTrait};
|
||||||
use crate::core::scattering::TrowbridgeReitzDistribution;
|
use crate::core::scattering::TrowbridgeReitzDistribution;
|
||||||
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
||||||
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator};
|
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator};
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::DevicePtr;
|
use crate::utils::Ptr;
|
||||||
use crate::utils::math::clamp;
|
use crate::utils::math::clamp;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct DielectricMaterial {
|
pub struct DielectricMaterial {
|
||||||
normal_map: DevicePtr<DeviceImage>,
|
normal_map: Ptr<Image>,
|
||||||
displacement: DevicePtr<GPUFloatTexture>,
|
displacement: Ptr<GPUFloatTexture>,
|
||||||
u_roughness: DevicePtr<GPUFloatTexture>,
|
u_roughness: Ptr<GPUFloatTexture>,
|
||||||
v_roughness: DevicePtr<GPUFloatTexture>,
|
v_roughness: Ptr<GPUFloatTexture>,
|
||||||
eta: DevicePtr<Spectrum>,
|
eta: Ptr<Spectrum>,
|
||||||
remap_roughness: bool,
|
remap_roughness: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,7 +51,7 @@ impl MaterialTrait for DielectricMaterial {
|
||||||
let distrib = TrowbridgeReitzDistribution::new(u_rough, v_rough);
|
let distrib = TrowbridgeReitzDistribution::new(u_rough, v_rough);
|
||||||
let bxdf = BxDF::Dielectric(DielectricBxDF::new(sampled_eta, distrib));
|
let bxdf = BxDF::Dielectric(DielectricBxDF::new(sampled_eta, distrib));
|
||||||
|
|
||||||
BSDF::new(ctx.ns, ctx.dpdus, DevicePtr::from(&bxdf))
|
BSDF::new(ctx.ns, ctx.dpdus, Ptr::from(&bxdf))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_bssrdf<T>(
|
fn get_bssrdf<T>(
|
||||||
|
|
@ -67,11 +67,11 @@ impl MaterialTrait for DielectricMaterial {
|
||||||
tex_eval.can_evaluate(&[self.u_roughness, self.v_roughness], &[])
|
tex_eval.can_evaluate(&[self.u_roughness, self.v_roughness], &[])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_normal_map(&self) -> Option<&DeviceImage> {
|
fn get_normal_map(&self) -> Option<&Image> {
|
||||||
Some(&*self.normal_map)
|
Some(&*self.normal_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_displacement(&self) -> DevicePtr<GPUFloatTexture> {
|
fn get_displacement(&self) -> Ptr<GPUFloatTexture> {
|
||||||
self.displacement
|
self.displacement
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,9 +83,9 @@ impl MaterialTrait for DielectricMaterial {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct ThinDielectricMaterial {
|
pub struct ThinDielectricMaterial {
|
||||||
pub displacement: DevicePtr<GPUFloatTexture>,
|
pub displacement: Ptr<GPUFloatTexture>,
|
||||||
pub normal_map: DevicePtr<DeviceImage>,
|
pub normal_map: Ptr<Image>,
|
||||||
pub eta: DevicePtr<Spectrum>,
|
pub eta: Ptr<Spectrum>,
|
||||||
}
|
}
|
||||||
impl MaterialTrait for ThinDielectricMaterial {
|
impl MaterialTrait for ThinDielectricMaterial {
|
||||||
fn get_bsdf<T: TextureEvaluator>(
|
fn get_bsdf<T: TextureEvaluator>(
|
||||||
|
|
@ -109,11 +109,11 @@ impl MaterialTrait for ThinDielectricMaterial {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_normal_map(&self) -> Option<&DeviceImage> {
|
fn get_normal_map(&self) -> Option<&Image> {
|
||||||
Some(&*self.normal_map)
|
Some(&*self.normal_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_displacement(&self) -> DevicePtr<GPUFloatTexture> {
|
fn get_displacement(&self) -> Ptr<GPUFloatTexture> {
|
||||||
self.displacement
|
self.displacement
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,21 +5,21 @@ use crate::bxdfs::{
|
||||||
use crate::core::bsdf::BSDF;
|
use crate::core::bsdf::BSDF;
|
||||||
use crate::core::bssrdf::BSSRDF;
|
use crate::core::bssrdf::BSSRDF;
|
||||||
use crate::core::bxdf::BxDF;
|
use crate::core::bxdf::BxDF;
|
||||||
use crate::core::image::DeviceImage;
|
use crate::core::image::Image;
|
||||||
use crate::core::material::{Material, MaterialEvalContext, MaterialTrait};
|
use crate::core::material::{Material, MaterialEvalContext, MaterialTrait};
|
||||||
use crate::core::scattering::TrowbridgeReitzDistribution;
|
use crate::core::scattering::TrowbridgeReitzDistribution;
|
||||||
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
||||||
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator};
|
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator};
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::DevicePtr;
|
use crate::utils::Ptr;
|
||||||
use crate::utils::math::clamp;
|
use crate::utils::math::clamp;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct DiffuseMaterial {
|
pub struct DiffuseMaterial {
|
||||||
pub normal_map: DevicePtr<DeviceImage>,
|
pub normal_map: Ptr<Image>,
|
||||||
pub displacement: DevicePtr<GPUFloatTexture>,
|
pub displacement: Ptr<GPUFloatTexture>,
|
||||||
pub reflectance: DevicePtr<GPUSpectrumTexture>,
|
pub reflectance: Ptr<GPUSpectrumTexture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MaterialTrait for DiffuseMaterial {
|
impl MaterialTrait for DiffuseMaterial {
|
||||||
|
|
@ -31,7 +31,7 @@ impl MaterialTrait for DiffuseMaterial {
|
||||||
) -> BSDF {
|
) -> BSDF {
|
||||||
let r = tex_eval.evaluate_spectrum(&self.reflectance, ctx, lambda);
|
let r = tex_eval.evaluate_spectrum(&self.reflectance, ctx, lambda);
|
||||||
let bxdf = BxDF::Diffuse(DiffuseBxDF::new(r));
|
let bxdf = BxDF::Diffuse(DiffuseBxDF::new(r));
|
||||||
BSDF::new(ctx.ns, ctx.dpdus, DevicePtr::from(&bxdf))
|
BSDF::new(ctx.ns, ctx.dpdus, Ptr::from(&bxdf))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_bssrdf<T>(
|
fn get_bssrdf<T>(
|
||||||
|
|
@ -47,11 +47,11 @@ impl MaterialTrait for DiffuseMaterial {
|
||||||
tex_eval.can_evaluate(&[], &[self.reflectance])
|
tex_eval.can_evaluate(&[], &[self.reflectance])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_normal_map(&self) -> Option<&DeviceImage> {
|
fn get_normal_map(&self) -> Option<&Image> {
|
||||||
Some(&*self.normal_map)
|
Some(&*self.normal_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_displacement(&self) -> DevicePtr<GPUFloatTexture> {
|
fn get_displacement(&self) -> Ptr<GPUFloatTexture> {
|
||||||
self.displacement
|
self.displacement
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,10 +63,10 @@ impl MaterialTrait for DiffuseMaterial {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct DiffuseTransmissionMaterial {
|
pub struct DiffuseTransmissionMaterial {
|
||||||
pub image: DevicePtr<DeviceImage>,
|
pub image: Ptr<Image>,
|
||||||
pub displacement: DevicePtr<GPUFloatTexture>,
|
pub displacement: Ptr<GPUFloatTexture>,
|
||||||
pub reflectance: DevicePtr<GPUFloatTexture>,
|
pub reflectance: Ptr<GPUFloatTexture>,
|
||||||
pub transmittance: DevicePtr<GPUFloatTexture>,
|
pub transmittance: Ptr<GPUFloatTexture>,
|
||||||
pub scale: Float,
|
pub scale: Float,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,11 +92,11 @@ impl MaterialTrait for DiffuseTransmissionMaterial {
|
||||||
tex_eval.can_evaluate(&[self.reflectance, self.transmittance], &[])
|
tex_eval.can_evaluate(&[self.reflectance, self.transmittance], &[])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_normal_map(&self) -> Option<&DeviceImage> {
|
fn get_normal_map(&self) -> Option<&Image> {
|
||||||
Some(&*self.image)
|
Some(&*self.image)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_displacement(&self) -> DevicePtr<GPUFloatTexture> {
|
fn get_displacement(&self) -> Ptr<GPUFloatTexture> {
|
||||||
self.displacement
|
self.displacement
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use crate::bxdfs::{
|
||||||
use crate::core::bsdf::BSDF;
|
use crate::core::bsdf::BSDF;
|
||||||
use crate::core::bssrdf::BSSRDF;
|
use crate::core::bssrdf::BSSRDF;
|
||||||
use crate::core::bxdf::BxDF;
|
use crate::core::bxdf::BxDF;
|
||||||
use crate::core::image::DeviceImage;
|
use crate::core::image::Image;
|
||||||
use crate::core::material::{Material, MaterialEvalContext, MaterialTrait};
|
use crate::core::material::{Material, MaterialEvalContext, MaterialTrait};
|
||||||
use crate::core::scattering::TrowbridgeReitzDistribution;
|
use crate::core::scattering::TrowbridgeReitzDistribution;
|
||||||
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
||||||
|
|
@ -12,13 +12,13 @@ use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::hash::hash_float;
|
use crate::utils::hash::hash_float;
|
||||||
use crate::utils::math::clamp;
|
use crate::utils::math::clamp;
|
||||||
use crate::utils::{DevicePtr, Ptr};
|
use crate::utils::{ArenaPtr, Ptr};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct MixMaterial {
|
pub struct MixMaterial {
|
||||||
pub amount: DevicePtr<GPUFloatTexture>,
|
pub amount: Ptr<GPUFloatTexture>,
|
||||||
pub materials: [Ptr<Material>; 2],
|
pub materials: [ArenaPtr<Material>; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MixMaterial {
|
impl MixMaterial {
|
||||||
|
|
@ -69,11 +69,11 @@ impl MaterialTrait for MixMaterial {
|
||||||
tex_eval.can_evaluate(&[self.amount], &[])
|
tex_eval.can_evaluate(&[self.amount], &[])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_normal_map(&self) -> Option<&DeviceImage> {
|
fn get_normal_map(&self) -> Option<&Image> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_displacement(&self) -> DevicePtr<GPUFloatTexture> {
|
fn get_displacement(&self) -> Ptr<GPUFloatTexture> {
|
||||||
panic!(
|
panic!(
|
||||||
"MixMaterial::get_displacement() shouldn't be called. \
|
"MixMaterial::get_displacement() shouldn't be called. \
|
||||||
Displacement is not supported on Mix materials directly."
|
Displacement is not supported on Mix materials directly."
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,17 @@ use crate::core::geometry::Point2f;
|
||||||
use crate::core::pbrt::Float;
|
use crate::core::pbrt::Float;
|
||||||
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum};
|
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum};
|
||||||
use crate::utils::math::SquareMatrix3f;
|
use crate::utils::math::SquareMatrix3f;
|
||||||
use crate::utils::ptr::DevicePtr;
|
use crate::utils::ptr::Ptr;
|
||||||
|
|
||||||
use std::cmp::{Eq, PartialEq};
|
use std::cmp::{Eq, PartialEq};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Debug, Clone)]
|
#[derive(Copy, Debug, Clone)]
|
||||||
pub struct StandardColorSpaces {
|
pub struct StandardColorSpaces {
|
||||||
pub srgb: DevicePtr<RGBColorSpace>,
|
pub srgb: Ptr<RGBColorSpace>,
|
||||||
pub dci_p3: DevicePtr<RGBColorSpace>,
|
pub dci_p3: Ptr<RGBColorSpace>,
|
||||||
pub rec2020: DevicePtr<RGBColorSpace>,
|
pub rec2020: Ptr<RGBColorSpace>,
|
||||||
pub aces2065_1: DevicePtr<RGBColorSpace>,
|
pub aces2065_1: Ptr<RGBColorSpace>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
@ -24,7 +24,7 @@ pub struct RGBColorSpace {
|
||||||
pub b: Point2f,
|
pub b: Point2f,
|
||||||
pub w: Point2f,
|
pub w: Point2f,
|
||||||
pub illuminant: DenselySampledSpectrum,
|
pub illuminant: DenselySampledSpectrum,
|
||||||
pub rgb_to_spectrum_table: DevicePtr<RGBToSpectrumTable>,
|
pub rgb_to_spectrum_table: Ptr<RGBToSpectrumTable>,
|
||||||
pub xyz_from_rgb: SquareMatrix3f,
|
pub xyz_from_rgb: SquareMatrix3f,
|
||||||
pub rgb_from_xyz: SquareMatrix3f,
|
pub rgb_from_xyz: SquareMatrix3f,
|
||||||
}
|
}
|
||||||
|
|
@ -52,14 +52,6 @@ impl RGBColorSpace {
|
||||||
|
|
||||||
self.rgb_from_xyz * other.xyz_from_rgb
|
self.rgb_from_xyz * other.xyz_from_rgb
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn luminance_vector(&self) -> RGB {
|
|
||||||
RGB::new(
|
|
||||||
self.xyz_from_rgb[1][0],
|
|
||||||
self.xyz_from_rgb[1][1],
|
|
||||||
self.xyz_from_rgb[1][2],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for RGBColorSpace {
|
impl PartialEq for RGBColorSpace {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use super::{
|
||||||
};
|
};
|
||||||
use crate::core::color::{RGB, RGBSigmoidPolynomial, XYZ};
|
use crate::core::color::{RGB, RGBSigmoidPolynomial, XYZ};
|
||||||
use crate::core::spectrum::SpectrumTrait;
|
use crate::core::spectrum::SpectrumTrait;
|
||||||
use crate::utils::DevicePtr;
|
use crate::utils::Ptr;
|
||||||
|
|
||||||
use crate::Float;
|
use crate::Float;
|
||||||
|
|
||||||
|
|
@ -69,7 +69,7 @@ impl SpectrumTrait for UnboundedRGBSpectrum {
|
||||||
pub struct RGBIlluminantSpectrum {
|
pub struct RGBIlluminantSpectrum {
|
||||||
pub scale: Float,
|
pub scale: Float,
|
||||||
pub rsp: RGBSigmoidPolynomial,
|
pub rsp: RGBSigmoidPolynomial,
|
||||||
pub illuminant: DevicePtr<DenselySampledSpectrum>,
|
pub illuminant: Ptr<DenselySampledSpectrum>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RGBIlluminantSpectrum {
|
impl RGBIlluminantSpectrum {
|
||||||
|
|
@ -85,7 +85,7 @@ impl RGBIlluminantSpectrum {
|
||||||
Self {
|
Self {
|
||||||
scale,
|
scale,
|
||||||
rsp,
|
rsp,
|
||||||
illuminant: DevicePtr::from(&illuminant),
|
illuminant: Ptr::from(&illuminant),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
use super::cie::*;
|
use super::cie::*;
|
||||||
use super::sampled::{LAMBDA_MAX, LAMBDA_MIN};
|
use super::sampled::{LAMBDA_MAX, LAMBDA_MIN};
|
||||||
use crate::Float;
|
|
||||||
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
||||||
use crate::spectra::{N_SPECTRUM_SAMPLES, SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{N_SPECTRUM_SAMPLES, SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::find_interval;
|
use crate::utils::ptr::Ptr;
|
||||||
use crate::utils::ptr::DevicePtr;
|
use crate::{Float, find_interval};
|
||||||
use core::slice;
|
use core::slice;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
@ -36,7 +35,7 @@ impl SpectrumTrait for ConstantSpectrum {
|
||||||
pub struct DenselySampledSpectrum {
|
pub struct DenselySampledSpectrum {
|
||||||
pub lambda_min: i32,
|
pub lambda_min: i32,
|
||||||
pub lambda_max: i32,
|
pub lambda_max: i32,
|
||||||
pub values: DevicePtr<Float>,
|
pub values: Ptr<Float>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for DenselySampledSpectrum {}
|
unsafe impl Send for DenselySampledSpectrum {}
|
||||||
|
|
@ -131,8 +130,8 @@ impl SpectrumTrait for DenselySampledSpectrum {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct PiecewiseLinearSpectrum {
|
pub struct PiecewiseLinearSpectrum {
|
||||||
pub lambdas: DevicePtr<Float>,
|
pub lambdas: Ptr<Float>,
|
||||||
pub values: DevicePtr<Float>,
|
pub values: Ptr<Float>,
|
||||||
pub count: u32,
|
pub count: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use crate::core::spectrum::Spectrum;
|
||||||
use crate::core::spectrum::SpectrumTrait;
|
use crate::core::spectrum::SpectrumTrait;
|
||||||
use crate::core::texture::{TextureEvalContext, TextureMapping2D};
|
use crate::core::texture::{TextureEvalContext, TextureMapping2D};
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::{DevicePtr, Transform};
|
use crate::utils::{Ptr, Transform};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
|
@ -48,19 +48,19 @@ impl FloatBilerpTexture {
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct SpectrumBilerpTexture {
|
pub struct SpectrumBilerpTexture {
|
||||||
pub mapping: TextureMapping2D,
|
pub mapping: TextureMapping2D,
|
||||||
pub v00: DevicePtr<Spectrum>,
|
pub v00: Ptr<Spectrum>,
|
||||||
pub v01: DevicePtr<Spectrum>,
|
pub v01: Ptr<Spectrum>,
|
||||||
pub v10: DevicePtr<Spectrum>,
|
pub v10: Ptr<Spectrum>,
|
||||||
pub v11: DevicePtr<Spectrum>,
|
pub v11: Ptr<Spectrum>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpectrumBilerpTexture {
|
impl SpectrumBilerpTexture {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
mapping: TextureMapping2D,
|
mapping: TextureMapping2D,
|
||||||
v00: DevicePtr<Spectrum>,
|
v00: Ptr<Spectrum>,
|
||||||
v01: DevicePtr<Spectrum>,
|
v01: Ptr<Spectrum>,
|
||||||
v10: DevicePtr<Spectrum>,
|
v10: Ptr<Spectrum>,
|
||||||
v11: DevicePtr<Spectrum>,
|
v11: Ptr<Spectrum>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
mapping,
|
mapping,
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,12 @@ use crate::core::texture::{
|
||||||
TextureMapping3DTrait,
|
TextureMapping3DTrait,
|
||||||
};
|
};
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::{DevicePtr, Ptr, math::square};
|
use crate::utils::{ArenaPtr, Ptr, math::square};
|
||||||
|
|
||||||
fn checkerboard(
|
fn checkerboard(
|
||||||
ctx: &TextureEvalContext,
|
ctx: &TextureEvalContext,
|
||||||
map2d: DevicePtr<TextureMapping2D>,
|
map2d: Ptr<TextureMapping2D>,
|
||||||
map3d: DevicePtr<TextureMapping3D>,
|
map3d: Ptr<TextureMapping3D>,
|
||||||
) -> Float {
|
) -> Float {
|
||||||
let d = |x: Float| -> Float {
|
let d = |x: Float| -> Float {
|
||||||
let y = x / 2. - (x / 2.).floor() - 0.5;
|
let y = x / 2. - (x / 2.).floor() - 0.5;
|
||||||
|
|
@ -43,9 +43,9 @@ fn checkerboard(
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct FloatCheckerboardTexture {
|
pub struct FloatCheckerboardTexture {
|
||||||
pub map2d: DevicePtr<TextureMapping2D>,
|
pub map2d: Ptr<TextureMapping2D>,
|
||||||
pub map3d: DevicePtr<TextureMapping3D>,
|
pub map3d: Ptr<TextureMapping3D>,
|
||||||
pub tex: [Ptr<GPUFloatTexture>; 2],
|
pub tex: [ArenaPtr<GPUFloatTexture>; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FloatCheckerboardTexture {
|
impl FloatCheckerboardTexture {
|
||||||
|
|
@ -74,9 +74,9 @@ impl FloatCheckerboardTexture {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct SpectrumCheckerboardTexture {
|
pub struct SpectrumCheckerboardTexture {
|
||||||
pub map2d: DevicePtr<TextureMapping2D>,
|
pub map2d: Ptr<TextureMapping2D>,
|
||||||
pub map3d: DevicePtr<TextureMapping3D>,
|
pub map3d: Ptr<TextureMapping3D>,
|
||||||
pub tex: [Ptr<GPUSpectrumTexture>; 2],
|
pub tex: [ArenaPtr<GPUSpectrumTexture>; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpectrumCheckerboardTexture {
|
impl SpectrumCheckerboardTexture {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use crate::core::texture::{
|
||||||
GPUFloatTexture, GPUSpectrumTexture, TextureEvalContext, TextureMapping2D,
|
GPUFloatTexture, GPUSpectrumTexture, TextureEvalContext, TextureMapping2D,
|
||||||
};
|
};
|
||||||
use crate::spectra::sampled::{SampledSpectrum, SampledWavelengths};
|
use crate::spectra::sampled::{SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::DevicePtr;
|
use crate::utils::Ptr;
|
||||||
use crate::utils::math::square;
|
use crate::utils::math::square;
|
||||||
use crate::utils::noise::noise_2d;
|
use crate::utils::noise::noise_2d;
|
||||||
|
|
||||||
|
|
@ -28,8 +28,8 @@ fn inside_polka_dot(st: Point2f) -> bool {
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct FloatDotsTexture {
|
pub struct FloatDotsTexture {
|
||||||
pub mapping: TextureMapping2D,
|
pub mapping: TextureMapping2D,
|
||||||
pub outside_dot: DevicePtr<GPUFloatTexture>,
|
pub outside_dot: Ptr<GPUFloatTexture>,
|
||||||
pub inside_dot: DevicePtr<GPUFloatTexture>,
|
pub inside_dot: Ptr<GPUFloatTexture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FloatDotsTexture {
|
impl FloatDotsTexture {
|
||||||
|
|
@ -53,8 +53,8 @@ impl FloatDotsTexture {
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct SpectrumDotsTexture {
|
pub struct SpectrumDotsTexture {
|
||||||
pub mapping: TextureMapping2D,
|
pub mapping: TextureMapping2D,
|
||||||
pub outside_dot: DevicePtr<GPUSpectrumTexture>,
|
pub outside_dot: Ptr<GPUSpectrumTexture>,
|
||||||
pub inside_dot: DevicePtr<GPUSpectrumTexture>,
|
pub inside_dot: Ptr<GPUSpectrumTexture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpectrumDotsTexture {
|
impl SpectrumDotsTexture {
|
||||||
|
|
|
||||||
|
|
@ -95,13 +95,11 @@ impl GPUFloatImageTexture {
|
||||||
let d_p_dy = [c.dsdy, c.dtdy];
|
let d_p_dy = [c.dsdy, c.dtdy];
|
||||||
let val: Float = unsafe { intrinsics::tex2d_grad(self.tex_obj, u, v, d_p_dx, d_p_dy) };
|
let val: Float = unsafe { intrinsics::tex2d_grad(self.tex_obj, u, v, d_p_dx, d_p_dy) };
|
||||||
|
|
||||||
let result = if self.invert {
|
if self.invert {
|
||||||
(1.0 - val).max(0.0) // Invert the pixel intensity
|
return (1. - v).max(0.);
|
||||||
} else {
|
} else {
|
||||||
val
|
return v;
|
||||||
};
|
}
|
||||||
|
|
||||||
return result * self.scale;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use crate::core::texture::{TextureEvalContext, TextureMapping3D};
|
||||||
use crate::spectra::{RGBAlbedoSpectrum, RGBColorSpace, SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{RGBAlbedoSpectrum, RGBColorSpace, SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::math::clamp;
|
use crate::utils::math::clamp;
|
||||||
use crate::utils::noise::fbm;
|
use crate::utils::noise::fbm;
|
||||||
use crate::utils::ptr::DevicePtr;
|
use crate::utils::ptr::Ptr;
|
||||||
use crate::utils::splines::evaluate_cubic_bezier;
|
use crate::utils::splines::evaluate_cubic_bezier;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
@ -18,7 +18,7 @@ pub struct MarbleTexture {
|
||||||
pub scale: Float,
|
pub scale: Float,
|
||||||
pub variation: Float,
|
pub variation: Float,
|
||||||
// TODO: DO not forget to pass StandardColorSpace here!!
|
// TODO: DO not forget to pass StandardColorSpace here!!
|
||||||
pub colorspace: DevicePtr<RGBColorSpace>,
|
pub colorspace: Ptr<RGBColorSpace>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for MarbleTexture {}
|
unsafe impl Send for MarbleTexture {}
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,14 @@ use crate::Float;
|
||||||
use crate::core::geometry::{Vector3f, VectorLike};
|
use crate::core::geometry::{Vector3f, VectorLike};
|
||||||
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvalContext};
|
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvalContext};
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::Ptr;
|
use crate::utils::ArenaPtr;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct GPUFloatMixTexture {
|
pub struct GPUFloatMixTexture {
|
||||||
pub tex1: Ptr<GPUFloatTexture>,
|
pub tex1: ArenaPtr<GPUFloatTexture>,
|
||||||
pub tex2: Ptr<GPUFloatTexture>,
|
pub tex2: ArenaPtr<GPUFloatTexture>,
|
||||||
pub amount: Ptr<GPUFloatTexture>,
|
pub amount: ArenaPtr<GPUFloatTexture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GPUFloatMixTexture {
|
impl GPUFloatMixTexture {
|
||||||
|
|
@ -34,8 +34,8 @@ impl GPUFloatMixTexture {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct GPUFloatDirectionMixTexture {
|
pub struct GPUFloatDirectionMixTexture {
|
||||||
pub tex1: Ptr<GPUFloatTexture>,
|
pub tex1: ArenaPtr<GPUFloatTexture>,
|
||||||
pub tex2: Ptr<GPUFloatTexture>,
|
pub tex2: ArenaPtr<GPUFloatTexture>,
|
||||||
pub dir: Vector3f,
|
pub dir: Vector3f,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,9 +61,9 @@ impl GPUFloatDirectionMixTexture {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct GPUSpectrumMixTexture {
|
pub struct GPUSpectrumMixTexture {
|
||||||
pub tex1: Ptr<GPUSpectrumTexture>,
|
pub tex1: ArenaPtr<GPUSpectrumTexture>,
|
||||||
pub tex2: Ptr<GPUSpectrumTexture>,
|
pub tex2: ArenaPtr<GPUSpectrumTexture>,
|
||||||
pub amount: Ptr<GPUFloatTexture>,
|
pub amount: ArenaPtr<GPUFloatTexture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GPUSpectrumMixTexture {
|
impl GPUSpectrumMixTexture {
|
||||||
|
|
@ -98,8 +98,8 @@ impl GPUSpectrumMixTexture {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct GPUSpectrumDirectionMixTexture {
|
pub struct GPUSpectrumDirectionMixTexture {
|
||||||
pub tex1: Ptr<GPUSpectrumTexture>,
|
pub tex1: ArenaPtr<GPUSpectrumTexture>,
|
||||||
pub tex2: Ptr<GPUSpectrumTexture>,
|
pub tex2: ArenaPtr<GPUSpectrumTexture>,
|
||||||
pub dir: Vector3f,
|
pub dir: Vector3f,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use crate::spectra::{
|
||||||
RGBAlbedoSpectrum, RGBColorSpace, RGBIlluminantSpectrum, RGBUnboundedSpectrum, SampledSpectrum,
|
RGBAlbedoSpectrum, RGBColorSpace, RGBIlluminantSpectrum, RGBUnboundedSpectrum, SampledSpectrum,
|
||||||
SampledWavelengths, StandardColorSpaces,
|
SampledWavelengths, StandardColorSpaces,
|
||||||
};
|
};
|
||||||
use crate::utils::ptr::{DevicePtr, Slice};
|
use crate::utils::ptr::{Ptr, Slice};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
use crate::Float;
|
use crate::Float;
|
||||||
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvalContext};
|
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvalContext};
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::Ptr;
|
use crate::utils::ArenaPtr;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct GPUFloatScaledTexture {
|
pub struct GPUFloatScaledTexture {
|
||||||
pub tex: Ptr<GPUFloatTexture>,
|
pub tex: ArenaPtr<GPUFloatTexture>,
|
||||||
pub scale: Ptr<GPUFloatTexture>,
|
pub scale: ArenaPtr<GPUFloatTexture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GPUFloatScaledTexture {
|
impl GPUFloatScaledTexture {
|
||||||
|
|
@ -23,8 +23,8 @@ impl GPUFloatScaledTexture {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct GPUSpectrumScaledTexture {
|
pub struct GPUSpectrumScaledTexture {
|
||||||
pub tex: Ptr<GPUSpectrumTexture>,
|
pub tex: ArenaPtr<GPUSpectrumTexture>,
|
||||||
pub scale: Ptr<GPUFloatTexture>,
|
pub scale: ArenaPtr<GPUFloatTexture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GPUSpectrumScaledTexture {
|
impl GPUSpectrumScaledTexture {
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ impl<T> Interpolatable for T where
|
||||||
pub struct Array2D<T> {
|
pub struct Array2D<T> {
|
||||||
pub values: *mut T,
|
pub values: *mut T,
|
||||||
pub extent: Bounds2i,
|
pub extent: Bounds2i,
|
||||||
pub stride: i32,
|
pub x_stride: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<T: Send> Send for Array2D<T> {}
|
unsafe impl<T: Send> Send for Array2D<T> {}
|
||||||
|
|
@ -51,7 +51,7 @@ impl<T> Array2D<T> {
|
||||||
fn offset(&self, p: Point2i) -> isize {
|
fn offset(&self, p: Point2i) -> isize {
|
||||||
let ox = p.x() - self.extent.p_min.x();
|
let ox = p.x() - self.extent.p_min.x();
|
||||||
let oy = p.y() - self.extent.p_min.y();
|
let oy = p.y() - self.extent.p_min.y();
|
||||||
(ox + oy * self.stride) as isize
|
(ox + oy * self.x_stride) as isize
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use crate::core::pbrt::{Float, FloatBitOps, FloatBits, ONE_MINUS_EPSILON, PI, PI
|
||||||
use crate::utils::hash::{hash_buffer, mix_bits};
|
use crate::utils::hash::{hash_buffer, mix_bits};
|
||||||
use crate::utils::sobol::{SOBOL_MATRICES_32, VDC_SOBOL_MATRICES, VDC_SOBOL_MATRICES_INV};
|
use crate::utils::sobol::{SOBOL_MATRICES_32, VDC_SOBOL_MATRICES, VDC_SOBOL_MATRICES_INV};
|
||||||
|
|
||||||
use crate::utils::DevicePtr;
|
use crate::utils::Ptr;
|
||||||
use half::f16;
|
use half::f16;
|
||||||
use num_traits::{Float as NumFloat, Num, One, Signed, Zero};
|
use num_traits::{Float as NumFloat, Num, One, Signed, Zero};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
@ -753,7 +753,7 @@ pub fn inverse_radical_inverse(mut inverse: u64, base: u64, n_digits: u64) -> u6
|
||||||
pub struct DigitPermutation {
|
pub struct DigitPermutation {
|
||||||
pub base: u32,
|
pub base: u32,
|
||||||
pub n_digits: u32,
|
pub n_digits: u32,
|
||||||
pub permutations: DevicePtr<u16>,
|
pub permutations: Ptr<u16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DigitPermutation {
|
impl DigitPermutation {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::Float;
|
use crate::Float;
|
||||||
use crate::core::geometry::{Normal3f, Point2f, Point3f, Vector3f};
|
use crate::core::geometry::{Normal3f, Point2f, Point3f, Vector3f};
|
||||||
use crate::utils::Ptr;
|
|
||||||
use crate::utils::Transform;
|
use crate::utils::Transform;
|
||||||
use crate::utils::sampling::DevicePiecewiseConstant2D;
|
use crate::utils::ptr::Ptr;
|
||||||
|
use crate::utils::sampling::PiecewiseConstant2D;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
|
@ -30,5 +30,5 @@ pub struct BilinearPatchMesh {
|
||||||
pub p: Ptr<Point3f>,
|
pub p: Ptr<Point3f>,
|
||||||
pub n: Ptr<Normal3f>,
|
pub n: Ptr<Normal3f>,
|
||||||
pub uv: Ptr<Point2f>,
|
pub uv: Ptr<Point2f>,
|
||||||
pub image_distribution: Ptr<DevicePiecewiseConstant2D>,
|
pub image_distribution: Ptr<PiecewiseConstant2D>,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,33 +15,9 @@ pub mod sobol;
|
||||||
pub mod splines;
|
pub mod splines;
|
||||||
pub mod transform;
|
pub mod transform;
|
||||||
|
|
||||||
pub use ptr::{DevicePtr, Ptr};
|
pub use ptr::{ArenaPtr, Ptr};
|
||||||
pub use transform::{AnimatedTransform, Transform, TransformGeneric};
|
pub use transform::{AnimatedTransform, Transform, TransformGeneric};
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn find_interval<F>(sz: u32, pred: F) -> u32
|
|
||||||
where
|
|
||||||
F: Fn(u32) -> bool,
|
|
||||||
{
|
|
||||||
let mut first = 0;
|
|
||||||
let mut len = sz;
|
|
||||||
|
|
||||||
while len > 0 {
|
|
||||||
let half = len >> 1;
|
|
||||||
let middle = first + half;
|
|
||||||
|
|
||||||
if pred(middle) {
|
|
||||||
first = middle + 1;
|
|
||||||
len -= half + 1;
|
|
||||||
} else {
|
|
||||||
len = half;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let ret = (first as i32 - 1).max(0) as u32;
|
|
||||||
ret.min(sz.saturating_sub(2))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn partition_slice<T, F>(data: &mut [T], predicate: F) -> usize
|
pub fn partition_slice<T, F>(data: &mut [T], predicate: F) -> usize
|
||||||
where
|
where
|
||||||
|
|
|
||||||
|
|
@ -1,136 +1,190 @@
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::ops::Index;
|
use core::ops::Index;
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(C)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Ptr<T: ?Sized> {
|
pub struct ArenaPtr<T: ?Sized> {
|
||||||
ptr: *const T,
|
offset: i32,
|
||||||
|
_marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized> Clone for Ptr<T> {
|
impl<T: ?Sized> Clone for ArenaPtr<T> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
*self
|
*self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: ?Sized> Copy for Ptr<T> {}
|
|
||||||
|
|
||||||
// Ptr is just a pointer - Send/Sync depends on T
|
impl<T: ?Sized> Copy for ArenaPtr<T> {}
|
||||||
unsafe impl<T: ?Sized + Send> Send for Ptr<T> {}
|
|
||||||
unsafe impl<T: ?Sized + Sync> Sync for Ptr<T> {}
|
|
||||||
|
|
||||||
impl<T> Ptr<T> {
|
impl<T> ArenaPtr<T> {
|
||||||
pub const fn null() -> Self {
|
pub fn null() -> Self {
|
||||||
Self {
|
Self {
|
||||||
ptr: std::ptr::null(),
|
offset: 0xFFFFFFFF,
|
||||||
|
_marker: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn is_null(self) -> bool {
|
pub fn is_null(&self) -> bool {
|
||||||
self.ptr.is_null()
|
self.offset == 0xFFFFFFFF
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_raw(ptr: *const T) -> Self {
|
|
||||||
Self { ptr }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_raw(self) -> *const T {
|
|
||||||
self.ptr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn as_ref<'a>(self) -> &'a T {
|
pub unsafe fn as_ptr(&self, base: *const u8) -> *const T {
|
||||||
debug_assert!(!self.is_null(), "null Ptr dereference");
|
|
||||||
unsafe { &*self.ptr }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get as Option - safe for optional fields
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn get<'a>(self) -> Option<&'a T> {
|
|
||||||
if self.is_null() {
|
if self.is_null() {
|
||||||
None
|
core::ptr::null()
|
||||||
} else {
|
} else {
|
||||||
Some(unsafe { &*self.ptr })
|
unsafe { base.add(self.offset as usize) as *const T }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn at<'a>(self, index: usize) -> &'a T {
|
pub unsafe fn as_ref<'a>(&self, base: *const u8) -> &'a T {
|
||||||
debug_assert!(!self.is_null(), "null Ptr array access");
|
unsafe { &*self.as_ptr(base) }
|
||||||
unsafe { &*self.ptr.add(index) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get element at index, returns None if ptr is null
|
#[cfg(not(target_os = "cuda"))]
|
||||||
#[inline(always)]
|
pub fn new(me: *const Self, target: *const T) -> Self {
|
||||||
pub fn get_at<'a>(self, index: usize) -> Option<&'a T> {
|
if target.is_null() {
|
||||||
if self.is_null() {
|
return Self::null();
|
||||||
None
|
}
|
||||||
} else {
|
|
||||||
Some(unsafe { &*self.ptr.add(index) })
|
let diff = unsafe { (target as *const u8).offset_from(me as *const u8) };
|
||||||
|
|
||||||
|
if diff > i32::MAX as isize || diff < i32::MIN as isize {
|
||||||
|
panic!("RelPtr offset out of i32 range! Are objects too far apart?");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Offset the pointer, returning a new Ptr
|
|
||||||
#[inline(always)]
|
|
||||||
pub unsafe fn add(self, count: usize) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
ptr: unsafe { self.ptr.add(count) },
|
offset: diff as i32,
|
||||||
}
|
_marker: PhantomData,
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert to slice (requires knowing the length)
|
|
||||||
#[inline(always)]
|
|
||||||
pub unsafe fn as_slice<'a>(self, len: usize) -> &'a [T] {
|
|
||||||
debug_assert!(!self.is_null() || len == 0, "null Ptr to non-empty slice");
|
|
||||||
if len == 0 {
|
|
||||||
&[]
|
|
||||||
} else {
|
|
||||||
unsafe { std::slice::from_raw_parts(self.ptr, len) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert to slice, returns None if null
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn get_slice<'a>(self, len: usize) -> Option<&'a [T]> {
|
|
||||||
if self.is_null() {
|
|
||||||
if len == 0 { Some(&[]) } else { None }
|
|
||||||
} else {
|
|
||||||
Some(unsafe { std::slice::from_raw_parts(self.ptr, len) })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Ptr<T>(pub *const T);
|
||||||
|
|
||||||
|
impl<T> Default for Ptr<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(core::ptr::null())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> PartialEq for Ptr<T> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.0 == other.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Eq for Ptr<T> {}
|
||||||
|
|
||||||
|
impl<T> Ptr<T> {
|
||||||
|
pub fn null() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_null(&self) -> bool {
|
||||||
|
self.0.is_null()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn as_ref(&self) -> Option<&T> {
|
||||||
|
unsafe { self.0.as_ref() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// UNSTABLE: Casts the const pointer to mutable and returns a mutable reference.
|
||||||
|
/// THIS IS VERY DANGEROUS
|
||||||
|
/// The underlying data is not currently borrowed or accessed by any other thread/kernel.
|
||||||
|
/// The memory is actually writable.
|
||||||
|
/// No other mutable references exist to this data.
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn as_mut(&mut self) -> &mut T {
|
||||||
|
debug_assert!(!self.is_null());
|
||||||
|
unsafe { &mut *(self.0 as *mut T) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T: Sync> Send for Ptr<T> {}
|
||||||
|
unsafe impl<T: Sync> Sync for Ptr<T> {}
|
||||||
|
|
||||||
impl<T> std::ops::Deref for Ptr<T> {
|
impl<T> std::ops::Deref for Ptr<T> {
|
||||||
type Target = T;
|
type Target = T;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn deref(&self) -> &T {
|
fn deref(&self) -> &Self::Target {
|
||||||
debug_assert!(!self.is_null(), "Null Ptr dereference");
|
unsafe { &*self.0 }
|
||||||
unsafe { &*self.ptr }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Default for Ptr<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::null()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> From<&T> for Ptr<T> {
|
impl<T> From<&T> for Ptr<T> {
|
||||||
fn from(r: &T) -> Self {
|
fn from(r: &T) -> Self {
|
||||||
Self { ptr: r as *const T }
|
Self(r as *const T)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> From<&[T]> for Ptr<T> {
|
impl<T> From<&mut T> for Ptr<T> {
|
||||||
fn from(slice: &[T]) -> Self {
|
fn from(r: &mut T) -> Self {
|
||||||
|
Self(r as *const T)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Index<usize> for Ptr<T> {
|
||||||
|
type Output = T;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
|
// There is no bounds checking because we dont know the length.
|
||||||
|
// It is host responsbility to check bounds
|
||||||
|
unsafe { &*self.0.add(index) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Index<u32> for Ptr<T> {
|
||||||
|
type Output = T;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn index(&self, index: u32) -> &Self::Output {
|
||||||
|
unsafe { &*self.0.add(index as usize) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct Slice<T> {
|
||||||
|
pub ptr: Ptr<T>,
|
||||||
|
pub len: u32,
|
||||||
|
pub _marker: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T: Sync> Send for Slice<T> {}
|
||||||
|
unsafe impl<T: Sync> Sync for Slice<T> {}
|
||||||
|
|
||||||
|
impl<T> Slice<T> {
|
||||||
|
pub fn new(ptr: Ptr<T>, len: u32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ptr: slice.as_ptr(),
|
ptr,
|
||||||
|
len,
|
||||||
|
_marker: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> From<*const T> for Ptr<T> {
|
pub fn as_ptr(&self) -> *const T {
|
||||||
fn from(ptr: *const T) -> Self {
|
self.ptr.0
|
||||||
Self { ptr }
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.len == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Index<usize> for Slice<T> {
|
||||||
|
type Output = T;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
|
unsafe { &*self.ptr.0.add(index) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,15 @@ use crate::core::geometry::{
|
||||||
};
|
};
|
||||||
use crate::core::pbrt::{RARE_EVENT_CONDITION_MET, RARE_EVENT_TOTAL_CALLS};
|
use crate::core::pbrt::{RARE_EVENT_CONDITION_MET, RARE_EVENT_TOTAL_CALLS};
|
||||||
use crate::utils::containers::Array2D;
|
use crate::utils::containers::Array2D;
|
||||||
use crate::utils::find_interval;
|
|
||||||
use crate::utils::math::{
|
use crate::utils::math::{
|
||||||
catmull_rom_weights, clamp, difference_of_products, evaluate_polynomial, lerp, logistic,
|
catmull_rom_weights, clamp, difference_of_products, evaluate_polynomial, lerp, logistic,
|
||||||
newton_bisection, safe_sqrt, square, sum_of_products,
|
newton_bisection, safe_sqrt, square, sum_of_products,
|
||||||
};
|
};
|
||||||
use crate::utils::ptr::Ptr;
|
use crate::utils::ptr::Ptr;
|
||||||
use crate::utils::rng::Rng;
|
use crate::utils::rng::Rng;
|
||||||
use crate::{Float, INV_2_PI, INV_4_PI, INV_PI, ONE_MINUS_EPSILON, PI, PI_OVER_2, PI_OVER_4};
|
use crate::{
|
||||||
|
Float, INV_2_PI, INV_4_PI, INV_PI, ONE_MINUS_EPSILON, PI, PI_OVER_2, PI_OVER_4, find_interval,
|
||||||
|
};
|
||||||
use num_traits::Num;
|
use num_traits::Num;
|
||||||
|
|
||||||
pub fn linear_pdf<T>(x: T, a: T, b: T) -> T
|
pub fn linear_pdf<T>(x: T, a: T, b: T) -> T
|
||||||
|
|
@ -701,7 +702,7 @@ pub struct PLSample {
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct DevicePiecewiseConstant1D {
|
pub struct PiecewiseConstant1D {
|
||||||
pub func: Ptr<Float>,
|
pub func: Ptr<Float>,
|
||||||
pub cdf: Ptr<Float>,
|
pub cdf: Ptr<Float>,
|
||||||
pub min: Float,
|
pub min: Float,
|
||||||
|
|
@ -710,10 +711,10 @@ pub struct DevicePiecewiseConstant1D {
|
||||||
pub func_integral: Float,
|
pub func_integral: Float,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for DevicePiecewiseConstant1D {}
|
unsafe impl Send for PiecewiseConstant1D {}
|
||||||
unsafe impl Sync for DevicePiecewiseConstant1D {}
|
unsafe impl Sync for PiecewiseConstant1D {}
|
||||||
|
|
||||||
impl DevicePiecewiseConstant1D {
|
impl PiecewiseConstant1D {
|
||||||
pub fn integral(&self) -> Float {
|
pub fn integral(&self) -> Float {
|
||||||
self.func_integral
|
self.func_integral
|
||||||
}
|
}
|
||||||
|
|
@ -722,48 +723,39 @@ impl DevicePiecewiseConstant1D {
|
||||||
self.n
|
self.n
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sample(&self, u: Float) -> (Float, Float, usize) {
|
pub fn sample(&self, u: Float) -> (Float, Float, u32) {
|
||||||
// Find offset via binary search on CDF
|
let o = find_interval(self.size(), |idx| self.cdf[idx] <= u) as usize;
|
||||||
let offset = self.find_interval(u);
|
let mut du = u - self.cdf[o];
|
||||||
|
if self.cdf[o + 1] - self.cdf[o] > 0. {
|
||||||
let cdf_offset = unsafe { *self.cdf.add(offset) };
|
du /= self.cdf[o + 1] - self.cdf[o];
|
||||||
let cdf_next = unsafe { *self.cdf.add(offset + 1) };
|
}
|
||||||
|
debug_assert!(!du.is_nan());
|
||||||
let du = if cdf_next - cdf_offset > 0.0 {
|
let value = lerp((o as Float + du) / self.size() as Float, self.min, self.max);
|
||||||
(u - cdf_offset) / (cdf_next - cdf_offset)
|
let pdf_val = if self.func_integral > 0. {
|
||||||
|
self.func[o] / self.func_integral
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.
|
||||||
};
|
};
|
||||||
|
(value, pdf_val, o as u32)
|
||||||
let delta = (self.max - self.min) / self.n as Float;
|
|
||||||
let x = self.min + (offset as Float + du) * delta;
|
|
||||||
|
|
||||||
let pdf = if self.func_integral > 0.0 {
|
|
||||||
(unsafe { *self.func.add(offset) }) / self.func_integral
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
};
|
|
||||||
|
|
||||||
(x, pdf, offset)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct DevicePiecewiseConstant2D {
|
pub struct PiecewiseConstant2D {
|
||||||
pub conditional: *const DevicePiecewiseConstant1D, // Array of n_v conditionals
|
pub domain: Bounds2f,
|
||||||
pub marginal: DevicePiecewiseConstant1D,
|
pub p_marginal: PiecewiseConstant1D,
|
||||||
pub n_u: u32,
|
pub n_conditionals: usize,
|
||||||
pub n_v: u32,
|
pub p_conditional_v: Ptr<PiecewiseConstant1D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DevicePiecewiseConstant2D {
|
impl PiecewiseConstant2D {
|
||||||
// pub fn resolution(&self) -> Point2i {
|
pub fn resolution(&self) -> Point2i {
|
||||||
// Point2i::new(
|
Point2i::new(
|
||||||
// self.p_conditional_v[0u32].size() as i32,
|
self.p_conditional_v[0u32].size() as i32,
|
||||||
// self.p_conditional_v[1u32].size() as i32,
|
self.p_conditional_v[1u32].size() as i32,
|
||||||
// )
|
)
|
||||||
// }
|
}
|
||||||
|
|
||||||
pub fn integral(&self) -> f32 {
|
pub fn integral(&self) -> f32 {
|
||||||
self.p_marginal.integral()
|
self.p_marginal.integral()
|
||||||
|
|
@ -777,20 +769,20 @@ impl DevicePiecewiseConstant2D {
|
||||||
(Point2f::new(d0, d1), pdf, offset)
|
(Point2f::new(d0, d1), pdf, offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pdf(&self, p: Point2f) -> Float {
|
pub fn pdf(&self, p: Point2f) -> f32 {
|
||||||
// Find which row
|
let p_offset = self.domain.offset(&p);
|
||||||
let delta_v = 1.0 / self.n_v as Float;
|
let nu = self.p_conditional_v[0u32].size();
|
||||||
let v_offset = ((p.y() * self.n_v as Float) as usize).min(self.n_v as usize - 1);
|
let nv = self.p_marginal.size();
|
||||||
|
|
||||||
let conditional = unsafe { &*self.conditional.add(v_offset) };
|
let iu = (p_offset.x() * nu as f32).clamp(0.0, nu as f32 - 1.0) as usize;
|
||||||
|
let iv = (p_offset.y() * nv as f32).clamp(0.0, nv as f32 - 1.0) as usize;
|
||||||
|
|
||||||
// Find which column
|
let integral = self.p_marginal.integral();
|
||||||
let delta_u = 1.0 / self.n_u as Float;
|
if integral == 0.0 {
|
||||||
let u_offset = ((p.x() * self.n_u as Float) as usize).min(self.n_u as usize - 1);
|
0.0
|
||||||
|
} else {
|
||||||
let func_val = unsafe { *conditional.func.add(u_offset) };
|
self.p_conditional_v[iv].func[iu] / integral
|
||||||
|
}
|
||||||
func_val / self.marginal.func_integral
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use super::math::{SquareMatrix, radians, safe_acos};
|
||||||
use super::quaternion::Quaternion;
|
use super::quaternion::Quaternion;
|
||||||
use crate::core::color::{RGB, XYZ};
|
use crate::core::color::{RGB, XYZ};
|
||||||
use crate::core::geometry::{
|
use crate::core::geometry::{
|
||||||
Bounds3f, Frame, Normal, Normal3f, Point, Point3f, Point3fi, Ray, Vector, Vector3f, Vector3fi,
|
Bounds3f, Normal, Normal3f, Point, Point3f, Point3fi, Ray, Vector, Vector3f, Vector3fi,
|
||||||
VectorLike,
|
VectorLike,
|
||||||
};
|
};
|
||||||
use crate::core::interaction::{
|
use crate::core::interaction::{
|
||||||
|
|
@ -714,30 +714,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Frame> for TransformGeneric<Float> {
|
|
||||||
fn from(frame: Frame) -> Self {
|
|
||||||
let values: &[Float; 16] = &[
|
|
||||||
frame.x.x(),
|
|
||||||
frame.x.y(),
|
|
||||||
frame.x.z(),
|
|
||||||
0.,
|
|
||||||
frame.y.x(),
|
|
||||||
frame.y.y(),
|
|
||||||
frame.y.z(),
|
|
||||||
0.,
|
|
||||||
frame.z.x(),
|
|
||||||
frame.z.y(),
|
|
||||||
frame.z.z(),
|
|
||||||
0.,
|
|
||||||
0.,
|
|
||||||
0.,
|
|
||||||
0.,
|
|
||||||
1.,
|
|
||||||
];
|
|
||||||
Self::from_flat(values).expect("Transform must be inversible")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<Quaternion> for TransformGeneric<Float> {
|
impl From<Quaternion> for TransformGeneric<Float> {
|
||||||
fn from(q: Quaternion) -> Self {
|
fn from(q: Quaternion) -> Self {
|
||||||
let xx = q.v.x() * q.v.x();
|
let xx = q.v.x() * q.v.x();
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use crate::Float;
|
|
||||||
use crate::core::geometry::{Bounds3f, Point3f, Ray, Vector3f};
|
use crate::core::geometry::{Bounds3f, Point3f, Ray, Vector3f};
|
||||||
|
use crate::core::pbrt::{Float, find_interval};
|
||||||
use crate::core::primitive::PrimitiveTrait;
|
use crate::core::primitive::PrimitiveTrait;
|
||||||
use crate::core::shape::ShapeIntersection;
|
use crate::core::shape::ShapeIntersection;
|
||||||
use crate::utils::math::encode_morton_3;
|
use crate::utils::math::encode_morton_3;
|
||||||
use crate::utils::math::next_float_down;
|
use crate::utils::math::next_float_down;
|
||||||
use crate::utils::{find_interval, partition_slice};
|
use crate::utils::partition_slice;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
@ -13,7 +13,7 @@ use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum SplitMethod {
|
pub enum SplitMethod {
|
||||||
AH,
|
SAH,
|
||||||
Hlbvh,
|
Hlbvh,
|
||||||
Middle,
|
Middle,
|
||||||
EqualCounts,
|
EqualCounts,
|
||||||
|
|
@ -345,6 +345,7 @@ impl BVHAggregate {
|
||||||
4,
|
4,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Add thread-local count to global atomic
|
||||||
total_nodes.fetch_add(nodes_created, AtomicOrdering::Relaxed);
|
total_nodes.fetch_add(nodes_created, AtomicOrdering::Relaxed);
|
||||||
|
|
||||||
root
|
root
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::core::image::ImageMetadata;
|
use crate::core::image::ImageMetadata;
|
||||||
use crate::utils::{Arena, FileLoc, ParameterDictionary};
|
use crate::utils::{FileLoc, ParameterDictionary};
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
use shared::cameras::*;
|
use shared::cameras::*;
|
||||||
use shared::core::camera::{Camera, CameraBase, CameraTrait, CameraTransform};
|
use shared::core::camera::{Camera, CameraBase, CameraTrait, CameraTransform};
|
||||||
|
|
@ -99,7 +99,6 @@ impl CameraFactory for Camera {
|
||||||
medium: Medium,
|
medium: Medium,
|
||||||
film: Arc<Film>,
|
film: Arc<Film>,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
arena: &mut Arena,
|
|
||||||
) -> Result<Self, String> {
|
) -> Result<Self, String> {
|
||||||
match name {
|
match name {
|
||||||
"perspective" => {
|
"perspective" => {
|
||||||
|
|
@ -144,7 +143,6 @@ impl CameraFactory for Camera {
|
||||||
|
|
||||||
let fov = params.get_one_float("fov", 90.);
|
let fov = params.get_one_float("fov", 90.);
|
||||||
let camera = PerspectiveCamera::new(base, fov, screen, lens_radius, focal_distance);
|
let camera = PerspectiveCamera::new(base, fov, screen, lens_radius, focal_distance);
|
||||||
arena.alloc(camera);
|
|
||||||
|
|
||||||
Ok(Camera::Perspective(camera))
|
Ok(Camera::Perspective(camera))
|
||||||
}
|
}
|
||||||
|
|
@ -189,7 +187,6 @@ impl CameraFactory for Camera {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let camera = OrthographicCamera::new(base, screen, lens_radius, focal_distance);
|
let camera = OrthographicCamera::new(base, screen, lens_radius, focal_distance);
|
||||||
arena.alloc(camera);
|
|
||||||
Ok(Camera::Orthographic(camera))
|
Ok(Camera::Orthographic(camera))
|
||||||
}
|
}
|
||||||
"realistic" => {
|
"realistic" => {
|
||||||
|
|
@ -370,7 +367,6 @@ impl CameraFactory for Camera {
|
||||||
aperture_image,
|
aperture_image,
|
||||||
);
|
);
|
||||||
|
|
||||||
arena.alloc(camera);
|
|
||||||
Ok(Camera::Realistic(camera))
|
Ok(Camera::Realistic(camera))
|
||||||
}
|
}
|
||||||
"spherical" => {
|
"spherical" => {
|
||||||
|
|
@ -427,7 +423,6 @@ impl CameraFactory for Camera {
|
||||||
|
|
||||||
let camera = SphericalCamera { mapping, base };
|
let camera = SphericalCamera { mapping, base };
|
||||||
|
|
||||||
arena.alloc(camera);
|
|
||||||
Ok(Camera::Spherical(camera))
|
Ok(Camera::Spherical(camera))
|
||||||
}
|
}
|
||||||
_ => Err(format!("Camera type '{}' unknown at {}", name, loc)),
|
_ => Err(format!("Camera type '{}' unknown at {}", name, loc)),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use super::{Image, ImageAndMetadata};
|
use super::ImageBuffer;
|
||||||
use crate::utils::error::ImageError;
|
use crate::utils::error::ImageError;
|
||||||
use anyhow::{Context, Result, bail};
|
use anyhow::{Context, Result, bail};
|
||||||
use exr::prelude::{read_first_rgba_layer_from_file, write_rgba_file};
|
use exr::prelude::{read_first_rgba_layer_from_file, write_rgba_file};
|
||||||
|
|
@ -6,7 +6,7 @@ use image_rs::ImageReader;
|
||||||
use image_rs::{DynamicImage, ImageBuffer, Rgb, Rgba};
|
use image_rs::{DynamicImage, ImageBuffer, Rgb, Rgba};
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
use shared::core::color::{ColorEncoding, LINEAR, SRGB};
|
use shared::core::color::{ColorEncoding, LINEAR, SRGB};
|
||||||
use shared::core::image::DeviceImage;
|
use shared::core::image::Image;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufRead, BufReader, BufWriter, Read, Write};
|
use std::io::{BufRead, BufReader, BufWriter, Read, Write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
@ -21,7 +21,7 @@ pub trait ImageIO {
|
||||||
fn to_u8_buffer(&self) -> Vec<u8>;
|
fn to_u8_buffer(&self) -> Vec<u8>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImageIO for Image {
|
impl ImageIO for ImageBuffer {
|
||||||
fn read(path: &Path, encoding: Option<ColorEncoding>) -> Result<ImageAndMetadata> {
|
fn read(path: &Path, encoding: Option<ColorEncoding>) -> Result<ImageAndMetadata> {
|
||||||
let ext = path
|
let ext = path
|
||||||
.extension()
|
.extension()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use shared::core::geometry::Point2i;
|
use shared::core::geometry::Point2i;
|
||||||
use shared::core::image::{DeviceImage, ImageAccess, ImageBase, PixelFormat, WrapMode};
|
use shared::core::image::{Image, PixelFormat};
|
||||||
use shared::utils::math::f16_to_f32;
|
use shared::utils::math::f16_to_f32;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
|
@ -8,7 +8,6 @@ pub mod metadata;
|
||||||
pub mod ops;
|
pub mod ops;
|
||||||
pub mod pixel;
|
pub mod pixel;
|
||||||
|
|
||||||
pub use io::ImageIO;
|
|
||||||
pub use metadata::*;
|
pub use metadata::*;
|
||||||
|
|
||||||
impl std::fmt::Display for PixelFormat {
|
impl std::fmt::Display for PixelFormat {
|
||||||
|
|
@ -71,75 +70,74 @@ impl DerefMut for ImageChannelValues {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum PixelStorage {
|
pub enum PixelStorage {
|
||||||
U8(Box<[u8]>),
|
U8(Vec<u8>),
|
||||||
F16(Box<[f16]>),
|
F16(Vec<f16>),
|
||||||
F32(Box<[f32]>),
|
F32(Vec<f32>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PixelStorage {
|
pub struct ImageBuffer {
|
||||||
pub fn as_pixels(&self) -> Pixels {
|
pub view: Image,
|
||||||
match self {
|
pub channel_names: Vec<String>,
|
||||||
PixelStorage::U8(data) => Pixels::U8(data.as_ptr()),
|
_storage: PixelStorage,
|
||||||
PixelStorage::F16(data) => Pixels::F16(data.as_ptr() as *const u16),
|
|
||||||
PixelStorage::F32(data) => Pixels::F32(data.as_ptr()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn format(&self) -> PixelFormat {
|
|
||||||
match self {
|
|
||||||
PixelStorage::U8(_) => PixelFormat::U8,
|
|
||||||
PixelStorage::F16(_) => PixelFormat::F16,
|
|
||||||
PixelStorage::F32(_) => PixelFormat::F32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
PixelStorage::U8(d) => d.len(),
|
|
||||||
PixelStorage::F16(d) => d.len(),
|
|
||||||
PixelStorage::F32(d) => d.len(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Image {
|
impl Deref for ImageBuffer {
|
||||||
storage: PixelStorage,
|
type Target = Image;
|
||||||
channel_names: Vec<String>,
|
fn deref(&self) -> &Self::Target {
|
||||||
device: DeviceImage,
|
&self.view
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ImageAndMetadata {
|
pub struct ImageAndMetadata {
|
||||||
pub image: Image,
|
pub image: ImageBuffer,
|
||||||
pub metadata: ImageMetadata,
|
pub metadata: ImageMetadata,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Image {
|
impl ImageBuffer {
|
||||||
// Constructors
|
fn resolution(&self) {
|
||||||
fn from_storage(
|
self.view.resolution()
|
||||||
storage: PixelStorage,
|
}
|
||||||
|
|
||||||
|
pub fn new_empty() -> Self {
|
||||||
|
Self {
|
||||||
|
channel_names: Vec::new(),
|
||||||
|
_storage: PixelStorage::U8(Vec::new()),
|
||||||
|
view: Image {
|
||||||
|
format: PixelFormat::U256,
|
||||||
|
resolution: Point2i::new(0, 0),
|
||||||
|
n_channels: 0,
|
||||||
|
pixels: Pixels::U8(std::ptr::null()),
|
||||||
|
encoding: ColorEncoding::default(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_vector(
|
||||||
|
format: PixelFormat,
|
||||||
resolution: Point2i,
|
resolution: Point2i,
|
||||||
channel_names: Vec<String>,
|
channel_names: Vec<String>,
|
||||||
encoding: ColorEncoding,
|
encoding: ColorEncoding,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let n_channels = channel_names.len() as i32;
|
let n_channels = channel_names.len() as i32;
|
||||||
let expected = (resolution.x() * resolution.y()) as usize * n_channels as usize;
|
let (format, pixels_view) = match &storage {
|
||||||
assert_eq!(storage.len(), expected, "Pixel data size mismatch");
|
PixelStorage::U8(vec) => (PixelFormat::U8, ImagePixels::U8(vec.as_ptr())),
|
||||||
|
PixelStorage::F16(vec) => (PixelFormat::F16, ImagePixels::F16(vec.as_ptr())),
|
||||||
|
PixelStorage::F32(vec) => (PixelFormat::F32, ImagePixels::F32(vec.as_ptr())),
|
||||||
|
};
|
||||||
|
|
||||||
let device = DeviceImage {
|
let view = Image {
|
||||||
base: ImageBase {
|
format,
|
||||||
format: storage.format(),
|
resolution,
|
||||||
encoding,
|
n_channels,
|
||||||
resolution,
|
encoding,
|
||||||
n_channels,
|
pixels: pixels_view,
|
||||||
},
|
|
||||||
pixels: storage.as_pixels(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
storage,
|
view,
|
||||||
|
_storage: storage,
|
||||||
channel_names,
|
channel_names,
|
||||||
device,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,12 +147,37 @@ impl Image {
|
||||||
channel_names: Vec<String>,
|
channel_names: Vec<String>,
|
||||||
encoding: ColorEncoding,
|
encoding: ColorEncoding,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::from_storage(
|
let n_channels = channel_names.len() as i32;
|
||||||
PixelStorage::U8(data.into_boxed_slice()),
|
let expected_len = (resolution.x * resolution.y) as usize * n_channels as usize;
|
||||||
|
if data.len() != expected_len {
|
||||||
|
panic!(
|
||||||
|
"ImageBuffer::from_u8: Data length {} does not match resolution {:?} * channels {}",
|
||||||
|
data.len(),
|
||||||
|
resolution,
|
||||||
|
n_channels
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let storage = PixelStorage::U8(data);
|
||||||
|
|
||||||
|
let ptr = match &storage {
|
||||||
|
PixelStorage::U8(v) => v.as_ptr(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let view = Image {
|
||||||
|
format: PixelFormat::U256,
|
||||||
resolution,
|
resolution,
|
||||||
|
n_channels,
|
||||||
|
pixels: Pixels::U8(ptr),
|
||||||
|
encoding: encoding.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
view,
|
||||||
channel_names,
|
channel_names,
|
||||||
encoding,
|
_storage: storage,
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_u8(
|
pub fn from_u8(
|
||||||
|
|
@ -163,30 +186,98 @@ impl Image {
|
||||||
channel_names: Vec<String>,
|
channel_names: Vec<String>,
|
||||||
encoding: ColorEncoding,
|
encoding: ColorEncoding,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::from_storage(
|
let n_channels = channel_names.len() as i32;
|
||||||
PixelStorage::U8(data.into_boxed_slice()),
|
let expected_len = (resolution.x * resolution.y) as usize * n_channels as usize;
|
||||||
|
|
||||||
|
if data.len() != expected_len {
|
||||||
|
panic!(
|
||||||
|
"ImageBuffer::from_u8: Data length {} does not match resolution {:?} * channels {}",
|
||||||
|
data.len(),
|
||||||
|
resolution,
|
||||||
|
n_channels
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let storage = PixelStorage::U8(data);
|
||||||
|
|
||||||
|
let ptr = match &storage {
|
||||||
|
PixelStorage::U8(v) => v.as_ptr(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let view = Image {
|
||||||
|
format: PixelFormat::U256,
|
||||||
resolution,
|
resolution,
|
||||||
|
n_channels,
|
||||||
|
pixels: Pixels::U8(ptr),
|
||||||
|
encoding: encoding.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
view,
|
||||||
channel_names,
|
channel_names,
|
||||||
encoding,
|
_storage: storage,
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_f16(data: Vec<half::f16>, resolution: Point2i, channel_names: Vec<String>) -> Self {
|
pub fn from_f16(data: Vec<half::f16>, resolution: Point2i, channel_names: Vec<String>) -> Self {
|
||||||
Self::from_storage(
|
let n_channels = channel_names.len() as i32;
|
||||||
PixelStorage::F16(data.into_boxed_slice()),
|
let expected_len = (resolution.x * resolution.y) as usize * n_channels as usize;
|
||||||
|
|
||||||
|
if data.len() != expected_len {
|
||||||
|
panic!("ImageBuffer::from_f16: Data length mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
|
let storage = PixelStorage::F16(data);
|
||||||
|
|
||||||
|
let ptr = match &storage {
|
||||||
|
PixelStorage::F16(v) => v.as_ptr() as *const u16,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let view = Image {
|
||||||
|
format: PixelFormat::Half,
|
||||||
resolution,
|
resolution,
|
||||||
|
n_channels,
|
||||||
|
pixels: Pixels::F16(ptr),
|
||||||
|
encoding: ColorEncoding::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
view,
|
||||||
channel_names,
|
channel_names,
|
||||||
ColorEncoding::Linear,
|
_storage: storage,
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_f32(data: Vec<f32>, resolution: Point2i, channel_names: Vec<String>) -> Self {
|
pub fn from_f32(data: Vec<f32>, resolution: Point2i, channel_names: Vec<String>) -> Self {
|
||||||
Self::from_storage(
|
let n_channels = channel_names.len() as i32;
|
||||||
PixelStorage::F32(data.into_boxed_slice()),
|
let expected_len = (resolution.x * resolution.y) as usize * n_channels as usize;
|
||||||
|
|
||||||
|
if data.len() != expected_len {
|
||||||
|
panic!("ImageBuffer::from_f32: Data length mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
|
let storage = PixelStorage::F32(data);
|
||||||
|
|
||||||
|
let ptr = match &storage {
|
||||||
|
PixelStorage::F32(v) => v.as_ptr(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let view = Image {
|
||||||
|
format: PixelFormat::Float,
|
||||||
resolution,
|
resolution,
|
||||||
|
n_channels,
|
||||||
|
pixels: Pixels::F32(ptr),
|
||||||
|
encoding: ColorEncoding::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
view,
|
||||||
channel_names,
|
channel_names,
|
||||||
ColorEncoding::Linear,
|
_storage: storage,
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
|
@ -205,24 +296,7 @@ impl Image {
|
||||||
PixelFormat::F32 => PixelStorage::F32(vec![0.0; pixel_count]),
|
PixelFormat::F32 => PixelStorage::F32(vec![0.0; pixel_count]),
|
||||||
};
|
};
|
||||||
|
|
||||||
Self::from_storage(storage, resolution, owned_names, encoding)
|
Self::from_vector(storage, resolution, owned_names, encoding)
|
||||||
}
|
|
||||||
|
|
||||||
// Access
|
|
||||||
pub fn device_image(&self) -> &DeviceImage {
|
|
||||||
&self.device
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resolution(&self) -> Point2i {
|
|
||||||
self.base.resolution
|
|
||||||
}
|
|
||||||
|
|
||||||
fn n_channels(&self) -> i32 {
|
|
||||||
self.base.n_channels
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn format(&self) -> PixelFormat {
|
|
||||||
self.device.base.format
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn channel_names(&self) -> Vec<&str> {
|
pub fn channel_names(&self) -> Vec<&str> {
|
||||||
|
|
@ -233,62 +307,60 @@ impl Image {
|
||||||
self.view.encoding
|
self.view.encoding
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pixel_offset(&self, p: Point2i) -> usize {
|
pub fn set_channel(&self, p: Point2i, c: i32, mut value: Float) {
|
||||||
let width = self.resolution().x() as usize;
|
if value.is_nan() {
|
||||||
let idx = p.y() as usize * width + p.x() as usize;
|
value = 0.0;
|
||||||
idx * self.n_channels() as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read
|
|
||||||
pub fn get_channel(&self, p: Point2i, c: i32) -> Float {
|
|
||||||
self.get_channel_with_wrap(p, c, WrapMode::Clamp.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_channel_with_wrap(&self, mut p: Point2i, c: i32, wrap_mode: WrapMode2D) -> Float {
|
|
||||||
if !self.device.base.remap_pixel_coords(&mut p, wrap_mode) {
|
|
||||||
return 0.0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let offset = self.pixel_offset(p) + c as usize;
|
if !self.view.resolution.inside_exclusive(p) {
|
||||||
|
return;
|
||||||
match &self.storage {
|
|
||||||
PixelStorage::U8(data) => self.device.base.encoding.to_linear_scalar(data[offset]),
|
|
||||||
PixelStorage::F16(data) => data[offset].to_f32(),
|
|
||||||
PixelStorage::F32(data) => data[offset],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_channels(&self, p: Point2i) -> ImageChannelValues {
|
|
||||||
self.get_channels_with_wrap(p, WrapMode::Clamp.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_channels_with_wrap(
|
|
||||||
&self,
|
|
||||||
mut p: Point2i,
|
|
||||||
wrap_mode: WrapMode2D,
|
|
||||||
) -> ImageChannelValues {
|
|
||||||
if !self.device.base.remap_pixel_coords(&mut p, wrap_mode) {
|
|
||||||
return ImageChannelValues(smallvec![0.0; self.n_channels() as usize]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let offset = self.pixel_offset(p);
|
let offset = self.view.pixel_offset(p) + c as usize;
|
||||||
let nc = self.n_channels() as usize;
|
match &mut self._storage {
|
||||||
let mut values = SmallVec::with_capacity(nc);
|
|
||||||
|
|
||||||
match &self.storage {
|
|
||||||
PixelStorage::U8(data) => {
|
PixelStorage::U8(data) => {
|
||||||
for i in 0..nc {
|
if !self.view.encoding.is_null() {
|
||||||
values.push(self.device.base.encoding.to_linear_scalar(data[offset + i]));
|
data[offset] = self.view.encoding.from_linear_scalar(value);
|
||||||
|
} else {
|
||||||
|
let val = (value * 255.0 + 0.5).clamp(0.0, 255.0);
|
||||||
|
data[offset] = val as u8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PixelStorage::F16(data) => {
|
PixelStorage::F16(data) => {
|
||||||
for i in 0..nc {
|
data[offset] = f16_to_f32(value);
|
||||||
values.push(data[offset + i].to_f32());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
PixelStorage::F32(data) => {
|
PixelStorage::F32(data) => {
|
||||||
for i in 0..nc {
|
data[offset] = value;
|
||||||
values.push(data[offset + i]);
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_channels_with_wrap(&self, p: Point2i, wrap_mode: WrapMode2D) -> ImageChannelValues {
|
||||||
|
let mut pp = p;
|
||||||
|
if !self.view.remap_pixel_coords(&mut pp, wrap_mode) {
|
||||||
|
return ImageChannelValues(smallvec![0.0; desc.offset.len()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let pixel_offset = self.view.pixel_offset(pp);
|
||||||
|
let mut values: SmallVec<[Float; 4]> = SmallVec::with_capacity(desc.offset.len());
|
||||||
|
match &self.pixels {
|
||||||
|
PixelData::U8(data) => {
|
||||||
|
for i in 0..self.view.n_channels() {
|
||||||
|
let raw_val = data[pixel_offset + i];
|
||||||
|
let linear = self.view.encoding.to_linear_scalar(raw_val);
|
||||||
|
values.push(linear);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PixelData::F16(data) => {
|
||||||
|
for i in 0..self.view.n_channels() {
|
||||||
|
let raw_val = data[pixel_offset];
|
||||||
|
values.push(f16_to_f32(raw_val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PixelData::F32(data) => {
|
||||||
|
for i in 0..self.view.n_channels() {
|
||||||
|
let val = data[pixel_offset + i];
|
||||||
|
values.push(val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -296,65 +368,43 @@ impl Image {
|
||||||
ImageChannelValues(values)
|
ImageChannelValues(values)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write
|
pub fn get_channels(&self, p: Point2i) -> ImageChannelValues {
|
||||||
pub fn set_channel(&mut self, p: Point2i, c: i32, mut value: Float) {
|
self.get_channels_with_wrap(p, WrapMode::Clamp.into())
|
||||||
if value.is_nan() {
|
|
||||||
value = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let res = self.resolution();
|
|
||||||
if p.x() < 0 || p.x() >= res.x() || p.y() < 0 || p.y() >= res.y() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let offset = self.pixel_offset(p) + c as usize;
|
|
||||||
|
|
||||||
match &mut self.storage {
|
|
||||||
PixelStorage::U8(data) => {
|
|
||||||
let data = Box::as_mut(data);
|
|
||||||
data[offset] = self.device.base.encoding.from_linear_scalar(value);
|
|
||||||
}
|
|
||||||
PixelStorage::F16(data) => {
|
|
||||||
let data = Box::as_mut(data);
|
|
||||||
data[offset] = f16::from_f32(value);
|
|
||||||
}
|
|
||||||
PixelStorage::F32(data) => {
|
|
||||||
let data = Box::as_mut(data);
|
|
||||||
data[offset] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Descriptions
|
|
||||||
pub fn get_channels_with_desc(
|
pub fn get_channels_with_desc(
|
||||||
&self,
|
&self,
|
||||||
p: Point2i,
|
p: Point2i,
|
||||||
desc: &ImageChannelDesc,
|
desc: &ImageChannelDesc,
|
||||||
wrap_mode: WrapMode2D,
|
wrap: WrapMode2D,
|
||||||
) -> ImageChannelValues {
|
) -> ImageChannelValues {
|
||||||
let mut pp = p;
|
let mut pp = p;
|
||||||
if !self.device.base.remap_pixel_coords(&mut pp, wrap_mode) {
|
if !self.view.remap_pixel_coords(&mut pp, wrap) {
|
||||||
return ImageChannelValues(smallvec![0.0; desc.offset.len()]);
|
return ImageChannelValues(smallvec![0.0; desc.offset.len()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let pixel_offset = self.pixel_offset(pp);
|
let pixel_offset = self.view.pixel_offset(pp);
|
||||||
let mut values = SmallVec::with_capacity(desc.offset.len());
|
|
||||||
|
|
||||||
match &self.storage {
|
let mut values: SmallVec<[Float; 4]> = SmallVec::with_capacity(desc.offset.len());
|
||||||
PixelStorage::U8(data) => {
|
|
||||||
for &c in &desc.offset {
|
match &self.pixels {
|
||||||
let raw = data[pixel_offset + c];
|
PixelData::U8(data) => {
|
||||||
values.push(self.device.base.encoding.to_linear_scalar(raw));
|
for &channel_idx in &desc.offset {
|
||||||
|
let raw_val = data[pixel_offset + channel_idx as usize];
|
||||||
|
let linear = self.view.encoding.to_linear_scalar(raw_val);
|
||||||
|
values.push(linear);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PixelStorage::F16(data) => {
|
PixelData::F16(data) => {
|
||||||
for &c in &desc.offset {
|
for &channel_idx in &desc.offset {
|
||||||
values.push(data[pixel_offset + c].to_f32());
|
let raw_val = data[pixel_offset + channel_idx as usize];
|
||||||
|
values.push(f16_to_f32(raw_val));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PixelStorage::F32(data) => {
|
PixelData::F32(data) => {
|
||||||
for &c in &desc.offset {
|
for &channel_idx in &desc.offset {
|
||||||
values.push(data[pixel_offset + c]);
|
let val = data[pixel_offset + channel_idx as usize];
|
||||||
|
values.push(val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -382,7 +432,7 @@ impl Image {
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Missing channel '{}'. Available: {:?}",
|
"Image is missing requested channel '{}'. Available channels: {:?}",
|
||||||
req, self.channel_names
|
req, self.channel_names
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
@ -394,53 +444,87 @@ impl Image {
|
||||||
|
|
||||||
pub fn all_channels_desc(&self) -> ImageChannelDesc {
|
pub fn all_channels_desc(&self) -> ImageChannelDesc {
|
||||||
ImageChannelDesc {
|
ImageChannelDesc {
|
||||||
offset: (0..self.n_channels() as usize).collect(),
|
offset: (0..self.n_channels()).collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_channels(&self, desc: &ImageChannelDesc) -> Self {
|
pub fn select_channels(&self, desc: &ImageChannelDesc) -> Self {
|
||||||
let new_names: Vec<String> = desc
|
let desc_channel_names: Vec<String> = desc
|
||||||
.offset
|
.offset
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&i| self.channel_names[i].clone())
|
.map(|&i| self.channel_names[i as usize])
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let res = self.resolution();
|
let new_storage = match &self._storage {
|
||||||
let pixel_count = (res.x() * res.y()) as usize;
|
PixelStorage::U8(src_data) => {
|
||||||
let src_nc = self.n_channels() as usize;
|
// Allocate destination buffer
|
||||||
let dst_nc = desc.offset.len();
|
let mut dst_data = vec![0u8; pixel_count * dst_n_channels];
|
||||||
|
|
||||||
let new_storage = match &self.storage {
|
// Iterate over every pixel (Flat loop is faster than nested x,y)
|
||||||
PixelStorage::U8(src) => {
|
|
||||||
let mut dst = vec![0u8; pixel_count * dst_nc];
|
|
||||||
for i in 0..pixel_count {
|
for i in 0..pixel_count {
|
||||||
for (out_idx, &in_c) in desc.offset.iter().enumerate() {
|
let src_pixel_start = i * src_n_channels;
|
||||||
dst[i * dst_nc + out_idx] = src[i * src_nc + in_c];
|
let dst_pixel_start = i * dst_n_channels;
|
||||||
|
|
||||||
|
// Copy specific channels based on desc
|
||||||
|
for (out_idx, &in_channel_offset) in desc.offset.iter().enumerate() {
|
||||||
|
let val = src_data[src_pixel_start + in_channel_offset as usize];
|
||||||
|
dst_data[dst_pixel_start + out_idx] = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PixelStorage::U8(dst.into_boxed_slice())
|
PixelStorage::U8(dst_data)
|
||||||
}
|
}
|
||||||
PixelStorage::F16(src) => {
|
PixelStorage::F16(src_data) => {
|
||||||
let mut dst = vec![f16::ZERO; pixel_count * dst_nc];
|
let mut dst_data = vec![half::f16::ZERO; pixel_count * dst_n_channels];
|
||||||
|
|
||||||
for i in 0..pixel_count {
|
for i in 0..pixel_count {
|
||||||
for (out_idx, &in_c) in desc.offset.iter().enumerate() {
|
let src_pixel_start = i * src_n_channels;
|
||||||
dst[i * dst_nc + out_idx] = src[i * src_nc + in_c];
|
let dst_pixel_start = i * dst_n_channels;
|
||||||
|
|
||||||
|
for (out_idx, &in_channel_offset) in desc.offset.iter().enumerate() {
|
||||||
|
let val = src_data[src_pixel_start + in_channel_offset as usize];
|
||||||
|
dst_data[dst_pixel_start + out_idx] = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PixelStorage::F16(dst.into_boxed_slice())
|
PixelStorage::F16(dst_data)
|
||||||
}
|
}
|
||||||
PixelStorage::F32(src) => {
|
PixelStorage::F32(src_data) => {
|
||||||
let mut dst = vec![0.0f32; pixel_count * dst_nc];
|
let mut dst_data = vec![0.0; pixel_count * dst_n_channels];
|
||||||
|
|
||||||
for i in 0..pixel_count {
|
for i in 0..pixel_count {
|
||||||
for (out_idx, &in_c) in desc.offset.iter().enumerate() {
|
let src_pixel_start = i * src_n_channels;
|
||||||
dst[i * dst_nc + out_idx] = src[i * src_nc + in_c];
|
let dst_pixel_start = i * dst_n_channels;
|
||||||
|
|
||||||
|
for (out_idx, &in_channel_offset) in desc.offset.iter().enumerate() {
|
||||||
|
let val = src_data[src_pixel_start + in_channel_offset as usize];
|
||||||
|
dst_data[dst_pixel_start + out_idx] = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PixelStorage::F32(dst.into_boxed_slice())
|
PixelStorage::F32(dst_data)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Self::from_storage(new_storage, res, new_names, self.encoding())
|
let image = Self::new(
|
||||||
|
self.format,
|
||||||
|
self.resolution,
|
||||||
|
desc_channel_names,
|
||||||
|
self.encoding(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_channels(
|
||||||
|
&mut self,
|
||||||
|
p: Point2i,
|
||||||
|
desc: &ImageChannelDesc,
|
||||||
|
mut values: &ImageChannelValues,
|
||||||
|
) {
|
||||||
|
assert_eq!(desc.size(), values.len());
|
||||||
|
for i in 0..desc.size() {
|
||||||
|
self.view.set_channel(p, desc.offset[i], values[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_channels_all(&mut self, p: Point2i, values: &ImageChannelValues) {
|
||||||
|
self.set_channels(p, &self.all_channels_desc(), values)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_sampling_distribution<F>(&self, dxd_a: F, domain: Bounds2f) -> Array2D<Float>
|
pub fn get_sampling_distribution<F>(&self, dxd_a: F, domain: Bounds2f) -> Array2D<Float>
|
||||||
|
|
@ -549,29 +633,4 @@ impl Image {
|
||||||
PixelStorage::F32(vec) => Pixels::F32(vec.as_ptr()),
|
PixelStorage::F32(vec) => Pixels::F32(vec.as_ptr()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_any_infinite_pixels(&self) -> bool {
|
|
||||||
if format == PixelFormat::Float {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for y in 0..self.resolution().y() {
|
|
||||||
for x in 0..self.resolution().x() {
|
|
||||||
for c in 0..self.n_channels() {
|
|
||||||
if self.get_channel(Point2i::new(x, y), c).is_infinite() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::Deref for Image {
|
|
||||||
type Target = DeviceImage;
|
|
||||||
|
|
||||||
fn deref(&self) -> &DeviceImage {
|
|
||||||
&self.device
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// use rayon::prelude::*;
|
// use rayon::prelude::*;
|
||||||
use super::Image;
|
use super::ImageBuffer;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
use shared::core::geometry::{Bounds2i, Point2i};
|
use shared::core::geometry::{Bounds2i, Point2i};
|
||||||
|
|
@ -13,7 +13,7 @@ pub struct ResampleWeight {
|
||||||
pub weight: [Float; 4],
|
pub weight: [Float; 4],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Image {
|
impl ImageBuffer {
|
||||||
pub fn flip_y(&mut self) {
|
pub fn flip_y(&mut self) {
|
||||||
let res = self.resolution;
|
let res = self.resolution;
|
||||||
let nc = self.n_channels();
|
let nc = self.n_channels();
|
||||||
|
|
|
||||||
|
|
@ -1,135 +1,31 @@
|
||||||
use crate::core::spectrum::{SPECTRUM_CACHE, spectrum_to_photometric};
|
use shared::core::geometry::{Bounds3f, Point2i};
|
||||||
use crate::core::texture::FloatTexture;
|
use shared::core::image::Image;
|
||||||
use crate::lights::*;
|
use shared::core::light::LightBase;
|
||||||
use crate::utils::containers::InternCache;
|
use shared::core::medium::MediumInterface;
|
||||||
use crate::utils::{Arena, FileLoc, ParameterDictionary, Upload, resolve_filename};
|
|
||||||
use shared::core::camera::CameraTransform;
|
|
||||||
use shared::core::light::Light;
|
|
||||||
use shared::core::medium::Medium;
|
|
||||||
use shared::core::spectrum::Spectrum;
|
use shared::core::spectrum::Spectrum;
|
||||||
use shared::core::texture::SpectrumType;
|
use shared::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
|
||||||
use shared::lights::*;
|
use shared::utils::Transform;
|
||||||
use shared::spectra::{DenselySampledSpectrum, RGBColorSpace};
|
use shared::{Float, PI};
|
||||||
use shared::utils::{Ptr, Transform};
|
|
||||||
|
|
||||||
pub fn lookup_spectrum(s: &Spectrum) -> DenselySampledSpectrum {
|
use crate::core::spectrum::SPECTRUM_CACHE;
|
||||||
let cache = SPECTRUM_CACHE.get_or_init(InternCache::new);
|
use crate::utils::containers::InternCache;
|
||||||
let dense_spectrum = DenselySampledSpectrum::from_spectrum(s);
|
|
||||||
cache.lookup(dense_spectrum).as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait CreateLight {
|
pub trait LightBaseTrait {
|
||||||
fn create(
|
pub fn lookup_spectrum(s: &Spectrum) -> DenselySampledSpectrum {
|
||||||
arena: &mut Arena,
|
let cache = SPECTRUM_CACHE.get_or_init(InternCache::new);
|
||||||
render_from_light: Transform,
|
let dense_spectrum = DenselySampledSpectrum::from_spectrum(s);
|
||||||
medium: Medium,
|
cache.lookup(dense_spectrum).as_ref()
|
||||||
parameters: &ParameterDictionary,
|
|
||||||
loc: &FileLoc,
|
|
||||||
shape: &Shape,
|
|
||||||
alpha_text: &FloatTexture,
|
|
||||||
colorspace: Option<&RGBColorSpace>,
|
|
||||||
) -> Light;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait LightFactory {
|
|
||||||
fn create(
|
|
||||||
name: &str,
|
|
||||||
arena: &mut Arena,
|
|
||||||
render_from_light: Transform,
|
|
||||||
medium: Medium,
|
|
||||||
parameters: &ParameterDictionary,
|
|
||||||
loc: &FileLoc,
|
|
||||||
shape: &Shape,
|
|
||||||
alpha_tex: &FloatTexture,
|
|
||||||
colorspace: Option<&RGBColorSpace>,
|
|
||||||
camera_transform: CameraTransform,
|
|
||||||
) -> Result<Self, Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LightFactory for Light {
|
|
||||||
fn create(
|
|
||||||
name: &str,
|
|
||||||
arena: &mut Arena,
|
|
||||||
render_from_light: Transform,
|
|
||||||
medium: Medium,
|
|
||||||
parameters: &ParameterDictionary,
|
|
||||||
loc: &FileLoc,
|
|
||||||
shape: &Shape,
|
|
||||||
alpha_tex: &FloatTexture,
|
|
||||||
colorspace: Option<&RGBColorSpace>,
|
|
||||||
camera_transform: CameraTransform,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
match name {
|
|
||||||
"diffuse" => lights::diffuse::create(
|
|
||||||
arena,
|
|
||||||
render_from_light,
|
|
||||||
medium,
|
|
||||||
parameters,
|
|
||||||
loc,
|
|
||||||
shape,
|
|
||||||
alpha_tex,
|
|
||||||
colorspace,
|
|
||||||
)?,
|
|
||||||
"point" => PointLight::create(
|
|
||||||
arena,
|
|
||||||
render_from_light,
|
|
||||||
medium,
|
|
||||||
parameters,
|
|
||||||
loc,
|
|
||||||
shape,
|
|
||||||
alpha_tex,
|
|
||||||
colorspace,
|
|
||||||
)?,
|
|
||||||
"spot" => SpotLight::create(
|
|
||||||
arena,
|
|
||||||
render_from_light,
|
|
||||||
medium,
|
|
||||||
parameters,
|
|
||||||
loc,
|
|
||||||
shape,
|
|
||||||
alpha_tex,
|
|
||||||
colorspace,
|
|
||||||
)?,
|
|
||||||
"goniometric" => GoniometricLight::create(
|
|
||||||
arena,
|
|
||||||
render_from_light,
|
|
||||||
medium,
|
|
||||||
parameters,
|
|
||||||
loc,
|
|
||||||
shape,
|
|
||||||
alpha_tex,
|
|
||||||
colorspace,
|
|
||||||
)?,
|
|
||||||
"projection" => ProjectionLight::create(
|
|
||||||
arena,
|
|
||||||
render_from_light,
|
|
||||||
medium,
|
|
||||||
parameters,
|
|
||||||
loc,
|
|
||||||
shape,
|
|
||||||
alpha_tex,
|
|
||||||
colorspace,
|
|
||||||
)?,
|
|
||||||
"distant" => DistantLight::create(
|
|
||||||
arena,
|
|
||||||
render_from_light,
|
|
||||||
medium,
|
|
||||||
parameters,
|
|
||||||
loc,
|
|
||||||
shape,
|
|
||||||
alpha_tex,
|
|
||||||
colorspace,
|
|
||||||
)?,
|
|
||||||
"infinite" => infinite::create(
|
|
||||||
arena,
|
|
||||||
render_from_light,
|
|
||||||
medium,
|
|
||||||
camera_transform,
|
|
||||||
parameters,
|
|
||||||
colorspace,
|
|
||||||
loc,
|
|
||||||
)?,
|
|
||||||
_ => Err(error!(loc, "unknown light type: \"{}\"", name)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl LightBaseTrait for LightBase {}
|
||||||
|
|
||||||
|
pub trait LightFactory {
|
||||||
|
fn new(
|
||||||
|
render_from_light: Transform,
|
||||||
|
medium_interface: MediumInterface,
|
||||||
|
scale: Float,
|
||||||
|
iemit: &Spectrum,
|
||||||
|
image: &Image,
|
||||||
|
) -> Self;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::core::image::Image;
|
use crate::core::image::ImageBuffer;
|
||||||
use crate::utils::error::FileLoc;
|
use crate::utils::error::FileLoc;
|
||||||
use crate::utils::parameters::ParameterDictionary;
|
use crate::utils::parameters::ParameterDictionary;
|
||||||
use shared::core::material::Material;
|
use shared::core::material::Material;
|
||||||
|
|
@ -8,10 +8,10 @@ use std::collections::HashMap;
|
||||||
pub trait CreateMaterial: Sized {
|
pub trait CreateMaterial: Sized {
|
||||||
fn create(
|
fn create(
|
||||||
parameters: &TextureParameterDictionary,
|
parameters: &TextureParameterDictionary,
|
||||||
normal_map: Option<Ptr<Image>>,
|
normal_map: Option<Arc<ImageBuffer>>,
|
||||||
named_materials: &HashMap<String, Material>,
|
named_materials: &HashMap<String, Material>,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
) -> Result<Self, Error>;
|
) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! make_material_factory {
|
macro_rules! make_material_factory {
|
||||||
|
|
@ -35,10 +35,10 @@ pub trait MaterialFactory {
|
||||||
fn create(
|
fn create(
|
||||||
name: &str,
|
name: &str,
|
||||||
params: &TextureParameterDictionary,
|
params: &TextureParameterDictionary,
|
||||||
normal_map: Ptr<Image>,
|
normal_map: Arc<ImageBuffer>,
|
||||||
named_materials: HashMap<String, Material>,
|
named_materials: HashMap<String, Material>,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
) -> Result<Self, Error>;
|
) -> Result<Self, String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MaterialFactory for Material {
|
impl MaterialFactory for Material {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
use crate::core::filter::FilterFactory;
|
use crate::core::filter::FilterFactory;
|
||||||
use crate::core::image::{Image, io::ImageIO};
|
use crate::core::image::{ImageBuffer, io::ImageIO};
|
||||||
use crate::core::texture::SpectrumTexture;
|
use crate::core::texture::SpectrumTexture;
|
||||||
use crate::utils::parallel::{AsyncJob, run_async};
|
use crate::utils::parallel::{AsyncJob, run_async};
|
||||||
use crate::utils::parameters::{
|
use crate::utils::parameters::{
|
||||||
NamedTextures, ParameterDictionary, ParsedParameterVector, TextureParameterDictionary,
|
NamedTextures, ParameterDictionary, ParsedParameterVector, TextureParameterDictionary,
|
||||||
};
|
};
|
||||||
use crate::utils::parser::ParserTarget;
|
use crate::utils::parser::ParserTarget;
|
||||||
use crate::utils::{Arena, Upload};
|
|
||||||
use crate::utils::{normalize_utf8, resolve_filename};
|
use crate::utils::{normalize_utf8, resolve_filename};
|
||||||
use image_rs::Primitive;
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
use shared::core::camera::{Camera, CameraTransform};
|
use shared::core::camera::{Camera, CameraTransform};
|
||||||
|
|
@ -18,9 +16,8 @@ use shared::core::filter::Filter;
|
||||||
use shared::core::geometry::{Point3f, Vector3f};
|
use shared::core::geometry::{Point3f, Vector3f};
|
||||||
use shared::core::lights::Light;
|
use shared::core::lights::Light;
|
||||||
use shared::core::material::Material;
|
use shared::core::material::Material;
|
||||||
use shared::core::medium::{Medium, MediumInterface};
|
use shared::core::medium::Medium;
|
||||||
use shared::core::options::RenderingCoordinateSystem;
|
use shared::core::options::RenderingCoordinateSystem;
|
||||||
use shared::core::primitive::{Primitive, PrimitiveTrait};
|
|
||||||
use shared::core::sampler::Sampler;
|
use shared::core::sampler::Sampler;
|
||||||
use shared::core::spectrum::{Spectrum, SpectrumType};
|
use shared::core::spectrum::{Spectrum, SpectrumType};
|
||||||
use shared::core::texture::{FloatTexture, SpectrumTexture};
|
use shared::core::texture::{FloatTexture, SpectrumTexture};
|
||||||
|
|
@ -29,6 +26,7 @@ use shared::spectra::RGBColorSpace;
|
||||||
use shared::utils::error::FileLoc;
|
use shared::utils::error::FileLoc;
|
||||||
use shared::utils::math::SquareMatrix;
|
use shared::utils::math::SquareMatrix;
|
||||||
use shared::utils::transform::{AnimatedTransform, Transform, look_at};
|
use shared::utils::transform::{AnimatedTransform, Transform, look_at};
|
||||||
|
use std::char::MAX;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::ops::{Index as IndexTrait, IndexMut as IndexMutTrait};
|
use std::ops::{Index as IndexTrait, IndexMut as IndexMutTrait};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
@ -174,33 +172,6 @@ pub struct BasicScene {
|
||||||
pub film_state: Mutex<SingletonState<Film>>,
|
pub film_state: Mutex<SingletonState<Film>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SceneLookup<'a> {
|
|
||||||
textures: &'a NamedTextures,
|
|
||||||
media: &'a HashMap<String, Medium>,
|
|
||||||
named_materials: &'a HashMap<String, Material>,
|
|
||||||
materials: &'a Vec<Material>,
|
|
||||||
shape_lights: &'a HashMap<usize, Vec<Light>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> SceneLookup<'a> {
|
|
||||||
fn find_medium(&self, name: &str, loc: &FileLoc) -> Option<Medium> {
|
|
||||||
if name.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
self.media.get(name).cloned().or_else(|| {
|
|
||||||
panic!("{}: medium '{}' not defined", loc, name);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_material(&self, name: &str, loc: &FileLoc) -> Material {
|
|
||||||
if !name.is_empty() {
|
|
||||||
*self.named_materials.get(name).expect("Material not found")
|
|
||||||
} else {
|
|
||||||
self.materials[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BasicScene {
|
impl BasicScene {
|
||||||
fn set_options(
|
fn set_options(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
|
|
@ -403,7 +374,7 @@ impl BasicScene {
|
||||||
|
|
||||||
pub fn add_light(&self, light: LightSceneEntity) {
|
pub fn add_light(&self, light: LightSceneEntity) {
|
||||||
if light.transformed_base.render_from_object.is_animated() {
|
if light.transformed_base.render_from_object.is_animated() {
|
||||||
log::warn!(
|
log::info!(
|
||||||
"{}: Animated world to texture not supported, using start",
|
"{}: Animated world to texture not supported, using start",
|
||||||
light.transformed_base.base.loc
|
light.transformed_base.base.loc
|
||||||
);
|
);
|
||||||
|
|
@ -559,190 +530,6 @@ impl BasicScene {
|
||||||
(named_materials_out, materials_out)
|
(named_materials_out, materials_out)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_aggregate(
|
|
||||||
&self,
|
|
||||||
arena: &mut Arena,
|
|
||||||
textures: &NamedTextures,
|
|
||||||
shape_lights: &HashMap<usize, Vec<Light>>,
|
|
||||||
) -> Arc<dyn PrimitiveTrait> {
|
|
||||||
let shapes_guard = self.shapes.lock().unwrap();
|
|
||||||
let animated_shapes_guard = self.animated_shapes.lock().unwrap();
|
|
||||||
let media_guard = self.media_state.lock().unwrap();
|
|
||||||
let mat_guard = self.material_state.lock().unwrap();
|
|
||||||
|
|
||||||
let lookup = SceneLookup {
|
|
||||||
textures: &textures.float_textures,
|
|
||||||
media: &media_guard.map,
|
|
||||||
named_materials: &mat_guard.named_materials.iter().cloned().collect(),
|
|
||||||
materials: &mat_guard.materials,
|
|
||||||
shape_lights,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut all_primitives = Vec::new();
|
|
||||||
|
|
||||||
// Parallel CPU Load
|
|
||||||
let loaded_static = self.load_shapes_parallel(&shapes_guard, &lookup);
|
|
||||||
// Serial Arena Upload
|
|
||||||
let static_prims = self.upload_shapes(arena, &shapes_guard, loaded_static, &lookup);
|
|
||||||
all_primitives.extend(static_prims);
|
|
||||||
|
|
||||||
// Parallel CPU Load
|
|
||||||
let loaded_animated = self.load_animated_shapes_parallel(&animated_shapes_guard, &lookup);
|
|
||||||
// Serial Arena Upload
|
|
||||||
let anim_prims =
|
|
||||||
self.upload_animated_shapes(arena, &animated_shapes_guard, loaded_animated, &lookup);
|
|
||||||
all_primitives.extend(anim_prims);
|
|
||||||
|
|
||||||
// (Similar pattern: Lock definitions, load parallel, upload serial)
|
|
||||||
// Call a helper `create_instance_primitives` here.
|
|
||||||
|
|
||||||
if !all_primitives.is_empty() {
|
|
||||||
todo!("Build BVH or KD-Tree")
|
|
||||||
} else {
|
|
||||||
todo!("Return empty")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_shapes_parallel(
|
|
||||||
&self,
|
|
||||||
entities: &[ShapeSceneEntity],
|
|
||||||
lookup: &SceneLookup,
|
|
||||||
) -> Vec<Vec<Shape>> {
|
|
||||||
entities
|
|
||||||
.par_iter()
|
|
||||||
.map(|sh| {
|
|
||||||
Shape::create(
|
|
||||||
&sh.base.name,
|
|
||||||
sh.render_from_object.as_ref(),
|
|
||||||
sh.object_from_render.as_ref(),
|
|
||||||
sh.reverse_orientation,
|
|
||||||
&sh.base.parameters,
|
|
||||||
lookup.float_textures,
|
|
||||||
&sh.base.loc,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_animated_shapes_parallel(
|
|
||||||
&self,
|
|
||||||
entities: &[AnimatedShapeSceneEntity],
|
|
||||||
lookup: &SceneLookup,
|
|
||||||
) -> Vec<Vec<Shape>> {
|
|
||||||
entities
|
|
||||||
.par_iter()
|
|
||||||
.map(|sh| {
|
|
||||||
Shape::create(
|
|
||||||
&sh.transformed_base.base.name,
|
|
||||||
&sh.identity,
|
|
||||||
&sh.identity,
|
|
||||||
sh.reverse_orientation,
|
|
||||||
&sh.transformed_base.base.parameters,
|
|
||||||
lookup.float_textures,
|
|
||||||
&sh.transformed_base.base.loc,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn upload_shapes(
|
|
||||||
&self,
|
|
||||||
arena: &mut Arena,
|
|
||||||
entities: &[ShapeSceneEntity],
|
|
||||||
loaded_shapes: Vec<Vec<Shape>>,
|
|
||||||
lookup: &SceneLookup,
|
|
||||||
) -> Vec<Primitive> {
|
|
||||||
let mut primitives = Vec::new();
|
|
||||||
|
|
||||||
for (i, (entity, shapes)) in entities.iter().zip(loaded_shapes).enumerate() {
|
|
||||||
if shapes.is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let alpha_tex = self.get_alpha_texture(
|
|
||||||
&entity.base.parameters,
|
|
||||||
&entity.base.loc,
|
|
||||||
arena,
|
|
||||||
lookup.float_textures,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mtl = lookup.resolve_material(&entity.material, &entity.base.loc);
|
|
||||||
let mi = MediumInterface::new(
|
|
||||||
lookup.find_medium(&entity.inside_medium, &entity.base.loc),
|
|
||||||
lookup.find_medium(&entity.outside_medium, &entity.base.loc),
|
|
||||||
);
|
|
||||||
|
|
||||||
let shape_lights_opt = lookup.shape_lights.get(&i);
|
|
||||||
|
|
||||||
for (j, shape_host) in shapes.into_iter().enumerate() {
|
|
||||||
let mut area_light = None;
|
|
||||||
if entity.light_index.is_some() {
|
|
||||||
if let Some(lights) = shape_lights_opt {
|
|
||||||
if j < lights.len() {
|
|
||||||
area_light = Some(lights[j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let shape_ptr = shape_host.upload(arena);
|
|
||||||
|
|
||||||
let prim = if area_light.is_none()
|
|
||||||
&& !mi.is_medium_transition()
|
|
||||||
&& alpha_tex.is_none()
|
|
||||||
{
|
|
||||||
let p = SimplePrimitive::new(shape_ptr, mtl.unwrap());
|
|
||||||
Primitive::Simple(arena.alloc(p))
|
|
||||||
} else {
|
|
||||||
let p =
|
|
||||||
GeometricPrimitive::new(shape_ptr, mtl.unwrap(), area_light, mi, alpha_tex);
|
|
||||||
Primitive::Geometric(arena.alloc(p))
|
|
||||||
};
|
|
||||||
|
|
||||||
primitives.push(prim);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
primitives
|
|
||||||
}
|
|
||||||
|
|
||||||
fn upload_animated_shapes(
|
|
||||||
&self,
|
|
||||||
arena: &mut Arena,
|
|
||||||
entities: &[AnimatedShapeSceneEntity],
|
|
||||||
loaded_shapes: Vec<Vec<Shape>>,
|
|
||||||
lookup: &SceneLookup,
|
|
||||||
) -> Vec<Primitive> {
|
|
||||||
// Logic mirrors upload_shapes, but constructs AnimatedPrimitive or BVH
|
|
||||||
// ...
|
|
||||||
// Note: For AnimatedPrimitives, you wrap the result in `arena.alloc(AnimatedPrimitive::new(...))`
|
|
||||||
Vec::new()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_alpha_texture(
|
|
||||||
&self,
|
|
||||||
params: &ParameterDictionary,
|
|
||||||
loc: &FileLoc,
|
|
||||||
arena: &mut Arena,
|
|
||||||
textures: &HashMap<String, FloatTexture>,
|
|
||||||
) -> Option<FloatTexture> {
|
|
||||||
let alpha_name = params.get_texture("alpha");
|
|
||||||
|
|
||||||
if let Some(name) = alpha_name {
|
|
||||||
match textures.get(&name) {
|
|
||||||
Some(tex) => Some(*tex),
|
|
||||||
None => panic!("{:?}: Alpha texture '{}' not found", loc, name),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let alpha_val = params.get_one_float("alpha", 1.0);
|
|
||||||
if alpha_val < 1.0 {
|
|
||||||
let tex = FloatConstantTexture::new(alpha_val);
|
|
||||||
let ptr = arena.alloc(tex);
|
|
||||||
Some(FloatTexture::Constant(ptr))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_camera(&self) -> Arc<Camera> {
|
pub fn get_camera(&self) -> Arc<Camera> {
|
||||||
self.get_singleton(&self.camera_state, "Camera")
|
self.get_singleton(&self.camera_state, "Camera")
|
||||||
}
|
}
|
||||||
|
|
@ -863,6 +650,7 @@ impl BasicScene {
|
||||||
}
|
}
|
||||||
|
|
||||||
// PRIVATE METHODS
|
// PRIVATE METHODS
|
||||||
|
|
||||||
fn get_singleton<T: Send + Sync + 'static>(
|
fn get_singleton<T: Send + Sync + 'static>(
|
||||||
&self,
|
&self,
|
||||||
mutex: &Mutex<SingletonState<T>>,
|
mutex: &Mutex<SingletonState<T>>,
|
||||||
|
|
@ -906,7 +694,7 @@ impl BasicScene {
|
||||||
let job = crate::parallel::run_async(move || {
|
let job = crate::parallel::run_async(move || {
|
||||||
let path = std::path::Path::new(&filename_clone);
|
let path = std::path::Path::new(&filename_clone);
|
||||||
|
|
||||||
let immeta = Image::read(path, Some(ColorEncoding::Linear))
|
let immeta = ImageBuffer::read(path, Some(ColorEncoding::Linear))
|
||||||
.unwrap_or_else(|e| panic!("{}: unable to read normal map: {}", filename_clone, e));
|
.unwrap_or_else(|e| panic!("{}: unable to read normal map: {}", filename_clone, e));
|
||||||
|
|
||||||
let image = &immeta.image;
|
let image = &immeta.image;
|
||||||
|
|
@ -1064,6 +852,7 @@ impl BasicSceneBuilder {
|
||||||
named_coordinate_systems: HashMap::new(),
|
named_coordinate_systems: HashMap::new(),
|
||||||
active_instance_definition: None,
|
active_instance_definition: None,
|
||||||
shapes: Vec::new(),
|
shapes: Vec::new(),
|
||||||
|
instances: Vec::new(),
|
||||||
named_material_names: HashSet::new(),
|
named_material_names: HashSet::new(),
|
||||||
medium_names: HashSet::new(),
|
medium_names: HashSet::new(),
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::core::texture::FloatTexture;
|
use crate::core::texture::FloatTexture;
|
||||||
use crate::shapes::TriQuadMesh;
|
use crate::shapes::TriQuadMesh;
|
||||||
use crate::utils::{Arena, FileLoc, ParameterDictionary, resolve_filename};
|
use crate::utils::{FileLoc, ParameterDictionary, resolve_filename};
|
||||||
use shared::core::options::get_options;
|
use shared::core::options::get_options;
|
||||||
use shared::core::shape::*;
|
use shared::core::shape::*;
|
||||||
use shared::shapes::*;
|
use shared::shapes::*;
|
||||||
|
|
@ -21,7 +21,6 @@ pub trait CreateShape {
|
||||||
parameters: ParameterDictionary,
|
parameters: ParameterDictionary,
|
||||||
float_textures: HashMap<String, FloatTexture>,
|
float_textures: HashMap<String, FloatTexture>,
|
||||||
loc: FileLoc,
|
loc: FileLoc,
|
||||||
arena: &mut Arena,
|
|
||||||
) -> Vec<Shape>;
|
) -> Vec<Shape>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -34,7 +33,6 @@ pub trait ShapeFactory {
|
||||||
parameters: ParameterDictionary,
|
parameters: ParameterDictionary,
|
||||||
float_textures: HashMap<String, FloatTexture>,
|
float_textures: HashMap<String, FloatTexture>,
|
||||||
loc: FileLoc,
|
loc: FileLoc,
|
||||||
arena: &mut Arena,
|
|
||||||
) -> Vec<Shape>;
|
) -> Vec<Shape>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -47,7 +45,6 @@ impl ShapeFactory for Shape {
|
||||||
parameters: ParameterDictionary,
|
parameters: ParameterDictionary,
|
||||||
float_textures: HashMap<String, FloatTexture>,
|
float_textures: HashMap<String, FloatTexture>,
|
||||||
loc: FileLoc,
|
loc: FileLoc,
|
||||||
arena: &mut Arena,
|
|
||||||
) -> Vec<Shape> {
|
) -> Vec<Shape> {
|
||||||
match name {
|
match name {
|
||||||
"sphere" => SphereShape::create(
|
"sphere" => SphereShape::create(
|
||||||
|
|
@ -58,7 +55,6 @@ impl ShapeFactory for Shape {
|
||||||
parameters,
|
parameters,
|
||||||
float_textures,
|
float_textures,
|
||||||
loc,
|
loc,
|
||||||
arena,
|
|
||||||
),
|
),
|
||||||
"cylinder" => CylinderShape::create(
|
"cylinder" => CylinderShape::create(
|
||||||
name,
|
name,
|
||||||
|
|
@ -68,7 +64,6 @@ impl ShapeFactory for Shape {
|
||||||
parameters,
|
parameters,
|
||||||
float_textures,
|
float_textures,
|
||||||
loc,
|
loc,
|
||||||
arena,
|
|
||||||
),
|
),
|
||||||
"disk" => DiskShape::create(
|
"disk" => DiskShape::create(
|
||||||
name,
|
name,
|
||||||
|
|
@ -78,7 +73,6 @@ impl ShapeFactory for Shape {
|
||||||
parameters,
|
parameters,
|
||||||
float_textures,
|
float_textures,
|
||||||
loc,
|
loc,
|
||||||
arena,
|
|
||||||
),
|
),
|
||||||
"bilinearmesh" => BilinearPatchShape::create(
|
"bilinearmesh" => BilinearPatchShape::create(
|
||||||
name,
|
name,
|
||||||
|
|
@ -88,7 +82,6 @@ impl ShapeFactory for Shape {
|
||||||
parameters,
|
parameters,
|
||||||
float_textures,
|
float_textures,
|
||||||
loc,
|
loc,
|
||||||
arena,
|
|
||||||
),
|
),
|
||||||
"trianglemesh" => TriangleShape::create(
|
"trianglemesh" => TriangleShape::create(
|
||||||
name,
|
name,
|
||||||
|
|
@ -98,7 +91,6 @@ impl ShapeFactory for Shape {
|
||||||
parameters,
|
parameters,
|
||||||
float_textures,
|
float_textures,
|
||||||
loc,
|
loc,
|
||||||
arena,
|
|
||||||
),
|
),
|
||||||
"plymesh" => {
|
"plymesh" => {
|
||||||
let filename = resolve_filename(parameters.get_one_string("filename", ""));
|
let filename = resolve_filename(parameters.get_one_string("filename", ""));
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,4 @@
|
||||||
use crate::core::light::LightBaseTrait;
|
|
||||||
use crate::spectra::cie_y;
|
|
||||||
use crate::utils::containers::InternCache;
|
use crate::utils::containers::InternCache;
|
||||||
use shared::Float;
|
|
||||||
use shared::core::light::LightBase;
|
|
||||||
use shared::core::spectrum::Spectrum;
|
|
||||||
|
|
||||||
pub static SPECTRUM_CACHE: Lazy<Mutex<HashMap<String, Spectrum>>> =
|
pub static SPECTRUM_CACHE: Lazy<Mutex<HashMap<String, Spectrum>>> =
|
||||||
Lazy::new(|| Mutex::new(HashMap::new()));
|
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||||
|
|
@ -11,11 +6,3 @@ pub static SPECTRUM_CACHE: Lazy<Mutex<HashMap<String, Spectrum>>> =
|
||||||
fn get_spectrum_cache() -> &'static InternCache<DenselySampledSpectrum> {
|
fn get_spectrum_cache() -> &'static InternCache<DenselySampledSpectrum> {
|
||||||
SPECTRUM_CACHE.get_or_init(InternCache::new)
|
SPECTRUM_CACHE.get_or_init(InternCache::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spectrum_to_photometric(s: Spectrum) -> Float {
|
|
||||||
let effective_spectrum = match s {
|
|
||||||
Spectrum::RGBIlluminant(ill) => &Spectrum::Dense(ill.illuminant),
|
|
||||||
_ => s,
|
|
||||||
};
|
|
||||||
effective_spectrum.inner_product(cie_y)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ use shared::core::spectrum::{Spectrum, SpectrumTrait};
|
||||||
use shared::core::texture::SpectrumType;
|
use shared::core::texture::SpectrumType;
|
||||||
use shared::lights::DiffuseAreaLight;
|
use shared::lights::DiffuseAreaLight;
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::RGBColorSpace;
|
||||||
use shared::utils::Transform;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::core::image::{Image, ImageIO};
|
use crate::core::image::{Image, ImageIO};
|
||||||
|
|
@ -18,33 +17,18 @@ use crate::core::texture::{
|
||||||
};
|
};
|
||||||
use crate::utils::{Arena, FileLoc, ParameterDictionary, Upload, resolve_filename};
|
use crate::utils::{Arena, FileLoc, ParameterDictionary, Upload, resolve_filename};
|
||||||
|
|
||||||
pub trait CreateDiffuseLight {
|
impl CreateLight for DiffuseAreaLight {
|
||||||
fn new(
|
fn new(
|
||||||
render_from_light: shared::utils::Transform,
|
render_from_light: shared::utils::Transform,
|
||||||
medium_interface: MediumInterface,
|
medium_interface: MediumInterface,
|
||||||
le: Spectrum,
|
le: Spectrum,
|
||||||
scale: Float,
|
scale: Float,
|
||||||
shape: Ptr<Shape>,
|
shape: Option<shared::utils::Ptr<Shape>>,
|
||||||
alpha: Ptr<FloatTexture>,
|
alpha: Option<shared::utils::Ptr<FloatTexture>>,
|
||||||
image: Ptr<Image>,
|
image: Option<shared::utils::Ptr<Image>>,
|
||||||
colorspace: Option<RGBColorSpace>,
|
image_color_space: Option<shared::utils::Ptr<RGBColorSpace>>,
|
||||||
two_sided: bool,
|
two_sided: Option<bool>,
|
||||||
fov: Float,
|
fov: Option<Float>,
|
||||||
) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CreateDiffuseLight for DiffuseAreaLight {
|
|
||||||
fn new(
|
|
||||||
render_from_light: shared::utils::Transform,
|
|
||||||
medium_interface: MediumInterface,
|
|
||||||
le: Spectrum,
|
|
||||||
scale: Float,
|
|
||||||
shape: Ptr<Shape>,
|
|
||||||
alpha: Ptr<FloatTexture>,
|
|
||||||
image: Ptr<Image>,
|
|
||||||
colorspace: Option<RGBColorSpace>,
|
|
||||||
two_sided: bool,
|
|
||||||
fov: Float,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let is_constant_zero = match &alpha {
|
let is_constant_zero = match &alpha {
|
||||||
FloatTexture::FloatConstant(tex) => tex.evaluate(&TextureEvalContext::default()) == 0.0,
|
FloatTexture::FloatConstant(tex) => tex.evaluate(&TextureEvalContext::default()) == 0.0,
|
||||||
|
|
@ -72,7 +56,7 @@ impl CreateDiffuseLight for DiffuseAreaLight {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
colorspace.is_some(),
|
image_color_space.is_some(),
|
||||||
"Image provided but ColorSpace is missing"
|
"Image provided but ColorSpace is missing"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -90,7 +74,7 @@ impl CreateDiffuseLight for DiffuseAreaLight {
|
||||||
base,
|
base,
|
||||||
area: shape.area(),
|
area: shape.area(),
|
||||||
image,
|
image,
|
||||||
colorspace,
|
image_color_space,
|
||||||
shape: Ptr::from(&*storage.shape),
|
shape: Ptr::from(&*storage.shape),
|
||||||
alpha: stored_alpha,
|
alpha: stored_alpha,
|
||||||
lemit,
|
lemit,
|
||||||
|
|
@ -98,9 +82,7 @@ impl CreateDiffuseLight for DiffuseAreaLight {
|
||||||
scale,
|
scale,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl CreateLight for DiffuseAreaLight {
|
|
||||||
fn create(
|
fn create(
|
||||||
arena: &mut Arena,
|
arena: &mut Arena,
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
|
|
@ -110,44 +92,40 @@ impl CreateLight for DiffuseAreaLight {
|
||||||
shape: &Shape,
|
shape: &Shape,
|
||||||
alpha_text: &FloatTexture,
|
alpha_text: &FloatTexture,
|
||||||
colorspace: Option<&RGBColorSpace>,
|
colorspace: Option<&RGBColorSpace>,
|
||||||
) -> Result<Light, Error> {
|
) -> Light {
|
||||||
let mut l = params.get_one_spectrum("l", None, SpectrumType::Illuminant);
|
let mut l = params.get_one_spectrum("l", None, SpectrumType::Illuminant);
|
||||||
let mut scale = params.get_one_float("scale", 1.);
|
let mut scale = params.get_one_float("scale", 1.);
|
||||||
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) = if !filename.is_empty() {
|
let mut image_color_space = colorspace.clone();
|
||||||
|
if !filename.is_empty() {
|
||||||
if l.is_some() {
|
if l.is_some() {
|
||||||
return Err(error!(loc, "both \"L\" and \"filename\" specified"));
|
panic!(
|
||||||
|
"{}: Both \"L\" and \"filename\" specified for DiffuseAreaLight.",
|
||||||
|
loc
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
let im = Image::read(filename, None);
|
||||||
let im = Image::read(&filename, None)?;
|
let image: Image = im.image;
|
||||||
|
if image.has_any_infinite_pixels() {
|
||||||
if im.image.has_any_infinite_pixels() {
|
panic!(
|
||||||
return Err(error!(loc, "{}: image has infinite pixel values", filename));
|
"{:?}: Image '{}' has NaN pixels. Not suitable for light.",
|
||||||
|
loc, filename
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if im.image.has_any_nan_pixels() {
|
let channel_desc = image.get_channel_desc(&["R", "G", "B"]).expect(&format!(
|
||||||
return Err(error!(loc, "{}: image has NaN pixel values", filename));
|
"{:?}: Image '{}' must have R, G, B channels",
|
||||||
}
|
loc, filename
|
||||||
|
));
|
||||||
let channel_desc = im
|
let selected_image = image.select_channels(channel_desc);
|
||||||
.image
|
image_color_space = im.metadata.colorspace;
|
||||||
.get_channel_desc(&["R", "G", "B"])
|
} else if l.is_none() {
|
||||||
.ok_or_else(|| error!(loc, "{}: image must have R, G, B channels", filename))?;
|
l = Some(colorpace.unwrap().Illuminant.clone());
|
||||||
|
}
|
||||||
let image = im.image.select_channels(&channel_desc);
|
|
||||||
let cs = im.metadata.get_color_space();
|
|
||||||
|
|
||||||
(Some(image), Some(cs))
|
|
||||||
} else {
|
|
||||||
if l.is_none() {
|
|
||||||
l = Some(colorspace.illuminant.clone());
|
|
||||||
}
|
|
||||||
(None, None)
|
|
||||||
};
|
|
||||||
|
|
||||||
let l_for_scale = l.as_ref().unwrap_or(&colorspace.illuminant);
|
let l_for_scale = l.as_ref().unwrap_or(&colorspace.illuminant);
|
||||||
scale /= spectrum_to_photometric(l_for_scale);
|
let lemit_data = lookup_spectrum(l_for_scale);
|
||||||
|
scale /= spectrum_to_photometric(lemit_data);
|
||||||
|
|
||||||
let phi_v = parameters.get_one_float("power", -1.0);
|
let phi_v = parameters.get_one_float("power", -1.0);
|
||||||
if phi_v > 0.0 {
|
if phi_v > 0.0 {
|
||||||
|
|
@ -187,16 +165,16 @@ impl CreateLight for DiffuseAreaLight {
|
||||||
let specific = DiffuseAreaLight::new(
|
let specific = DiffuseAreaLight::new(
|
||||||
render_from_light,
|
render_from_light,
|
||||||
medium.into(),
|
medium.into(),
|
||||||
l.as_ref(),
|
l,
|
||||||
scale,
|
scale,
|
||||||
shape.upload(arena),
|
shape.upload(arena),
|
||||||
alpha.upload(arena),
|
alpha.upload(arena),
|
||||||
image.upload(arena),
|
image.upload(arena),
|
||||||
image_color_space.upload(arena),
|
image_color_space.upload(arena),
|
||||||
true,
|
Some(true),
|
||||||
shape.area(),
|
shape.area(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(Light::DiffuseArea(specific))
|
Light::DiffuseArea(specific)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,21 @@ use shared::core::medium::{Medium, MediumInterface};
|
||||||
use shared::core::shape::Shape;
|
use shared::core::shape::Shape;
|
||||||
use shared::lights::DistantLight;
|
use shared::lights::DistantLight;
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::RGBColorSpace;
|
||||||
use shared::utils::{Ptr, Transform};
|
use shared::utils::Transform;
|
||||||
|
|
||||||
pub trait CreateDistantLight {
|
impl CreateLight for DistantLight {
|
||||||
fn new(render_from_light: Transform, le: Spectrum, scale: Float) -> Self;
|
fn new(
|
||||||
}
|
render_from_light: Transform,
|
||||||
|
medium_interface: shared::core::medium::MediumInterface,
|
||||||
impl CreateDistantLight for DistantLight {
|
le: shared::core::spectrum::Spectrum,
|
||||||
fn new(render_from_light: Transform, le: Spectrum, scale: Float) -> Self {
|
scale: shared::Float,
|
||||||
|
shape: Option<shared::utils::Ptr<Shape>>,
|
||||||
|
alpha: Option<shared::utils::Ptr<FloatTexture>>,
|
||||||
|
image: Option<shared::utils::Ptr<Image>>,
|
||||||
|
image_color_space: Option<shared::utils::Ptr<RGBColorSpace>>,
|
||||||
|
two_sided: Option<bool>,
|
||||||
|
fov: Option<shared::Float>,
|
||||||
|
) -> Self {
|
||||||
let base = LightBase::new(
|
let base = LightBase::new(
|
||||||
LightType::DeltaDirection,
|
LightType::DeltaDirection,
|
||||||
render_from_light,
|
render_from_light,
|
||||||
|
|
@ -30,9 +37,7 @@ impl CreateDistantLight for DistantLight {
|
||||||
scene_radius: 0.,
|
scene_radius: 0.,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl CreateLight for DistantLight {
|
|
||||||
fn create(
|
fn create(
|
||||||
arena: &mut Arena,
|
arena: &mut Arena,
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
|
|
@ -42,7 +47,7 @@ impl CreateLight for DistantLight {
|
||||||
shape: &Shape,
|
shape: &Shape,
|
||||||
alpha_text: &FloatTexture,
|
alpha_text: &FloatTexture,
|
||||||
colorspace: Option<&RGBColorSpace>,
|
colorspace: Option<&RGBColorSpace>,
|
||||||
) -> Result<Light, Error> {
|
) -> Light {
|
||||||
let l = parameters
|
let l = parameters
|
||||||
.get_one_spectrum(
|
.get_one_spectrum(
|
||||||
"L",
|
"L",
|
||||||
|
|
@ -50,6 +55,7 @@ impl CreateLight for DistantLight {
|
||||||
SpectrumType::Illuminant,
|
SpectrumType::Illuminant,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let lemit = lookup_spectrum(l);
|
||||||
let mut scale = parameters.get_one_float("scale", 1);
|
let mut scale = parameters.get_one_float("scale", 1);
|
||||||
|
|
||||||
let from = parameters.get_one_point3f("from", Point3f::new(0., 0., 0.));
|
let from = parameters.get_one_point3f("from", Point3f::new(0., 0., 0.));
|
||||||
|
|
@ -84,9 +90,19 @@ impl CreateLight for DistantLight {
|
||||||
if e_v > 0. {
|
if e_v > 0. {
|
||||||
sc *= e_v;
|
sc *= e_v;
|
||||||
}
|
}
|
||||||
|
let specific = DistantLight::new(
|
||||||
|
final_render,
|
||||||
|
medium.into(),
|
||||||
|
le,
|
||||||
|
scale,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
let specific = DistantLight::new(final_render, l, scale);
|
Light::Distant(specific)
|
||||||
|
|
||||||
Ok(Light::Distant(specific))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,8 @@ use crate::core::image::{Image, ImageIO, ImageMetadata};
|
||||||
use crate::core::light::{CreateLight, lookup_spectrum};
|
use crate::core::light::{CreateLight, 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::lights::distant::CreateDistantLight;
|
|
||||||
use crate::utils::sampling::PiecewiseConstant2D;
|
use crate::utils::sampling::PiecewiseConstant2D;
|
||||||
use crate::utils::{Arena, FileLoc, ParameterDictionary, resolve_filename};
|
use crate::utils::{Arena, FileLoc, ParameterDictionary, resolve_filename};
|
||||||
use shared::Float;
|
|
||||||
use shared::core::image::ImageBase;
|
use shared::core::image::ImageBase;
|
||||||
use shared::core::light::{Light, LightBase, LightType};
|
use shared::core::light::{Light, LightBase, LightType};
|
||||||
use shared::core::medium::{Medium, MediumInterface};
|
use shared::core::medium::{Medium, MediumInterface};
|
||||||
|
|
@ -13,26 +11,21 @@ use shared::core::spectrum::Spectrum;
|
||||||
use shared::core::texture::SpectrumType;
|
use shared::core::texture::SpectrumType;
|
||||||
use shared::lights::GoniometricLight;
|
use shared::lights::GoniometricLight;
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::RGBColorSpace;
|
||||||
|
use shared::utils::Transform;
|
||||||
use shared::utils::containers::Array2D;
|
use shared::utils::containers::Array2D;
|
||||||
use shared::utils::{Ptr, Transform};
|
|
||||||
|
|
||||||
pub trait CreateGoniometricLight {
|
impl CreateLight for GoniometricLight {
|
||||||
fn new(
|
fn new(
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
medium_interface: MediumInterface,
|
medium_interface: MediumInterface,
|
||||||
le: Spectrum,
|
le: Spectrum,
|
||||||
scale: Float,
|
scale: shared::Float,
|
||||||
image: Ptr<Image>,
|
shape: Option<shared::utils::Ptr<Shape>>,
|
||||||
) -> Self;
|
alpha: Option<shared::utils::Ptr<FloatTexture>>,
|
||||||
}
|
image: Option<shared::utils::Ptr<Image>>,
|
||||||
|
image_color_space: Option<shared::utils::Ptr<RGBColorSpace>>,
|
||||||
impl CreateGoniometricLight for GoniometricLight {
|
two_sided: Option<bool>,
|
||||||
fn new(
|
fov: Option<shared::Float>,
|
||||||
render_from_light: Transform,
|
|
||||||
medium_interface: MediumInterface,
|
|
||||||
le: Spectrum,
|
|
||||||
scale: Float,
|
|
||||||
image: Ptr<Image>,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let base = LightBase::new(
|
let base = LightBase::new(
|
||||||
LightType::DeltaPosition,
|
LightType::DeltaPosition,
|
||||||
|
|
@ -51,9 +44,7 @@ impl CreateGoniometricLight for GoniometricLight {
|
||||||
distrib,
|
distrib,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl CreateLight for GoniometricLight {
|
|
||||||
fn create(
|
fn create(
|
||||||
arena: &mut Arena,
|
arena: &mut Arena,
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
|
|
@ -63,50 +54,96 @@ impl CreateLight for GoniometricLight {
|
||||||
shape: &Shape,
|
shape: &Shape,
|
||||||
alpha_text: &FloatTexture,
|
alpha_text: &FloatTexture,
|
||||||
colorspace: Option<&RGBColorSpace>,
|
colorspace: Option<&RGBColorSpace>,
|
||||||
) -> Result<Light, Error> {
|
) -> Light {
|
||||||
let i = params.get_one_spectrum(
|
let i = params.get_one_spectrum(
|
||||||
"I",
|
"I",
|
||||||
colorspace.unwrap().illuminant,
|
colorspace.unwrap().illuminant,
|
||||||
SpectrumType::Illuminant,
|
SpectrumType::Illuminant,
|
||||||
);
|
);
|
||||||
|
let lemit_data = lookup_spectrum(&i_spectrum_def);
|
||||||
let sc = params.get_one_float("scale", 1.);
|
let sc = params.get_one_float("scale", 1.);
|
||||||
let filename = resolve_filename(params.get_one_string("filename", ""));
|
let filename = resolve_filename(params.get_one_string("filename", ""));
|
||||||
let mut image: Option<Image> = None;
|
let mut image: Option<Image> = None;
|
||||||
let image = if filename.is_empty() {
|
|
||||||
None
|
if filename.is_empty() {
|
||||||
|
println!(
|
||||||
|
"{}: Both \"L\" and \"filename\" specified for DiffuseAreaLight.",
|
||||||
|
loc
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
let im = Image::read(&filename, None)
|
let im = Image::read(filename, None).expect("Could not load image");
|
||||||
.map_err(|e| error!(loc, "could not load image '{}': {}", filename, e))?;
|
let loaded_img: Image = im.image;
|
||||||
|
let res = loaded_img.resolution();
|
||||||
let loaded = im.image;
|
let metadata: ImageMetadata = im.metadata;
|
||||||
let res = loaded.resolution();
|
if loaded_img.has_any_infinite_pixels() {
|
||||||
|
panic!(
|
||||||
if loaded.has_any_infinite_pixels() {
|
"{:?}: Image '{}' has NaN pixels. Not suitable for light.",
|
||||||
return Err(error!(
|
loc, filename
|
||||||
loc,
|
);
|
||||||
"image '{}' has infinite pixels, not suitable for light", filename
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
if res.x() != res.y() {
|
||||||
if res.x != res.y {
|
panic!(
|
||||||
return Err(error!(
|
"{:?}: Image resolution ({}, {}) is non square. Unlikely that this is an equal area map.",
|
||||||
loc,
|
loc,
|
||||||
"image resolution ({}, {}) is non-square; unlikely to be an equal-area map",
|
res.x(),
|
||||||
res.x,
|
res.y(),
|
||||||
res.y
|
y()
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
|
let rgb_desc = loaded_img.get_channel_desc(&["R", "G", "B"]);
|
||||||
|
let y_desc = loaded_img.get_channel_desc(&["Y"]);
|
||||||
|
if let Ok(rgb) = rgb_desc {
|
||||||
|
if y_desc.is_ok() {
|
||||||
|
panic!(
|
||||||
|
"{:?}: Image '{}' has both RGB and Y channels. Ambiguous.",
|
||||||
|
loc, filename
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Some(convert_to_luminance_image(&loaded, &filename, loc)?)
|
let mut y_pixels = Vec::with_capacity((res.x * res.y) as usize);
|
||||||
};
|
|
||||||
|
for y in 0..res.y {
|
||||||
|
for x in 0..res.x {
|
||||||
|
let r = loaded_img.get_channel(Point2i::new(x, y), 0);
|
||||||
|
let g = loaded_img.get_channel(Point2i::new(x, y), 1);
|
||||||
|
let b = loaded_img.get_channel(Point2i::new(x, y), 2);
|
||||||
|
|
||||||
|
y_pixels.push((r + g + b) / 3.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loaded_img = Some(Image::new(
|
||||||
|
PixelFormat::F32,
|
||||||
|
res,
|
||||||
|
&["Y"],
|
||||||
|
ColorEncoding::Linear,
|
||||||
|
));
|
||||||
|
} else if y_desc.is_ok() {
|
||||||
|
image = Some(loaded_img);
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"{:?}: Image '{}' has neither RGB nor Y channels.",
|
||||||
|
loc, filename
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
scale /= spectrum_to_photometric(&lemit_data);
|
scale /= spectrum_to_photometric(&lemit_data);
|
||||||
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 {
|
||||||
if let Some(ref img) = image {
|
if let Some(ref img) = image {
|
||||||
let k_e = compute_emissive_power(image);
|
let mut sum_y = 0.0;
|
||||||
scale *= phi_v / phi_e;
|
let res = img.resolution();
|
||||||
|
|
||||||
|
for y in 0..res.y {
|
||||||
|
for x in 0..res.x {
|
||||||
|
sum_y += img.get_channel(Point2i::new(x, y), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let k_e = 4.0 * PI * sum_y / (res.x * res.y) as Float;
|
||||||
|
scale *= phi_v / k_e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,71 +153,22 @@ impl CreateLight for GoniometricLight {
|
||||||
let t = Transform::from_flat(swap_yz);
|
let t = Transform::from_flat(swap_yz);
|
||||||
let final_render_from_light = render_from_light * t;
|
let final_render_from_light = render_from_light * t;
|
||||||
|
|
||||||
let specific =
|
let d: Array2D<Float> = image.get_sampling_distribution();
|
||||||
GoniometricLight::new(final_render_from_light, medium.into(), le, scale, image);
|
distrib = PiecewiseConstant2D::new_with_data(d);
|
||||||
|
|
||||||
Ok(Light::Goniometric(specific))
|
let specific = GoniometricLight::new(
|
||||||
|
render_from_light,
|
||||||
|
medium_interface,
|
||||||
|
le,
|
||||||
|
scale,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(image),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
Light::Goniometric(specific)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_to_luminance_image(
|
|
||||||
image: &Image,
|
|
||||||
filename: &str,
|
|
||||||
loc: &FileLoc,
|
|
||||||
) -> Result<Image, Error> {
|
|
||||||
let res = image.resolution();
|
|
||||||
let rgb_desc = image.get_channel_desc(&["R", "G", "B"]);
|
|
||||||
let y_desc = image.get_channel_desc(&["Y"]);
|
|
||||||
|
|
||||||
match (rgb_desc, y_desc) {
|
|
||||||
(Ok(_), Ok(_)) => Err(error!(
|
|
||||||
loc,
|
|
||||||
"image '{}' has both RGB and Y channels; ambiguous", filename
|
|
||||||
)),
|
|
||||||
|
|
||||||
(Ok(_), Err(_)) => {
|
|
||||||
// Convert RGB to Y (luminance)
|
|
||||||
let mut y_pixels = Vec::with_capacity((res.x * res.y) as usize);
|
|
||||||
|
|
||||||
for y in 0..res.y {
|
|
||||||
for x in 0..res.x {
|
|
||||||
let r = image.get_channel(Point2i::new(x, y), 0);
|
|
||||||
let g = image.get_channel(Point2i::new(x, y), 1);
|
|
||||||
let b = image.get_channel(Point2i::new(x, y), 2);
|
|
||||||
y_pixels.push((r + g + b) / 3.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Image::from_pixels(
|
|
||||||
PixelFormat::F32,
|
|
||||||
res,
|
|
||||||
&["Y"],
|
|
||||||
ColorEncoding::Linear,
|
|
||||||
&y_pixels,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
(Err(_), Ok(_)) => {
|
|
||||||
// Already has Y channel, use as-is
|
|
||||||
Ok(image.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
(Err(_), Err(_)) => Err(error!(
|
|
||||||
loc,
|
|
||||||
"image '{}' has neither RGB nor Y channels", filename
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compute_emissive_power(image: &Image) -> Float {
|
|
||||||
let res = image.resolution();
|
|
||||||
let mut sum_y = 0.0;
|
|
||||||
|
|
||||||
for y in 0..res.y {
|
|
||||||
for x in 0..res.x {
|
|
||||||
sum_y += image.get_channel(Point2i::new(x, y), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
4.0 * PI * sum_y / (res.x * res.y) as Float
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -2,32 +2,26 @@ use shared::Float;
|
||||||
use shared::core::geometry::{Bounds3f, Point2f};
|
use shared::core::geometry::{Bounds3f, Point2f};
|
||||||
use shared::core::light::{CreateLight, Light, LightBase, LightType};
|
use shared::core::light::{CreateLight, Light, LightBase, LightType};
|
||||||
use shared::core::medium::MediumInterface;
|
use shared::core::medium::MediumInterface;
|
||||||
use shared::core::spectrum::Spectrum;
|
use shared::lights::{InfiniteImageLight, InfinitePortalLight, InfiniteUniformLight};
|
||||||
use shared::lights::{ImageInfiniteLight, PortalInfiniteLight, UniformInfiniteLight};
|
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::RGBColorSpace;
|
||||||
|
use shared::utils::Transform;
|
||||||
use shared::utils::sampling::DevicePiecewiseConstant2D;
|
use shared::utils::sampling::DevicePiecewiseConstant2D;
|
||||||
use shared::utils::{Ptr, Transform};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::core::light::{LightBaseTrait, lookup_spectrum};
|
use crate::core::light::{LightBaseTrait, lookup_spectrum};
|
||||||
|
|
||||||
pub trait CreateImageInfiniteLight {
|
impl CreateLight for InfiniteImageLight {
|
||||||
fn new(
|
fn new(
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
medium_interface: MediumInterface,
|
medium_interface: MediumInterface,
|
||||||
|
le: shared::core::spectrum::Spectrum,
|
||||||
scale: Float,
|
scale: Float,
|
||||||
image: Ptr<Image>,
|
shape: Option<shared::utils::Ptr<Shape>>,
|
||||||
image_color_space: Ptr<RGBColorSpace>,
|
alpha: Option<shared::utils::Ptr<FloatTexture>>,
|
||||||
) -> Self;
|
image: Option<shared::utils::Ptr<Image>>,
|
||||||
}
|
image_color_space: Option<shared::utils::Ptr<RGBColorSpace>>,
|
||||||
|
two_sided: Option<bool>,
|
||||||
impl CreateImageInfiniteLight for ImageInfiniteLight {
|
fov: Option<Float>,
|
||||||
fn new(
|
|
||||||
render_from_light: Transform,
|
|
||||||
medium_interface: MediumInterface,
|
|
||||||
scale: Float,
|
|
||||||
image: Ptr<Image>,
|
|
||||||
image_color_space: Ptr<RGBColorSpace>,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let base = LightBase::new(
|
let base = LightBase::new(
|
||||||
LightType::Infinite,
|
LightType::Infinite,
|
||||||
|
|
@ -71,7 +65,7 @@ impl CreateImageInfiniteLight for ImageInfiniteLight {
|
||||||
|
|
||||||
let compensated_distrib = DevicePiecewiseConstant2D::new_with_bounds(&d, domain);
|
let compensated_distrib = DevicePiecewiseConstant2D::new_with_bounds(&d, domain);
|
||||||
|
|
||||||
ImageInfiniteLight {
|
InfiniteImageLight {
|
||||||
base,
|
base,
|
||||||
image: &image,
|
image: &image,
|
||||||
image_color_space: &storage.image_color_space,
|
image_color_space: &storage.image_color_space,
|
||||||
|
|
@ -82,6 +76,19 @@ impl CreateImageInfiniteLight for ImageInfiniteLight {
|
||||||
compensated_distrib: &compensated_distrib,
|
compensated_distrib: &compensated_distrib,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create(
|
||||||
|
arena: &mut crate::utils::Arena,
|
||||||
|
render_from_light: Transform,
|
||||||
|
medium: Medium,
|
||||||
|
parameters: &crate::utils::ParameterDictionary,
|
||||||
|
loc: &FileLoc,
|
||||||
|
shape: &Shape,
|
||||||
|
alpha_tex: &FloatTexture,
|
||||||
|
colorspace: Option<&RGBColorSpace>,
|
||||||
|
) -> shared::core::light::Light {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -92,29 +99,24 @@ struct InfinitePortalLightStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PortalInfiniteLightHost {
|
pub struct InfinitePortalLightHost {
|
||||||
pub view: PortalInfiniteLight,
|
pub view: InfinitePortalLight,
|
||||||
pub filename: String,
|
pub filename: String,
|
||||||
_storage: Arc<InfinitePortalLightStorage>,
|
_storage: Arc<InfinitePortalLightStorage>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait CreatePortalInfiniteLight {
|
impl CreateLight for InfinitePortalLightHost {
|
||||||
fn new(
|
fn new(
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
|
medium_interface: MediumInterface,
|
||||||
|
le: shared::core::spectrum::Spectrum,
|
||||||
scale: Float,
|
scale: Float,
|
||||||
image: Ptr<Image>,
|
shape: Option<shared::utils::Ptr<Shape>>,
|
||||||
image_color_space: Ptr<RGBColorSpace>,
|
alpha: Option<shared::utils::Ptr<FloatTexture>>,
|
||||||
points: Ptr<Point3f>,
|
image: Option<shared::utils::Ptr<Image>>,
|
||||||
) -> Self;
|
image_color_space: Option<shared::utils::Ptr<RGBColorSpace>>,
|
||||||
}
|
two_sided: Option<bool>,
|
||||||
|
fov: Option<Float>,
|
||||||
impl CreatePortalInfiniteLight for PortalInfiniteLight {
|
|
||||||
fn new(
|
|
||||||
render_from_light: Transform,
|
|
||||||
scale: Float,
|
|
||||||
image: Ptr<Image>,
|
|
||||||
image_color_space: Ptr<RGBColorSpace>,
|
|
||||||
points: Ptr<Point3f>,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let base = LightBase::new(
|
let base = LightBase::new(
|
||||||
LightType::Infinite,
|
LightType::Infinite,
|
||||||
|
|
@ -122,7 +124,7 @@ impl CreatePortalInfiniteLight for PortalInfiniteLight {
|
||||||
&MediumInterface::default(),
|
&MediumInterface::default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let desc = image
|
let desc = equal_area_image
|
||||||
.get_channel_desc(&["R", "G", "B"])
|
.get_channel_desc(&["R", "G", "B"])
|
||||||
.unwrap_or_else(|_| {
|
.unwrap_or_else(|_| {
|
||||||
panic!(
|
panic!(
|
||||||
|
|
@ -223,10 +225,16 @@ impl CreatePortalInfiniteLight for PortalInfiniteLight {
|
||||||
|
|
||||||
let distribution = WindowedPiecewiseConstant2D::new(d);
|
let distribution = WindowedPiecewiseConstant2D::new(d);
|
||||||
|
|
||||||
PortalInfiniteLight {
|
let storage = Arc::new(InfinitePortalLightStorage {
|
||||||
|
image,
|
||||||
|
distribution,
|
||||||
|
image_color_space,
|
||||||
|
});
|
||||||
|
|
||||||
|
InfinitePortalLight {
|
||||||
base,
|
base,
|
||||||
image,
|
image,
|
||||||
image_color_space: &image_color_space,
|
image_color_space: &storage.image_color_space,
|
||||||
scale,
|
scale,
|
||||||
scene_center: Point3f::default(),
|
scene_center: Point3f::default(),
|
||||||
scene_radius: 0.,
|
scene_radius: 0.,
|
||||||
|
|
@ -235,14 +243,36 @@ impl CreatePortalInfiniteLight for PortalInfiniteLight {
|
||||||
distribution,
|
distribution,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create(
|
||||||
|
arena: &mut crate::utils::Arena,
|
||||||
|
render_from_light: Transform,
|
||||||
|
medium: Medium,
|
||||||
|
parameters: &crate::utils::ParameterDictionary,
|
||||||
|
loc: &FileLoc,
|
||||||
|
shape: &Shape,
|
||||||
|
alpha_tex: &FloatTexture,
|
||||||
|
colorspace: Option<&RGBColorSpace>,
|
||||||
|
) -> Light {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait CreateUniformInfiniteLight {
|
impl CreateLight for InfiniteUniformLight {
|
||||||
fn new(render_from_light: Transform, le: Spectrum, scale: Float) -> Self;
|
fn new(
|
||||||
}
|
render_from_light: Transform,
|
||||||
|
medium_interface: MediumInterface,
|
||||||
impl CreateUniformInfiniteLight for UniformInfiniteLight {
|
le: shared::core::spectrum::Spectrum,
|
||||||
fn new(render_from_light: Transform, le: Spectrum, scale: Float) -> Self {
|
scale: Float,
|
||||||
|
shape: Option<shared::utils::Ptr<Shape>>,
|
||||||
|
alpha: Option<shared::utils::Ptr<FloatTexture>>,
|
||||||
|
image: Option<shared::utils::Ptr<Image>>,
|
||||||
|
image_color_space: Option<shared::utils::Ptr<RGBColorSpace>>,
|
||||||
|
two_sided: Option<bool>,
|
||||||
|
fov: Option<Float>,
|
||||||
|
cos_fallof_start: Option<Float>,
|
||||||
|
total_width: Option<Float>,
|
||||||
|
) -> Self {
|
||||||
let base = LightBase::new(
|
let base = LightBase::new(
|
||||||
LightType::Infinite,
|
LightType::Infinite,
|
||||||
&render_from_light,
|
&render_from_light,
|
||||||
|
|
@ -257,135 +287,17 @@ impl CreateUniformInfiniteLight for UniformInfiniteLight {
|
||||||
scene_radius: 0.,
|
scene_radius: 0.,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create(
|
fn create(
|
||||||
arena: &mut Arena,
|
arena: &mut crate::utils::Arena,
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
medium: MediumInterface,
|
medium: Medium,
|
||||||
camera_transform: CameraTransform,
|
parameters: &crate::utils::ParameterDictionary,
|
||||||
parameters: &ParameterDictionary,
|
loc: &FileLoc,
|
||||||
colorspace: &RGBColorSpace,
|
shape: &Shape,
|
||||||
loc: &FileLoc,
|
alpha_tex: &FloatTexture,
|
||||||
) -> Result<Light, Error> {
|
colorspace: Option<&RGBColorSpace>,
|
||||||
let l = parameters.get_spectrum_array("L", SpectrumType::Illuminant);
|
) -> Light {
|
||||||
let mut scale = parameters.get_one_float("scale", 1.0);
|
todo!()
|
||||||
let portal = parameters.get_point3f_array("portal");
|
|
||||||
let filename = resolve_filename(parameters.get_one_string("filename", ""));
|
|
||||||
let e_v = parameters.get_one_float("illuminance", -1.0);
|
|
||||||
|
|
||||||
let has_spectrum = !l.is_empty();
|
|
||||||
let has_file = !filename.is_empty();
|
|
||||||
let has_portal = !portal.is_empty();
|
|
||||||
|
|
||||||
if has_spectrum && has_file {
|
|
||||||
return Err(error!(loc, "cannot specify both \"L\" and \"filename\""));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !has_file && !has_portal {
|
|
||||||
let spectrum = if has_spectrum {
|
|
||||||
scale /= spectrum_to_photometric(&l[0]);
|
|
||||||
&l[0]
|
|
||||||
} else {
|
|
||||||
&colorspace.illuminant
|
|
||||||
};
|
|
||||||
|
|
||||||
if e_v > 0.0 {
|
|
||||||
scale *= e_v / PI;
|
|
||||||
}
|
|
||||||
|
|
||||||
let light = UniformInfiniteLight::new(render_from_light, lookup_spectrum(spectrum), scale);
|
|
||||||
return Ok(Light::InfiniteUniform(light));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Image based
|
|
||||||
|
|
||||||
let (image, image_cs) = load_image_or_constant(&filename, &l, colorspace, loc)?;
|
|
||||||
|
|
||||||
scale /= spectrum_to_photometric(&image_cs.illuminant);
|
|
||||||
|
|
||||||
if e_v > 0.0 {
|
|
||||||
let k_e = compute_hemisphere_illuminance(&image, &image_cs);
|
|
||||||
scale *= e_v / k_e;
|
|
||||||
}
|
|
||||||
|
|
||||||
let image_ptr = image.upload(arena);
|
|
||||||
let cs_ptr = image_cs.upload(arena);
|
|
||||||
|
|
||||||
if has_portal {
|
|
||||||
let portal_render: Vec<Point3f> = portal
|
|
||||||
.iter()
|
|
||||||
.map(|p| camera_transform.render_from_world(*p))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let light = PortalInfiniteLight::new(
|
|
||||||
render_from_light,
|
|
||||||
scale,
|
|
||||||
image_ptr,
|
|
||||||
cs_ptr,
|
|
||||||
arena.alloc_slice(&portal_render),
|
|
||||||
);
|
|
||||||
Ok(Light::InfinitePortal(light))
|
|
||||||
} else {
|
|
||||||
let light = ImageInfiniteLight::new(render_from_light, medium, scale, image_ptr, cs_ptr);
|
|
||||||
Ok(Light::InfiniteImage(light))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_image_or_constant(
|
|
||||||
filename: &str,
|
|
||||||
l: &[Spectrum],
|
|
||||||
colorspace: &RGBColorSpace,
|
|
||||||
loc: &FileLoc,
|
|
||||||
) -> Result<(Image, RGBColorSpace), Error> {
|
|
||||||
if filename.is_empty() {
|
|
||||||
let rgb = spectrum_to_rgb(&l[0], colorspace);
|
|
||||||
let image = Image::new_constant(Point2i::new(1, 1), &["R", "G", "B"], &rgb);
|
|
||||||
Ok((image, colorspace.clone()))
|
|
||||||
} else {
|
|
||||||
let im = Image::read(filename, None)
|
|
||||||
.map_err(|e| error!(loc, "failed to load '{}': {}", filename, e))?;
|
|
||||||
|
|
||||||
if im.image.has_any_infinite_pixels() || im.image.has_any_nan_pixels() {
|
|
||||||
return Err(error!(loc, "image '{}' has invalid pixels", filename));
|
|
||||||
}
|
|
||||||
|
|
||||||
im.image
|
|
||||||
.get_channel_desc(&["R", "G", "B"])
|
|
||||||
.map_err(|_| error!(loc, "image '{}' must have R, G, B channels", filename))?;
|
|
||||||
|
|
||||||
let cs = im
|
|
||||||
.metadata
|
|
||||||
.color_space
|
|
||||||
.unwrap_or_else(|| colorspace.clone());
|
|
||||||
let selected = im.image.select_channels(&["R", "G", "B"])?;
|
|
||||||
|
|
||||||
Ok((selected, cs))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compute_hemisphere_illuminance(image: &Image, cs: &RGBColorSpace) -> Float {
|
|
||||||
let lum = cs.luminance_vector();
|
|
||||||
let res = image.resolution();
|
|
||||||
let mut sum = 0.0;
|
|
||||||
|
|
||||||
for y in 0..res.y {
|
|
||||||
let v = (y as Float + 0.5) / res.y as Float;
|
|
||||||
for x in 0..res.x {
|
|
||||||
let u = (x as Float + 0.5) / res.x as Float;
|
|
||||||
let w = equal_area_square_to_sphere(Point2f::new(u, v));
|
|
||||||
|
|
||||||
if w.z <= 0.0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let r = image.get_channel(Point2i::new(x, y), 0);
|
|
||||||
let g = image.get_channel(Point2i::new(x, y), 1);
|
|
||||||
let b = image.get_channel(Point2i::new(x, y), 2);
|
|
||||||
|
|
||||||
sum += (r * lum[0] + g * lum[1] + b * lum[2]) * cos_theta(&w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sum * 2.0 * PI / (res.x * res.y) as Float
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,3 @@ pub mod point;
|
||||||
pub mod projection;
|
pub mod projection;
|
||||||
pub mod sampler;
|
pub mod sampler;
|
||||||
pub mod spot;
|
pub mod spot;
|
||||||
|
|
||||||
pub use diffuse::CreateDiffuseLight;
|
|
||||||
pub use distant::CreateDistantLight;
|
|
||||||
pub use goniometric::CreateGoniometricLight;
|
|
||||||
pub use infinite::{
|
|
||||||
CreateImageInfiniteLight, CreatePortalInfiniteLight, CreateUniformInfiniteLight,
|
|
||||||
};
|
|
||||||
pub use point::CreatePointLight;
|
|
||||||
pub use projection::CreateProjectionLight;
|
|
||||||
pub use spot::CreateSpotLight;
|
|
||||||
|
|
|
||||||
|
|
@ -2,31 +2,27 @@ use crate::core::light::{CreateLight, LightBaseTrait, 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::{Arena, FileLoc, ParameterDictionary};
|
use crate::utils::{Arena, FileLoc, ParameterDictionary};
|
||||||
|
use shared::PI;
|
||||||
use shared::core::geometry::VectorLike;
|
use shared::core::geometry::VectorLike;
|
||||||
use shared::core::light::{Light, LightBase, LightType};
|
use shared::core::light::{Light, LightBase, LightType};
|
||||||
use shared::core::medium::{Medium, MediumInterface};
|
use shared::core::medium::Medium;
|
||||||
use shared::core::shape::Shape;
|
use shared::core::shape::Shape;
|
||||||
use shared::core::spectrum::Spectrum;
|
|
||||||
use shared::lights::PointLight;
|
use shared::lights::PointLight;
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::RGBColorSpace;
|
||||||
use shared::utils::Transform;
|
use shared::utils::Transform;
|
||||||
use shared::{Float, PI};
|
|
||||||
|
|
||||||
pub trait CreatePointLight {
|
impl CreateLight for PointLight {
|
||||||
fn new(
|
fn new(
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
medium_interface: MediumInterface,
|
medium_interface: shared::core::medium::MediumInterface,
|
||||||
le: Spectrum,
|
le: shared::core::spectrum::Spectrum,
|
||||||
scale: Float,
|
scale: shared::Float,
|
||||||
) -> Self;
|
_shape: Option<shared::utils::Ptr<Shape>>,
|
||||||
}
|
_alpha: Option<shared::utils::Ptr<FloatTexture>>,
|
||||||
|
_image: Option<shared::utils::Ptr<Image>>,
|
||||||
impl CreatePointLight for PointLight {
|
_image_color_space: Option<shared::utils::Ptr<RGBColorSpace>>,
|
||||||
fn new(
|
_two_sided: Option<bool>,
|
||||||
render_from_light: Transform,
|
_fov: Option<shared::Float>,
|
||||||
medium_interface: MediumInterface,
|
|
||||||
le: Spectrum,
|
|
||||||
scale: Float,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let base = LightBase::new(
|
let base = LightBase::new(
|
||||||
LightType::DeltaPosition,
|
LightType::DeltaPosition,
|
||||||
|
|
@ -37,9 +33,7 @@ impl CreatePointLight for PointLight {
|
||||||
|
|
||||||
Self { base, scale, i }
|
Self { base, scale, i }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl CreateLight for PointLight {
|
|
||||||
fn create(
|
fn create(
|
||||||
arena: &mut Arena,
|
arena: &mut Arena,
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
|
|
@ -49,7 +43,7 @@ impl CreateLight for PointLight {
|
||||||
shape: &Shape,
|
shape: &Shape,
|
||||||
alpha_text: &FloatTexture,
|
alpha_text: &FloatTexture,
|
||||||
colorspace: Option<&RGBColorSpace>,
|
colorspace: Option<&RGBColorSpace>,
|
||||||
) -> Result<Light, Error> {
|
) -> Light {
|
||||||
let l = parameters
|
let l = parameters
|
||||||
.get_one_spectrum(
|
.get_one_spectrum(
|
||||||
"L",
|
"L",
|
||||||
|
|
@ -57,6 +51,7 @@ impl CreateLight for PointLight {
|
||||||
SpectrumType::Illuminant,
|
SpectrumType::Illuminant,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let lemit = lookup_spectrum(l);
|
||||||
let mut scale = parameters.get_one_float("scale", 1);
|
let mut scale = parameters.get_one_float("scale", 1);
|
||||||
scale /= spectrum_to_photometric(l.unwrap());
|
scale /= spectrum_to_photometric(l.unwrap());
|
||||||
let phi_v = parameters.get_one_float("power", 1.);
|
let phi_v = parameters.get_one_float("power", 1.);
|
||||||
|
|
@ -68,7 +63,12 @@ impl CreateLight for PointLight {
|
||||||
let from = parameters.get_one_point3f("from", Point3f::zero());
|
let from = parameters.get_one_point3f("from", Point3f::zero());
|
||||||
let tf = Transform::translate(from.into());
|
let tf = Transform::translate(from.into());
|
||||||
let final_render = render_from_light * tf;
|
let final_render = render_from_light * tf;
|
||||||
let specific = PointLight::new(final_render, medium.into(), l, scale);
|
let base = LightBase::new(LightType::DeltaPosition, render_from_light, medium.into());
|
||||||
Ok(Light::Point(specific))
|
let specific = PointLight {
|
||||||
|
base,
|
||||||
|
scale,
|
||||||
|
i: arena.alloc(lemit),
|
||||||
|
};
|
||||||
|
Light::Point(specific)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,7 @@ use crate::core::image::{Image, ImageIO, ImageMetadata};
|
||||||
use crate::core::light::CreateLight;
|
use crate::core::light::CreateLight;
|
||||||
use crate::core::spectrum::spectrum_to_photometric;
|
use crate::core::spectrum::spectrum_to_photometric;
|
||||||
use crate::spectra::colorspace::new;
|
use crate::spectra::colorspace::new;
|
||||||
use crate::utils::{Arena, ParameterDictionary, Ptr, Upload, resolve_filename};
|
use crate::utils::{Ptr, Upload, resolve_filename};
|
||||||
use shared::Float;
|
|
||||||
use shared::core::geometry::{Bounds2f, VectorLike};
|
use shared::core::geometry::{Bounds2f, VectorLike};
|
||||||
use shared::core::image::ImageAccess;
|
use shared::core::image::ImageAccess;
|
||||||
use shared::core::light::{Light, LightBase};
|
use shared::core::light::{Light, LightBase};
|
||||||
|
|
@ -14,27 +13,18 @@ use shared::spectra::RGBColorSpace;
|
||||||
use shared::utils::math::{radians, square};
|
use shared::utils::math::{radians, square};
|
||||||
use shared::utils::{Ptr, Transform};
|
use shared::utils::{Ptr, Transform};
|
||||||
|
|
||||||
pub trait CreateProjectionLight {
|
impl CreateLight for ProjectionLight {
|
||||||
fn new(
|
fn new(
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
medium_interface: MediumInterface,
|
medium_interface: MediumInterface,
|
||||||
le: Spectrum,
|
le: Spectrum,
|
||||||
scale: Float,
|
scale: shared::Float,
|
||||||
image: Ptr<Image>,
|
shape: Option<Ptr<Shape>>,
|
||||||
image_color_space: Ptr<RGBColorSpace>,
|
alpha: Option<Ptr<FloatTexture>>,
|
||||||
fov: Float,
|
image: Option<Ptr<Image>>,
|
||||||
) -> Self;
|
image_color_space: Option<Ptr<RGBColorSpace>>,
|
||||||
}
|
two_sided: Option<bool>,
|
||||||
|
fov: Option<shared::Float>,
|
||||||
impl CreateProjectionLight for ProjectionLight {
|
|
||||||
fn new(
|
|
||||||
render_from_light: Transform,
|
|
||||||
medium_interface: MediumInterface,
|
|
||||||
le: Spectrum,
|
|
||||||
scale: Float,
|
|
||||||
image: Ptr<Image>,
|
|
||||||
image_color_space: Ptr<RGBColorSpace>,
|
|
||||||
fov: Float,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let base = LightBase::new(
|
let base = LightBase::new(
|
||||||
LightType::DeltaPosition,
|
LightType::DeltaPosition,
|
||||||
|
|
@ -79,122 +69,96 @@ impl CreateProjectionLight for ProjectionLight {
|
||||||
a,
|
a,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl CreateLight for ProjectionLight {
|
|
||||||
fn create(
|
fn create(
|
||||||
arena: &mut Arena,
|
arena: &mut crate::utils::Arena,
|
||||||
render_from_light: Transform,
|
render_from_light: shared::utils::Transform,
|
||||||
medium: Medium,
|
medium: Medium,
|
||||||
parameters: &ParameterDictionary,
|
parameters: &crate::utils::ParameterDictionary,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
_shape: &Shape,
|
_shape: &Shape,
|
||||||
_alpha_text: &FloatTexture,
|
_alpha_text: &FloatTexture,
|
||||||
colorspace: Option<&RGBColorSpace>,
|
_colorspace: Option<&shared::spectra::RGBColorSpace>,
|
||||||
) -> Result<Light, Error> {
|
) -> Light {
|
||||||
let mut scale = parameters.get_one_float("scale", 1.);
|
let mut scale = parameters.get_one_float("scale", 1.);
|
||||||
let power = parameters.get_one_float("power", -1.);
|
let power = parameters.get_one_float("power", -1.);
|
||||||
let fov = parameters.get_one_float("fov", 90.);
|
let fov = parameters.get_one_float("fov", 90.);
|
||||||
|
|
||||||
let filename = resolve_filename(parameters.get_one_string("filename", ""));
|
let filename = resolve_filename(parameters.get_one_string("filename", ""));
|
||||||
if filename.is_empty() {
|
if filename.is_empty() {
|
||||||
return Err(error!(loc, "must provide filename for projection light"));
|
panic!(
|
||||||
|
"{}: Must provide filename for projection light source.",
|
||||||
|
loc
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
let im = Image::read(filename, None).unwrap();
|
||||||
let im = Image::read(&filename, None)
|
let image: Image = im.image;
|
||||||
.map_err(|e| error!(loc, "could not load image '{}': {}", filename, e))?;
|
let metadata: ImageMetadata = im.metadata;
|
||||||
|
if image.has_any_infinite_pixels() {
|
||||||
if im.image.has_any_infinite_pixels() {
|
panic!(
|
||||||
return Err(error!(
|
"{:?}: Image '{}' has NaN pixels. Not suitable for light.",
|
||||||
loc,
|
loc, filename
|
||||||
"image '{}' has infinite pixels, not suitable for light", filename
|
);
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
let colorspace: RGBColorSpace = metadata.colorspace.unwrap();
|
||||||
if im.image.has_any_nan_pixels() {
|
let channel_desc = image
|
||||||
return Err(error!(
|
|
||||||
loc,
|
|
||||||
"image '{}' has NaN pixels, not suitable for light", filename
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let channel_desc = im
|
|
||||||
.image
|
|
||||||
.get_channel_desc(&["R", "G", "B"])
|
.get_channel_desc(&["R", "G", "B"])
|
||||||
.map_err(|_| error!(loc, "image '{}' must have R, G, B channels", filename))?;
|
.unwrap_or_else(|_| {
|
||||||
|
panic!(
|
||||||
let image = im.image.select_channels(&channel_desc);
|
"{:?}: Image '{}' must have R, G and B channels.",
|
||||||
let colorspace = im
|
loc, filename
|
||||||
.metadata
|
)
|
||||||
.colorspace
|
});
|
||||||
.ok_or_else(|| error!(loc, "image '{}' missing colorspace metadata", filename))?;
|
let image = image.select_channels(channel_desc);
|
||||||
|
|
||||||
scale /= spectrum_to_photometric(colorspace.illuminant);
|
scale /= spectrum_to_photometric(colorspace.illuminant);
|
||||||
if power > 0. {
|
if power > 0. {
|
||||||
let k_e = compute_emissive_power(&image, colorspace, fov);
|
let hither = 1e-3;
|
||||||
|
let aspect = image.resolution().x() as Float / image.resolution().y() as Float;
|
||||||
|
let screen_bounds = if aspect > 1. {
|
||||||
|
Bounds2f::from_poins(Point2f::new(-aspect, -1.), Point2f::new(aspect, 1.))
|
||||||
|
} else {
|
||||||
|
Bounds2f::from_points(
|
||||||
|
Point2f::new(-1., -1. / aspect),
|
||||||
|
Point2f::new(1., 1. / aspect),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let screen_from_light = Transform::perspective(fov, hither, 1e30);
|
||||||
|
let light_from_screen = screen_from_light.inverse();
|
||||||
|
let opposite = (radians(fov) / 2.).tan();
|
||||||
|
let aspect_factor = if aspect > 1. { aspect } else { 1. / aspect };
|
||||||
|
let a = 4. * square(aspect_factor);
|
||||||
|
let mut sum = 0;
|
||||||
|
let luminance = colorspace.luminance_vector();
|
||||||
|
let res = image.resolution();
|
||||||
|
for y in 0..res.y() {
|
||||||
|
for x in 0..res.x() {
|
||||||
|
let lerp_factor = Point2f::new((x + 0.5) / res.x(), (y + 0.5) / res.y());
|
||||||
|
let ps = screen_bounds.lerp(lerp_factor);
|
||||||
|
let w_point =
|
||||||
|
light_from_screen.apply_to_point(Point3f::new(ps.x(), ps.y(), 0.));
|
||||||
|
let w = Vector3f::from(w_point).normalize();
|
||||||
|
let dwda = w.z().powi(3);
|
||||||
|
for c in 0..3 {
|
||||||
|
sum += image.get_channel(Point2f::new(x, y), c) * luminance[c] * dwda;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scale *= power / (a * sum / (res.x() + res.y()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let flip = Transform::scale(1., -1., 1.);
|
let flip = Transform::scale(1., -1., 1.);
|
||||||
let render_from_light_flip = render_from_light * flip;
|
let render_from_light_flip = render_from_light * flip;
|
||||||
|
let specific = Self::new(
|
||||||
let specific = ProjectionLight::new(
|
render_from_light,
|
||||||
render_from_light_flip,
|
|
||||||
medium_interface,
|
medium_interface,
|
||||||
le,
|
le,
|
||||||
scale,
|
scale,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
image.upload(arena),
|
image.upload(arena),
|
||||||
colorspace.upload(arena),
|
None,
|
||||||
fov,
|
None,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
Light::Projection(specific)
|
||||||
Ok(Light::Projection(specific))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_screen_bounds(aspect: Float) -> Bounds2f {
|
|
||||||
if aspect > 1.0 {
|
|
||||||
Bounds2f::from_points(Point2f::new(-aspect, -1.0), Point2f::new(aspect, 1.0))
|
|
||||||
} else {
|
|
||||||
Bounds2f::from_points(
|
|
||||||
Point2f::new(-1.0, -1.0 / aspect),
|
|
||||||
Point2f::new(1.0, 1.0 / aspect),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compute_emissive_power(image: &Image, colorspace: &RGBColorSpace, fov: Float) -> Float {
|
|
||||||
let res = image.resolution();
|
|
||||||
let aspect = res.x() as Float / res.y() as Float;
|
|
||||||
let screen_bounds = compute_screen_bounds(aspect);
|
|
||||||
|
|
||||||
let hither = 1e-3;
|
|
||||||
let screen_from_light =
|
|
||||||
Transform::perspective(fov, hither, 1e30).expect("Failed to create perspective transform");
|
|
||||||
let light_from_screen = screen_from_light.inverse();
|
|
||||||
|
|
||||||
let opposite = (radians(fov) / 2.0).tan();
|
|
||||||
let aspect_factor = if aspect > 1.0 { aspect } else { 1.0 / aspect };
|
|
||||||
let a = 4.0 * square(opposite) * aspect_factor;
|
|
||||||
|
|
||||||
let luminance = colorspace.luminance_vector();
|
|
||||||
let mut sum: Float = 0.0;
|
|
||||||
|
|
||||||
for y in 0..res.y() {
|
|
||||||
for x in 0..res.x() {
|
|
||||||
let lerp_factor = Point2f::new(
|
|
||||||
(x as Float + 0.5) / res.x() as Float,
|
|
||||||
(y as Float + 0.5) / res.y() as Float,
|
|
||||||
);
|
|
||||||
let ps = screen_bounds.lerp(lerp_factor);
|
|
||||||
let w_point = light_from_screen.apply_to_point(Point3f::new(ps.x(), ps.y(), 0.0));
|
|
||||||
let w = Vector3f::from(w_point).normalize();
|
|
||||||
let dwda = w.z().powi(3);
|
|
||||||
|
|
||||||
for c in 0..3 {
|
|
||||||
sum += image.get_channel(Point2i::new(x, y), c) * luminance[c] * dwda;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
a * sum / (res.x() * res.y()) as Float
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ use crate::core::image::{Image, ImageIO, ImageMetadata};
|
||||||
use crate::core::light::CreateLight;
|
use crate::core::light::CreateLight;
|
||||||
use crate::core::spectrum::spectrum_to_photometric;
|
use crate::core::spectrum::spectrum_to_photometric;
|
||||||
use crate::spectra::colorspace::new;
|
use crate::spectra::colorspace::new;
|
||||||
use crate::utils::{Arena, ParameterDictionary, Ptr, Upload, resolve_filename};
|
use crate::utils::{Ptr, Upload, resolve_filename};
|
||||||
|
use shared::PI;
|
||||||
use shared::core::geometry::{Bounds2f, Frame, VectorLike};
|
use shared::core::geometry::{Bounds2f, Frame, VectorLike};
|
||||||
use shared::core::image::ImageAccess;
|
use shared::core::image::ImageAccess;
|
||||||
use shared::core::light::{Light, LightBase, LightType};
|
use shared::core::light::{Light, LightBase, LightType};
|
||||||
|
|
@ -13,28 +14,21 @@ use shared::lights::{ProjectionLight, SpotLight};
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::RGBColorSpace;
|
||||||
use shared::utils::math::{radians, square};
|
use shared::utils::math::{radians, square};
|
||||||
use shared::utils::{Ptr, Transform};
|
use shared::utils::{Ptr, Transform};
|
||||||
use shared::{Float, PI};
|
|
||||||
|
|
||||||
pub trait CreateSpotLight {
|
impl CreateLight for SpotLight {
|
||||||
fn new(
|
fn new(
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
medium_interface: MediumInterface,
|
medium_interface: MediumInterface,
|
||||||
le: Spectrum,
|
le: Spectrum,
|
||||||
scale: shared::Float,
|
scale: shared::Float,
|
||||||
cos_falloff_start: Float,
|
shape: Option<Ptr<Shape>>,
|
||||||
total_width: Float,
|
alpha: Option<Ptr<FloatTexture>>,
|
||||||
) -> Self;
|
image: Option<Ptr<Image>>,
|
||||||
}
|
image_color_space: Option<Ptr<RGBColorSpace>>,
|
||||||
|
two_sided: Option<bool>,
|
||||||
impl CreateSpotLight for SpotLight {
|
fov: Option<shared::Float>,
|
||||||
fn new(
|
cos_falloff_start: Option<shared::Float>,
|
||||||
render_from_light: Transform,
|
total_width: Option<shared::Float>,
|
||||||
medium_interface: MediumInterface,
|
|
||||||
le: Spectrum,
|
|
||||||
scale: shared::Float,
|
|
||||||
image: Ptr<Image>,
|
|
||||||
cos_falloff_start: Float,
|
|
||||||
total_width: Float,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let base = LightBase::new(
|
let base = LightBase::new(
|
||||||
LightType::DeltaPosition,
|
LightType::DeltaPosition,
|
||||||
|
|
@ -47,23 +41,21 @@ impl CreateSpotLight for SpotLight {
|
||||||
base,
|
base,
|
||||||
iemit,
|
iemit,
|
||||||
scale,
|
scale,
|
||||||
cos_falloff_end: radians(total_width).cos(),
|
cos_falloff_end: radians(total_width.unwrap().cos()),
|
||||||
cos_falloff_start: radians(cos_falloff_start).cos(),
|
cos_falloff_start: radians(cos_falloff_start.unwrap().cos()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl CreateLight for SpotLight {
|
|
||||||
fn create(
|
fn create(
|
||||||
arena: &mut Arena,
|
arena: &mut crate::utils::Arena,
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
medium: Medium,
|
medium: Medium,
|
||||||
parameters: &ParameterDictionary,
|
parameters: &crate::utils::ParameterDictionary,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
shape: &Shape,
|
shape: &Shape,
|
||||||
alpha_tex: &FloatTexture,
|
alpha_tex: &FloatTexture,
|
||||||
colorspace: Option<&RGBColorSpace>,
|
colorspace: Option<&RGBColorSpace>,
|
||||||
) -> Result<Light, Error> {
|
) -> Light {
|
||||||
let i = parameters
|
let i = parameters
|
||||||
.get_one_spectrum(
|
.get_one_spectrum(
|
||||||
"I",
|
"I",
|
||||||
|
|
@ -95,9 +87,14 @@ impl CreateLight for SpotLight {
|
||||||
medium.into(),
|
medium.into(),
|
||||||
le,
|
le,
|
||||||
scale,
|
scale,
|
||||||
coneangle,
|
None,
|
||||||
coneangle - conedelta,
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(coneangle),
|
||||||
|
Some(coneangle - conedelta),
|
||||||
);
|
);
|
||||||
Ok(Light::Spot(specific))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::core::image::Image;
|
use crate::core::image::ImageBuffer;
|
||||||
use crate::core::material::CreateMaterial;
|
use crate::core::material::CreateMaterial;
|
||||||
use crate::utils::{Arena, ParameterDictionary};
|
use crate::utils::parameters::ParameterDictionary;
|
||||||
use shared::core::spectrum::Spectrum;
|
use shared::core::spectrum::Spectrum;
|
||||||
use shared::core::texture::SpectrumType;
|
use shared::core::texture::SpectrumType;
|
||||||
use shared::materials::coated::*;
|
use shared::materials::coated::*;
|
||||||
|
|
@ -10,10 +10,9 @@ use shared::textures::SpectrumConstantTexture;
|
||||||
impl CreateMaterial for CoatedDiffuseMaterial {
|
impl CreateMaterial for CoatedDiffuseMaterial {
|
||||||
fn create(
|
fn create(
|
||||||
parameters: &TextureParameterDictionary,
|
parameters: &TextureParameterDictionary,
|
||||||
normal_map: Option<Arc<Image>>,
|
normal_map: Option<Arc<ImageBuffer>>,
|
||||||
named_materials: &HashMap<String, Material>,
|
named_materials: &HashMap<String, Material>,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
area: &mut Arena,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let reflectance = parameters
|
let reflectance = parameters
|
||||||
.get_spectrum_texture("reflectance", None, SpectrumType::Albedo)
|
.get_spectrum_texture("reflectance", None, SpectrumType::Albedo)
|
||||||
|
|
@ -46,30 +45,15 @@ impl CreateMaterial for CoatedDiffuseMaterial {
|
||||||
});
|
});
|
||||||
let displacement = parameters.get_float_texture("displacement");
|
let displacement = parameters.get_float_texture("displacement");
|
||||||
let remap_roughness = parameters.get_one_bool("remaproughness", true);
|
let remap_roughness = parameters.get_one_bool("remaproughness", true);
|
||||||
arena.alloc(Self::new(
|
|
||||||
reflectance,
|
|
||||||
u_roughness,
|
|
||||||
v_roughness,
|
|
||||||
thickness,
|
|
||||||
albedo,
|
|
||||||
g,
|
|
||||||
eta,
|
|
||||||
displacement,
|
|
||||||
normal_map,
|
|
||||||
remap_roughness,
|
|
||||||
max_depth,
|
|
||||||
n_samples,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CreateMaterial for CoatedConductorMaterial {
|
impl CreateMaterial for CoatedConductorMaterial {
|
||||||
fn create(
|
fn create(
|
||||||
parameters: &TextureParameterDictionary,
|
parameters: &TextureParameterDictionary,
|
||||||
normal_map: Option<Arc<Image>>,
|
normal_map: Option<Arc<ImageBuffer>>,
|
||||||
named_materials: &HashMap<String, Material>,
|
named_materials: &HashMap<String, Material>,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
arena: &mut Arena,
|
|
||||||
) -> Result<Self, String> {
|
) -> Result<Self, String> {
|
||||||
let interface_u_roughness = parameters
|
let interface_u_roughness = parameters
|
||||||
.get_float_texture_or_null("interface.uroughness")
|
.get_float_texture_or_null("interface.uroughness")
|
||||||
|
|
@ -122,7 +106,7 @@ impl CreateMaterial for CoatedConductorMaterial {
|
||||||
});
|
});
|
||||||
let displacement = parameters.get_float_texture_or_null("displacement");
|
let displacement = parameters.get_float_texture_or_null("displacement");
|
||||||
let remap_roughness = parameters.get_one_bool("remaproughness", true);
|
let remap_roughness = parameters.get_one_bool("remaproughness", true);
|
||||||
let material = Self::new(
|
Self::new(
|
||||||
displacement,
|
displacement,
|
||||||
normal_map,
|
normal_map,
|
||||||
interface_u_roughness,
|
interface_u_roughness,
|
||||||
|
|
@ -139,8 +123,6 @@ impl CreateMaterial for CoatedConductorMaterial {
|
||||||
remap_roughness,
|
remap_roughness,
|
||||||
max_depth,
|
max_depth,
|
||||||
n_samples,
|
n_samples,
|
||||||
);
|
)
|
||||||
arena.alloc(material);
|
|
||||||
return material;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::core::material::CreateMaterial;
|
use crate::core::material::CreateMaterial;
|
||||||
use crate::utils::{Arena, FileLoc, TextureParameterDictionary};
|
|
||||||
use shared::core::bxdf::HairBxDF;
|
use shared::core::bxdf::HairBxDF;
|
||||||
use shared::core::spectrum::Spectrum;
|
use shared::core::spectrum::Spectrum;
|
||||||
use shared::core::texture::{GPUFloatTexture, GPUSpectrumTexture};
|
use shared::core::texture::{GPUFloatTexture, GPUSpectrumTexture};
|
||||||
|
|
@ -11,7 +10,6 @@ impl CreateMaterial for HairMaterial {
|
||||||
normal_map: Option<Arc<ImageBuffer>>,
|
normal_map: Option<Arc<ImageBuffer>>,
|
||||||
named_materials: &HashMap<String, Material>,
|
named_materials: &HashMap<String, Material>,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
arena: &mut Arena,
|
|
||||||
) -> Result<Self, String> {
|
) -> Result<Self, String> {
|
||||||
let sigma_a = parameters.get_spectrum_texture_or_null("sigma_a", SpectrumType::Unbounded);
|
let sigma_a = parameters.get_spectrum_texture_or_null("sigma_a", SpectrumType::Unbounded);
|
||||||
let reflectance = parameters
|
let reflectance = parameters
|
||||||
|
|
@ -59,7 +57,7 @@ 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(
|
HairMaterial::new(
|
||||||
sigma_a,
|
sigma_a,
|
||||||
reflectance,
|
reflectance,
|
||||||
eumelanin,
|
eumelanin,
|
||||||
|
|
@ -68,10 +66,7 @@ impl CreateMaterial for HairMaterial {
|
||||||
beta_m,
|
beta_m,
|
||||||
beta_n,
|
beta_n,
|
||||||
alpha,
|
alpha,
|
||||||
);
|
)
|
||||||
|
|
||||||
arena.alloc(material);
|
|
||||||
return material;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ use crate::core::scattering::TrowbridgeReitzDistribution;
|
||||||
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
||||||
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator};
|
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator};
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
|
use crate::utils::Ptr;
|
||||||
use crate::utils::math::clamp;
|
use crate::utils::math::clamp;
|
||||||
use shared::utils::Ptr;
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
use crate::core::shape::{ALL_BILINEAR_MESHES, CreateMesh, CreateShape};
|
use crate::core::shape::{ALL_BILINEAR_MESHES, CreateShape};
|
||||||
use crate::core::texture::FloatTexture;
|
use crate::core::texture::FloatTexture;
|
||||||
use crate::shapes::mesh::Mesh;
|
use crate::shapes::mesh::BilinearPatchMeshHost;
|
||||||
use crate::utils::{Arena, FileLoc, ParameterDictionary};
|
use crate::utils::{FileLoc, ParameterDictionary};
|
||||||
use shared::shapes::BilinearPatchShape;
|
use shared::shapes::BilinearPatchShape;
|
||||||
use shared::utils::Transform;
|
use shared::utils::Transform;
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
impl CreateMesh for BilinearPatchShape {
|
impl CreateShape for BilinearPatchShape {
|
||||||
fn create(
|
fn create(
|
||||||
render_from_object: &Transform,
|
render_from_object: &Transform,
|
||||||
_object_from_render: &Transform,
|
_object_from_render: &Transform,
|
||||||
|
|
@ -14,7 +13,6 @@ impl CreateMesh for BilinearPatchShape {
|
||||||
parameters: &ParameterDictionary,
|
parameters: &ParameterDictionary,
|
||||||
_float_textures: &HashMap<String, FloatTexture>,
|
_float_textures: &HashMap<String, FloatTexture>,
|
||||||
_loc: &FileLoc,
|
_loc: &FileLoc,
|
||||||
arena: &mut Arena,
|
|
||||||
) -> Result<Mesh, String> {
|
) -> Result<Mesh, String> {
|
||||||
let mut vertex_indices = parameters.get_int_array("indices");
|
let mut vertex_indices = parameters.get_int_array("indices");
|
||||||
let mut p = parameters.get_point3f_array("P");
|
let mut p = parameters.get_point3f_array("P");
|
||||||
|
|
@ -130,7 +128,6 @@ impl CreateMesh for BilinearPatchShape {
|
||||||
rectangle: false,
|
rectangle: false,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
arena.alloc(shapes);
|
|
||||||
Ok(shapes)
|
Ok(shapes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,6 @@ impl CreateShape for CurveShape {
|
||||||
|
|
||||||
curves.extend(new_curves);
|
curves.extend(new_curves);
|
||||||
}
|
}
|
||||||
arena.alloc(curves);
|
|
||||||
Ok(curves)
|
Ok(curves)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::core::shape::CreateShape;
|
use crate::core::shape::CreateShape;
|
||||||
use crate::core::texture::FloatTexture;
|
use crate::core::texture::FloatTexture;
|
||||||
use crate::utils::{Arena, FileLoc, ParameterDictionary};
|
use crate::utils::{FileLoc, ParameterDictionary};
|
||||||
use shared::core::shape::Shape;
|
use shared::core::shape::Shape;
|
||||||
use shared::shapes::CylinderShape;
|
use shared::shapes::CylinderShape;
|
||||||
use shared::utils::Transform;
|
use shared::utils::Transform;
|
||||||
|
|
@ -15,7 +15,6 @@ impl CreateShape for CylinderShape {
|
||||||
parameters: ParameterDictionary,
|
parameters: ParameterDictionary,
|
||||||
float_textures: HashMap<String, FloatTexture>,
|
float_textures: HashMap<String, FloatTexture>,
|
||||||
loc: FileLoc,
|
loc: FileLoc,
|
||||||
arena: &mut Arena,
|
|
||||||
) -> Result<Vec<Shape>, String> {
|
) -> Result<Vec<Shape>, String> {
|
||||||
let radius = parameters.get_one_float("radius", 1.);
|
let radius = parameters.get_one_float("radius", 1.);
|
||||||
let z_min = parameters.get_one_float("zmin", -1.);
|
let z_min = parameters.get_one_float("zmin", -1.);
|
||||||
|
|
@ -31,7 +30,6 @@ impl CreateShape for CylinderShape {
|
||||||
phi_max,
|
phi_max,
|
||||||
);
|
);
|
||||||
|
|
||||||
arena.alloc(Shape::Cylinder(shape));
|
|
||||||
Ok(vec![Shape::Cylinder(shape)])
|
Ok(vec![Shape::Cylinder(shape)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::core::shape::CreateShape;
|
use crate::core::shape::CreateShape;
|
||||||
use crate::core::texture::FloatTexture;
|
use crate::core::texture::FloatTexture;
|
||||||
use crate::utils::{Arena, FileLoc, ParameterDictionary};
|
use crate::utils::{FileLoc, ParameterDictionary};
|
||||||
use shared::core::shape::Shape;
|
use shared::core::shape::Shape;
|
||||||
use shared::shapes::DiskShape;
|
use shared::shapes::DiskShape;
|
||||||
use shared::utils::Transform;
|
use shared::utils::Transform;
|
||||||
|
|
@ -15,7 +15,6 @@ impl CreateShape for DiskShape {
|
||||||
parameters: ParameterDictionary,
|
parameters: ParameterDictionary,
|
||||||
float_textures: HashMap<String, FloatTexture>,
|
float_textures: HashMap<String, FloatTexture>,
|
||||||
loc: FileLoc,
|
loc: FileLoc,
|
||||||
arena: &mut Arena,
|
|
||||||
) -> Result<Vec<Shape>, String> {
|
) -> Result<Vec<Shape>, String> {
|
||||||
let height = parameters.get_one_float("height", 0.);
|
let height = parameters.get_one_float("height", 0.);
|
||||||
let radius = parameters.get_one_float("radius", 1.);
|
let radius = parameters.get_one_float("radius", 1.);
|
||||||
|
|
@ -31,7 +30,6 @@ impl CreateShape for DiskShape {
|
||||||
reverse_orientation,
|
reverse_orientation,
|
||||||
);
|
);
|
||||||
|
|
||||||
arena.alloc(Shape::Disk(shape));
|
|
||||||
Ok(vec![Shape::Disk(shape)])
|
Ok(vec![Shape::Disk(shape)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use ply_rs::parser::Parser;
|
||||||
use ply_rs::ply::{DefaultElement, Property};
|
use ply_rs::ply::{DefaultElement, Property};
|
||||||
use shared::utils::Transform;
|
use shared::utils::Transform;
|
||||||
use shared::utils::mesh::{BilinearPatchMesh, TriangleMesh};
|
use shared::utils::mesh::{BilinearPatchMesh, TriangleMesh};
|
||||||
use shared::utils::sampling::DevicePiecewiseConstant2D;
|
use shared::utils::sampling::PiecewiseConstant2D;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
@ -211,32 +211,40 @@ impl TriQuadMesh {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait TriangleMeshFactory {
|
#[derive(Debug, Clone, Copy)]
|
||||||
fn create(
|
pub struct TriangleMeshStorage {
|
||||||
arena: &mut Arena,
|
vertex_indices: Vec<u32>,
|
||||||
render_from_object: &Transform,
|
p: Vec<Point3f>,
|
||||||
reverse_orientation: bool,
|
n: Vec<Normal3f>,
|
||||||
vertex_indices: Vec<u32>,
|
s: Vec<Vector3f>,
|
||||||
p: Vec<Point3f>,
|
uv: Vec<Point2f>,
|
||||||
n: Vec<Normal3f>,
|
face_indices: Vec<u32>,
|
||||||
s: Vec<Vector3f>,
|
|
||||||
uv: Vec<Point2f>,
|
|
||||||
face_indices: Vec<u32>,
|
|
||||||
) -> ArenaPtr<TriangleMesh>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TriangleMeshFactory for TriangleMesh {
|
#[derive(Debug, Clone, Copy)]
|
||||||
fn create(
|
pub struct TriangleMeshHost {
|
||||||
arena: &mut Arena,
|
pub view: TriangleMesh,
|
||||||
render_from_object: &Transform,
|
_storage: Arc<TriangleMeshStorage>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for TriangleMeshHost {
|
||||||
|
type Target = TriangleMesh;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.view
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TriangleMeshHost {
|
||||||
|
pub fn new(
|
||||||
|
render_from_object: Transform,
|
||||||
reverse_orientation: bool,
|
reverse_orientation: bool,
|
||||||
vertex_indices: Vec<u32>,
|
vertex_indices: Vec<usize>,
|
||||||
p: Vec<Point3f>,
|
mut p: Vec<Point3f>,
|
||||||
n: Vec<Normal3f>,
|
mut s: Vec<Vector3f>,
|
||||||
s: Vec<Vector3f>,
|
mut n: Vec<Normal3f>,
|
||||||
uv: Vec<Point2f>,
|
uv: Vec<Point2f>,
|
||||||
face_indices: Vec<u32>,
|
face_indices: Vec<usize>,
|
||||||
) -> ArenaPtr<TriangleMesh> {
|
) -> Self {
|
||||||
let n_triangles = indices.len() / 3;
|
let n_triangles = indices.len() / 3;
|
||||||
let n_vertices = p.len();
|
let n_vertices = p.len();
|
||||||
for pt in p.iter_mut() {
|
for pt in p.iter_mut() {
|
||||||
|
|
@ -334,7 +342,7 @@ pub struct BilinearMeshStorage {
|
||||||
p: Vec<Point3f>,
|
p: Vec<Point3f>,
|
||||||
n: Vec<Normal3f>,
|
n: Vec<Normal3f>,
|
||||||
uv: Vec<Point2f>,
|
uv: Vec<Point2f>,
|
||||||
image_distribution: Option<DevicePiecewiseConstant2D>,
|
image_distribution: Option<PiecewiseConstant2D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
@ -358,7 +366,7 @@ impl BilinearPatchMeshHost {
|
||||||
mut p: Vec<Point3f>,
|
mut p: Vec<Point3f>,
|
||||||
mut n: Vec<Normal3f>,
|
mut n: Vec<Normal3f>,
|
||||||
uv: Vec<Point2f>,
|
uv: Vec<Point2f>,
|
||||||
image_distribution: Option<DevicePiecewiseConstant2D>,
|
image_distribution: Option<PiecewiseConstant2D>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let n_patches = indices.len() / 3;
|
let n_patches = indices.len() / 3;
|
||||||
let n_vertices = p.len();
|
let n_vertices = p.len();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::core::shape::CreateShape;
|
use crate::core::shape::CreateShape;
|
||||||
use crate::core::texture::FloatTexture;
|
use crate::core::texture::FloatTexture;
|
||||||
use crate::utils::{Arena, FileLoc, ParameterDictionary};
|
use crate::utils::{FileLoc, ParameterDictionary};
|
||||||
use shared::core::shape::Shape;
|
use shared::core::shape::Shape;
|
||||||
use shared::shapes::SphereShape;
|
use shared::shapes::SphereShape;
|
||||||
use shared::utils::Transform;
|
use shared::utils::Transform;
|
||||||
|
|
@ -14,7 +14,6 @@ impl CreateShape for SphereShape {
|
||||||
parameters: ParameterDictionary,
|
parameters: ParameterDictionary,
|
||||||
float_textures: HashMap<String, FloatTexture>,
|
float_textures: HashMap<String, FloatTexture>,
|
||||||
loc: FileLoc,
|
loc: FileLoc,
|
||||||
arena: &mut Arena,
|
|
||||||
) -> Result<Vec<Shape>, String> {
|
) -> Result<Vec<Shape>, String> {
|
||||||
let radius = parameters.get_one_float("radius", 1.);
|
let radius = parameters.get_one_float("radius", 1.);
|
||||||
let zmin = parameters.get_one_float("zmin", -radius);
|
let zmin = parameters.get_one_float("zmin", -radius);
|
||||||
|
|
@ -29,7 +28,6 @@ impl CreateShape for SphereShape {
|
||||||
zmax,
|
zmax,
|
||||||
phimax,
|
phimax,
|
||||||
);
|
);
|
||||||
arena.alloc(vec![Shape::Sphere(SphereShape)]);
|
|
||||||
Ok(vec![Shape::Sphere(SphereShape)])
|
Ok(vec![Shape::Sphere(SphereShape)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::core::shape::{ALL_TRIANGLE_MESHES, CreateShape};
|
use crate::core::shape::{ALL_TRIANGLE_MESHES, CreateShape};
|
||||||
use crate::core::texture::FloatTexture;
|
use crate::core::texture::FloatTexture;
|
||||||
use crate::shapes::mesh::TriangleMeshHost;
|
use crate::shapes::mesh::TriangleMeshHost;
|
||||||
use crate::utils::{Arena, FileLoc, ParameterDictionary};
|
use crate::utils::{FileLoc, ParameterDictionary};
|
||||||
use shared::shapes::TriangleShape;
|
use shared::shapes::TriangleShape;
|
||||||
use shared::utils::Transform;
|
use shared::utils::Transform;
|
||||||
|
|
||||||
|
|
@ -12,7 +12,6 @@ impl CreateShape for TriangleShape {
|
||||||
reverse_orientation: bool,
|
reverse_orientation: bool,
|
||||||
parameters: ParameterDictionary,
|
parameters: ParameterDictionary,
|
||||||
loc: FileLoc,
|
loc: FileLoc,
|
||||||
arena: &mut Arena,
|
|
||||||
) -> Result<Mesh, String> {
|
) -> Result<Mesh, String> {
|
||||||
let mut vertex_indices = parameters.get_int_array("indices");
|
let mut vertex_indices = parameters.get_int_array("indices");
|
||||||
let mut p = parameters.get_point3f_array("P");
|
let mut p = parameters.get_point3f_array("P");
|
||||||
|
|
@ -105,8 +104,6 @@ impl CreateShape for TriangleShape {
|
||||||
tri_index: i,
|
tri_index: i,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
arena.alloc(shapes);
|
|
||||||
Ok(shapes)
|
Ok(shapes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ impl SpectrumTextureTrait for SpectrumImageTexture {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FloatImageTexture {
|
pub struct FloatImageTexture {
|
||||||
pub base: ImageTextureBase,
|
base: ImageTextureBase,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FloatImageTexture {
|
impl FloatImageTexture {
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
use crate::core::texture::{
|
use crate::core::texture::{
|
||||||
FloatTexture, FloatTextureTrait, SpectrumTexture, SpectrumTextureTrait,
|
FloatTexture, FloatTextureTrait, SpectrumTexture, SpectrumTextureTrait,
|
||||||
};
|
};
|
||||||
use crate::utils::{Arena, FileLoc, TextureParameterDictionary};
|
use crate::utils::{FileLoc, TextureParameterDictionary};
|
||||||
use shared::core::geometry::Vector3f;
|
use shared::core::geometry::Vector3f;
|
||||||
use shared::utils::Transform;
|
use shared::utils::Transform;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FloatMixTexture {
|
pub struct FloatMixTexture {
|
||||||
pub tex1: Arc<FloatTexture>,
|
tex1: Arc<FloatTexture>,
|
||||||
pub tex2: Arc<FloatTexture>,
|
tex2: Arc<FloatTexture>,
|
||||||
pub amount: Arc<FloatTexture>,
|
amount: Arc<FloatTexture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FloatMixTexture {
|
impl FloatMixTexture {
|
||||||
|
|
@ -26,12 +26,11 @@ impl FloatMixTexture {
|
||||||
_render_from_texture: &Transform,
|
_render_from_texture: &Transform,
|
||||||
params: &TextureParameterDictionary,
|
params: &TextureParameterDictionary,
|
||||||
_loc: &FileLoc,
|
_loc: &FileLoc,
|
||||||
arena: &mut Arena,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let tex1 = params.get_float_texture("tex1", 0.);
|
let tex1 = params.get_float_texture("tex1", 0.);
|
||||||
let tex2 = params.get_float_texture("tex2", 1.);
|
let tex2 = params.get_float_texture("tex2", 1.);
|
||||||
let amount = params.get_float_texture("amount", 0.5);
|
let amount = params.get_float_texture("amount", 0.5);
|
||||||
arena.alloc(Self::new(tex1, tex2, amount))
|
Self::new(tex1, tex2, amount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,9 +51,9 @@ impl FloatTextureTrait for FloatMixTexture {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FloatDirectionMixTexture {
|
pub struct FloatDirectionMixTexture {
|
||||||
pub tex1: Arc<FloatTexture>,
|
tex1: Arc<FloatTexture>,
|
||||||
pub tex2: Arc<FloatTexture>,
|
tex2: Arc<FloatTexture>,
|
||||||
pub dir: Vector3f,
|
dir: Vector3f,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FloatDirectionMixTexture {
|
impl FloatDirectionMixTexture {
|
||||||
|
|
@ -66,13 +65,12 @@ impl FloatDirectionMixTexture {
|
||||||
render_from_texture: &Transform,
|
render_from_texture: &Transform,
|
||||||
params: &TextureParameterDictionary,
|
params: &TextureParameterDictionary,
|
||||||
_loc: &FileLoc,
|
_loc: &FileLoc,
|
||||||
arena: &mut Arena,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let dir_raw = params.get_one_vector3f("dir", Vector3f::new(0., 1., 0.));
|
let dir_raw = params.get_one_vector3f("dir", Vector3f::new(0., 1., 0.));
|
||||||
let dir = render_from_texture.apply_to_vector(dir_raw).normalize();
|
let dir = render_from_texture.apply_to_vector(dir_raw).normalize();
|
||||||
let tex1 = params.get_float_texture("tex1", 0.);
|
let tex1 = params.get_float_texture("tex1", 0.);
|
||||||
let tex2 = params.get_float_texture("tex2", 1.);
|
let tex2 = params.get_float_texture("tex2", 1.);
|
||||||
arena.alloc(Self::new(tex1, tex2, dir))
|
Self::new(tex1, tex2, dir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::utils::{Arena, FileLoc, TextureParameterDictionary};
|
use crate::utils::{FileLoc, TextureParameterDictionary};
|
||||||
use shared::core::texture::{SpectrumType, TextureEvalContext};
|
use shared::core::texture::{SpectrumType, TextureEvalContext};
|
||||||
use shared::spectra::ColorEncoding;
|
use shared::spectra::ColorEncoding;
|
||||||
use shared::spectra::color::RGB;
|
use shared::spectra::color::RGB;
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
use crate::core::texture::{
|
use crate::core::texture::{
|
||||||
FloatTexture, FloatTextureTrait, SpectrumTexture, SpectrumTextureTrait,
|
FloatTexture, FloatTextureTrait, SpectrumTexture, SpectrumTextureTrait,
|
||||||
};
|
};
|
||||||
use crate::utils::{Arena, FileLoc, TextureParameterDictionary};
|
use crate::utils::{FileLoc, TextureParameterDictionary};
|
||||||
use shared::core::texture::TextureEvalContext;
|
use shared::core::texture::TextureEvalContext;
|
||||||
use shared::spectra::{SampledSpectrum, SampledWavelengths};
|
use shared::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use shared::utils::Transform;
|
use shared::utils::Transform;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FloatScaledTexture {
|
pub struct FloatScaledTexture {
|
||||||
pub tex: Arc<FloatTexture>,
|
tex: Arc<FloatTexture>,
|
||||||
pub scale: Arc<FloatTexture>,
|
scale: Arc<FloatTexture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FloatScaledTexture {
|
impl FloatScaledTexture {
|
||||||
|
|
@ -21,7 +21,6 @@ impl FloatScaledTexture {
|
||||||
_render_from_texture: &Transform,
|
_render_from_texture: &Transform,
|
||||||
params: &TextureParameterDictionary,
|
params: &TextureParameterDictionary,
|
||||||
_loc: &FileLoc,
|
_loc: &FileLoc,
|
||||||
arena: &mut Arena,
|
|
||||||
) -> FloatTexture {
|
) -> FloatTexture {
|
||||||
let mut tex = params.get_float_texture("tex", 1.);
|
let mut tex = params.get_float_texture("tex", 1.);
|
||||||
let mut scale = params.get_float_texture("scale", 1.);
|
let mut scale = params.get_float_texture("scale", 1.);
|
||||||
|
|
@ -41,7 +40,6 @@ impl FloatScaledTexture {
|
||||||
std::mem::swap(&mut tex, &mut scale);
|
std::mem::swap(&mut tex, &mut scale);
|
||||||
}
|
}
|
||||||
std::mem::swap(&mut tex, &mut scale);
|
std::mem::swap(&mut tex, &mut scale);
|
||||||
arena.alloc(FloatScaledTexture::new(tex, scale));
|
|
||||||
FloatTexture::FloatScaled(FloatScaledTexture::new(tex, scale))
|
FloatTexture::FloatScaled(FloatScaledTexture::new(tex, scale))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -58,8 +56,8 @@ impl FloatTextureTrait for FloatScaledTexture {
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SpectrumScaledTexture {
|
pub struct SpectrumScaledTexture {
|
||||||
pub tex: Arc<SpectrumTexture>,
|
tex: Arc<SpectrumTexture>,
|
||||||
pub scale: Arc<FloatTexture>,
|
scale: Arc<FloatTexture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpectrumTextureTrait for SpectrumScaledTexture {
|
impl SpectrumTextureTrait for SpectrumScaledTexture {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::core::image::Image;
|
use crate::core::image::Image;
|
||||||
use crate::core::texture::FloatTexture;
|
use crate::core::texture::FloatTexture;
|
||||||
use crate::utils::sampling::PiecewiseConstant2D;
|
use crate::utils::sampling::PiecewiseConstant2D;
|
||||||
|
use core::alloc::Layout;
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
use shared::core::color::RGBToSpectrumTable;
|
use shared::core::color::RGBToSpectrumTable;
|
||||||
use shared::core::image::DeviceImage;
|
use shared::core::image::DeviceImage;
|
||||||
|
|
@ -10,75 +11,61 @@ use shared::spectra::{RGBColorSpace, StandardColorSpaces};
|
||||||
use shared::textures::*;
|
use shared::textures::*;
|
||||||
use shared::utils::Ptr;
|
use shared::utils::Ptr;
|
||||||
use shared::utils::sampling::DevicePiecewiseConstant2D;
|
use shared::utils::sampling::DevicePiecewiseConstant2D;
|
||||||
use std::alloc::Layout;
|
|
||||||
|
|
||||||
pub struct Arena {
|
pub struct Arena {
|
||||||
buffer: Vec<(*mut u8, Layout)>,
|
buffer: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Arena {
|
impl Arena {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self { buffer: Vec::new() }
|
Self {
|
||||||
|
buffer: Vec::with_capacity(64 * 1024 * 1024),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn alloc<T>(&mut self, value: T) -> Ptr<T> {
|
pub fn alloc<T: Copy>(&mut self, value: T) -> Ptr<T> {
|
||||||
let layout = Layout::new::<T>();
|
let layout = Layout::new::<T>();
|
||||||
|
let offset = self.alloc_raw(layout);
|
||||||
|
|
||||||
let ptr = unsafe { self.alloc_unified(layout) } as *mut T;
|
|
||||||
|
|
||||||
// Write the value
|
|
||||||
unsafe {
|
unsafe {
|
||||||
ptr.write(value);
|
let ptr = self.buffer.as_mut_ptr().add(offset) as *mut T;
|
||||||
|
std::ptr::write(ptr, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ptr::from_raw(ptr)
|
Ptr {
|
||||||
}
|
offset: offset as i32,
|
||||||
|
_marker: std::marker::PhantomData,
|
||||||
pub fn alloc_opt<T>(&mut self, value: Option<T>) -> Ptr<T> {
|
|
||||||
match value {
|
|
||||||
Some(v) => self.alloc(v),
|
|
||||||
None => Ptr::null(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn alloc_slice<T: Copy>(&mut self, values: &[T]) -> (Ptr<T>, usize) {
|
pub fn alloc_slice<T: Copy>(&mut self, values: &[T]) -> Ptr<T> {
|
||||||
if values.is_empty() {
|
|
||||||
return (Ptr::null(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
let layout = Layout::array::<T>(values.len()).unwrap();
|
let layout = Layout::array::<T>(values.len()).unwrap();
|
||||||
let ptr = unsafe { self.alloc_unified(layout) } as *mut T;
|
let offset = self.alloc_raw(layout);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
let ptr = self.buffer.as_mut_ptr().add(offset) as *mut T;
|
||||||
std::ptr::copy_nonoverlapping(values.as_ptr(), ptr, values.len());
|
std::ptr::copy_nonoverlapping(values.as_ptr(), ptr, values.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
(Ptr::from_raw(ptr), values.len())
|
Ptr {
|
||||||
|
offset: offset as i32,
|
||||||
|
_marker: std::marker::PhantomData,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "cuda")]
|
fn alloc_raw(&mut self, layout: Layout) -> usize {
|
||||||
unsafe fn alloc_unified(&mut self, layout: Layout) -> *mut u8 {
|
let len = self.buffer.len();
|
||||||
use cuda_runtime_sys::*;
|
let align_offset = (len + layout.align() - 1) & !(layout.align() - 1);
|
||||||
|
let new_len = align_offset + layout.size();
|
||||||
|
|
||||||
let mut ptr: *mut std::ffi::c_void = std::ptr::null_mut();
|
if new_len > self.buffer.capacity() {
|
||||||
let size = layout.size().max(layout.align());
|
// Growth strategy: Double capacity to reduce frequency of resizing
|
||||||
|
let new_cap = std::cmp::max(self.buffer.capacity() * 2, new_len);
|
||||||
let result = cudaMallocManaged(&mut ptr, size, cudaMemAttachGlobal);
|
self.buffer.reserve(new_cap - self.buffer.len());
|
||||||
|
|
||||||
if result != cudaError::cudaSuccess {
|
|
||||||
panic!("cudaMallocManaged failed: {:?}", result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.allocations.push((ptr as *mut u8, layout));
|
self.buffer.resize(new_len, 0);
|
||||||
ptr as *mut u8
|
align_offset
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "cuda"))]
|
|
||||||
unsafe fn alloc_unified(&mut self, layout: Layout) -> *mut u8 {
|
|
||||||
// Fallback: regular allocation for CPU-only testing
|
|
||||||
let ptr = std::alloc::alloc(layout);
|
|
||||||
self.allocations.push((ptr, layout));
|
|
||||||
ptr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn raw_data(&self) -> &[u8] {
|
pub fn raw_data(&self) -> &[u8] {
|
||||||
|
|
@ -86,23 +73,6 @@ impl Arena {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for UnifiedArena {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
for (ptr, layout) in self.allocations.drain(..) {
|
|
||||||
unsafe {
|
|
||||||
#[cfg(feature = "cuda")]
|
|
||||||
{
|
|
||||||
cuda_runtime_sys::cudaFree(ptr as *mut _);
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "cuda"))]
|
|
||||||
{
|
|
||||||
std::alloc::dealloc(ptr, layout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Upload {
|
pub trait Upload {
|
||||||
type Target: Copy;
|
type Target: Copy;
|
||||||
|
|
||||||
|
|
@ -117,10 +87,10 @@ impl Upload for Shape {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Upload for Image {
|
impl Upload for Image {
|
||||||
type Target = Image;
|
type Target = DeviceImage;
|
||||||
fn upload(&self, arena: &mut Arena) -> Ptr<Self::Target> {
|
fn upload(&self, arena: &mut Arena) -> Ptr<Self::Target> {
|
||||||
let pixels_ptr = arena.alloc_slice(&self.storage_as_slice());
|
let pixels_ptr = arena.alloc_slice(&self.storage_as_slice());
|
||||||
let device_img = Image {
|
let device_img = DeviceImage {
|
||||||
base: self.base,
|
base: self.base,
|
||||||
pixels: pixels_ptr,
|
pixels: pixels_ptr,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::core::geometry::{Bounds2i, Point2i};
|
use crate::core::geometry::{Bounds2i, Point2i};
|
||||||
use shared::utils::containers::Array2D;
|
use crate::shared::utils::containers::Array2D;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
pub struct InternCache<T> {
|
pub struct InternCache<T> {
|
||||||
|
|
@ -20,7 +20,7 @@ where
|
||||||
let mut lock = self.cache.lock().unwrap();
|
let mut lock = self.cache.lock().unwrap();
|
||||||
|
|
||||||
if let Some(existing) = lock.get(&value) {
|
if let Some(existing) = lock.get(&value) {
|
||||||
return existing.clone();
|
return existing.clone(); // Returns a cheap Arc copy
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_item = Arc::new(value);
|
let new_item = Arc::new(value);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use half::f16;
|
use half::f16;
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
use shared::utils::DevicePtr;
|
use shared::utils::Ptr;
|
||||||
use shared::utils::math::{DigitPermutation, PRIMES};
|
use shared::utils::math::{DigitPermutation, PRIMES};
|
||||||
|
|
||||||
pub fn new_digit_permutation(base: u32, seed: u64) -> Vec<u16> {
|
pub fn new_digit_permutation(base: u32, seed: u64) -> Vec<u16> {
|
||||||
|
|
@ -56,7 +56,7 @@ pub fn compute_radical_inverse_permutations(seed: u64) -> (Vec<u16>, Vec<DigitPe
|
||||||
views.push(DigitPermutation::new(
|
views.push(DigitPermutation::new(
|
||||||
base as u32,
|
base as u32,
|
||||||
n_digits,
|
n_digits,
|
||||||
DevicePtr(ptr_to_data),
|
Ptr(ptr_to_data),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
pub mod arena;
|
|
||||||
pub mod containers;
|
pub mod containers;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod file;
|
pub mod file;
|
||||||
|
|
@ -9,9 +8,7 @@ pub mod parallel;
|
||||||
pub mod parameters;
|
pub mod parameters;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod sampling;
|
pub mod sampling;
|
||||||
pub mod strings;
|
|
||||||
|
|
||||||
pub use arena::{Arena, Upload};
|
|
||||||
pub use error::FileLoc;
|
pub use error::FileLoc;
|
||||||
pub use file::{read_float_file, resolve_filename};
|
pub use file::{read_float_file, resolve_filename};
|
||||||
pub use parameters::{
|
pub use parameters::{
|
||||||
|
|
|
||||||
|
|
@ -1,186 +1,128 @@
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
use shared::core::geometry::{Bounds2f, Point2f};
|
use shared::core::geometry::{Bounds2f, Point2f};
|
||||||
use shared::utils::DevicePtr;
|
use shared::utils::Ptr;
|
||||||
use shared::utils::sampling::{
|
use shared::utils::sampling::{
|
||||||
AliasTable, DevicePiecewiseConstant1D, DevicePiecewiseConstant2D, PiecewiseLinear2D,
|
AliasTable, PiecewiseConstant1D, PiecewiseConstant2D, PiecewiseLinear2D,
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PiecewiseConstant1D {
|
pub struct PiecewiseConstant1DHost {
|
||||||
func: Box<[Float]>,
|
pub view: PiecewiseConstant1D,
|
||||||
cdf: Box<[Float]>,
|
_func: Vec<Float>,
|
||||||
pub device: DevicePiecewiseConstant1D,
|
_cdf: Vec<Float>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PiecewiseConstant1D {
|
impl std::ops::Deref for PiecewiseConstant1DHost {
|
||||||
// Constructors
|
type Target = PiecewiseConstant1D;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.view
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PiecewiseConstant1DHost {
|
||||||
pub fn new(f: &[Float]) -> Self {
|
pub fn new(f: &[Float]) -> Self {
|
||||||
Self::new_with_bounds(f, 0.0, 1.0)
|
Self::new_with_bounds(f, 0.0, 1.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_shared(&self, arena: &mut Arena) -> DevicePiecewiseConstant1D {
|
pub fn new_with_bounds(f: &[Float], min: Float, max: Float) -> Self {
|
||||||
let func_ptr = arena.alloc_slice(&self.func);
|
|
||||||
let cdf_ptr = arena.alloc_slice(&self.cdf);
|
|
||||||
|
|
||||||
DevicePiecewiseConstant1D {
|
|
||||||
func: func_ptr,
|
|
||||||
cdf: cdf_ptr,
|
|
||||||
func_integral: self.func_integral,
|
|
||||||
n: func.len(),
|
|
||||||
min: self.min,
|
|
||||||
max: self.max,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_func<F>(f: F, min: Float, max: Float, n: usize) -> Self
|
|
||||||
where
|
|
||||||
F: Fn(Float) -> Float,
|
|
||||||
{
|
|
||||||
let delta = (max - min) / n as Float;
|
|
||||||
let values: Vec<Float> = (0..n)
|
|
||||||
.map(|i| {
|
|
||||||
let x = min + (i as Float + 0.5) * delta;
|
|
||||||
f(x)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Self::new_with_bounds(values, min, max)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_with_bounds(f: Vec<Float>, min: Float, max: Float) -> Self {
|
|
||||||
let n = f.len();
|
let n = f.len();
|
||||||
let mut cdf = Vec::with_capacity(n + 1);
|
|
||||||
cdf.push(0.0);
|
|
||||||
|
|
||||||
let delta = (max - min) / n as Float;
|
let mut func_vec = f.to_vec();
|
||||||
for i in 0..n {
|
let mut cdf_vec = vec![0.0; n + 1];
|
||||||
cdf.push(cdf[i] + f[i] * delta);
|
|
||||||
|
cdf_vec[0] = 0.0;
|
||||||
|
for i in 1..=n {
|
||||||
|
cdf_vec[i] = cdf_vec[i - 1] + func_vec[i - 1] / n as Float;
|
||||||
}
|
}
|
||||||
|
|
||||||
let func_integral = cdf[n];
|
let func_integral = cdf_vec[n];
|
||||||
|
|
||||||
if func_integral > 0.0 {
|
if func_integral > 0.0 {
|
||||||
for c in &mut cdf {
|
for i in 1..=n {
|
||||||
*c /= func_integral;
|
cdf_vec[i] /= func_integral;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i in 1..=n {
|
||||||
|
cdf_vec[i] = i as Float / n as Float;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to boxed slices (no more reallocation possible)
|
let view = PiecewiseConstant1D {
|
||||||
let func: Box<[Float]> = f.into_boxed_slice();
|
func: Ptr(func_vec.as_ptr()),
|
||||||
let cdf: Box<[Float]> = cdf.into_boxed_slice();
|
cdf: Ptr(cdf_vec.as_ptr()),
|
||||||
|
|
||||||
let device = DevicePiecewiseConstant1D {
|
|
||||||
func: func.as_ptr(),
|
|
||||||
cdf: cdf.as_ptr(),
|
|
||||||
min,
|
min,
|
||||||
max,
|
max,
|
||||||
n: n as u32,
|
n: n as u32,
|
||||||
func_integral,
|
func_integral,
|
||||||
};
|
};
|
||||||
|
|
||||||
Self { func, cdf, device }
|
Self {
|
||||||
}
|
view,
|
||||||
|
_func: func_vec,
|
||||||
// Accessors
|
_cdf: cdf_vec,
|
||||||
pub fn min(&self) -> Float {
|
}
|
||||||
self.device.min
|
|
||||||
}
|
|
||||||
pub fn max(&self) -> Float {
|
|
||||||
self.device.max
|
|
||||||
}
|
|
||||||
pub fn n(&self) -> usize {
|
|
||||||
self.device.n as usize
|
|
||||||
}
|
|
||||||
pub fn integral(&self) -> Float {
|
|
||||||
self.device.func_integral
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn func(&self) -> &[Float] {
|
|
||||||
&self.func
|
|
||||||
}
|
|
||||||
pub fn cdf(&self) -> &[Float] {
|
|
||||||
&self.cdf
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Deref for PiecewiseConstant1D {
|
#[derive(Debug, Clone)]
|
||||||
type Target = DevicePiecewiseConstant1D;
|
pub struct PiecewiseConstant2DHost {
|
||||||
|
pub view: PiecewiseConstant2D,
|
||||||
|
_p_conditional_v: Vec<PiecewiseConstant1D>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for PiecewiseConstant2DHost {
|
||||||
|
type Target = PiecewiseConstant2D;
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.device
|
&self.view
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PiecewiseConstant2D {
|
impl PiecewiseConstant2DHost {
|
||||||
conditionals: Vec<PiecewiseConstant1D>,
|
pub fn new(data: &Array2D<Float>, x_size: u32, y_size: u32, domain: Bounds2f) -> Self {
|
||||||
marginal: PiecewiseConstant1D,
|
let nu = x_size as usize;
|
||||||
conditional_devices: Box<[DevicePiecewiseConstant1D]>,
|
let nv = y_size as usize;
|
||||||
pub device: DevicePiecewiseConstant2D,
|
let mut conditionals = Vec::with_capacity(nv);
|
||||||
}
|
for v in 0..nv {
|
||||||
|
let row = unsafe { core::slice::from_raw_parts(data.values.add(v * nu), nu) };
|
||||||
impl PiecewiseConstant2D {
|
conditionals.push(PiecewiseConstant1D::new_with_bounds(
|
||||||
pub fn new(data: &[Float], n_u: usize, n_v: usize) -> Self {
|
row,
|
||||||
assert_eq!(data.len(), n_u * n_v);
|
domain.p_min.x(),
|
||||||
|
domain.p_max.x(),
|
||||||
// Build conditional distributions p(u|v) for each row
|
));
|
||||||
let mut conditionals = Vec::with_capacity(n_v);
|
|
||||||
let mut marginal_func = Vec::with_capacity(n_v);
|
|
||||||
|
|
||||||
for v in 0..n_v {
|
|
||||||
let row_start = v * n_u;
|
|
||||||
let row: Vec<Float> = data[row_start..row_start + n_u].to_vec();
|
|
||||||
|
|
||||||
let conditional = PiecewiseConstant1D::new_with_bounds(row, 0.0, 1.0);
|
|
||||||
marginal_func.push(conditional.integral());
|
|
||||||
conditionals.push(conditional);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build marginal distribution p(v)
|
let marginal_funcs: Vec<Float> = conditionals.iter().map(|c| c.func_integral).collect();
|
||||||
let marginal = PiecewiseConstant1D::new_with_bounds(marginal_func, 0.0, 1.0);
|
let p_marginal = PiecewiseConstant1D::new_with_bounds(
|
||||||
|
&marginal_funcs,
|
||||||
|
domain.p_min.y(),
|
||||||
|
domain.p_max.y(),
|
||||||
|
);
|
||||||
|
|
||||||
// Create array of device structs
|
let p_conditional_v = conditionals.as_mut_ptr();
|
||||||
let conditional_devices: Box<[DevicePiecewiseConstant1D]> = conditionals
|
std::mem::forget(conditionals);
|
||||||
.iter()
|
let view = PiecewiseConstant2D {
|
||||||
.map(|c| c.device)
|
domain,
|
||||||
.collect::<Vec<_>>()
|
p_marginal,
|
||||||
.into_boxed_slice();
|
n_conditionals: nv,
|
||||||
|
p_conditional_v: Ptr(p_conditional_v),
|
||||||
let device = DevicePiecewiseConstant2D {
|
|
||||||
conditional: conditional_devices.as_ptr(),
|
|
||||||
marginal: marginal.device,
|
|
||||||
n_u: n_u as u32,
|
|
||||||
n_v: n_v as u32,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
conditionals,
|
view,
|
||||||
marginal,
|
_p_conditional_v: p_conditional_v,
|
||||||
conditional_devices,
|
|
||||||
device,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_image(image: &Image) -> Self {
|
pub fn new_with_bounds(data: &Array2D<Float>, domain: Bounds2f) -> Self {
|
||||||
let res = image.resolution();
|
Self::new(data, data.x_size(), data.y_size(), domain)
|
||||||
let n_u = res.x() as usize;
|
|
||||||
let n_v = res.y() as usize;
|
|
||||||
|
|
||||||
let mut data = Vec::with_capacity(n_u * n_v);
|
|
||||||
|
|
||||||
for v in 0..n_v {
|
|
||||||
for u in 0..n_u {
|
|
||||||
let p = Point2i::new(u as i32, v as i32);
|
|
||||||
let luminance = image.get_channels(p).average();
|
|
||||||
data.push(luminance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Self::new(&data, n_u, n_v)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn integral(&self) -> Float {
|
pub fn new_with_data(data: &Array2D<Float>) -> Self {
|
||||||
self.marginal.integral()
|
let nx = data.x_size();
|
||||||
|
let ny = data.y_size();
|
||||||
|
let domain = Bounds2f::from_points(Point2f::new(0.0, 0.0), Point2f::new(1.0, 1.0));
|
||||||
|
|
||||||
|
Self::new(data, nx, ny, domain)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue