Compare commits
5 commits
dad7300a14
...
050698c1d0
| Author | SHA1 | Date | |
|---|---|---|---|
| 050698c1d0 | |||
| 5b4928e1aa | |||
| 31106696bd | |||
| a4c751bbcd | |||
| fa4692bfe6 |
112 changed files with 2720 additions and 4811 deletions
|
|
@ -9,7 +9,7 @@ use_f64 = []
|
||||||
use_gpu = ["dep:wgpu"]
|
use_gpu = ["dep:wgpu"]
|
||||||
use_nvtx = ["dep:nvtx"]
|
use_nvtx = ["dep:nvtx"]
|
||||||
cuda = ["dep:cudarc", "dep:cust", "dep:cust_raw", "dep:cuda-runtime-sys"]
|
cuda = ["dep:cudarc", "dep:cust", "dep:cust_raw", "dep:cuda-runtime-sys"]
|
||||||
vulkan = ["ash", "gpu-allocator"]
|
vulkan = ["dep:ash", "dep:gpu-allocator", "shared/vulkan"]
|
||||||
ash = ["dep:ash"]
|
ash = ["dep:ash"]
|
||||||
gpu-allocator = ["dep:gpu-allocator"]
|
gpu-allocator = ["dep:gpu-allocator"]
|
||||||
jemalloc = ["jemallocator"]
|
jemalloc = ["jemallocator"]
|
||||||
|
|
@ -54,6 +54,7 @@ cust_raw = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", def
|
||||||
cuda-runtime-sys = { version = "0.3.0-alpha.1", optional = true}
|
cuda-runtime-sys = { version = "0.3.0-alpha.1", optional = true}
|
||||||
cudarc = { version = "0.18.2", features = ["cuda-13000"], optional = true }
|
cudarc = { version = "0.18.2", features = ["cuda-13000"], optional = true }
|
||||||
jemallocator = { version = "0.5", optional = true }
|
jemallocator = { version = "0.5", optional = true }
|
||||||
|
syn = "2.0.117"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
spirv-builder = { git = "https://github.com/rust-gpu/rust-gpu", branch = "main", optional = true }
|
spirv-builder = { git = "https://github.com/rust-gpu/rust-gpu", branch = "main", optional = true }
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,12 @@ edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "2.10.0"
|
bitflags = "2.10.0"
|
||||||
|
half = "2.7.1"
|
||||||
bytemuck = { version = "1.24.0", features = ["derive"] }
|
bytemuck = { version = "1.24.0", features = ["derive"] }
|
||||||
enum_dispatch = "0.3.13"
|
enum_dispatch = "0.3.13"
|
||||||
|
ash = { version = "0.38", optional = true }
|
||||||
|
parking_lot = { version = "0.12.5", optional = true }
|
||||||
|
gpu-allocator = { version = "0.28", features = ["vulkan"], optional = true }
|
||||||
num-traits = { version = "0.2.19", default-features = false, features = ["libm"] }
|
num-traits = { version = "0.2.19", default-features = false, features = ["libm"] }
|
||||||
cuda_std = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", default-features = false, optional = true }
|
cuda_std = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", default-features = false, optional = true }
|
||||||
|
|
||||||
|
|
@ -14,3 +18,4 @@ cuda_std = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", def
|
||||||
use_f64 = []
|
use_f64 = []
|
||||||
cuda = ["cuda_std"]
|
cuda = ["cuda_std"]
|
||||||
cpu_debug = []
|
cpu_debug = []
|
||||||
|
vulkan = ["dep:ash", "dep:gpu-allocator", "dep:parking_lot"]
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,13 @@ use num_traits::Float as NumFloat;
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct MeasuredBxDFData {
|
pub struct MeasuredBxDFData {
|
||||||
pub wavelengths: Ptr<Float>,
|
|
||||||
pub spectra: PiecewiseLinear2D<3>,
|
|
||||||
pub ndf: PiecewiseLinear2D<0>,
|
|
||||||
pub vndf: PiecewiseLinear2D<2>,
|
|
||||||
pub sigma: PiecewiseLinear2D<0>,
|
|
||||||
pub isotropic: bool,
|
pub isotropic: bool,
|
||||||
pub luminance: PiecewiseLinear2D<2>,
|
pub wavelengths: Ptr<Float>,
|
||||||
|
pub spectra: Ptr<PiecewiseLinear2D<3>>,
|
||||||
|
pub ndf: Ptr<PiecewiseLinear2D<0>>,
|
||||||
|
pub vndf: Ptr<PiecewiseLinear2D<2>>,
|
||||||
|
pub sigma: Ptr<PiecewiseLinear2D<0>>,
|
||||||
|
pub luminance: Ptr<PiecewiseLinear2D<2>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,16 @@
|
||||||
use crate::PI;
|
|
||||||
use crate::core::camera::{CameraBase, CameraRay, CameraTrait, CameraTransform};
|
use crate::core::camera::{CameraBase, CameraRay, CameraTrait, CameraTransform};
|
||||||
use crate::core::color::SRGB;
|
use crate::core::color::SRGB;
|
||||||
use crate::core::film::Film;
|
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::sampler::CameraSample;
|
use crate::core::sampler::CameraSample;
|
||||||
use crate::core::scattering::refract;
|
use crate::core::scattering::refract;
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::Ptr;
|
|
||||||
use crate::utils::math::{lerp, quadratic, square};
|
use crate::utils::math::{lerp, quadratic, square};
|
||||||
|
use crate::{Float, GVec, Ptr, PI, gvec};
|
||||||
use num_traits::Float as NumFloat;
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
@ -34,19 +32,92 @@ pub struct ExitPupilSample {
|
||||||
pub const EXIT_PUPIL_SAMPLES: usize = 64;
|
pub const EXIT_PUPIL_SAMPLES: usize = 64;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct RealisticCamera {
|
pub struct RealisticCamera {
|
||||||
pub base: CameraBase,
|
pub base: CameraBase,
|
||||||
pub focus_distance: Float,
|
pub focus_distance: Float,
|
||||||
pub set_aperture_diameter: Float,
|
pub set_aperture_diameter: Float,
|
||||||
pub aperture_image: Ptr<DeviceImage>,
|
pub aperture_image: Ptr<Image>,
|
||||||
pub element_interfaces: Ptr<LensElementInterface>,
|
pub element_interfaces: GVec<LensElementInterface>,
|
||||||
pub n_elements: usize,
|
pub n_elements: usize,
|
||||||
pub physical_extent: Bounds2f,
|
pub physical_extent: Bounds2f,
|
||||||
pub exit_pupil_bounds: [Bounds2f; EXIT_PUPIL_SAMPLES],
|
pub exit_pupil_bounds: [Bounds2f; EXIT_PUPIL_SAMPLES],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RealisticCamera {
|
impl RealisticCamera {
|
||||||
|
pub fn new(
|
||||||
|
base: CameraBase,
|
||||||
|
lens_params: &[Float],
|
||||||
|
focus_distance: Float,
|
||||||
|
set_aperture_diameter: Float,
|
||||||
|
aperture_image: Ptr<Image>,
|
||||||
|
) -> Self {
|
||||||
|
let film_ptr = base.film;
|
||||||
|
if film_ptr.is_null() {
|
||||||
|
panic!("Camera must have a film");
|
||||||
|
}
|
||||||
|
let film = &*film_ptr;
|
||||||
|
|
||||||
|
let aspect = film.full_resolution().x() as Float / film.full_resolution().y() as Float;
|
||||||
|
let diagonal = film.diagonal();
|
||||||
|
let x = (square(diagonal) / (1.0 + square(diagonal))).sqrt();
|
||||||
|
let y = x * aspect;
|
||||||
|
let physical_extent =
|
||||||
|
Bounds2f::from_points(Point2f::new(-x / 2., -y / 2.), Point2f::new(x / 2., y / 2.));
|
||||||
|
let mut element_interfaces: GVec<LensElementInterface> = gvec();
|
||||||
|
|
||||||
|
for i in (0..lens_params.len()).step_by(4) {
|
||||||
|
let curvature_radius = lens_params[i] / 1000.0;
|
||||||
|
let thickness = lens_params[i + 1] / 1000.0;
|
||||||
|
let eta = lens_params[i + 2];
|
||||||
|
let mut aperture_diameter = lens_params[i + 3] / 1000.0;
|
||||||
|
|
||||||
|
if curvature_radius == 0.0 {
|
||||||
|
aperture_diameter /= 1000.0;
|
||||||
|
if set_aperture_diameter > aperture_diameter {
|
||||||
|
// println!("Aperture is larger than possible")
|
||||||
|
aperture_diameter = -1.;
|
||||||
|
} else {
|
||||||
|
aperture_diameter = set_aperture_diameter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let el_int = LensElementInterface {
|
||||||
|
curvature_radius,
|
||||||
|
thickness,
|
||||||
|
eta,
|
||||||
|
aperture_radius: aperture_diameter / 2.0,
|
||||||
|
};
|
||||||
|
element_interfaces.push(el_int);
|
||||||
|
}
|
||||||
|
|
||||||
|
let half_diag = film.diagonal() / 2.0;
|
||||||
|
let mut exit_pupil_bounds = [Bounds2f::default(); EXIT_PUPIL_SAMPLES];
|
||||||
|
|
||||||
|
for i in 0..EXIT_PUPIL_SAMPLES {
|
||||||
|
let r0 = (i as Float / EXIT_PUPIL_SAMPLES as Float) * half_diag;
|
||||||
|
let r1 = ((i + 1) as Float / EXIT_PUPIL_SAMPLES as Float) * half_diag;
|
||||||
|
exit_pupil_bounds[i] =
|
||||||
|
RealisticCamera::compute_exit_pupil_bounds(&element_interfaces, r0, r1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let n_elements = element_interfaces.len();
|
||||||
|
|
||||||
|
RealisticCamera {
|
||||||
|
base,
|
||||||
|
focus_distance,
|
||||||
|
element_interfaces,
|
||||||
|
n_elements,
|
||||||
|
physical_extent,
|
||||||
|
set_aperture_diameter,
|
||||||
|
aperture_image,
|
||||||
|
exit_pupil_bounds,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn lens(&self, idx: usize) -> &LensElementInterface {
|
||||||
|
unsafe { &*self.element_interfaces.as_ptr().add(idx) }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn compute_cardinal_points(r_in: Ray, r_out: Ray) -> (Float, Float) {
|
pub fn compute_cardinal_points(r_in: Ray, r_out: Ray) -> (Float, Float) {
|
||||||
let tf = -r_out.o.x() / r_out.d.x();
|
let tf = -r_out.o.x() / r_out.d.x();
|
||||||
let tp = (r_in.o.x() - r_out.o.x()) / r_out.d.x();
|
let tp = (r_in.o.x() - r_out.o.x()) / r_out.d.x();
|
||||||
|
|
@ -96,14 +167,12 @@ impl RealisticCamera {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let delta = (pz[1] - z + pz[0] - c.sqrt()) / 2.;
|
let delta = (pz[1] - z + pz[0] - c.sqrt()) / 2.;
|
||||||
let last_interface = unsafe { self.element_interfaces.add(self.n_elements) };
|
let last_interface = unsafe { self.lens(self.n_elements - 1) };
|
||||||
last_interface.thickness + delta
|
last_interface.thickness + delta
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bound_exit_pupil(&self, film_x_0: Float, film_x_1: Float) -> Bounds2f {
|
pub fn bound_exit_pupil(&self, film_x_0: Float, film_x_1: Float) -> Bounds2f {
|
||||||
let interface_array = unsafe {
|
let interface_array = self.element_interfaces.as_slice();
|
||||||
core::slice::from_raw_parts(self.element_interfaces.as_raw(), self.n_elements as usize)
|
|
||||||
};
|
|
||||||
Self::compute_exit_pupil_bounds(interface_array, film_x_0, film_x_1)
|
Self::compute_exit_pupil_bounds(interface_array, film_x_0, film_x_1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,7 +194,6 @@ impl RealisticCamera {
|
||||||
let trace_lenses_from_film = |_ray: Ray, _place: Option<Ray>| true;
|
let trace_lenses_from_film = |_ray: Ray, _place: Option<Ray>| true;
|
||||||
for i in 0..n_samples {
|
for i in 0..n_samples {
|
||||||
// Find location of sample points on $x$ segment and rear lens element
|
// Find location of sample points on $x$ segment and rear lens element
|
||||||
//
|
|
||||||
let p_film = Point3f::new(
|
let p_film = Point3f::new(
|
||||||
lerp((i as Float + 0.5) / n_samples as Float, film_x_0, film_x_1),
|
lerp((i as Float + 0.5) / n_samples as Float, film_x_0, film_x_1),
|
||||||
0.,
|
0.,
|
||||||
|
|
@ -206,7 +274,7 @@ impl RealisticCamera {
|
||||||
);
|
);
|
||||||
|
|
||||||
for i in (0..self.n_elements - 1).rev() {
|
for i in (0..self.n_elements - 1).rev() {
|
||||||
let element: &LensElementInterface = unsafe { &self.element_interfaces.add(i) };
|
let element: &LensElementInterface = unsafe { self.lens(i) };
|
||||||
// Update ray from film accounting for interaction with _element_
|
// Update ray from film accounting for interaction with _element_
|
||||||
element_z -= element.thickness;
|
element_z -= element.thickness;
|
||||||
|
|
||||||
|
|
@ -245,7 +313,7 @@ impl RealisticCamera {
|
||||||
// Update ray path for element interface interaction
|
// Update ray path for element interface interaction
|
||||||
if !is_stop {
|
if !is_stop {
|
||||||
let eta_i = element.eta;
|
let eta_i = element.eta;
|
||||||
let interface_i = unsafe { self.element_interfaces.add(i - 1) };
|
let interface_i = unsafe { self.lens(i) };
|
||||||
let eta_t = if i > 0 && interface_i.eta != 0. {
|
let eta_t = if i > 0 && interface_i.eta != 0. {
|
||||||
interface_i.eta
|
interface_i.eta
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -306,21 +374,21 @@ impl RealisticCamera {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lens_rear_z(&self) -> Float {
|
pub fn lens_rear_z(&self) -> Float {
|
||||||
let last_interface = unsafe { self.element_interfaces.add(self.n_elements - 1) };
|
let last_interface = unsafe { self.lens(self.n_elements - 1) };
|
||||||
last_interface.thickness
|
last_interface.thickness
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lens_front_z(&self) -> Float {
|
pub fn lens_front_z(&self) -> Float {
|
||||||
let mut z_sum = 0.;
|
let mut z_sum = 0.;
|
||||||
for i in 0..self.n_elements {
|
for i in 0..self.n_elements {
|
||||||
let element = unsafe { self.element_interfaces.add(i) };
|
let element = unsafe { self.lens(i) };
|
||||||
z_sum += element.thickness;
|
z_sum += element.thickness;
|
||||||
}
|
}
|
||||||
z_sum
|
z_sum
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rear_element_radius(&self) -> Float {
|
pub fn rear_element_radius(&self) -> Float {
|
||||||
let last_interface = unsafe { self.element_interfaces.add(self.n_elements - 1) };
|
let last_interface = unsafe { self.lens(self.n_elements - 1) };
|
||||||
last_interface.aperture_radius
|
last_interface.aperture_radius
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
use crate::core::geometry::{Bounds3f, Ray, Vector3f};
|
use crate::core::geometry::{Bounds3f, Ray, Vector3f};
|
||||||
use crate::core::pbrt::Float;
|
|
||||||
use crate::core::primitive::{Primitive, PrimitiveTrait};
|
use crate::core::primitive::{Primitive, PrimitiveTrait};
|
||||||
use crate::core::shape::ShapeIntersection;
|
use crate::core::shape::ShapeIntersection;
|
||||||
use crate::utils::Ptr;
|
use crate::{Float, Ptr, GVec, gvec};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
@ -15,40 +14,40 @@ pub struct LinearBVHNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DeviceBVHAggregate {
|
pub struct BVHAggregate {
|
||||||
pub max_prims_in_node: u32,
|
|
||||||
pub primitives: Ptr<Primitive>,
|
|
||||||
pub primitive_count: u32,
|
|
||||||
pub nodes: Ptr<LinearBVHNode>,
|
|
||||||
pub node_count: u32,
|
pub node_count: u32,
|
||||||
|
pub max_prims_in_node: u32,
|
||||||
|
pub primitive_count: u32,
|
||||||
|
pub primitives: GVec<Primitive>,
|
||||||
|
pub nodes: GVec<LinearBVHNode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeviceBVHAggregate {
|
impl BVHAggregate {
|
||||||
pub const fn empty() -> Self {
|
pub fn empty() -> Self {
|
||||||
Self {
|
Self {
|
||||||
max_prims_in_node: 0,
|
max_prims_in_node: 0,
|
||||||
primitives: Ptr::null(),
|
primitives: gvec(),
|
||||||
primitive_count: 0,
|
primitive_count: 0,
|
||||||
nodes: Ptr::null(),
|
nodes: gvec(),
|
||||||
node_count: 0,
|
node_count: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn node(&self, i: usize) -> &LinearBVHNode {
|
fn node(&self, i: usize) -> &LinearBVHNode {
|
||||||
unsafe { self.nodes.at(i) }
|
unsafe { self.nodes.get_unchecked(i) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn primitive(&self, i: usize) -> &Primitive {
|
fn primitive(&self, i: usize) -> &Primitive {
|
||||||
unsafe { self.primitives.at(i) }
|
unsafe { self.primitives.get_unchecked(i) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrimitiveTrait for DeviceBVHAggregate {
|
impl PrimitiveTrait for BVHAggregate {
|
||||||
fn bounds(&self) -> Bounds3f {
|
fn bounds(&self) -> Bounds3f {
|
||||||
if self.nodes.is_null() || self.node_count == 0 {
|
if self.nodes.is_empty() || self.node_count == 0 {
|
||||||
Bounds3f::default()
|
Bounds3f::default()
|
||||||
} else {
|
} else {
|
||||||
self.node(0).bounds
|
self.node(0).bounds
|
||||||
|
|
@ -56,7 +55,7 @@ impl PrimitiveTrait for DeviceBVHAggregate {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn intersect(&self, r: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection> {
|
fn intersect(&self, r: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection> {
|
||||||
if self.nodes.is_null() {
|
if self.nodes.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -124,7 +123,7 @@ impl PrimitiveTrait for DeviceBVHAggregate {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn intersect_p(&self, r: &Ray, t_max: Option<Float>) -> bool {
|
fn intersect_p(&self, r: &Ray, t_max: Option<Float>) -> bool {
|
||||||
if self.nodes.is_null() || self.node_count == 0 {
|
if self.nodes.is_empty() || self.node_count == 0 {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ pub struct CameraBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[enum_dispatch(CameraTrait)]
|
#[enum_dispatch(CameraTrait)]
|
||||||
pub enum Camera {
|
pub enum Camera {
|
||||||
Perspective(PerspectiveCamera),
|
Perspective(PerspectiveCamera),
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,15 @@
|
||||||
|
use crate::core::geometry::Point2f;
|
||||||
|
use crate::core::spectrum::Spectrum;
|
||||||
|
use crate::utils::find_interval;
|
||||||
|
use crate::utils::math::{clamp, evaluate_polynomial, lerp, SquareMatrix, SquareMatrix3f};
|
||||||
|
use crate::{Float, GVec, Ptr};
|
||||||
use core::any::TypeId;
|
use core::any::TypeId;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use core::ops::{
|
use core::ops::{
|
||||||
Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
|
Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::Float;
|
|
||||||
use crate::core::geometry::Point2f;
|
|
||||||
use crate::core::spectrum::Spectrum;
|
|
||||||
use crate::utils::Ptr;
|
|
||||||
use crate::utils::find_interval;
|
|
||||||
use crate::utils::math::{SquareMatrix, SquareMatrix3f, clamp, evaluate_polynomial, lerp};
|
|
||||||
use num_traits::Float as NumFloat;
|
|
||||||
|
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Default, Clone, Copy)]
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
|
|
@ -311,17 +308,33 @@ impl RGB {
|
||||||
|
|
||||||
pub fn min_component_index(&self) -> u32 {
|
pub fn min_component_index(&self) -> u32 {
|
||||||
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 {
|
||||||
if self.g < self.b { 1 } else { 2 }
|
if self.g < self.b {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn max_component_index(&self) -> u32 {
|
pub fn max_component_index(&self) -> u32 {
|
||||||
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 {
|
||||||
if self.g > self.b { 1 } else { 2 }
|
if self.g > self.b {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -606,7 +619,6 @@ pub struct RGBSigmoidPolynomial {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RGBSigmoidPolynomial {
|
impl RGBSigmoidPolynomial {
|
||||||
#[cfg(not(target_os = "cuda"))]
|
|
||||||
pub fn new(c0: Float, c1: Float, c2: Float) -> Self {
|
pub fn new(c0: Float, c1: Float, c2: Float) -> Self {
|
||||||
Self { c0, c1, c2 }
|
Self { c0, c1, c2 }
|
||||||
}
|
}
|
||||||
|
|
@ -1087,7 +1099,7 @@ impl Mul<Float> for Coeffs {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct RGBToSpectrumTable {
|
pub struct RGBToSpectrumTable {
|
||||||
pub z_nodes: Ptr<Float>,
|
pub z_nodes: Ptr<Float>,
|
||||||
pub coeffs: Ptr<Coeffs>,
|
pub coeffs: Ptr<Coeffs>,
|
||||||
|
|
@ -1115,7 +1127,11 @@ impl RGBToSpectrumTable {
|
||||||
|
|
||||||
// Identify the primary bucket (c) based on the dominant axis
|
// Identify the primary bucket (c) based on the dominant axis
|
||||||
let c_idx = if rgb[0] > rgb[1] {
|
let c_idx = if rgb[0] > rgb[1] {
|
||||||
if rgb[0] > rgb[2] { 0 } else { 2 }
|
if rgb[0] > rgb[2] {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
2
|
||||||
|
}
|
||||||
} else if rgb[1] > rgb[2] {
|
} else if rgb[1] > rgb[2] {
|
||||||
1
|
1
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1137,10 +1153,9 @@ 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 =
|
let z_nodes = unsafe { core::slice::from_raw_parts(self.z_nodes.as_raw(), RES as usize) };
|
||||||
unsafe { core::slice::from_raw_parts(self.z_nodes.as_raw(), RES as usize) };
|
let zi = find_interval(RES, |i| z_nodes[i as usize] < z) as usize;
|
||||||
let zi = find_interval(RES, |i| z_nodes_slice[i as usize] < z) as usize;
|
let dz = (z - z_nodes[zi]) / (z_nodes[zi + 1] - z_nodes[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 u32).min(RES - 2);
|
||||||
let dx = x_float - xi as Float;
|
let dx = x_float - xi as Float;
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,23 @@
|
||||||
use crate::core::camera::CameraTransform;
|
use crate::core::camera::CameraTransform;
|
||||||
use crate::core::color::{MatrixMulColor, RGB, SRGB, XYZ, white_balance};
|
use crate::core::color::{white_balance, MatrixMulColor, RGB, SRGB, XYZ};
|
||||||
use crate::core::filter::{Filter, FilterTrait};
|
use crate::core::filter::{Filter, FilterTrait};
|
||||||
use crate::core::geometry::{
|
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::spectrum::{Spectrum, SpectrumTrait, StandardSpectra};
|
use crate::core::spectrum::{Spectrum, SpectrumTrait, StandardSpectra};
|
||||||
use crate::spectra::{
|
use crate::spectra::{
|
||||||
ConstantSpectrum, DenselySampledSpectrum, LAMBDA_MAX, LAMBDA_MIN, N_SPECTRUM_SAMPLES,
|
colorspace, ConstantSpectrum, DenselySampledSpectrum, PiecewiseLinearSpectrum, RGBColorSpace,
|
||||||
PiecewiseLinearSpectrum, RGBColorSpace, SampledSpectrum, SampledWavelengths, colorspace,
|
SampledSpectrum, SampledWavelengths, LAMBDA_MAX, LAMBDA_MIN, N_SPECTRUM_SAMPLES,
|
||||||
};
|
};
|
||||||
use crate::utils::containers::DeviceArray2D;
|
|
||||||
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::{wrap_equal_area_square, SquareMatrix};
|
||||||
use crate::utils::sampling::VarianceEstimator;
|
use crate::utils::sampling::VarianceEstimator;
|
||||||
use crate::utils::transform::AnimatedTransform;
|
use crate::utils::transform::AnimatedTransform;
|
||||||
use crate::utils::{AtomicFloat, Ptr, gpu_array_from_fn};
|
use crate::utils::{gpu_array_from_fn, AtomicFloat};
|
||||||
|
use crate::{gvec_from_slice, gvec_with_capacity, Array2D, Float, GVec, Ptr};
|
||||||
use num_traits::Float as NumFloat;
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
@ -29,7 +28,7 @@ pub struct RGBFilm {
|
||||||
pub write_fp16: bool,
|
pub write_fp16: bool,
|
||||||
pub filter_integral: Float,
|
pub filter_integral: Float,
|
||||||
pub output_rgbf_from_sensor_rgb: SquareMatrix<Float, 3>,
|
pub output_rgbf_from_sensor_rgb: SquareMatrix<Float, 3>,
|
||||||
pub pixels: DeviceArray2D<RGBPixel>,
|
pub pixels: Array2D<RGBPixel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
@ -51,6 +50,43 @@ impl Default for RGBPixel {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RGBFilm {
|
impl RGBFilm {
|
||||||
|
pub fn new(
|
||||||
|
base: FilmBase,
|
||||||
|
colorspace: &RGBColorSpace,
|
||||||
|
max_component_value: Float,
|
||||||
|
write_fp16: bool,
|
||||||
|
) -> Self {
|
||||||
|
let sensor_ptr = base.sensor;
|
||||||
|
// TODO: This wont work on gpu, need to add check on host side
|
||||||
|
if sensor_ptr.is_null() {
|
||||||
|
panic!("Film must have a sensor");
|
||||||
|
}
|
||||||
|
let sensor = &*sensor_ptr;
|
||||||
|
let filter_integral = base.filter.integral();
|
||||||
|
let sensor_matrix = sensor.xyz_from_sensor_rgb;
|
||||||
|
let output_rgbf_from_sensor_rgb = colorspace.rgb_from_xyz * sensor_matrix;
|
||||||
|
|
||||||
|
let width = base.pixel_bounds.p_max.x() - base.pixel_bounds.p_min.x();
|
||||||
|
let height = base.pixel_bounds.p_max.y() - base.pixel_bounds.p_min.y();
|
||||||
|
let count = (width * height) as usize;
|
||||||
|
|
||||||
|
let mut pixel_vec = gvec_with_capacity(count);
|
||||||
|
for _ in 0..count {
|
||||||
|
pixel_vec.push(RGBPixel::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
let pixels: Array2D<RGBPixel> = Array2D::new(base.pixel_bounds);
|
||||||
|
|
||||||
|
RGBFilm {
|
||||||
|
base,
|
||||||
|
max_component_value,
|
||||||
|
write_fp16,
|
||||||
|
filter_integral,
|
||||||
|
output_rgbf_from_sensor_rgb,
|
||||||
|
pixels,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn base(&self) -> &FilmBase {
|
pub fn base(&self) -> &FilmBase {
|
||||||
&self.base
|
&self.base
|
||||||
}
|
}
|
||||||
|
|
@ -59,7 +95,7 @@ impl RGBFilm {
|
||||||
&mut self.base
|
&mut self.base
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_sensor(&self) -> &DevicePixelSensor {
|
pub fn get_sensor(&self) -> &PixelSensor {
|
||||||
#[cfg(not(target_os = "cuda"))]
|
#[cfg(not(target_os = "cuda"))]
|
||||||
{
|
{
|
||||||
if self.base.sensor.is_null() {
|
if self.base.sensor.is_null() {
|
||||||
|
|
@ -206,7 +242,7 @@ pub struct GBufferFilm {
|
||||||
pub base: FilmBase,
|
pub base: FilmBase,
|
||||||
pub output_from_render: AnimatedTransform,
|
pub output_from_render: AnimatedTransform,
|
||||||
pub apply_inverse: bool,
|
pub apply_inverse: bool,
|
||||||
pub pixels: DeviceArray2D<GBufferPixel>,
|
pub pixels: Array2D<GBufferPixel>,
|
||||||
pub colorspace: RGBColorSpace,
|
pub colorspace: RGBColorSpace,
|
||||||
pub max_component_value: Float,
|
pub max_component_value: Float,
|
||||||
pub write_fp16: bool,
|
pub write_fp16: bool,
|
||||||
|
|
@ -215,6 +251,37 @@ pub struct GBufferFilm {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GBufferFilm {
|
impl GBufferFilm {
|
||||||
|
pub fn new(
|
||||||
|
base: &FilmBase,
|
||||||
|
output_from_render: &AnimatedTransform,
|
||||||
|
apply_inverse: bool,
|
||||||
|
colorspace: &RGBColorSpace,
|
||||||
|
max_component_value: Float,
|
||||||
|
write_fp16: bool,
|
||||||
|
) -> Self {
|
||||||
|
assert!(!base.pixel_bounds.is_empty());
|
||||||
|
let sensor_ptr = base.sensor;
|
||||||
|
if sensor_ptr.is_null() {
|
||||||
|
panic!("Film must have a sensor");
|
||||||
|
}
|
||||||
|
let sensor = &*sensor_ptr;
|
||||||
|
let output_rgbf_from_sensor_rgb = colorspace.rgb_from_xyz * sensor.xyz_from_sensor_rgb;
|
||||||
|
let filter_integral = base.filter.integral();
|
||||||
|
let pixels = Array2D::new(base.pixel_bounds);
|
||||||
|
|
||||||
|
GBufferFilm {
|
||||||
|
base: base.clone(),
|
||||||
|
output_from_render: *output_from_render,
|
||||||
|
apply_inverse,
|
||||||
|
pixels,
|
||||||
|
colorspace: colorspace.clone(),
|
||||||
|
max_component_value,
|
||||||
|
write_fp16,
|
||||||
|
filter_integral,
|
||||||
|
output_rgbf_from_sensor_rgb,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn base(&self) -> &FilmBase {
|
pub fn base(&self) -> &FilmBase {
|
||||||
&self.base
|
&self.base
|
||||||
}
|
}
|
||||||
|
|
@ -223,7 +290,7 @@ impl GBufferFilm {
|
||||||
&mut self.base
|
&mut self.base
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_sensor(&self) -> &DevicePixelSensor {
|
pub fn get_sensor(&self) -> &PixelSensor {
|
||||||
#[cfg(not(target_os = "cuda"))]
|
#[cfg(not(target_os = "cuda"))]
|
||||||
{
|
{
|
||||||
if self.base.sensor.is_null() {
|
if self.base.sensor.is_null() {
|
||||||
|
|
@ -352,24 +419,68 @@ impl Default for SpectralPixel {
|
||||||
#[cfg_attr(target_os = "cuda", derive(Copy, Clone))]
|
#[cfg_attr(target_os = "cuda", derive(Copy, Clone))]
|
||||||
pub struct SpectralFilm {
|
pub struct SpectralFilm {
|
||||||
pub base: FilmBase,
|
pub base: FilmBase,
|
||||||
pub colorspace: RGBColorSpace,
|
|
||||||
pub lambda_min: Float,
|
pub lambda_min: Float,
|
||||||
pub lambda_max: Float,
|
pub lambda_max: Float,
|
||||||
pub n_buckets: usize,
|
pub n_buckets: usize,
|
||||||
pub max_component_value: Float,
|
pub max_component_value: Float,
|
||||||
pub write_fp16: bool,
|
pub write_fp16: bool,
|
||||||
pub filter_integral: Float,
|
pub filter_integral: Float,
|
||||||
pub pixels: DeviceArray2D<SpectralPixel>,
|
pub colorspace: RGBColorSpace,
|
||||||
|
pub pixels: Array2D<SpectralPixel>,
|
||||||
pub output_rgbf_from_sensor_rgb: SquareMatrix<Float, 3>,
|
pub output_rgbf_from_sensor_rgb: SquareMatrix<Float, 3>,
|
||||||
pub bucket_sums: *mut f64,
|
pub bucket_sums: GVec<f64>,
|
||||||
pub weight_sums: *mut f64,
|
pub weight_sums: GVec<f64>,
|
||||||
pub bucket_splats: *mut AtomicFloat,
|
pub bucket_splats: GVec<AtomicFloat>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for SpectralFilm {}
|
unsafe impl Send for SpectralFilm {}
|
||||||
unsafe impl Sync for SpectralFilm {}
|
unsafe impl Sync for SpectralFilm {}
|
||||||
|
|
||||||
impl SpectralFilm {
|
impl SpectralFilm {
|
||||||
|
pub fn new(
|
||||||
|
base: &FilmBase,
|
||||||
|
lambda_min: Float,
|
||||||
|
lambda_max: Float,
|
||||||
|
n_buckets: usize,
|
||||||
|
colorspace: &RGBColorSpace,
|
||||||
|
max_component_value: Float,
|
||||||
|
write_fp16: bool,
|
||||||
|
) -> Self {
|
||||||
|
let n_pixels = base.pixel_bounds.area() as usize;
|
||||||
|
let total_buckets = n_pixels * n_buckets;
|
||||||
|
|
||||||
|
let bucket_sums = gvec_with_capacity(total_buckets);
|
||||||
|
let weight_sums = gvec_with_capacity(total_buckets);
|
||||||
|
|
||||||
|
let mut bucket_splats = gvec_with_capacity(total_buckets);
|
||||||
|
for _ in 0..total_buckets {
|
||||||
|
bucket_splats.push(AtomicFloat::new(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut pixels = Array2D::<SpectralPixel>::new(base.pixel_bounds);
|
||||||
|
|
||||||
|
for i in 0..n_pixels {
|
||||||
|
let pixel = pixels.get_linear_mut(i);
|
||||||
|
pixel.bucket_offset = i * n_buckets;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpectralFilm {
|
||||||
|
base: *base,
|
||||||
|
colorspace: colorspace.clone(),
|
||||||
|
lambda_min,
|
||||||
|
lambda_max,
|
||||||
|
n_buckets,
|
||||||
|
max_component_value,
|
||||||
|
write_fp16,
|
||||||
|
filter_integral: base.filter.integral(),
|
||||||
|
output_rgbf_from_sensor_rgb: SquareMatrix::identity(),
|
||||||
|
pixels: Array2D::from_slice(base.pixel_bounds, pixels.as_slice()),
|
||||||
|
bucket_sums,
|
||||||
|
weight_sums,
|
||||||
|
bucket_splats,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn base(&self) -> &FilmBase {
|
pub fn base(&self) -> &FilmBase {
|
||||||
&self.base
|
&self.base
|
||||||
}
|
}
|
||||||
|
|
@ -404,15 +515,15 @@ impl SpectralFilm {
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct DevicePixelSensor {
|
pub struct PixelSensor {
|
||||||
pub xyz_from_sensor_rgb: SquareMatrix<Float, 3>,
|
pub xyz_from_sensor_rgb: SquareMatrix<Float, 3>,
|
||||||
pub r_bar: DenselySampledSpectrum,
|
pub r_bar: Ptr<DenselySampledSpectrum>,
|
||||||
pub g_bar: DenselySampledSpectrum,
|
pub g_bar: Ptr<DenselySampledSpectrum>,
|
||||||
pub b_bar: DenselySampledSpectrum,
|
pub b_bar: Ptr<DenselySampledSpectrum>,
|
||||||
pub imaging_ratio: Float,
|
pub imaging_ratio: Float,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DevicePixelSensor {
|
impl PixelSensor {
|
||||||
pub fn project_reflectance<T>(
|
pub fn project_reflectance<T>(
|
||||||
refl: &Spectrum,
|
refl: &Spectrum,
|
||||||
illum: &Spectrum,
|
illum: &Spectrum,
|
||||||
|
|
@ -492,7 +603,7 @@ pub struct FilmBase {
|
||||||
pub pixel_bounds: Bounds2i,
|
pub pixel_bounds: Bounds2i,
|
||||||
pub filter: Filter,
|
pub filter: Filter,
|
||||||
pub diagonal: Float,
|
pub diagonal: Float,
|
||||||
pub sensor: Ptr<DevicePixelSensor>,
|
pub sensor: Ptr<PixelSensor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
use crate::core::geometry::{Bounds2f, Bounds2i, Point2f, Point2i, Vector2f};
|
use crate::core::geometry::{Bounds2f, Bounds2i, Point2f, Point2i, Vector2f};
|
||||||
use crate::core::pbrt::Float;
|
|
||||||
use crate::filters::*;
|
use crate::filters::*;
|
||||||
use crate::utils::containers::DeviceArray2D;
|
|
||||||
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 crate::{Array2D, Float, Ptr};
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
|
|
||||||
pub struct FilterSample {
|
pub struct FilterSample {
|
||||||
|
|
@ -12,23 +11,45 @@ pub struct FilterSample {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Debug, Copy)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct DeviceFilterSampler {
|
pub struct FilterSampler {
|
||||||
pub domain: Bounds2f,
|
pub domain: Bounds2f,
|
||||||
pub distrib: DevicePiecewiseConstant2D,
|
pub distrib: PiecewiseConstant2D,
|
||||||
pub f: DeviceArray2D<Float>,
|
pub f: Array2D<Float>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeviceFilterSampler {
|
impl FilterSampler {
|
||||||
|
pub fn new<F>(radius: Vector2f, func: F) -> Self
|
||||||
|
where
|
||||||
|
F: Fn(Point2f) -> Float,
|
||||||
|
{
|
||||||
|
let domain = Bounds2f::from_points(
|
||||||
|
Point2f::new(-radius.x(), -radius.y()),
|
||||||
|
Point2f::new(radius.x(), radius.y()),
|
||||||
|
);
|
||||||
|
let nx = (32.0 * radius.x()) as i32;
|
||||||
|
let ny = (32.0 * radius.y()) as i32;
|
||||||
|
let mut f = Array2D::new_dims(nx, ny);
|
||||||
|
for y in 0..f.y_size() {
|
||||||
|
for x in 0..f.x_size() {
|
||||||
|
let p = domain.lerp(Point2f::new(
|
||||||
|
(x as Float + 0.5) / f.x_size() as Float,
|
||||||
|
(y as Float + 0.5) / f.y_size() as Float,
|
||||||
|
));
|
||||||
|
f[(x as i32, y as i32)] = func(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let distrib = PiecewiseConstant2D::new_with_bounds(&f, domain);
|
||||||
|
Self { domain, distrib, f }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn sample(&self, u: Point2f) -> FilterSample {
|
pub fn sample(&self, u: Point2f) -> FilterSample {
|
||||||
let (p, pdf, pi) = self.distrib.sample(u);
|
let (p, pdf, pi) = self.distrib.sample(u);
|
||||||
|
|
||||||
if pdf == 0.0 {
|
if pdf == 0.0 {
|
||||||
return FilterSample { p, weight: 0.0 };
|
return FilterSample { p, weight: 0.0 };
|
||||||
}
|
}
|
||||||
|
let idx = pi.x() as usize + pi.y() as usize * self.f.x_size();
|
||||||
let idx = pi.x() as u32 + self.f.x_size();
|
let weight = self.f.as_slice()[idx] / pdf;
|
||||||
let weight = *self.f.get_linear(idx as usize) / pdf;
|
|
||||||
FilterSample { p, weight }
|
FilterSample { p, weight }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -38,16 +59,33 @@ pub trait FilterTrait {
|
||||||
fn radius(&self) -> Vector2f;
|
fn radius(&self) -> Vector2f;
|
||||||
fn evaluate(&self, p: Point2f) -> Float;
|
fn evaluate(&self, p: Point2f) -> Float;
|
||||||
fn integral(&self) -> Float;
|
fn integral(&self) -> Float;
|
||||||
fn sample(&self, u: Point2f) -> DeviceFilterSample;
|
fn sample(&self, u: Point2f) -> FilterSample;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[enum_dispatch(FilterTrait)]
|
#[enum_dispatch(FilterTrait)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum Filter {
|
pub enum Filter {
|
||||||
Box(BoxFilter),
|
Box(Ptr<BoxFilter>),
|
||||||
Gaussian(GaussianFilter),
|
Gaussian(Ptr<GaussianFilter>),
|
||||||
Mitchell(MitchellFilter),
|
Mitchell(Ptr<MitchellFilter>),
|
||||||
LanczosSinc(LanczosSincFilter),
|
LanczosSinc(Ptr<LanczosSincFilter>),
|
||||||
Triangle(TriangleFilter),
|
Triangle(Ptr<TriangleFilter>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: FilterTrait> FilterTrait for Ptr<T> {
|
||||||
|
fn radius(&self) -> Vector2f {
|
||||||
|
unsafe { self.as_ref().radius() }
|
||||||
|
}
|
||||||
|
fn integral(&self) -> Float {
|
||||||
|
unsafe { self.as_ref().integral() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn evaluate(&self, p: Point2f) -> Float {
|
||||||
|
unsafe { self.as_ref().evaluate(p) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sample(&self, p: Point2f) -> FilterSample {
|
||||||
|
unsafe { self.as_ref().sample(p) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
use crate::Float;
|
|
||||||
use crate::core::color::{ColorEncoding, ColorEncodingTrait, LINEAR};
|
use crate::core::color::{ColorEncoding, ColorEncodingTrait, LINEAR};
|
||||||
use crate::core::geometry::{Bounds2f, Point2f, Point2fi, Point2i};
|
use crate::core::geometry::{Bounds2f, Point2f, Point2fi, Point2i};
|
||||||
use crate::utils::Ptr;
|
|
||||||
use crate::utils::containers::DeviceArray2D;
|
|
||||||
use crate::utils::math::{f16_to_f32_software, lerp, square};
|
use crate::utils::math::{f16_to_f32_software, lerp, square};
|
||||||
|
use crate::{gvec_with_capacity, Float, GVec};
|
||||||
use core::hash;
|
use core::hash;
|
||||||
use core::ops::{Deref, DerefMut};
|
use core::ops::{Deref, DerefMut};
|
||||||
use num_traits::Float as NumFloat;
|
use num_traits::Float as NumFloat;
|
||||||
|
|
@ -68,60 +66,134 @@ impl PixelFormat {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Pixels {
|
pub struct Pixels {
|
||||||
ptr: Ptr<u8>,
|
data: GVec<u8>,
|
||||||
format: PixelFormat,
|
format: PixelFormat,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pixels {
|
impl Pixels {
|
||||||
pub fn new_u8(ptr: Ptr<u8>) -> Self {
|
pub fn new(data: GVec<u8>, format: PixelFormat) -> Self {
|
||||||
|
Self { data, format }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format(&self) -> PixelFormat {
|
||||||
|
self.format
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_ptr(&self) -> *const u8 {
|
||||||
|
self.data.as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.data.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn texel_count(&self) -> usize {
|
||||||
|
self.data.len() / self.format.texel_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn read_u8(&self, texel_offset: usize) -> u8 {
|
||||||
|
unsafe { *self.data.as_ptr().add(texel_offset) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn read_f16(&self, texel_offset: usize) -> u16 {
|
||||||
|
let byte_offset = texel_offset * 2;
|
||||||
|
unsafe { *(self.data.as_ptr().add(byte_offset) as *const u16) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn read_f32(&self, texel_offset: usize) -> f32 {
|
||||||
|
let byte_offset = texel_offset * 4;
|
||||||
|
unsafe { *(self.data.as_ptr().add(byte_offset) as *const f32) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn read(&self, texel_offset: usize, encoding: &ColorEncoding) -> Float {
|
||||||
|
match self.format {
|
||||||
|
PixelFormat::U8 => encoding.to_linear_scalar(self.read_u8(texel_offset)),
|
||||||
|
PixelFormat::F16 => f16_to_f32_software(self.read_f16(texel_offset)),
|
||||||
|
PixelFormat::F32 => self.read_f32(texel_offset),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn write_u8(&mut self, texel_offset: usize, val: u8) {
|
||||||
|
unsafe { *self.data.as_mut_ptr().add(texel_offset) = val };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn write_f16(&mut self, texel_offset: usize, val: u16) {
|
||||||
|
let byte_offset = texel_offset * 2;
|
||||||
|
unsafe { *(self.data.as_mut_ptr().add(byte_offset) as *mut u16) = val };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn write_f32(&mut self, texel_offset: usize, val: f32) {
|
||||||
|
let byte_offset = texel_offset * 4;
|
||||||
|
unsafe { *(self.data.as_mut_ptr().add(byte_offset) as *mut f32) = val };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn empty(texel_count: usize, format: PixelFormat) -> Self {
|
||||||
|
let byte_count = texel_count * format.texel_bytes();
|
||||||
|
let mut data = gvec_with_capacity(byte_count);
|
||||||
|
data.resize(byte_count, 0u8);
|
||||||
|
Self { data, format }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_u8_slice(slice: &[u8]) -> Self {
|
||||||
|
let mut data = gvec_with_capacity(slice.len());
|
||||||
|
data.extend_from_slice(slice);
|
||||||
Self {
|
Self {
|
||||||
ptr,
|
data,
|
||||||
format: PixelFormat::U8,
|
format: PixelFormat::U8,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_f16(ptr: Ptr<u16>) -> Self {
|
pub fn from_f32_slice(slice: &[f32]) -> Self {
|
||||||
|
let byte_len = slice.len() * 4;
|
||||||
|
let mut data = gvec_with_capacity(byte_len);
|
||||||
|
let bytes = unsafe { core::slice::from_raw_parts(slice.as_ptr() as *const u8, byte_len) };
|
||||||
|
data.extend_from_slice(bytes);
|
||||||
Self {
|
Self {
|
||||||
ptr: Ptr::from_raw(ptr.as_raw() as *const u8),
|
data,
|
||||||
format: PixelFormat::F16,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_f32(ptr: Ptr<f32>) -> Self {
|
|
||||||
Self {
|
|
||||||
ptr: Ptr::from_raw(ptr.as_raw() as *const u8),
|
|
||||||
format: PixelFormat::F32,
|
format: PixelFormat::F32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn read_u8(&self, byte_offset: usize) -> u8 {
|
pub fn from_f16_slice(slice: &[u16]) -> Self {
|
||||||
unsafe { *self.ptr.as_raw().add(byte_offset) }
|
let byte_len = slice.len() * 2;
|
||||||
|
let mut data = gvec_with_capacity(byte_len);
|
||||||
|
let bytes = unsafe { core::slice::from_raw_parts(slice.as_ptr() as *const u8, byte_len) };
|
||||||
|
data.extend_from_slice(bytes);
|
||||||
|
Self {
|
||||||
|
data,
|
||||||
|
format: PixelFormat::F16,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn read_f16(&self, elem_offset: usize) -> u16 {
|
pub fn as_u8_mut(&mut self) -> &mut [u8] {
|
||||||
let byte_offset = elem_offset * 2;
|
&mut self.data
|
||||||
unsafe { *(self.ptr.as_raw().add(byte_offset) as *const u16) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn read_f32(&self, elem_offset: usize) -> f32 {
|
pub fn as_f16_mut(&mut self) -> &mut [u16] {
|
||||||
let byte_offset = elem_offset * 4;
|
assert_eq!(self.format, PixelFormat::F16);
|
||||||
unsafe { *(self.ptr.as_raw().add(byte_offset) as *const f32) }
|
unsafe {
|
||||||
|
core::slice::from_raw_parts_mut(self.data.as_mut_ptr() as *mut u16, self.data.len() / 2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unsure if ill need this
|
pub fn as_f32_slice(&self) -> &[f32] {
|
||||||
// pub unsafe fn read(&self, offset: usize) -> Float {
|
assert_eq!(self.format, PixelFormat::F32);
|
||||||
// match self.format {
|
unsafe {
|
||||||
// PixelFormat::U8 => unsafe { self.read_u8(offset) as Float / 255.0 },
|
core::slice::from_raw_parts(self.data.as_ptr() as *const f32, self.data.len() / 4)
|
||||||
// PixelFormat::F16 => unsafe { f16_to_f32_software(self.read_f16(offset)) },
|
}
|
||||||
// PixelFormat::F32 => unsafe { self.read_f32(offset) },
|
}
|
||||||
// }
|
|
||||||
// }
|
pub fn as_f32_slice_mut(&mut self) -> &mut [f32] {
|
||||||
|
assert_eq!(self.format, PixelFormat::F32);
|
||||||
|
unsafe {
|
||||||
|
core::slice::from_raw_parts_mut(self.data.as_mut_ptr() as *mut f32, self.data.len() / 4)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[derive(Clone, Debug)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct ImageBase {
|
pub struct ImageBase {
|
||||||
pub format: PixelFormat,
|
pub format: PixelFormat,
|
||||||
pub encoding: ColorEncoding,
|
pub encoding: ColorEncoding,
|
||||||
|
|
@ -129,58 +201,205 @@ pub struct ImageBase {
|
||||||
pub n_channels: i32,
|
pub n_channels: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImageBase {
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Image {
|
||||||
|
pub format: PixelFormat,
|
||||||
|
pub encoding: ColorEncoding,
|
||||||
|
pub resolution: Point2i,
|
||||||
|
pub n_channels: i32,
|
||||||
|
pub pixels: Pixels,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Image {
|
||||||
|
pub fn new(
|
||||||
|
format: PixelFormat,
|
||||||
|
resolution: Point2i,
|
||||||
|
n_channels: i32,
|
||||||
|
encoding: ColorEncoding,
|
||||||
|
) -> Self {
|
||||||
|
let texel_count = (resolution.x() * resolution.y()) as usize * n_channels as usize;
|
||||||
|
Self {
|
||||||
|
format,
|
||||||
|
encoding,
|
||||||
|
resolution,
|
||||||
|
n_channels,
|
||||||
|
pixels: Pixels::empty(texel_count, format),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_u8(
|
||||||
|
data: &[u8],
|
||||||
|
resolution: Point2i,
|
||||||
|
n_channels: i32,
|
||||||
|
encoding: ColorEncoding,
|
||||||
|
) -> Self {
|
||||||
|
let expected = (resolution.x() * resolution.y()) as usize * n_channels as usize;
|
||||||
|
assert_eq!(data.len(), expected, "Pixel data size mismatch");
|
||||||
|
Self {
|
||||||
|
format: PixelFormat::U8,
|
||||||
|
encoding,
|
||||||
|
resolution,
|
||||||
|
n_channels,
|
||||||
|
pixels: Pixels::from_u8_slice(data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_f32(data: &[f32], resolution: Point2i, n_channels: i32) -> Self {
|
||||||
|
let expected = (resolution.x() * resolution.y()) as usize * n_channels as usize;
|
||||||
|
assert_eq!(data.len(), expected, "Pixel data size mismatch");
|
||||||
|
Self {
|
||||||
|
format: PixelFormat::F32,
|
||||||
|
encoding: LINEAR,
|
||||||
|
resolution,
|
||||||
|
n_channels,
|
||||||
|
pixels: Pixels::from_f32_slice(data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_f16(data: &[u16], resolution: Point2i, n_channels: i32) -> Self {
|
||||||
|
let expected = (resolution.x() * resolution.y()) as usize * n_channels as usize;
|
||||||
|
assert_eq!(data.len(), expected, "Pixel data size mismatch");
|
||||||
|
Self {
|
||||||
|
format: PixelFormat::F16,
|
||||||
|
encoding: LINEAR,
|
||||||
|
resolution,
|
||||||
|
n_channels,
|
||||||
|
pixels: Pixels::from_f16_slice(data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolution(&self) -> Point2i {
|
||||||
|
self.resolution
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn n_channels(&self) -> i32 {
|
||||||
|
self.n_channels
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format(&self) -> PixelFormat {
|
||||||
|
self.format
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_valid(&self) -> bool {
|
||||||
|
self.resolution.x() > 0 && self.resolution.y() > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pixel_offset(&self, p: Point2i) -> usize {
|
||||||
|
let width = self.resolution.x() as usize;
|
||||||
|
(p.y() as usize * width + p.x() as usize) * self.n_channels as usize
|
||||||
|
}
|
||||||
|
|
||||||
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 {
|
pub fn base(&self) -> ImageBase {
|
||||||
self.base
|
ImageBase {
|
||||||
|
format: self.format,
|
||||||
|
encoding: self.encoding,
|
||||||
|
resolution: self.resolution,
|
||||||
|
n_channels: self.n_channels,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolution(&self) -> Point2i {
|
pub fn get_channel(&self, p: Point2i, c: i32) -> Float {
|
||||||
self.base.resolution
|
self.get_channel_with_wrap(p, c, WrapMode::Clamp.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_valid(&self) -> bool {
|
pub fn get_channel_with_wrap(&self, mut p: Point2i, c: i32, wrap_mode: WrapMode2D) -> Float {
|
||||||
self.resolution().x() > 0 && self.resolution().y() > 0
|
if !self.remap_pixel_coords(&mut p, wrap_mode) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
let offset = self.pixel_offset(p) + c as usize;
|
||||||
|
unsafe { self.pixels.read(offset, &self.encoding) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format(&self) -> PixelFormat {
|
pub fn lookup_nearest_channel(&self, p: Point2f, c: i32) -> Float {
|
||||||
self.base().format
|
self.lookup_nearest_channel_with_wrap(p, c, WrapMode::Clamp.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn n_channels(&self) -> i32 {
|
pub fn lookup_nearest_channel_with_wrap(
|
||||||
self.base().n_channels
|
&self,
|
||||||
|
p: Point2f,
|
||||||
|
c: i32,
|
||||||
|
wrap_mode: WrapMode2D,
|
||||||
|
) -> Float {
|
||||||
|
let pi = Point2i::new(
|
||||||
|
p.x() as i32 * self.resolution().x(),
|
||||||
|
p.y() as i32 * self.resolution().y(),
|
||||||
|
);
|
||||||
|
|
||||||
|
self.get_channel_with_wrap(pi, c, wrap_mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pixel_offset(&self, p: Point2i) -> u32 {
|
pub fn get_channels<const N: usize>(&self, p: Point2i) -> [Float; N] {
|
||||||
let width = self.resolution().x() as u32;
|
self.get_channels_with_wrap(p, WrapMode::Clamp.into())
|
||||||
let idx = p.y() as u32 * width + p.x() as u32;
|
}
|
||||||
idx * (self.n_channels() as u32)
|
|
||||||
|
pub fn get_channels_with_wrap<const N: usize>(
|
||||||
|
&self,
|
||||||
|
mut p: Point2i,
|
||||||
|
wrap_mode: WrapMode2D,
|
||||||
|
) -> [Float; N] {
|
||||||
|
debug_assert!(N <= self.n_channels as usize);
|
||||||
|
let mut result = [0.0; N];
|
||||||
|
if !self.remap_pixel_coords(&mut p, wrap_mode) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
let offset = self.pixel_offset(p);
|
||||||
|
for i in 0..N {
|
||||||
|
result[i] = unsafe { self.pixels.read(offset + i, &self.encoding) };
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_channels_average(&self, p: Point2i) -> Float {
|
||||||
|
let offset = self.pixel_offset(p);
|
||||||
|
let nc = self.n_channels as usize;
|
||||||
|
let mut sum = 0.0;
|
||||||
|
for i in 0..nc {
|
||||||
|
sum += unsafe { self.pixels.read(offset + i, &self.encoding) };
|
||||||
|
}
|
||||||
|
sum / nc as Float
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_channel(&mut self, p: Point2i, c: i32, mut value: Float) {
|
||||||
|
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;
|
||||||
|
unsafe {
|
||||||
|
match self.format {
|
||||||
|
PixelFormat::U8 => {
|
||||||
|
self.pixels
|
||||||
|
.write_u8(offset, self.encoding.from_linear_scalar(value));
|
||||||
|
}
|
||||||
|
PixelFormat::F16 => {
|
||||||
|
self.pixels
|
||||||
|
.write_f16(offset, half::f16::from_f32(value).to_bits());
|
||||||
|
}
|
||||||
|
PixelFormat::F32 => {
|
||||||
|
self.pixels.write_f32(offset, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bilerp_channel(&self, p: Point2f, c: i32) -> Float {
|
pub fn bilerp_channel(&self, p: Point2f, c: i32) -> Float {
|
||||||
|
|
@ -188,8 +407,8 @@ impl DeviceImage {
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|
@ -200,51 +419,30 @@ 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 has_any_infinite_pixels(&self) -> bool {
|
||||||
fn get_channel_with_wrap(&self, p: Point2i, c: i32, wrap_mode: WrapMode2D) -> Float;
|
for y in 0..self.resolution.y() {
|
||||||
fn get_channel(&self, p: Point2i, c: i32) -> Float;
|
for x in 0..self.resolution.x() {
|
||||||
fn lookup_nearest_channel_with_wrap(&self, p: Point2f, c: i32, wrap_mode: WrapMode2D) -> Float;
|
for c in 0..self.n_channels {
|
||||||
fn lookup_nearest_channel(&self, p: Point2f, c: i32) -> Float;
|
if self.get_channel(Point2i::new(x, y), c).is_infinite() {
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
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) as usize;
|
|
||||||
unsafe {
|
|
||||||
match self.pixels.format {
|
|
||||||
PixelFormat::U8 => {
|
|
||||||
let raw_val = self.pixels.read_u8(offset);
|
|
||||||
self.base.encoding.to_linear_scalar(raw_val)
|
|
||||||
}
|
}
|
||||||
PixelFormat::F16 => {
|
|
||||||
let raw_val = self.pixels.read_f16(offset);
|
|
||||||
f16_to_f32_software(raw_val)
|
|
||||||
}
|
|
||||||
PixelFormat::F32 => self.pixels.read_f32(offset),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_channel(&self, p: Point2i, c: i32) -> Float {
|
pub fn has_any_nan_pixels(&self) -> bool {
|
||||||
self.get_channel_with_wrap(p, c, WrapMode::Clamp.into())
|
for y in 0..self.resolution.y() {
|
||||||
}
|
for x in 0..self.resolution.x() {
|
||||||
|
for c in 0..self.n_channels {
|
||||||
fn lookup_nearest_channel_with_wrap(&self, p: Point2f, c: i32, wrap_mode: WrapMode2D) -> Float {
|
if self.get_channel(Point2i::new(x, y), c).is_nan() {
|
||||||
let pi = Point2i::new(
|
return true;
|
||||||
p.x() as i32 * self.resolution().x(),
|
}
|
||||||
p.y() as i32 * self.resolution().y(),
|
}
|
||||||
);
|
}
|
||||||
|
}
|
||||||
self.get_channel_with_wrap(pi, c, wrap_mode)
|
false
|
||||||
}
|
|
||||||
|
|
||||||
fn lookup_nearest_channel(&self, p: Point2f, c: i32) -> Float {
|
|
||||||
self.lookup_nearest_channel_with_wrap(p, c, WrapMode::Clamp.into())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,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,
|
||||||
|
|
@ -348,7 +348,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,6 @@ 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::interaction::{
|
use crate::core::interaction::{
|
||||||
Interaction, InteractionBase, InteractionTrait, MediumInteraction, SimpleInteraction,
|
Interaction, InteractionBase, InteractionTrait, MediumInteraction, SimpleInteraction,
|
||||||
SurfaceInteraction,
|
SurfaceInteraction,
|
||||||
|
|
@ -17,7 +16,6 @@ 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::sampling::DevicePiecewiseConstant2D;
|
|
||||||
use crate::{Float, PI};
|
use crate::{Float, PI};
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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};
|
||||||
|
|
@ -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);
|
||||||
|
|
@ -173,7 +173,7 @@ 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) -> Ptr<GPUFloatTexture>;
|
fn get_displacement(&self) -> Ptr<GPUFloatTexture>;
|
||||||
fn has_subsurface_scattering(&self) -> bool;
|
fn has_subsurface_scattering(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,18 @@
|
||||||
use enum_dispatch::enum_dispatch;
|
|
||||||
|
|
||||||
use crate::core::geometry::{
|
use crate::core::geometry::{
|
||||||
Bounds3f, Frame, Point2f, Point3f, Point3i, Ray, Vector3f, VectorLike, spherical_direction,
|
spherical_direction, Bounds3f, Frame, Point2f, Point3f, Point3i, Ray, Vector3f, VectorLike,
|
||||||
};
|
};
|
||||||
use crate::core::pbrt::{Float, INV_4_PI, PI};
|
use crate::core::pbrt::{Float, INV_4_PI, PI};
|
||||||
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
||||||
use crate::spectra::{
|
use crate::spectra::{
|
||||||
BlackbodySpectrum, DenselySampledSpectrum, LAMBDA_MAX, LAMBDA_MIN, RGBIlluminantSpectrum,
|
BlackbodySpectrum, DenselySampledSpectrum, RGBIlluminantSpectrum, RGBUnboundedSpectrum,
|
||||||
RGBUnboundedSpectrum, SampledSpectrum, SampledWavelengths,
|
SampledSpectrum, SampledWavelengths, LAMBDA_MAX, LAMBDA_MIN,
|
||||||
};
|
};
|
||||||
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::Ptr;
|
|
||||||
use crate::utils::rng::Rng;
|
use crate::utils::rng::Rng;
|
||||||
use crate::utils::transform::Transform;
|
use crate::utils::transform::Transform;
|
||||||
|
use crate::{gvec_with_capacity, GVec, Ptr};
|
||||||
|
use enum_dispatch::enum_dispatch;
|
||||||
use num_traits::Float as NumFloat;
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
@ -90,11 +89,11 @@ impl PhaseFunctionTrait for HGPhaseFunction {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct MajorantGrid {
|
pub struct MajorantGrid {
|
||||||
pub bounds: Bounds3f,
|
pub bounds: Bounds3f,
|
||||||
pub res: Point3i,
|
pub res: Point3i,
|
||||||
pub voxels: *mut Float,
|
pub voxels: GVec<Float>,
|
||||||
pub n_voxels: u32,
|
pub n_voxels: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,21 +101,21 @@ unsafe impl Send for MajorantGrid {}
|
||||||
unsafe impl Sync for MajorantGrid {}
|
unsafe impl Sync for MajorantGrid {}
|
||||||
|
|
||||||
impl MajorantGrid {
|
impl MajorantGrid {
|
||||||
// #[cfg(not(target_os = "cuda"))]
|
#[cfg(not(target_os = "cuda"))]
|
||||||
// pub fn new(bounds: Bounds3f, res: Point3i) -> Self {
|
pub fn new(bounds: Bounds3f, res: Point3i) -> Self {
|
||||||
// let n_voxels = (res.x() * res.y() * res.z()) as usize;
|
let n_voxels = (res.x() * res.y() * res.z()) as usize;
|
||||||
// let voxels = Vec::with_capacity(n_voxels);
|
let voxels = gvec_with_capacity(n_voxels);
|
||||||
// Self {
|
Self {
|
||||||
// bounds,
|
bounds,
|
||||||
// res,
|
res,
|
||||||
// voxels: voxels.as_ptr(),
|
voxels,
|
||||||
// n_voxels: n_voxels as u32,
|
n_voxels: n_voxels as u32,
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn is_valid(&self) -> bool {
|
fn is_valid(&self) -> bool {
|
||||||
!self.voxels.is_null()
|
!self.voxels.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
|
@ -128,7 +127,7 @@ impl MajorantGrid {
|
||||||
let idx = z * self.res.x() * self.res.y() + y * self.res.x() + x;
|
let idx = z * self.res.x() * self.res.y() + y * self.res.x() + x;
|
||||||
|
|
||||||
if idx >= 0 && (idx as u32) < self.n_voxels {
|
if idx >= 0 && (idx as u32) < self.n_voxels {
|
||||||
unsafe { *self.voxels.add(idx as usize) }
|
unsafe { *self.voxels.as_ptr().add(idx as usize) }
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
}
|
}
|
||||||
|
|
@ -143,7 +142,7 @@ impl MajorantGrid {
|
||||||
let idx = x + self.res.x() * (y + self.res.y() * z);
|
let idx = x + self.res.x() * (y + self.res.y() * z);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
*self.voxels.add(idx as usize) = v;
|
*self.voxels.as_mut_ptr().add(idx as usize) = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -230,7 +229,7 @@ pub struct DDAMajorantIterator {
|
||||||
sigma_t: SampledSpectrum,
|
sigma_t: SampledSpectrum,
|
||||||
t_min: Float,
|
t_min: Float,
|
||||||
t_max: Float,
|
t_max: Float,
|
||||||
grid: MajorantGrid,
|
grid: Ptr<MajorantGrid>,
|
||||||
next_crossing_t: [Float; 3],
|
next_crossing_t: [Float; 3],
|
||||||
delta_t: [Float; 3],
|
delta_t: [Float; 3],
|
||||||
step: [i32; 3],
|
step: [i32; 3],
|
||||||
|
|
@ -250,7 +249,7 @@ impl DDAMajorantIterator {
|
||||||
t_min,
|
t_min,
|
||||||
t_max,
|
t_max,
|
||||||
sigma_t: *sigma_t,
|
sigma_t: *sigma_t,
|
||||||
grid: *grid,
|
grid: Ptr::from(&*grid),
|
||||||
next_crossing_t: [0.0; 3],
|
next_crossing_t: [0.0; 3],
|
||||||
delta_t: [0.0; 3],
|
delta_t: [0.0; 3],
|
||||||
step: [0; 3],
|
step: [0; 3],
|
||||||
|
|
@ -453,9 +452,9 @@ pub enum Medium {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct HomogeneousMedium {
|
pub struct HomogeneousMedium {
|
||||||
pub sigma_a_spec: DenselySampledSpectrum,
|
pub sigma_a_spec: Ptr<DenselySampledSpectrum>,
|
||||||
pub sigma_s_spec: DenselySampledSpectrum,
|
pub sigma_s_spec: Ptr<DenselySampledSpectrum>,
|
||||||
pub le_spec: DenselySampledSpectrum,
|
pub le_spec: Ptr<DenselySampledSpectrum>,
|
||||||
pub phase: HGPhaseFunction,
|
pub phase: HGPhaseFunction,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -497,15 +496,15 @@ impl MediumTrait for HomogeneousMedium {
|
||||||
pub struct GridMedium {
|
pub struct GridMedium {
|
||||||
pub bounds: Bounds3f,
|
pub bounds: Bounds3f,
|
||||||
pub render_from_medium: Transform,
|
pub render_from_medium: Transform,
|
||||||
pub sigma_a_spec: DenselySampledSpectrum,
|
pub sigma_a_spec: Ptr<DenselySampledSpectrum>,
|
||||||
pub sigma_s_spec: DenselySampledSpectrum,
|
pub sigma_s_spec: Ptr<DenselySampledSpectrum>,
|
||||||
pub density_grid: SampledGrid<Float>,
|
pub density_grid: Ptr<SampledGrid<Float>>,
|
||||||
pub phase: HGPhaseFunction,
|
pub phase: HGPhaseFunction,
|
||||||
pub temperature_grid: Option<SampledGrid<Float>>,
|
pub temperature_grid: Ptr<SampledGrid<Float>>,
|
||||||
pub le_spec: DenselySampledSpectrum,
|
pub le_spec: Ptr<DenselySampledSpectrum>,
|
||||||
pub le_scale: SampledGrid<Float>,
|
pub le_scale: Ptr<SampledGrid<Float>>,
|
||||||
pub is_emissive: bool,
|
pub is_emissive: bool,
|
||||||
pub majorant_grid: MajorantGrid,
|
pub majorant_grid: Ptr<MajorantGrid>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MediumTrait for GridMedium {
|
impl MediumTrait for GridMedium {
|
||||||
|
|
@ -528,8 +527,8 @@ impl MediumTrait for GridMedium {
|
||||||
};
|
};
|
||||||
|
|
||||||
let le = if scale > 0.0 {
|
let le = if scale > 0.0 {
|
||||||
let raw_emission = if let Some(temp_grid) = &self.temperature_grid {
|
let raw_emission = if !self.temperature_grid.is_null() {
|
||||||
let temp = temp_grid.lookup(p);
|
let temp = self.temperature_grid.lookup(p);
|
||||||
BlackbodySpectrum::new(temp).sample(lambda)
|
BlackbodySpectrum::new(temp).sample(lambda)
|
||||||
} else {
|
} else {
|
||||||
self.le_spec.sample(lambda)
|
self.le_spec.sample(lambda)
|
||||||
|
|
@ -593,10 +592,10 @@ pub struct RGBGridMedium {
|
||||||
pub phase: HGPhaseFunction,
|
pub phase: HGPhaseFunction,
|
||||||
pub le_scale: Float,
|
pub le_scale: Float,
|
||||||
pub sigma_scale: Float,
|
pub sigma_scale: Float,
|
||||||
pub sigma_a_grid: SampledGrid<RGBUnboundedSpectrum>,
|
pub sigma_a_grid: Ptr<SampledGrid<RGBUnboundedSpectrum>>,
|
||||||
pub sigma_s_grid: SampledGrid<RGBUnboundedSpectrum>,
|
pub sigma_s_grid: Ptr<SampledGrid<RGBUnboundedSpectrum>>,
|
||||||
pub le_grid: SampledGrid<RGBIlluminantSpectrum>,
|
pub le_grid: Ptr<SampledGrid<RGBIlluminantSpectrum>>,
|
||||||
pub majorant_grid: MajorantGrid,
|
pub majorant_grid: Ptr<MajorantGrid>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MediumTrait for RGBGridMedium {
|
impl MediumTrait for RGBGridMedium {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ use crate::core::geometry::Lerp;
|
||||||
use core::ops::{Add, Mul};
|
use core::ops::{Add, Mul};
|
||||||
use num_traits::{Num, PrimInt};
|
use num_traits::{Num, PrimInt};
|
||||||
|
|
||||||
use crate::core::image::DeviceImage;
|
|
||||||
use crate::core::light::LightTrait;
|
use crate::core::light::LightTrait;
|
||||||
use crate::core::shape::Shape;
|
use crate::core::shape::Shape;
|
||||||
use crate::core::texture::GPUFloatTexture;
|
use crate::core::texture::GPUFloatTexture;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::core::geometry::{Bounds3f, Ray};
|
use crate::core::geometry::{Bounds3f, Ray};
|
||||||
use crate::core::aggregates::DeviceBVHAggregate;
|
use crate::core::aggregates::BVHAggregate;
|
||||||
use crate::core::interaction::{Interaction, InteractionTrait, SurfaceInteraction};
|
use crate::core::interaction::{Interaction, InteractionTrait, SurfaceInteraction};
|
||||||
use crate::core::light::Light;
|
use crate::core::light::Light;
|
||||||
use crate::core::material::Material;
|
use crate::core::material::Material;
|
||||||
|
|
@ -213,6 +213,23 @@ pub enum Primitive {
|
||||||
Geometric(GeometricPrimitive),
|
Geometric(GeometricPrimitive),
|
||||||
Transformed(TransformedPrimitive),
|
Transformed(TransformedPrimitive),
|
||||||
Animated(AnimatedPrimitive),
|
Animated(AnimatedPrimitive),
|
||||||
BVH(DeviceBVHAggregate),
|
BVH(Ptr<BVHAggregate>),
|
||||||
KdTree(KdTreeAggregate),
|
KdTree(KdTreeAggregate),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<T: PrimitiveTrait> PrimitiveTrait for Ptr<T> {
|
||||||
|
fn bounds(&self) -> Bounds3f {
|
||||||
|
unsafe { self.as_ref().bounds() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn intersect(&self, r: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection> {
|
||||||
|
unsafe { self.as_ref().intersect(r, t_max) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn intersect_p(&self, r: &Ray, t_max: Option<Float>) -> bool {
|
||||||
|
unsafe { self.as_ref().intersect_p(r, t_max) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
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::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};
|
||||||
use crate::utils::Ptr;
|
|
||||||
use crate::utils::containers::DeviceArray2D;
|
|
||||||
use crate::utils::math::{
|
use crate::utils::math::{
|
||||||
BinaryPermuteScrambler, DeviceDigitPermutation, FastOwenScrambler, NoRandomizer, OwenScrambler,
|
clamp, encode_morton_2, inverse_radical_inverse, lerp, log2_int,
|
||||||
PRIME_TABLE_SIZE, Scrambler, clamp, encode_morton_2, inverse_radical_inverse, lerp, log2_int,
|
|
||||||
owen_scrambled_radical_inverse, permutation_element, radical_inverse, round_up_pow2,
|
owen_scrambled_radical_inverse, permutation_element, radical_inverse, round_up_pow2,
|
||||||
scrambled_radical_inverse, sobol_interval_to_index, sobol_sample,
|
scrambled_radical_inverse, sobol_interval_to_index, sobol_sample, BinaryPermuteScrambler,
|
||||||
|
DigitPermutation, FastOwenScrambler, NoRandomizer, OwenScrambler, Scrambler,
|
||||||
|
PRIME_TABLE_SIZE,
|
||||||
};
|
};
|
||||||
use crate::utils::rng::Rng;
|
use crate::utils::rng::Rng;
|
||||||
use crate::utils::sobol::N_SOBOL_DIMENSIONS;
|
use crate::utils::sobol::N_SOBOL_DIMENSIONS;
|
||||||
use crate::utils::{hash::*, sobol};
|
use crate::utils::{hash::*, sobol};
|
||||||
|
use crate::{gvec, GVec, Ptr};
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
@ -100,7 +100,7 @@ pub struct HaltonSampler {
|
||||||
pub mult_inverse: [u64; 2],
|
pub mult_inverse: [u64; 2],
|
||||||
pub halton_index: u64,
|
pub halton_index: u64,
|
||||||
pub dim: u32,
|
pub dim: u32,
|
||||||
pub digit_permutations: Ptr<DeviceDigitPermutation>,
|
pub digit_permutations: Ptr<DigitPermutation>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::derivable_impls)]
|
#[allow(clippy::derivable_impls)]
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use crate::Float;
|
use crate::{Float, Ptr};
|
||||||
use crate::core::color::{RGB, XYZ};
|
use crate::core::color::{RGB, XYZ};
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
|
|
||||||
pub use crate::spectra::*;
|
pub use crate::spectra::*;
|
||||||
|
|
||||||
#[enum_dispatch]
|
#[enum_dispatch]
|
||||||
pub trait SpectrumTrait: Copy {
|
pub trait SpectrumTrait {
|
||||||
fn evaluate(&self, lambda: Float) -> Float;
|
fn evaluate(&self, lambda: Float) -> Float;
|
||||||
fn sample(&self, lambda: &SampledWavelengths) -> SampledSpectrum {
|
fn sample(&self, lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
SampledSpectrum::from_fn(|i| self.evaluate(lambda[i]))
|
SampledSpectrum::from_fn(|i| self.evaluate(lambda[i]))
|
||||||
|
|
@ -16,10 +16,10 @@ pub trait SpectrumTrait: Copy {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct StandardSpectra {
|
pub struct StandardSpectra {
|
||||||
pub x: DenselySampledSpectrum,
|
pub x: Ptr<DenselySampledSpectrum>,
|
||||||
pub y: DenselySampledSpectrum,
|
pub y: Ptr<DenselySampledSpectrum>,
|
||||||
pub z: DenselySampledSpectrum,
|
pub z: Ptr<DenselySampledSpectrum>,
|
||||||
pub d65: DenselySampledSpectrum,
|
pub d65: Ptr<DenselySampledSpectrum>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for StandardSpectra {}
|
unsafe impl Send for StandardSpectra {}
|
||||||
|
|
@ -30,14 +30,23 @@ unsafe impl Sync for StandardSpectra {}
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum Spectrum {
|
pub enum Spectrum {
|
||||||
Constant(ConstantSpectrum),
|
Constant(ConstantSpectrum),
|
||||||
Dense(DenselySampledSpectrum),
|
Dense(Ptr<DenselySampledSpectrum>),
|
||||||
Piecewise(PiecewiseLinearSpectrum),
|
Piecewise(Ptr<PiecewiseLinearSpectrum>),
|
||||||
Blackbody(BlackbodySpectrum),
|
Blackbody(BlackbodySpectrum),
|
||||||
RGBAlbedo(RGBAlbedoSpectrum),
|
RGBAlbedo(RGBAlbedoSpectrum),
|
||||||
RGBIlluminant(RGBIlluminantSpectrum),
|
RGBIlluminant(RGBIlluminantSpectrum),
|
||||||
RGBUnbounded(RGBUnboundedSpectrum),
|
RGBUnbounded(RGBUnboundedSpectrum),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: SpectrumTrait> SpectrumTrait for Ptr<T> {
|
||||||
|
fn evaluate(&self, lambda: Float) -> Float {
|
||||||
|
unsafe { self.as_ref().evaluate(lambda) }
|
||||||
|
}
|
||||||
|
fn max_value(&self) -> Float {
|
||||||
|
unsafe { self.as_ref().max_value() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Spectrum {
|
impl Spectrum {
|
||||||
pub fn std_illuminant_d65() -> Self {
|
pub fn std_illuminant_d65() -> Self {
|
||||||
unimplemented!("Use crate::spectra::default_illuminant() on host")
|
unimplemented!("Use crate::spectra::default_illuminant() on host")
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::Float;
|
use crate::Float;
|
||||||
use crate::core::filter::{DeviceFilterSample, FilterTrait};
|
use crate::core::filter::{FilterSample, FilterTrait};
|
||||||
use crate::core::geometry::{Point2f, Vector2f};
|
use crate::core::geometry::{Point2f, Vector2f};
|
||||||
use crate::utils::math::lerp;
|
use crate::utils::math::lerp;
|
||||||
|
|
||||||
|
|
@ -31,7 +31,7 @@ impl FilterTrait for BoxFilter {
|
||||||
(2.0 * self.radius.x()) * (2.0 * self.radius.y())
|
(2.0 * self.radius.x()) * (2.0 * self.radius.y())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample(&self, u: Point2f) -> DeviceFilterSample {
|
fn sample(&self, u: Point2f) -> FilterSample {
|
||||||
let p = Point2f::new(
|
let p = Point2f::new(
|
||||||
lerp(u[0], -self.radius.x(), self.radius.x()),
|
lerp(u[0], -self.radius.x(), self.radius.x()),
|
||||||
lerp(u[1], -self.radius.y(), self.radius.y()),
|
lerp(u[1], -self.radius.y(), self.radius.y()),
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use crate::Float;
|
use crate::core::filter::{FilterSample, FilterSampler, FilterTrait};
|
||||||
use crate::core::filter::{DeviceFilterSample, FilterSampler, FilterTrait};
|
|
||||||
use crate::core::geometry::{Point2f, Vector2f};
|
use crate::core::geometry::{Point2f, Vector2f};
|
||||||
use crate::utils::math::{gaussian, gaussian_integral};
|
use crate::utils::math::{gaussian, gaussian_integral};
|
||||||
|
use crate::{Ptr, Float};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Debug, Copy)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct GaussianFilter {
|
pub struct GaussianFilter {
|
||||||
pub radius: Vector2f,
|
pub radius: Vector2f,
|
||||||
pub sigma: Float,
|
pub sigma: Float,
|
||||||
|
|
@ -13,6 +13,25 @@ pub struct GaussianFilter {
|
||||||
pub sampler: FilterSampler,
|
pub sampler: FilterSampler,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GaussianFilter {
|
||||||
|
pub fn new(radius: Vector2f, sigma: Float) -> Self {
|
||||||
|
let exp_x = gaussian(radius.x(), 0.0, sigma);
|
||||||
|
let exp_y = gaussian(radius.y(), 0.0, sigma);
|
||||||
|
let sampler = FilterSampler::new(radius, move |p: Point2f| {
|
||||||
|
let gx = (gaussian(p.x(), 0.0, sigma) - exp_x).max(0.0);
|
||||||
|
let gy = (gaussian(p.y(), 0.0, sigma) - exp_y).max(0.0);
|
||||||
|
gx * gy
|
||||||
|
});
|
||||||
|
Self {
|
||||||
|
radius,
|
||||||
|
sigma,
|
||||||
|
exp_x,
|
||||||
|
exp_y,
|
||||||
|
sampler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FilterTrait for GaussianFilter {
|
impl FilterTrait for GaussianFilter {
|
||||||
fn radius(&self) -> Vector2f {
|
fn radius(&self) -> Vector2f {
|
||||||
self.radius
|
self.radius
|
||||||
|
|
@ -30,7 +49,7 @@ impl FilterTrait for GaussianFilter {
|
||||||
- 2.0 * self.radius.y() * self.exp_y)
|
- 2.0 * self.radius.y() * self.exp_y)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample(&self, u: Point2f) -> DeviceFilterSample {
|
fn sample(&self, u: Point2f) -> FilterSample {
|
||||||
self.sampler.sample(u)
|
self.sampler.sample(u)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
use crate::Float;
|
use crate::core::filter::{FilterSampler, FilterSample, FilterTrait};
|
||||||
use crate::core::filter::{DeviceFilterSample, FilterSampler, FilterTrait};
|
|
||||||
use crate::core::geometry::{Point2f, Vector2f};
|
use crate::core::geometry::{Point2f, Vector2f};
|
||||||
use crate::utils::math::{lerp, windowed_sinc};
|
use crate::utils::math::{lerp, windowed_sinc};
|
||||||
|
use crate::utils::rng::Rng;
|
||||||
|
use crate::Float;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Debug, Copy)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct LanczosSincFilter {
|
pub struct LanczosSincFilter {
|
||||||
pub radius: Vector2f,
|
pub radius: Vector2f,
|
||||||
pub tau: Float,
|
pub tau: Float,
|
||||||
|
|
@ -12,6 +13,38 @@ pub struct LanczosSincFilter {
|
||||||
pub integral: Float,
|
pub integral: Float,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl LanczosSincFilter {
|
||||||
|
pub fn new(radius: Vector2f, tau: Float) -> Self {
|
||||||
|
let evaluate = move |p: Point2f| -> Float {
|
||||||
|
windowed_sinc(p.x(), radius.x(), tau) * windowed_sinc(p.y(), radius.y(), tau)
|
||||||
|
};
|
||||||
|
|
||||||
|
let sampler = FilterSampler::new(radius, evaluate);
|
||||||
|
|
||||||
|
let sqrt_samples = 64u32;
|
||||||
|
let n_samples = sqrt_samples * sqrt_samples;
|
||||||
|
let area = (2.0 * radius.x()) * (2.0 * radius.y());
|
||||||
|
let mut sum = 0.0;
|
||||||
|
let mut rng = Rng::new(0);
|
||||||
|
for y in 0..sqrt_samples {
|
||||||
|
for x in 0..sqrt_samples {
|
||||||
|
let u = Point2f::new(
|
||||||
|
(x as Float + rng.uniform::<Float>()) / sqrt_samples as Float,
|
||||||
|
(y as Float + rng.uniform::<Float>()) / sqrt_samples as Float,
|
||||||
|
);
|
||||||
|
let p = Point2f::new(
|
||||||
|
lerp(u.x(), -radius.x(), radius.x()),
|
||||||
|
lerp(u.y(), -radius.y(), radius.y()),
|
||||||
|
);
|
||||||
|
sum += evaluate(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let integral = sum / n_samples as Float * area;
|
||||||
|
|
||||||
|
Self { radius, tau, sampler, integral }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FilterTrait for LanczosSincFilter {
|
impl FilterTrait for LanczosSincFilter {
|
||||||
fn radius(&self) -> Vector2f {
|
fn radius(&self) -> Vector2f {
|
||||||
self.radius
|
self.radius
|
||||||
|
|
@ -26,7 +59,7 @@ impl FilterTrait for LanczosSincFilter {
|
||||||
self.integral
|
self.integral
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample(&self, u: Point2f) -> DeviceFilterSample {
|
fn sample(&self, u: Point2f) -> FilterSample {
|
||||||
self.sampler.sample(u)
|
self.sampler.sample(u)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,50 @@
|
||||||
use crate::Float;
|
use crate::core::filter::{FilterSample, FilterSampler, FilterTrait};
|
||||||
use crate::core::filter::{DeviceFilterSample, FilterSampler, FilterTrait};
|
|
||||||
use crate::core::geometry::{Point2f, Vector2f};
|
use crate::core::geometry::{Point2f, Vector2f};
|
||||||
|
use crate::Float;
|
||||||
use num_traits::Float as NumFloat;
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct MitchellFilter {
|
pub struct MitchellFilter {
|
||||||
pub radius: Vector2f,
|
pub radius: Vector2f,
|
||||||
pub b: Float,
|
pub b: Float,
|
||||||
pub c: Float,
|
pub c: Float,
|
||||||
pub sampler: DeviceFilterSampler,
|
pub sampler: FilterSampler,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mitchell_1d_eval(b: Float, c: Float, x: Float) -> Float {
|
||||||
|
let x = x.abs();
|
||||||
|
if x <= 1.0 {
|
||||||
|
((12.0 - 9.0 * b - 6.0 * c) * x.powi(3)
|
||||||
|
+ (-18.0 + 12.0 * b + 6.0 * c) * x.powi(2)
|
||||||
|
+ (6.0 - 2.0 * b))
|
||||||
|
* (1.0 / 6.0)
|
||||||
|
} else if x <= 2.0 {
|
||||||
|
((-b - 6.0 * c) * x.powi(3)
|
||||||
|
+ (6.0 * b + 30.0 * c) * x.powi(2)
|
||||||
|
+ (-12.0 * b - 48.0 * c) * x
|
||||||
|
+ (8.0 * b + 24.0 * c))
|
||||||
|
* (1.0 / 6.0)
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MitchellFilter {
|
impl MitchellFilter {
|
||||||
pub fn mitchell_1d_eval(b: Float, c: Float, x: Float) -> Float {
|
pub fn new(radius: Vector2f, b: Float, c: Float) -> Self {
|
||||||
let x = x.abs();
|
let sampler = FilterSampler::new(radius, move |p: Point2f| {
|
||||||
if x <= 1.0 {
|
mitchell_1d_eval(p.x() / radius.x(), b, c) * mitchell_1d_eval(p.y() / radius.y(), b, c)
|
||||||
((12.0 - 9.0 * b - 6.0 * c) * x.powi(3)
|
});
|
||||||
+ (-18.0 + 12.0 * b + 6.0 * c) * x.powi(2)
|
Self {
|
||||||
+ (6.0 - 2.0 * b))
|
radius,
|
||||||
* (1.0 / 6.0)
|
b,
|
||||||
} else if x <= 2.0 {
|
c,
|
||||||
((-b - 6.0 * c) * x.powi(3)
|
sampler,
|
||||||
+ (6.0 * b + 30.0 * c) * x.powi(2)
|
|
||||||
+ (-12.0 * b - 48.0 * c) * x
|
|
||||||
+ (8.0 * b + 24.0 * c))
|
|
||||||
* (1.0 / 6.0)
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mitchell_1d(&self, x: Float) -> Float {
|
fn mitchell_1d(&self, x: Float) -> Float {
|
||||||
Self::mitchell_1d_eval(self.b, self.c, x)
|
mitchell_1d_eval(self.b, self.c, x)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,7 +62,7 @@ impl FilterTrait for MitchellFilter {
|
||||||
self.radius.x() * self.radius.y() / 4.0
|
self.radius.x() * self.radius.y() / 4.0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample(&self, u: Point2f) -> DeviceFilterSample {
|
fn sample(&self, u: Point2f) -> FilterSample {
|
||||||
self.sampler.sample(u)
|
self.sampler.sample(u)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::Float;
|
use crate::Float;
|
||||||
use crate::core::filter::{DeviceFilterSample, FilterTrait};
|
use crate::core::filter::{FilterSample, FilterTrait};
|
||||||
use crate::core::geometry::{Point2f, Vector2f};
|
use crate::core::geometry::{Point2f, Vector2f};
|
||||||
use crate::utils::math::sample_tent;
|
use crate::utils::math::sample_tent;
|
||||||
use num_traits::Float as NumFloat;
|
use num_traits::Float as NumFloat;
|
||||||
|
|
@ -29,11 +29,11 @@ impl FilterTrait for TriangleFilter {
|
||||||
self.radius.x().powi(2) * self.radius.y().powi(2)
|
self.radius.x().powi(2) * self.radius.y().powi(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample(&self, u: Point2f) -> DeviceFilterSample {
|
fn sample(&self, u: Point2f) -> FilterSample {
|
||||||
let p = Point2f::new(
|
let p = Point2f::new(
|
||||||
sample_tent(u[0], self.radius.x()),
|
sample_tent(u[0], self.radius.x()),
|
||||||
sample_tent(u[1], self.radius.y()),
|
sample_tent(u[1], self.radius.y()),
|
||||||
);
|
);
|
||||||
DeviceFilterSample { p, weight: 1.0 }
|
FilterSample { p, weight: 1.0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#![allow(unused_imports, dead_code)]
|
#![allow(unused_imports, dead_code)]
|
||||||
|
#![feature(allocator_api)]
|
||||||
#![feature(associated_type_defaults)]
|
#![feature(associated_type_defaults)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
@ -17,5 +18,5 @@ pub mod textures;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
pub use core::pbrt::*;
|
pub use core::pbrt::*;
|
||||||
pub use utils::PBRTOptions;
|
pub use utils::alloc::{gbox, gvec, gvec_from_slice, gvec_with_capacity, GVec, GBox};
|
||||||
pub use utils::ptr::Ptr;
|
pub use utils::{Array2D, PBRTOptions, Ptr, Transform};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
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,
|
||||||
};
|
};
|
||||||
|
|
@ -27,7 +27,7 @@ pub struct DiffuseAreaLight {
|
||||||
pub alpha: Ptr<GPUFloatTexture>,
|
pub alpha: Ptr<GPUFloatTexture>,
|
||||||
pub colorspace: Ptr<RGBColorSpace>,
|
pub colorspace: 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,
|
||||||
|
|
@ -171,8 +171,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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,7 +7,7 @@ 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};
|
||||||
|
|
||||||
|
|
@ -16,8 +16,8 @@ pub struct GoniometricLight {
|
||||||
pub base: LightBase,
|
pub base: LightBase,
|
||||||
pub iemit: Ptr<DenselySampledSpectrum>,
|
pub iemit: Ptr<DenselySampledSpectrum>,
|
||||||
pub scale: Float,
|
pub scale: Float,
|
||||||
pub image: Ptr<DeviceImage>,
|
pub image: Ptr<Image>,
|
||||||
pub distrib: Ptr<DevicePiecewiseConstant2D>,
|
pub distrib: Ptr<PiecewiseConstant2D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GoniometricLight {
|
impl GoniometricLight {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ 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::geometry::{Frame, VectorLike};
|
||||||
use crate::core::image::{DeviceImage, ImageAccess, PixelFormat, WrapMode};
|
use crate::core::image::{Image, PixelFormat, WrapMode};
|
||||||
use crate::core::interaction::InteractionBase;
|
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::{
|
||||||
|
|
@ -15,8 +15,8 @@ use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths
|
||||||
use crate::spectra::{RGBColorSpace, RGBIlluminantSpectrum};
|
use crate::spectra::{RGBColorSpace, RGBIlluminantSpectrum};
|
||||||
use crate::utils::math::{clamp, equal_area_sphere_to_square, equal_area_square_to_sphere, square};
|
use crate::utils::math::{clamp, equal_area_sphere_to_square, equal_area_square_to_sphere, square};
|
||||||
use crate::utils::sampling::{
|
use crate::utils::sampling::{
|
||||||
AliasTable, DevicePiecewiseConstant2D, DeviceWindowedPiecewiseConstant2D,
|
sample_uniform_sphere, uniform_sphere_pdf, AliasTable, PiecewiseConstant2D,
|
||||||
sample_uniform_sphere, uniform_sphere_pdf,
|
WindowedPiecewiseConstant2D,
|
||||||
};
|
};
|
||||||
use crate::utils::{Ptr, Transform};
|
use crate::utils::{Ptr, Transform};
|
||||||
use crate::{Float, PI};
|
use crate::{Float, PI};
|
||||||
|
|
@ -35,6 +35,27 @@ pub struct UniformInfiniteLight {
|
||||||
unsafe impl Send for UniformInfiniteLight {}
|
unsafe impl Send for UniformInfiniteLight {}
|
||||||
unsafe impl Sync for UniformInfiniteLight {}
|
unsafe impl Sync for UniformInfiniteLight {}
|
||||||
|
|
||||||
|
impl UniformInfiniteLight {
|
||||||
|
pub fn new(
|
||||||
|
render_from_light: Transform,
|
||||||
|
scale: Float,
|
||||||
|
lemit: Ptr<DenselySampledSpectrum>,
|
||||||
|
) -> Self {
|
||||||
|
let base = LightBase::new(
|
||||||
|
LightType::Infinite,
|
||||||
|
render_from_light,
|
||||||
|
MediumInterface::default(),
|
||||||
|
);
|
||||||
|
Self {
|
||||||
|
base,
|
||||||
|
lemit,
|
||||||
|
scale,
|
||||||
|
scene_center: Point3f::default(),
|
||||||
|
scene_radius: 0.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl LightTrait for UniformInfiniteLight {
|
impl LightTrait for UniformInfiniteLight {
|
||||||
fn base(&self) -> &LightBase {
|
fn base(&self) -> &LightBase {
|
||||||
&self.base
|
&self.base
|
||||||
|
|
@ -114,10 +135,10 @@ impl LightTrait for UniformInfiniteLight {
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct ImageInfiniteLight {
|
pub struct ImageInfiniteLight {
|
||||||
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,
|
||||||
|
|
@ -127,6 +148,31 @@ unsafe impl Send for ImageInfiniteLight {}
|
||||||
unsafe impl Sync for ImageInfiniteLight {}
|
unsafe impl Sync for ImageInfiniteLight {}
|
||||||
|
|
||||||
impl ImageInfiniteLight {
|
impl ImageInfiniteLight {
|
||||||
|
pub fn new(
|
||||||
|
render_from_light: Transform,
|
||||||
|
scale: Float,
|
||||||
|
image: Ptr<Image>,
|
||||||
|
image_color_space: Ptr<RGBColorSpace>,
|
||||||
|
distrib: Ptr<PiecewiseConstant2D>,
|
||||||
|
compensated_distrib: Ptr<PiecewiseConstant2D>,
|
||||||
|
) -> Self {
|
||||||
|
let base = LightBase::new(
|
||||||
|
LightType::Infinite,
|
||||||
|
render_from_light,
|
||||||
|
MediumInterface::default(),
|
||||||
|
);
|
||||||
|
Self {
|
||||||
|
base,
|
||||||
|
image,
|
||||||
|
image_color_space,
|
||||||
|
scale,
|
||||||
|
distrib,
|
||||||
|
compensated_distrib,
|
||||||
|
scene_center: Point3f::default(),
|
||||||
|
scene_radius: 0.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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 {
|
||||||
|
|
@ -250,17 +296,44 @@ impl LightTrait for ImageInfiniteLight {
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct PortalInfiniteLight {
|
pub struct PortalInfiniteLight {
|
||||||
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],
|
||||||
pub portal_frame: Frame,
|
pub portal_frame: Frame,
|
||||||
pub distribution: DeviceWindowedPiecewiseConstant2D,
|
pub distribution: Ptr<WindowedPiecewiseConstant2D>,
|
||||||
pub scene_center: Point3f,
|
pub scene_center: Point3f,
|
||||||
pub scene_radius: Float,
|
pub scene_radius: Float,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PortalInfiniteLight {
|
impl PortalInfiniteLight {
|
||||||
|
pub fn new(
|
||||||
|
render_from_light: Transform,
|
||||||
|
scale: Float,
|
||||||
|
image: Ptr<Image>,
|
||||||
|
image_color_space: Ptr<RGBColorSpace>,
|
||||||
|
portal: [Point3f; 4],
|
||||||
|
portal_frame: Frame,
|
||||||
|
distribution: Ptr<WindowedPiecewiseConstant2D>,
|
||||||
|
) -> Self {
|
||||||
|
let base = LightBase::new(
|
||||||
|
LightType::Infinite,
|
||||||
|
render_from_light,
|
||||||
|
MediumInterface::default(),
|
||||||
|
);
|
||||||
|
Self {
|
||||||
|
base,
|
||||||
|
image,
|
||||||
|
image_color_space,
|
||||||
|
scale,
|
||||||
|
portal,
|
||||||
|
portal_frame,
|
||||||
|
distribution,
|
||||||
|
scene_center: Point3f::default(),
|
||||||
|
scene_radius: 0.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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 {
|
||||||
|
|
|
||||||
|
|
@ -7,8 +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::{Float, PI, Ptr, Transform};
|
||||||
use crate::{Float, PI};
|
|
||||||
use num_traits::Float as NumFloat;
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[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, 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,
|
||||||
};
|
};
|
||||||
|
|
@ -13,7 +13,7 @@ use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::math::{radians, square};
|
use crate::utils::math::{radians, square};
|
||||||
use crate::{
|
use crate::{
|
||||||
spectra::{RGBColorSpace, RGBIlluminantSpectrum},
|
spectra::{RGBColorSpace, RGBIlluminantSpectrum},
|
||||||
utils::{Ptr, Transform, sampling::DevicePiecewiseConstant2D},
|
utils::{Ptr, Transform, sampling::PiecewiseConstant2D},
|
||||||
};
|
};
|
||||||
use num_traits::Float as NumFloat;
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
|
|
@ -27,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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -198,12 +198,12 @@ pub enum LightSampler {
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct UniformLightSampler {
|
pub struct UniformLightSampler {
|
||||||
lights: *const Light,
|
lights: Ptr<Light>,
|
||||||
lights_len: u32,
|
lights_len: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UniformLightSampler {
|
impl UniformLightSampler {
|
||||||
pub fn new(lights: *const Light, lights_len: u32) -> Self {
|
pub fn new(lights: Ptr<Light>, lights_len: u32) -> Self {
|
||||||
Self { lights, lights_len }
|
Self { lights, lights_len }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -251,7 +251,7 @@ pub struct Alias {
|
||||||
pub struct PowerLightSampler {
|
pub struct PowerLightSampler {
|
||||||
pub lights: Ptr<Light>,
|
pub lights: Ptr<Light>,
|
||||||
pub lights_len: u32,
|
pub lights_len: u32,
|
||||||
pub alias_table: AliasTable,
|
pub alias_table: Ptr<AliasTable>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for PowerLightSampler {}
|
unsafe impl Send for PowerLightSampler {}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,7 @@ use crate::core::interaction::{Interaction, InteractionBase, InteractionTrait, S
|
||||||
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::{Float, PI, Ptr, Transform};
|
||||||
use crate::{Float, PI};
|
|
||||||
use num_traits::Float as NumFloat;
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
|
||||||
|
|
@ -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: Ptr<GPUFloatTexture>,
|
g: Ptr<GPUFloatTexture>,
|
||||||
eta: Ptr<Spectrum>,
|
eta: Ptr<Spectrum>,
|
||||||
displacement: Ptr<GPUFloatTexture>,
|
displacement: Ptr<GPUFloatTexture>,
|
||||||
normal_map: Ptr<DeviceImage>,
|
normal_map: Ptr<Image>,
|
||||||
remap_roughness: bool,
|
remap_roughness: bool,
|
||||||
max_depth: u32,
|
max_depth: u32,
|
||||||
n_samples: u32,
|
n_samples: u32,
|
||||||
|
|
@ -137,7 +137,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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -153,7 +153,7 @@ impl MaterialTrait for CoatedDiffuseMaterial {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct CoatedConductorMaterial {
|
pub struct CoatedConductorMaterial {
|
||||||
normal_map: Ptr<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>,
|
||||||
|
|
@ -175,7 +175,7 @@ pub struct CoatedConductorMaterial {
|
||||||
impl CoatedConductorMaterial {
|
impl CoatedConductorMaterial {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
normal_map: Ptr<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>,
|
||||||
|
|
@ -333,7 +333,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,7 +6,7 @@ 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};
|
||||||
|
|
@ -77,7 +77,7 @@ impl MaterialTrait for HairMaterial {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_normal_map(&self) -> Option<&DeviceImage> {
|
fn get_normal_map(&self) -> Option<&Image> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,7 +94,7 @@ impl MaterialTrait for HairMaterial {
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct MeasuredMaterial {
|
pub struct MeasuredMaterial {
|
||||||
pub displacement: Ptr<GPUFloatTexture>,
|
pub displacement: Ptr<GPUFloatTexture>,
|
||||||
pub normal_map: Ptr<DeviceImage>,
|
pub normal_map: Ptr<Image>,
|
||||||
pub brdf: Ptr<MeasuredBxDFData>,
|
pub brdf: Ptr<MeasuredBxDFData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,7 +122,7 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -138,7 +138,7 @@ 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: Ptr<DeviceImage>,
|
pub normal_map: Ptr<Image>,
|
||||||
pub displacement: Ptr<GPUFloatTexture>,
|
pub displacement: Ptr<GPUFloatTexture>,
|
||||||
pub sigma_a: Ptr<GPUSpectrumTexture>,
|
pub sigma_a: Ptr<GPUSpectrumTexture>,
|
||||||
pub sigma_s: Ptr<GPUSpectrumMixTexture>,
|
pub sigma_s: Ptr<GPUSpectrumMixTexture>,
|
||||||
|
|
@ -174,7 +174,7 @@ impl MaterialTrait for SubsurfaceMaterial {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_normal_map(&self) -> Option<&DeviceImage> {
|
fn get_normal_map(&self) -> Option<&Image> {
|
||||||
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,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 DielectricMaterial {
|
pub struct DielectricMaterial {
|
||||||
pub normal_map: Ptr<DeviceImage>,
|
pub normal_map: Ptr<Image>,
|
||||||
pub displacement: Ptr<GPUFloatTexture>,
|
pub displacement: Ptr<GPUFloatTexture>,
|
||||||
pub u_roughness: Ptr<GPUFloatTexture>,
|
pub u_roughness: Ptr<GPUFloatTexture>,
|
||||||
pub v_roughness: Ptr<GPUFloatTexture>,
|
pub v_roughness: Ptr<GPUFloatTexture>,
|
||||||
|
|
@ -67,7 +67,7 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,7 +84,7 @@ impl MaterialTrait for DielectricMaterial {
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct ThinDielectricMaterial {
|
pub struct ThinDielectricMaterial {
|
||||||
pub displacement: Ptr<GPUFloatTexture>,
|
pub displacement: Ptr<GPUFloatTexture>,
|
||||||
pub normal_map: Ptr<DeviceImage>,
|
pub normal_map: Ptr<Image>,
|
||||||
pub eta: Ptr<Spectrum>,
|
pub eta: Ptr<Spectrum>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -110,7 +110,7 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,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};
|
||||||
|
|
@ -17,7 +17,7 @@ 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: Ptr<DeviceImage>,
|
pub normal_map: Ptr<Image>,
|
||||||
pub displacement: Ptr<GPUFloatTexture>,
|
pub displacement: Ptr<GPUFloatTexture>,
|
||||||
pub reflectance: Ptr<GPUSpectrumTexture>,
|
pub reflectance: Ptr<GPUSpectrumTexture>,
|
||||||
}
|
}
|
||||||
|
|
@ -47,7 +47,7 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,7 +63,7 @@ impl MaterialTrait for DiffuseMaterial {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct DiffuseTransmissionMaterial {
|
pub struct DiffuseTransmissionMaterial {
|
||||||
pub image: Ptr<DeviceImage>,
|
pub image: Ptr<Image>,
|
||||||
pub displacement: Ptr<GPUFloatTexture>,
|
pub displacement: Ptr<GPUFloatTexture>,
|
||||||
pub reflectance: Ptr<GPUFloatTexture>,
|
pub reflectance: Ptr<GPUFloatTexture>,
|
||||||
pub transmittance: Ptr<GPUFloatTexture>,
|
pub transmittance: Ptr<GPUFloatTexture>,
|
||||||
|
|
@ -92,7 +92,7 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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};
|
||||||
|
|
@ -69,7 +69,7 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,16 @@
|
||||||
use crate::core::geometry::{
|
use crate::core::geometry::{
|
||||||
Bounds3f, DirectionCone, Normal, Normal3f, Point2f, Point3f, Point3fi, Ray, Tuple, Vector3f,
|
spherical_quad_area, Bounds3f, DirectionCone, Normal, Normal3f, Point2f, Point3f, Point3fi,
|
||||||
VectorLike, spherical_quad_area,
|
Ray, Tuple, Vector3f, VectorLike,
|
||||||
};
|
};
|
||||||
use crate::core::interaction::{Interaction, InteractionTrait, SurfaceInteraction};
|
use crate::core::interaction::{Interaction, InteractionTrait, SurfaceInteraction};
|
||||||
use crate::core::pbrt::{Float, gamma};
|
use crate::core::pbrt::{gamma, Float};
|
||||||
use crate::core::shape::{Shape, ShapeIntersection, ShapeSample, ShapeSampleContext, ShapeTrait};
|
use crate::core::shape::{Shape, ShapeIntersection, ShapeSample, ShapeSampleContext, ShapeTrait};
|
||||||
use crate::utils::Ptr;
|
use crate::shapes::mesh::BilinearPatchMesh;
|
||||||
use crate::utils::Transform;
|
use crate::utils::math::{clamp, difference_of_products, lerp, quadratic, SquareMatrix};
|
||||||
use crate::utils::math::{SquareMatrix, clamp, difference_of_products, lerp, quadratic};
|
|
||||||
use crate::utils::mesh::DeviceBilinearPatchMesh;
|
|
||||||
use crate::utils::sampling::{
|
use crate::utils::sampling::{
|
||||||
bilinear_pdf, invert_spherical_rectangle_sample, sample_bilinear, sample_spherical_rectangle,
|
bilinear_pdf, invert_spherical_rectangle_sample, sample_bilinear, sample_spherical_rectangle,
|
||||||
};
|
};
|
||||||
|
use crate::{GVec, Ptr, Transform};
|
||||||
use core::ops::Add;
|
use core::ops::Add;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
@ -47,7 +46,7 @@ impl BilinearIntersection {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct BilinearPatchShape {
|
pub struct BilinearPatchShape {
|
||||||
pub mesh: Ptr<DeviceBilinearPatchMesh>,
|
pub mesh: Ptr<BilinearPatchMesh>,
|
||||||
pub blp_index: i32,
|
pub blp_index: i32,
|
||||||
pub area: Float,
|
pub area: Float,
|
||||||
pub rectangle: bool,
|
pub rectangle: bool,
|
||||||
|
|
@ -55,14 +54,14 @@ pub struct BilinearPatchShape {
|
||||||
|
|
||||||
impl BilinearPatchShape {
|
impl BilinearPatchShape {
|
||||||
pub const MIN_SPHERICAL_SAMPLE_AREA: Float = 1e-4;
|
pub const MIN_SPHERICAL_SAMPLE_AREA: Float = 1e-4;
|
||||||
fn mesh(&self) -> Ptr<DeviceBilinearPatchMesh> {
|
fn mesh(&self) -> Ptr<BilinearPatchMesh> {
|
||||||
self.mesh
|
self.mesh
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn get_vertex_indices(&self) -> [usize; 4] {
|
fn get_vertex_indices(&self) -> [usize; 4] {
|
||||||
unsafe {
|
unsafe {
|
||||||
let base_ptr = self.mesh.vertex_indices.add((self.blp_index as usize) * 4);
|
let base_ptr = self.mesh.vertex_indices.as_ptr().add((self.blp_index as usize) * 4);
|
||||||
[
|
[
|
||||||
*base_ptr.add(0) as usize,
|
*base_ptr.add(0) as usize,
|
||||||
*base_ptr.add(1) as usize,
|
*base_ptr.add(1) as usize,
|
||||||
|
|
@ -74,51 +73,33 @@ impl BilinearPatchShape {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn get_points(&self) -> [Point3f; 4] {
|
fn get_points(&self) -> [Point3f; 4] {
|
||||||
|
let mesh = self.mesh();
|
||||||
let [v0, v1, v2, v3] = self.get_vertex_indices();
|
let [v0, v1, v2, v3] = self.get_vertex_indices();
|
||||||
unsafe {
|
[mesh.p[v0], mesh.p[v1], mesh.p[v2], mesh.p[v3]]
|
||||||
[
|
|
||||||
*self.mesh.p.add(v0),
|
|
||||||
*self.mesh.p.add(v1),
|
|
||||||
*self.mesh.p.add(v2),
|
|
||||||
*self.mesh.p.add(v3),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn get_uvs(&self) -> Option<[Point2f; 4]> {
|
fn get_uvs(&self) -> Option<[Point2f; 4]> {
|
||||||
if self.mesh.uv.is_null() {
|
let mesh = self.mesh();
|
||||||
|
if mesh.uv.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let [v0, v1, v2, v3] = self.get_vertex_indices();
|
let [v0, v1, v2, v3] = self.get_vertex_indices();
|
||||||
unsafe {
|
Some([mesh.uv[v0], mesh.uv[v1], mesh.uv[v2], mesh.uv[v3]])
|
||||||
Some([
|
|
||||||
*self.mesh.uv.add(v0),
|
|
||||||
*self.mesh.uv.add(v1),
|
|
||||||
*self.mesh.uv.add(v2),
|
|
||||||
*self.mesh.uv.add(v3),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn get_shading_normals(&self) -> Option<[Normal3f; 4]> {
|
fn get_shading_normals(&self) -> Option<[Normal3f; 4]> {
|
||||||
if self.mesh.n.is_null() {
|
let mesh = self.mesh();
|
||||||
|
if mesh.n.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let [v0, v1, v2, v3] = self.get_vertex_indices();
|
let [v0, v1, v2, v3] = self.get_vertex_indices();
|
||||||
unsafe {
|
Some([mesh.n[v0], mesh.n[v1], mesh.n[v2], mesh.n[v3]])
|
||||||
Some([
|
|
||||||
*self.mesh.n.add(v0),
|
|
||||||
*self.mesh.n.add(v1),
|
|
||||||
*self.mesh.n.add(v2),
|
|
||||||
*self.mesh.n.add(v3),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "cuda"))]
|
#[cfg(not(target_os = "cuda"))]
|
||||||
pub fn new(mesh: Ptr<DeviceBilinearPatchMesh>, blp_index: i32) -> Self {
|
pub fn new(mesh: Ptr<BilinearPatchMesh>, blp_index: i32) -> Self {
|
||||||
let mut bp = BilinearPatchShape {
|
let mut bp = BilinearPatchShape {
|
||||||
mesh,
|
mesh,
|
||||||
blp_index,
|
blp_index,
|
||||||
|
|
@ -456,7 +437,11 @@ impl BilinearPatchShape {
|
||||||
|
|
||||||
ss.pdf *= dist_sq / abs_dot;
|
ss.pdf *= dist_sq / abs_dot;
|
||||||
|
|
||||||
if ss.pdf.is_infinite() { None } else { Some(ss) }
|
if ss.pdf.is_infinite() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(ss)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample_parametric_coords(&self, corners: &[Point3f; 4], u: Point2f) -> (Point2f, Float) {
|
fn sample_parametric_coords(&self, corners: &[Point3f; 4], u: Point2f) -> (Point2f, Float) {
|
||||||
|
|
@ -723,7 +708,11 @@ impl ShapeTrait for BilinearPatchShape {
|
||||||
|
|
||||||
let (_, dpdu, dpdv) = self.calculate_base_derivatives(&corners, uv);
|
let (_, dpdu, dpdv) = self.calculate_base_derivatives(&corners, uv);
|
||||||
let cross = dpdu.cross(dpdv).norm();
|
let cross = dpdu.cross(dpdv).norm();
|
||||||
if cross == 0. { 0. } else { param_pdf / cross }
|
if cross == 0. {
|
||||||
|
0.
|
||||||
|
} else {
|
||||||
|
param_pdf / cross
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -754,7 +743,11 @@ impl ShapeTrait for BilinearPatchShape {
|
||||||
return 0.;
|
return 0.;
|
||||||
}
|
}
|
||||||
let pdf = isect_pdf * distsq / absdot;
|
let pdf = isect_pdf * distsq / absdot;
|
||||||
if pdf.is_infinite() { 0. } else { pdf }
|
if pdf.is_infinite() {
|
||||||
|
0.
|
||||||
|
} else {
|
||||||
|
pdf
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut pdf = 1. / spherical_quad_area(v00, v10, v01, v11);
|
let mut pdf = 1. / spherical_quad_area(v00, v10, v01, v11);
|
||||||
if ctx.ns != Normal3f::zero() {
|
if ctx.ns != Normal3f::zero() {
|
||||||
|
|
|
||||||
159
shared/src/shapes/mesh.rs
Normal file
159
shared/src/shapes/mesh.rs
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
use crate::core::geometry::{Normal3f, Point2f, Point3f, Vector3f};
|
||||||
|
use crate::utils::sampling::PiecewiseConstant2D;
|
||||||
|
use crate::{gvec_from_slice, gvec_with_capacity, Float, GVec, Ptr, Transform};
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TriangleMesh {
|
||||||
|
pub p: GVec<Point3f>,
|
||||||
|
pub n: GVec<Normal3f>,
|
||||||
|
pub s: GVec<Vector3f>,
|
||||||
|
pub uv: GVec<Point2f>,
|
||||||
|
pub vertex_indices: GVec<i32>,
|
||||||
|
pub face_indices: GVec<i32>,
|
||||||
|
pub n_triangles: u32,
|
||||||
|
pub n_vertices: u32,
|
||||||
|
pub reverse_orientation: bool,
|
||||||
|
pub transform_swaps_handedness: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct BilinearPatchMesh {
|
||||||
|
pub p: GVec<Point3f>,
|
||||||
|
pub n: GVec<Normal3f>,
|
||||||
|
pub uv: GVec<Point2f>,
|
||||||
|
pub vertex_indices: GVec<i32>,
|
||||||
|
pub n_patches: u32,
|
||||||
|
pub n_vertices: u32,
|
||||||
|
pub reverse_orientation: bool,
|
||||||
|
pub transform_swaps_handedness: bool,
|
||||||
|
pub image_distribution: Ptr<PiecewiseConstant2D>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for TriangleMesh {}
|
||||||
|
unsafe impl Sync for TriangleMesh {}
|
||||||
|
unsafe impl Send for BilinearPatchMesh {}
|
||||||
|
unsafe impl Sync for BilinearPatchMesh {}
|
||||||
|
|
||||||
|
impl TriangleMesh {
|
||||||
|
pub fn new(
|
||||||
|
render_from_object: &Transform,
|
||||||
|
reverse_orientation: bool,
|
||||||
|
vertex_indices: &[i32],
|
||||||
|
p: &[Point3f],
|
||||||
|
n: &[Normal3f],
|
||||||
|
s: &[Vector3f],
|
||||||
|
uv: &[Point2f],
|
||||||
|
face_indices: &[i32],
|
||||||
|
) -> Self {
|
||||||
|
let n_triangles = (vertex_indices.len() / 3) as u32;
|
||||||
|
let n_vertices = p.len() as u32;
|
||||||
|
|
||||||
|
let mut p_gvec = gvec_with_capacity(p.len());
|
||||||
|
for pt in p {
|
||||||
|
p_gvec.push(render_from_object.apply_to_point(*pt));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut n_gvec = gvec_with_capacity(n.len());
|
||||||
|
if !n.is_empty() {
|
||||||
|
assert_eq!(n_vertices as usize, n.len(), "Normal count mismatch");
|
||||||
|
for nn in n {
|
||||||
|
let mut transformed = render_from_object.apply_to_normal(*nn);
|
||||||
|
if reverse_orientation {
|
||||||
|
transformed = -transformed;
|
||||||
|
}
|
||||||
|
n_gvec.push(transformed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut s_gvec = gvec_with_capacity(s.len());
|
||||||
|
if !s.is_empty() {
|
||||||
|
assert_eq!(n_vertices as usize, s.len(), "Tangent count mismatch");
|
||||||
|
for ss in s {
|
||||||
|
s_gvec.push(render_from_object.apply_to_vector(*ss));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
uv.is_empty() || uv.len() == n_vertices as usize,
|
||||||
|
"UV count mismatch"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
face_indices.is_empty() || face_indices.len() == n_triangles as usize,
|
||||||
|
"Face index count mismatch"
|
||||||
|
);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
vertex_indices: gvec_from_slice(vertex_indices),
|
||||||
|
p: p_gvec,
|
||||||
|
n: n_gvec,
|
||||||
|
s: s_gvec,
|
||||||
|
uv: gvec_from_slice(uv),
|
||||||
|
face_indices: gvec_from_slice(face_indices),
|
||||||
|
n_triangles,
|
||||||
|
n_vertices,
|
||||||
|
reverse_orientation,
|
||||||
|
transform_swaps_handedness: render_from_object.swaps_handedness(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn positions(&self) -> &[Point3f] {
|
||||||
|
&self.p
|
||||||
|
}
|
||||||
|
pub fn indices(&self) -> &[i32] {
|
||||||
|
&self.vertex_indices
|
||||||
|
}
|
||||||
|
pub fn normals(&self) -> &[Normal3f] {
|
||||||
|
&self.n
|
||||||
|
}
|
||||||
|
pub fn uvs(&self) -> &[Point2f] {
|
||||||
|
&self.uv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BilinearPatchMesh {
|
||||||
|
pub fn new(
|
||||||
|
render_from_object: &Transform,
|
||||||
|
reverse_orientation: bool,
|
||||||
|
vertex_indices: &[i32],
|
||||||
|
p: &[Point3f],
|
||||||
|
n: &[Normal3f],
|
||||||
|
uv: &[Point2f],
|
||||||
|
image_distribution: Option<&PiecewiseConstant2D>,
|
||||||
|
) -> Self {
|
||||||
|
let n_patches = (vertex_indices.len() / 4) as u32;
|
||||||
|
let n_vertices = p.len() as u32;
|
||||||
|
|
||||||
|
let mut p_gvec = gvec_with_capacity(p.len());
|
||||||
|
for pt in p {
|
||||||
|
p_gvec.push(render_from_object.apply_to_point(*pt));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut n_gvec = gvec_with_capacity(n.len());
|
||||||
|
if !n.is_empty() {
|
||||||
|
assert_eq!(n_vertices as usize, n.len());
|
||||||
|
for nn in n {
|
||||||
|
let mut transformed = render_from_object.apply_to_normal(*nn);
|
||||||
|
if reverse_orientation {
|
||||||
|
transformed = -transformed;
|
||||||
|
}
|
||||||
|
n_gvec.push(transformed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(uv.is_empty() || uv.len() == n_vertices as usize);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
vertex_indices: gvec_from_slice(vertex_indices),
|
||||||
|
p: p_gvec,
|
||||||
|
n: n_gvec,
|
||||||
|
uv: gvec_from_slice(uv),
|
||||||
|
image_distribution: Ptr::from(image_distribution),
|
||||||
|
n_patches,
|
||||||
|
n_vertices,
|
||||||
|
reverse_orientation,
|
||||||
|
transform_swaps_handedness: render_from_object.swaps_handedness(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ pub mod cylinder;
|
||||||
pub mod disk;
|
pub mod disk;
|
||||||
pub mod sphere;
|
pub mod sphere;
|
||||||
pub mod triangle;
|
pub mod triangle;
|
||||||
|
pub mod mesh;
|
||||||
|
|
||||||
pub use bilinear::*;
|
pub use bilinear::*;
|
||||||
pub use curves::*;
|
pub use curves::*;
|
||||||
|
|
@ -11,3 +12,4 @@ pub use cylinder::*;
|
||||||
pub use disk::*;
|
pub use disk::*;
|
||||||
pub use sphere::*;
|
pub use sphere::*;
|
||||||
pub use triangle::*;
|
pub use triangle::*;
|
||||||
|
pub use mesh::*;
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,19 @@
|
||||||
use crate::Float;
|
use crate::core::geometry::{spherical_triangle_area, SqrtExt, Tuple, VectorLike};
|
||||||
use crate::core::geometry::{
|
use crate::core::geometry::{
|
||||||
Bounds3f, DirectionCone, Normal, Normal3f, Point2f, Point3f, Point3fi, Ray, Vector2f, Vector3,
|
Bounds3f, DirectionCone, Normal, Normal3f, Point2f, Point3f, Point3fi, Ray, Vector2f, Vector3,
|
||||||
Vector3f,
|
Vector3f,
|
||||||
};
|
};
|
||||||
use crate::core::geometry::{SqrtExt, Tuple, VectorLike, spherical_triangle_area};
|
|
||||||
use crate::core::interaction::{
|
use crate::core::interaction::{
|
||||||
Interaction, InteractionBase, InteractionTrait, SimpleInteraction, SurfaceInteraction,
|
Interaction, InteractionBase, InteractionTrait, SimpleInteraction, SurfaceInteraction,
|
||||||
};
|
};
|
||||||
use crate::core::pbrt::gamma;
|
|
||||||
use crate::core::shape::{ShapeIntersection, ShapeSample, ShapeSampleContext, ShapeTrait};
|
use crate::core::shape::{ShapeIntersection, ShapeSample, ShapeSampleContext, ShapeTrait};
|
||||||
use crate::utils::Ptr;
|
use crate::shapes::mesh::TriangleMesh;
|
||||||
use crate::utils::math::{difference_of_products, square};
|
use crate::utils::math::{difference_of_products, square};
|
||||||
use crate::utils::mesh::DeviceTriangleMesh;
|
|
||||||
use crate::utils::sampling::{
|
use crate::utils::sampling::{
|
||||||
bilinear_pdf, invert_spherical_triangle_sample, sample_bilinear, sample_spherical_triangle,
|
bilinear_pdf, invert_spherical_triangle_sample, sample_bilinear, sample_spherical_triangle,
|
||||||
sample_uniform_triangle,
|
sample_uniform_triangle,
|
||||||
};
|
};
|
||||||
|
use crate::{gamma, Float, GVec, Ptr};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
@ -35,7 +33,7 @@ impl TriangleIntersection {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct TriangleShape {
|
pub struct TriangleShape {
|
||||||
pub mesh: Ptr<DeviceTriangleMesh>,
|
pub mesh: Ptr<TriangleMesh>,
|
||||||
pub tri_index: i32,
|
pub tri_index: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,80 +41,58 @@ impl TriangleShape {
|
||||||
pub const MIN_SPHERICAL_SAMPLE_AREA: Float = 3e-4;
|
pub const MIN_SPHERICAL_SAMPLE_AREA: Float = 3e-4;
|
||||||
pub const MAX_SPHERICAL_SAMPLE_AREA: Float = 6.22;
|
pub const MAX_SPHERICAL_SAMPLE_AREA: Float = 6.22;
|
||||||
|
|
||||||
#[inline(always)]
|
fn mesh(&self) -> &TriangleMesh {
|
||||||
|
&*self.mesh
|
||||||
|
}
|
||||||
fn get_vertex_indices(&self) -> [usize; 3] {
|
fn get_vertex_indices(&self) -> [usize; 3] {
|
||||||
unsafe {
|
let mesh = self.mesh();
|
||||||
let base_ptr = self.mesh.vertex_indices.add((self.tri_index as usize) * 3);
|
let base = (self.tri_index as usize) * 3;
|
||||||
[
|
[
|
||||||
*base_ptr.add(0) as usize,
|
mesh.vertex_indices[base] as usize,
|
||||||
*base_ptr.add(1) as usize,
|
mesh.vertex_indices[base + 1] as usize,
|
||||||
*base_ptr.add(2) as usize,
|
mesh.vertex_indices[base + 2] as usize,
|
||||||
]
|
]
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn get_points(&self) -> [Point3f; 3] {
|
fn get_points(&self) -> [Point3f; 3] {
|
||||||
|
let mesh = self.mesh();
|
||||||
let [v0, v1, v2] = self.get_vertex_indices();
|
let [v0, v1, v2] = self.get_vertex_indices();
|
||||||
unsafe {
|
[mesh.p[v0], mesh.p[v1], mesh.p[v2]]
|
||||||
[
|
|
||||||
*self.mesh.p.add(v0),
|
|
||||||
*self.mesh.p.add(v1),
|
|
||||||
*self.mesh.p.add(v2),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn get_uvs(&self) -> Option<[Point2f; 3]> {
|
|
||||||
if self.mesh.uv.is_null() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let [v0, v1, v2] = self.get_vertex_indices();
|
|
||||||
unsafe {
|
|
||||||
Some([
|
|
||||||
*self.mesh.uv.add(v0),
|
|
||||||
*self.mesh.uv.add(v1),
|
|
||||||
*self.mesh.uv.add(v2),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn get_tangents(&self) -> Option<[Vector3f; 3]> {
|
|
||||||
if self.mesh.s.is_null() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let [v0, v1, v2] = self.get_vertex_indices();
|
|
||||||
unsafe {
|
|
||||||
Some([
|
|
||||||
*self.mesh.s.add(v0),
|
|
||||||
*self.mesh.s.add(v1),
|
|
||||||
*self.mesh.s.add(v2),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn get_shading_normals(&self) -> Option<[Normal3f; 3]> {
|
fn get_shading_normals(&self) -> Option<[Normal3f; 3]> {
|
||||||
if self.mesh.n.is_null() {
|
let mesh = self.mesh();
|
||||||
|
if mesh.n.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let [v0, v1, v2] = self.get_vertex_indices();
|
let [v0, v1, v2] = self.get_vertex_indices();
|
||||||
unsafe {
|
Some([mesh.n[v0], mesh.n[v1], mesh.n[v2]])
|
||||||
Some([
|
|
||||||
*self.mesh.n.add(v0),
|
|
||||||
*self.mesh.n.add(v1),
|
|
||||||
*self.mesh.n.add(v2),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(mesh: Ptr<DeviceTriangleMesh>, tri_index: i32) -> Self {
|
fn get_tangents(&self) -> Option<[Vector3f; 3]> {
|
||||||
|
let mesh = self.mesh();
|
||||||
|
if mesh.s.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let [v0, v1, v2] = self.get_vertex_indices();
|
||||||
|
Some([mesh.s[v0], mesh.s[v1], mesh.s[v2]])
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn get_uvs(&self) -> Option<[Point2f; 3]> {
|
||||||
|
let mesh = self.mesh();
|
||||||
|
if mesh.s.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let [v0, v1, v2] = self.get_vertex_indices();
|
||||||
|
Some([mesh.uv[v0], mesh.uv[v1], mesh.uv[v2]])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(mesh: Ptr<TriangleMesh>, tri_index: i32) -> Self {
|
||||||
Self { mesh, tri_index }
|
Self { mesh, tri_index }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_mesh(&self) -> Ptr<DeviceTriangleMesh> {
|
pub fn get_mesh(&self) -> Ptr<TriangleMesh> {
|
||||||
self.mesh
|
self.mesh
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -209,8 +185,8 @@ impl TriangleShape {
|
||||||
flip_normal,
|
flip_normal,
|
||||||
);
|
);
|
||||||
|
|
||||||
isect.face_index = if !self.mesh.face_indices.is_null() {
|
isect.face_index = if !self.mesh.face_indices.is_empty() {
|
||||||
unsafe { *self.mesh.face_indices.add(self.tri_index as usize) }
|
unsafe { *self.mesh.face_indices.as_ptr().add(self.tri_index as usize) }
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
@ -218,7 +194,7 @@ impl TriangleShape {
|
||||||
isect.common.n = ng;
|
isect.common.n = ng;
|
||||||
isect.shading.n = ng;
|
isect.shading.n = ng;
|
||||||
|
|
||||||
if !self.mesh.p.is_null() || !self.mesh.s.is_null() {
|
if !self.mesh.p.is_empty() || !self.mesh.s.is_empty() {
|
||||||
self.compute_shading_geometry(&mut isect, &ti, uv, dpdu, determinant, degenerate);
|
self.compute_shading_geometry(&mut isect, &ti, uv, dpdu, determinant, degenerate);
|
||||||
}
|
}
|
||||||
isect
|
isect
|
||||||
|
|
|
||||||
|
|
@ -62,16 +62,16 @@ impl ColorSpaceId {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct RGBColorSpace {
|
pub struct RGBColorSpace {
|
||||||
pub r: Point2f,
|
pub r: Point2f,
|
||||||
pub g: Point2f,
|
pub g: Point2f,
|
||||||
pub b: Point2f,
|
pub b: Point2f,
|
||||||
pub w: Point2f,
|
pub w: Point2f,
|
||||||
pub illuminant: DenselySampledSpectrum,
|
|
||||||
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,
|
||||||
|
pub illuminant: Ptr<DenselySampledSpectrum>,
|
||||||
|
pub rgb_to_spectrum_table: RGBToSpectrumTable,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for RGBColorSpace {}
|
unsafe impl Send for RGBColorSpace {}
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ impl RGBIlluminantSpectrum {
|
||||||
Self {
|
Self {
|
||||||
scale,
|
scale,
|
||||||
rsp,
|
rsp,
|
||||||
illuminant: Ptr::from(&illuminant),
|
illuminant,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
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::{SampledSpectrum, SampledWavelengths, N_SPECTRUM_SAMPLES};
|
||||||
use crate::utils::find_interval;
|
use crate::utils::find_interval;
|
||||||
use crate::utils::ptr::Ptr;
|
use crate::{gvec, gvec_with_capacity, Float, GVec, Ptr};
|
||||||
use core::slice;
|
use core::hash::{Hash, Hasher};
|
||||||
use num_traits::Float as NumFloat;
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
@ -31,20 +30,73 @@ impl SpectrumTrait for ConstantSpectrum {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DenselySampledSpectrum {
|
pub struct DenselySampledSpectrum {
|
||||||
pub lambda_min: i32,
|
pub lambda_min: i32,
|
||||||
pub lambda_max: i32,
|
pub lambda_max: i32,
|
||||||
pub values: Ptr<Float>,
|
pub values: GVec<Float>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for DenselySampledSpectrum {}
|
unsafe impl Send for DenselySampledSpectrum {}
|
||||||
unsafe impl Sync for DenselySampledSpectrum {}
|
unsafe impl Sync for DenselySampledSpectrum {}
|
||||||
|
|
||||||
impl DenselySampledSpectrum {
|
impl DenselySampledSpectrum {
|
||||||
|
pub fn new(lambda_min: i32, lambda_max: i32, values: GVec<Float>) -> Self {
|
||||||
|
let func_integral = 0.0;
|
||||||
|
Self {
|
||||||
|
lambda_min,
|
||||||
|
lambda_max,
|
||||||
|
values,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_zero(lambda_min: i32, lambda_max: i32) -> Self {
|
||||||
|
let n = (lambda_max - lambda_min + 1).max(0) as usize;
|
||||||
|
let mut values = gvec_with_capacity(n);
|
||||||
|
values.resize(n, 0.0);
|
||||||
|
Self {
|
||||||
|
lambda_min,
|
||||||
|
lambda_max,
|
||||||
|
values,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_spectrum(spec: &Spectrum) -> Self {
|
||||||
|
let mut values = gvec_with_capacity((LAMBDA_MAX - LAMBDA_MIN + 1) as usize);
|
||||||
|
for lambda in LAMBDA_MIN..=LAMBDA_MAX {
|
||||||
|
values.push(spec.evaluate(lambda as Float));
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
lambda_min: LAMBDA_MIN,
|
||||||
|
lambda_max: LAMBDA_MAX,
|
||||||
|
values,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_function<F>(f: F, lambda_min: i32, lambda_max: i32) -> Self
|
||||||
|
where
|
||||||
|
F: Fn(Float) -> Float,
|
||||||
|
{
|
||||||
|
let mut values = gvec_with_capacity((lambda_max - lambda_min + 1) as usize);
|
||||||
|
for lambda in lambda_min..=lambda_max {
|
||||||
|
values.push(f(lambda as Float));
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
lambda_min,
|
||||||
|
lambda_max,
|
||||||
|
values,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scale(&mut self, s: Float) {
|
||||||
|
for v in &mut self.values {
|
||||||
|
*v *= s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn count(&self) -> usize {
|
pub fn count(&self) -> usize {
|
||||||
if self.values.is_null() {
|
if self.values.is_empty() {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
(self.lambda_max - self.lambda_min + 1) as usize
|
(self.lambda_max - self.lambda_min + 1) as usize
|
||||||
|
|
@ -52,8 +104,8 @@ impl DenselySampledSpectrum {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn get(&self, idx: u32) -> Float {
|
pub fn value(&self, idx: u32) -> Float {
|
||||||
unsafe { *self.values.add(idx as usize) }
|
unsafe { *self.values.as_ptr().add(idx as usize) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,21 +119,41 @@ impl PartialEq for DenselySampledSpectrum {
|
||||||
|
|
||||||
impl Eq for DenselySampledSpectrum {}
|
impl Eq for DenselySampledSpectrum {}
|
||||||
|
|
||||||
|
impl Hash for DenselySampledSpectrum {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.lambda_min.hash(state);
|
||||||
|
self.lambda_max.hash(state);
|
||||||
|
for &val in self.values.iter() {
|
||||||
|
val.to_bits().hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SpectrumTrait for DenselySampledSpectrum {
|
impl SpectrumTrait for DenselySampledSpectrum {
|
||||||
|
fn max_value(&self) -> Float {
|
||||||
|
if self.values.is_empty() {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
let mut max_val = Float::NEG_INFINITY;
|
||||||
|
for i in 0..self.count() {
|
||||||
|
let val = self.value(i as u32);
|
||||||
|
if val > max_val {
|
||||||
|
max_val = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
max_val
|
||||||
|
}
|
||||||
|
|
||||||
fn sample(&self, lambda: &SampledWavelengths) -> SampledSpectrum {
|
fn sample(&self, lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
let mut s = SampledSpectrum::default();
|
let mut s = SampledSpectrum::default();
|
||||||
let n = self.count() as i32;
|
let n = self.count() as i32;
|
||||||
|
|
||||||
for i in 0..N_SPECTRUM_SAMPLES {
|
for i in 0..N_SPECTRUM_SAMPLES {
|
||||||
let offset = lambda[i].round() as i32 - self.lambda_min;
|
let offset = lambda[i].round() as i32 - self.lambda_min;
|
||||||
|
s[i] = if offset < 0 || offset >= n {
|
||||||
if offset < 0 || offset >= n {
|
0.0
|
||||||
s[i] = 0.0;
|
|
||||||
} else {
|
} else {
|
||||||
unsafe {
|
self.value(offset as u32)
|
||||||
s[i] = *self.values.add(offset as usize);
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
@ -92,47 +164,66 @@ impl SpectrumTrait for DenselySampledSpectrum {
|
||||||
if offset < 0 || offset >= n {
|
if offset < 0 || offset >= n {
|
||||||
0.0
|
0.0
|
||||||
} else {
|
} else {
|
||||||
unsafe { *self.values.add(offset as usize) }
|
self.value(offset as u32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn max_value(&self) -> Float {
|
|
||||||
if self.values.is_null() {
|
|
||||||
return 0.;
|
|
||||||
}
|
|
||||||
|
|
||||||
let n = self.count();
|
|
||||||
let mut max_val = Float::NEG_INFINITY;
|
|
||||||
|
|
||||||
for i in 0..n {
|
|
||||||
unsafe {
|
|
||||||
let val = *self.values.add(i);
|
|
||||||
if val > max_val {
|
|
||||||
max_val = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
max_val
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PiecewiseLinearSpectrum {
|
pub struct PiecewiseLinearSpectrum {
|
||||||
pub lambdas: Ptr<Float>,
|
pub lambdas: GVec<Float>,
|
||||||
pub values: Ptr<Float>,
|
pub values: GVec<Float>,
|
||||||
pub count: u32,
|
pub count: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PiecewiseLinearSpectrum {
|
impl PiecewiseLinearSpectrum {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn lambda(&self, i: u32) -> Float {
|
pub fn count(&self) -> usize {
|
||||||
unsafe { *self.lambdas.add(i as usize) }
|
self.count.try_into().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn lambda(&self, idx: u32) -> Float {
|
||||||
|
unsafe { *self.lambdas.as_ptr().add(idx as usize) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn value(&self, i: u32) -> Float {
|
pub fn value(&self, idx: u32) -> Float {
|
||||||
unsafe { *self.values.add(i as usize) }
|
unsafe { *self.values.as_ptr().add(idx as usize) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(lambdas: GVec<Float>, values: GVec<Float>) -> Self {
|
||||||
|
assert_eq!(lambdas.len(), values.len());
|
||||||
|
let count = lambdas.len() as u32;
|
||||||
|
Self {
|
||||||
|
lambdas,
|
||||||
|
values,
|
||||||
|
count,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_interleaved(data: &[Float], _normalize: bool) -> Self {
|
||||||
|
assert!(
|
||||||
|
data.len() % 2 == 0,
|
||||||
|
"Interleaved data must have even length"
|
||||||
|
);
|
||||||
|
|
||||||
|
let n = data.len() / 2;
|
||||||
|
let mut pairs: GVec<(Float, Float)> = gvec_with_capacity(n);
|
||||||
|
for chunk in data.chunks(2) {
|
||||||
|
pairs.push((chunk[0], chunk[1]));
|
||||||
|
}
|
||||||
|
pairs.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(core::cmp::Ordering::Equal));
|
||||||
|
|
||||||
|
let mut lambdas = gvec_with_capacity(n);
|
||||||
|
let mut values = gvec_with_capacity(n);
|
||||||
|
for (l, v) in pairs.iter() {
|
||||||
|
lambdas.push(*l);
|
||||||
|
values.push(*v);
|
||||||
|
}
|
||||||
|
Self::new(lambdas, values)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,7 +232,7 @@ unsafe impl Sync for PiecewiseLinearSpectrum {}
|
||||||
|
|
||||||
impl SpectrumTrait for PiecewiseLinearSpectrum {
|
impl SpectrumTrait for PiecewiseLinearSpectrum {
|
||||||
fn evaluate(&self, lambda: Float) -> Float {
|
fn evaluate(&self, lambda: Float) -> Float {
|
||||||
if self.lambdas.is_null() {
|
if self.lambdas.is_empty() {
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,7 +256,7 @@ impl SpectrumTrait for PiecewiseLinearSpectrum {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn max_value(&self) -> Float {
|
fn max_value(&self) -> Float {
|
||||||
if self.values.is_null() {
|
if self.values.is_empty() {
|
||||||
return 0.;
|
return 0.;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -174,7 +265,7 @@ impl SpectrumTrait for PiecewiseLinearSpectrum {
|
||||||
|
|
||||||
for i in 0..n {
|
for i in 0..n {
|
||||||
unsafe {
|
unsafe {
|
||||||
let val = *self.values.add(i as usize);
|
let val = *self.values.as_ptr().add(i as usize);
|
||||||
if val > max_val {
|
if val > max_val {
|
||||||
max_val = val;
|
max_val = val;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ pub struct GPUSpectrumImageTexture {
|
||||||
pub scale: Float,
|
pub scale: Float,
|
||||||
pub invert: bool,
|
pub invert: bool,
|
||||||
pub is_single_channel: bool,
|
pub is_single_channel: bool,
|
||||||
pub color_space: RGBColorSpace,
|
pub color_space: Ptr<RGBColorSpace>,
|
||||||
pub spectrum_type: SpectrumType,
|
pub spectrum_type: SpectrumType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
202
shared/src/utils/alloc.rs
Normal file
202
shared/src/utils/alloc.rs
Normal file
|
|
@ -0,0 +1,202 @@
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
use alloc::alloc::Global;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
use core::alloc::{AllocError, Allocator, Layout};
|
||||||
|
use core::ptr::NonNull;
|
||||||
|
|
||||||
|
// CPU fallback, delegates to Global
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct SystemAlloc;
|
||||||
|
|
||||||
|
unsafe impl Allocator for SystemAlloc {
|
||||||
|
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
|
Global.allocate(layout)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
|
||||||
|
Global.deallocate(ptr, layout)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn grow(
|
||||||
|
&self,
|
||||||
|
ptr: NonNull<u8>,
|
||||||
|
old_layout: Layout,
|
||||||
|
new_layout: Layout,
|
||||||
|
) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
|
Global.grow(ptr, old_layout, new_layout)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn shrink(
|
||||||
|
&self,
|
||||||
|
ptr: NonNull<u8>,
|
||||||
|
old_layout: Layout,
|
||||||
|
new_layout: Layout,
|
||||||
|
) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
|
Global.shrink(ptr, old_layout, new_layout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unified memory via cudaMallocManaged
|
||||||
|
#[cfg(feature = "cuda")]
|
||||||
|
pub mod cuda {
|
||||||
|
use super::*;
|
||||||
|
use cust::memory::{cuda_free_unified, cuda_malloc_unified, UnifiedPointer};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct CudaAlloc;
|
||||||
|
|
||||||
|
unsafe impl Allocator for CudaAlloc {
|
||||||
|
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
|
if layout.size() == 0 {
|
||||||
|
// Zero-sized allocations: return a dangling aligned pointer
|
||||||
|
// with zero length, which is valid for ZSTs.
|
||||||
|
let ptr = NonNull::new(layout.align() as *mut u8).ok_or(AllocError)?;
|
||||||
|
return Ok(NonNull::slice_from_raw_parts(ptr, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
let ptr = cuda_malloc_unified::<u8>(layout.size()).map_err(|_| AllocError)?;
|
||||||
|
|
||||||
|
let raw = ptr.as_raw_mut();
|
||||||
|
core::mem::forget(ptr); // Arena owns the raw pointer, not the RAII wrapper
|
||||||
|
|
||||||
|
let nn = NonNull::new(raw).ok_or(AllocError)?;
|
||||||
|
Ok(NonNull::slice_from_raw_parts(nn, layout.size()))
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
|
||||||
|
if layout.size() == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let _ = cuda_free_unified(UnifiedPointer::wrap(ptr.as_ptr()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Host-visible GPU memory via gpu-allocator
|
||||||
|
|
||||||
|
#[cfg(feature = "vulkan")]
|
||||||
|
pub mod vulkan {
|
||||||
|
use super::*;
|
||||||
|
use ash::vk;
|
||||||
|
use gpu_allocator::vulkan::{
|
||||||
|
Allocation, AllocationCreateDesc, AllocationScheme, Allocator as VkAllocator,
|
||||||
|
AllocatorCreateDesc,
|
||||||
|
};
|
||||||
|
use gpu_allocator::MemoryLocation;
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
/// Wraps a gpu-allocator instance. Clone is cheap
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct VulkanAlloc {
|
||||||
|
inner: Arc<Mutex<VulkanInner>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VulkanInner {
|
||||||
|
allocator: VkAllocator,
|
||||||
|
allocations: HashMap<usize, Allocation>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VulkanAlloc {
|
||||||
|
pub fn new(
|
||||||
|
instance: &ash::Instance,
|
||||||
|
device: ash::Device,
|
||||||
|
physical_device: vk::PhysicalDevice,
|
||||||
|
) -> Self {
|
||||||
|
let allocator = VkAllocator::new(&AllocatorCreateDesc {
|
||||||
|
instance: instance.clone(),
|
||||||
|
device: device.clone(),
|
||||||
|
physical_device,
|
||||||
|
debug_settings: Default::default(),
|
||||||
|
buffer_device_address: false,
|
||||||
|
allocation_sizes: Default::default(),
|
||||||
|
})
|
||||||
|
.expect("Failed to create Vulkan allocator");
|
||||||
|
|
||||||
|
Self {
|
||||||
|
inner: Arc::new(Mutex::new(VulkanInner {
|
||||||
|
allocator,
|
||||||
|
allocations: HashMap::new(),
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Allocator for VulkanAlloc {
|
||||||
|
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
|
||||||
|
if layout.size() == 0 {
|
||||||
|
let ptr = NonNull::new(layout.align() as *mut u8).ok_or(AllocError)?;
|
||||||
|
return Ok(NonNull::slice_from_raw_parts(ptr, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut inner = self.inner.lock();
|
||||||
|
|
||||||
|
let allocation = inner
|
||||||
|
.allocator
|
||||||
|
.allocate(&AllocationCreateDesc {
|
||||||
|
name: "gvec_alloc",
|
||||||
|
requirements: vk::MemoryRequirements {
|
||||||
|
size: layout.size() as u64,
|
||||||
|
alignment: layout.align() as u64,
|
||||||
|
memory_type_bits: u32::MAX,
|
||||||
|
},
|
||||||
|
location: MemoryLocation::CpuToGpu,
|
||||||
|
linear: true,
|
||||||
|
allocation_scheme: AllocationScheme::GpuAllocatorManaged,
|
||||||
|
})
|
||||||
|
.map_err(|_| AllocError)?;
|
||||||
|
|
||||||
|
let ptr = allocation.mapped_ptr().ok_or(AllocError)?.as_ptr() as *mut u8;
|
||||||
|
|
||||||
|
let nn = NonNull::new(ptr).ok_or(AllocError)?;
|
||||||
|
inner.allocations.insert(ptr as usize, allocation);
|
||||||
|
Ok(NonNull::slice_from_raw_parts(nn, layout.size()))
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
|
||||||
|
if layout.size() == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut inner = self.inner.lock();
|
||||||
|
if let Some(allocation) = inner.allocations.remove(&(ptr.as_ptr() as usize)) {
|
||||||
|
inner
|
||||||
|
.allocator
|
||||||
|
.free(allocation)
|
||||||
|
.expect("Vulkan free failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "cuda")]
|
||||||
|
pub type GpuAlloc = cuda::CudaAlloc;
|
||||||
|
|
||||||
|
#[cfg(all(feature = "vulkan", not(feature = "cuda")))]
|
||||||
|
pub type GpuAlloc = vulkan::VulkanAlloc;
|
||||||
|
|
||||||
|
#[cfg(not(any(feature = "cuda", feature = "vulkan")))]
|
||||||
|
pub type GpuAlloc = SystemAlloc;
|
||||||
|
|
||||||
|
pub type GVec<T> = alloc::vec::Vec<T, GpuAlloc>;
|
||||||
|
pub type GBox<T> = alloc::boxed::Box<T, GpuAlloc>;
|
||||||
|
|
||||||
|
pub fn gvec<T>() -> GVec<T> {
|
||||||
|
Vec::new_in(GpuAlloc::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gvec_with_capacity<T>(cap: usize) -> GVec<T> {
|
||||||
|
Vec::with_capacity_in(cap, GpuAlloc::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gvec_from_slice<T: Clone>(slice: &[T]) -> GVec<T> {
|
||||||
|
let mut v = gvec_with_capacity(slice.len());
|
||||||
|
v.extend_from_slice(slice);
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gbox<T>(value: T) -> GBox<T> {
|
||||||
|
Box::new_in(value, GpuAlloc::default())
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
use crate::core::geometry::{
|
use crate::core::geometry::{
|
||||||
Bounds2i, Bounds3f, Bounds3i, Point2i, Point3f, Point3i, Vector2i, Vector3f, Vector3i,
|
Bounds2i, Bounds3f, Bounds3i, Point2i, Point3f, Point3i, Vector2i, Vector3f, Vector3i,
|
||||||
};
|
};
|
||||||
use crate::core::pbrt::Float;
|
|
||||||
use crate::utils::Ptr;
|
|
||||||
use crate::utils::math::lerp;
|
use crate::utils::math::lerp;
|
||||||
|
use crate::{gvec, gvec_from_slice, Float, GVec};
|
||||||
use core::ops::{Add, Index, IndexMut, Mul, Sub};
|
use core::ops::{Add, Index, IndexMut, Mul, Sub};
|
||||||
|
|
||||||
pub trait Interpolatable:
|
pub trait Interpolatable:
|
||||||
|
|
@ -16,107 +15,121 @@ impl<T> Interpolatable for T where
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[derive(Debug, Clone)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
pub struct Array2D<T> {
|
||||||
pub struct DeviceArray2D<T> {
|
extent: Bounds2i,
|
||||||
pub values: *mut T,
|
values: GVec<T>,
|
||||||
pub extent: Bounds2i,
|
|
||||||
pub stride: i32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<T: Send> Send for DeviceArray2D<T> {}
|
impl<T> Array2D<T> {
|
||||||
unsafe impl<T: Sync> Sync for DeviceArray2D<T> {}
|
pub fn extent(&self) -> Bounds2i {
|
||||||
|
self.extent
|
||||||
impl<T> DeviceArray2D<T> {
|
|
||||||
#[inline]
|
|
||||||
pub fn x_size(&self) -> u32 {
|
|
||||||
(self.extent.p_max.x() - self.extent.p_min.x()) as u32
|
|
||||||
}
|
}
|
||||||
|
pub fn stride(&self) -> i32 {
|
||||||
#[inline]
|
self.extent.p_max.x() - self.extent.p_min.x()
|
||||||
pub fn y_size(&self) -> u32 {
|
|
||||||
(self.extent.p_max.y() - self.extent.p_min.y()) as u32
|
|
||||||
}
|
}
|
||||||
|
pub fn x_size(&self) -> usize {
|
||||||
#[inline]
|
self.stride() as usize
|
||||||
pub fn size(&self) -> u32 {
|
|
||||||
self.extent.area() as u32
|
|
||||||
}
|
}
|
||||||
|
pub fn y_size(&self) -> usize {
|
||||||
#[inline(always)]
|
(self.extent.p_max.y() - self.extent.p_min.y()) as usize
|
||||||
fn offset(&self, p: Point2i) -> isize {
|
|
||||||
let ox = p.x() - self.extent.p_min.x();
|
|
||||||
let oy = p.y() - self.extent.p_min.y();
|
|
||||||
(ox + oy * self.stride) as isize
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn index(&self, x: i32, y: i32) -> u32 {
|
|
||||||
let nx = x - self.extent.p_min.x();
|
|
||||||
let ny = y - self.extent.p_min.y();
|
|
||||||
nx as u32 + self.x_size() * ny as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn get(&self, p: Point2i) -> &T {
|
|
||||||
unsafe { &*self.values.offset(self.offset(p)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn get_mut(&mut self, p: Point2i) -> &mut T {
|
|
||||||
unsafe { &mut *self.values.offset(self.offset(p)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn get_linear(&self, index: usize) -> &T {
|
|
||||||
unsafe { &*self.values.add(index) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn get_linear_mut(&mut self, index: usize) -> &mut T {
|
|
||||||
unsafe { &mut *self.values.add(index) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_slice(&self) -> &[T] {
|
pub fn as_slice(&self) -> &[T] {
|
||||||
unsafe { core::slice::from_raw_parts(self.values, self.size() as usize) }
|
&self.values
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_mut_slice(&mut self) -> &mut [T] {
|
pub fn as_mut_slice(&mut self) -> &mut [T] {
|
||||||
unsafe { core::slice::from_raw_parts_mut(self.values, self.size() as usize) }
|
&mut self.values
|
||||||
|
}
|
||||||
|
pub fn as_ptr(&self) -> *const T {
|
||||||
|
self.values.as_ptr()
|
||||||
|
}
|
||||||
|
pub fn as_mut_ptr(&mut self) -> *mut T {
|
||||||
|
self.values.as_mut_ptr()
|
||||||
|
}
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.values.len()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Index<Point2i> for DeviceArray2D<T> {
|
unsafe impl<T: Send> Send for Array2D<T> {}
|
||||||
|
unsafe impl<T: Sync> Sync for Array2D<T> {}
|
||||||
|
|
||||||
|
impl<T: Default + Clone> Array2D<T> {
|
||||||
|
pub fn new(extent: Bounds2i) -> Self {
|
||||||
|
let n = extent.area() as usize;
|
||||||
|
let mut values = gvec();
|
||||||
|
values.resize(n, T::default());
|
||||||
|
Self { extent, values }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_dims(nx: i32, ny: i32) -> Self {
|
||||||
|
Self::new(Bounds2i::from_points(
|
||||||
|
Point2i::new(0, 0),
|
||||||
|
Point2i::new(nx, ny),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_filled(nx: i32, ny: i32, val: T) -> Self {
|
||||||
|
let extent = Bounds2i::from_points(Point2i::new(0, 0), Point2i::new(nx, ny));
|
||||||
|
let n = (nx * ny) as usize;
|
||||||
|
let mut values = gvec();
|
||||||
|
values.resize(n, val);
|
||||||
|
Self { extent, values }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_slice(extent: Bounds2i, slice: &[T]) -> Self {
|
||||||
|
assert_eq!(slice.len(), extent.area() as usize);
|
||||||
|
Self {
|
||||||
|
extent,
|
||||||
|
values: gvec_from_slice(slice),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, p: Point2i) -> &T {
|
||||||
|
&self[p]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_linear_mut(&mut self, index: usize) -> &mut T {
|
||||||
|
unsafe { &mut *self.values.as_mut_ptr().add(index) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Index<(i32, i32)> for Array2D<T> {
|
||||||
type Output = T;
|
type Output = T;
|
||||||
#[inline(always)]
|
fn index(&self, (x, y): (i32, i32)) -> &T {
|
||||||
fn index(&self, p: Point2i) -> &Self::Output {
|
let offset = (y - self.extent.p_min.y()) * self.stride() + (x - self.extent.p_min.x());
|
||||||
self.get(p)
|
&self.values[offset as usize]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> IndexMut<Point2i> for DeviceArray2D<T> {
|
impl<T> IndexMut<(i32, i32)> for Array2D<T> {
|
||||||
fn index_mut(&mut self, p: Point2i) -> &mut Self::Output {
|
fn index_mut(&mut self, (x, y): (i32, i32)) -> &mut T {
|
||||||
self.get_mut(p)
|
let offset = (y - self.extent.p_min.y()) * self.stride() + (x - self.extent.p_min.x());
|
||||||
|
&mut self.values[offset as usize]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Index<(i32, i32)> for DeviceArray2D<T> {
|
impl<T> Index<Point2i> for Array2D<T> {
|
||||||
type Output = T;
|
type Output = T;
|
||||||
fn index(&self, (x, y): (i32, i32)) -> &Self::Output {
|
fn index(&self, p: Point2i) -> &T {
|
||||||
&self[Point2i::new(x, y)]
|
let offset =
|
||||||
|
(p.y() - self.extent.p_min.y()) * self.stride() + (p.x() - self.extent.p_min.x());
|
||||||
|
&self.values[offset as usize]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> IndexMut<(i32, i32)> for DeviceArray2D<T> {
|
impl<T> IndexMut<Point2i> for Array2D<T> {
|
||||||
fn index_mut(&mut self, (x, y): (i32, i32)) -> &mut Self::Output {
|
fn index_mut(&mut self, p: Point2i) -> &mut T {
|
||||||
&mut self[Point2i::new(x, y)]
|
let offset =
|
||||||
|
(p.y() - self.extent.p_min.y()) * self.stride() + (p.x() - self.extent.p_min.x());
|
||||||
|
&mut self.values[offset as usize]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SampledGrid<T> {
|
pub struct SampledGrid<T> {
|
||||||
pub values: Ptr<T>,
|
pub values: GVec<T>,
|
||||||
pub values_len: u32,
|
pub values_len: u32,
|
||||||
pub nx: i32,
|
pub nx: i32,
|
||||||
pub ny: i32,
|
pub ny: i32,
|
||||||
|
|
@ -126,12 +139,11 @@ pub struct SampledGrid<T> {
|
||||||
unsafe impl<T: Sync> Sync for SampledGrid<T> {}
|
unsafe impl<T: Sync> Sync for SampledGrid<T> {}
|
||||||
unsafe impl<T: Send> Send for SampledGrid<T> {}
|
unsafe impl<T: Send> Send for SampledGrid<T> {}
|
||||||
|
|
||||||
impl<T> SampledGrid<T> {
|
impl<T: core::clone::Clone> SampledGrid<T> {
|
||||||
#[cfg(not(target_os = "cuda"))]
|
|
||||||
pub fn new(slice: &[T], nx: i32, ny: i32, nz: i32) -> Self {
|
pub fn new(slice: &[T], nx: i32, ny: i32, nz: i32) -> Self {
|
||||||
assert_eq!(slice.len(), (nx * ny * nz) as usize);
|
assert_eq!(slice.len(), (nx * ny * nz) as usize);
|
||||||
Self {
|
Self {
|
||||||
values: Ptr::from(slice),
|
values: gvec_from_slice(slice),
|
||||||
values_len: (nx * ny * nz) as u32,
|
values_len: (nx * ny * nz) as u32,
|
||||||
nx,
|
nx,
|
||||||
ny,
|
ny,
|
||||||
|
|
@ -141,7 +153,7 @@ impl<T> SampledGrid<T> {
|
||||||
|
|
||||||
pub fn empty() -> Self {
|
pub fn empty() -> Self {
|
||||||
Self {
|
Self {
|
||||||
values: Ptr::null(),
|
values: gvec(),
|
||||||
values_len: 0,
|
values_len: 0,
|
||||||
nx: 0,
|
nx: 0,
|
||||||
ny: 0,
|
ny: 0,
|
||||||
|
|
@ -150,7 +162,7 @@ impl<T> SampledGrid<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_valid(&self) -> bool {
|
pub fn is_valid(&self) -> bool {
|
||||||
!self.values.is_null() && self.nx > 0 && self.ny > 0 && self.nz > 0
|
!self.values.is_empty() && self.nx > 0 && self.ny > 0 && self.nz > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bytes_allocated(&self) -> u32 {
|
pub fn bytes_allocated(&self) -> u32 {
|
||||||
|
|
@ -189,7 +201,7 @@ impl<T> SampledGrid<T> {
|
||||||
|
|
||||||
let idx = (p.z() * self.ny + p.y()) * self.nx + p.x();
|
let idx = (p.z() * self.ny + p.y()) * self.nx + p.x();
|
||||||
unsafe {
|
unsafe {
|
||||||
let val = &*self.values.add(idx as usize);
|
let val = &*self.values.as_ptr().add(idx as usize);
|
||||||
convert(val)
|
convert(val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use crate::core::color::{RGB, XYZ};
|
use crate::core::color::{RGB, XYZ};
|
||||||
use crate::core::geometry::{Lerp, MulAdd, Point, Point2f, Point2i, Vector, Vector3f, VectorLike};
|
use crate::core::geometry::{Lerp, MulAdd, Point, Point2f, Point2i, Vector, Vector3f, VectorLike};
|
||||||
use crate::core::pbrt::{Float, FloatBitOps, FloatBits, ONE_MINUS_EPSILON, PI, PI_OVER_4};
|
use crate::core::pbrt::{Float, FloatBitOps, FloatBits, ONE_MINUS_EPSILON, PI, PI_OVER_4};
|
||||||
|
use crate::utils::gpu_array_from_fn;
|
||||||
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::{gvec, gvec_with_capacity, GVec, Ptr};
|
||||||
use crate::utils::{Ptr, gpu_array_from_fn};
|
|
||||||
use core::fmt::{self, Display, Write};
|
use core::fmt::{self, Display, Write};
|
||||||
use core::iter::{Product, Sum};
|
use core::iter::{Product, Sum};
|
||||||
use core::mem;
|
use core::mem;
|
||||||
|
|
@ -137,7 +137,12 @@ pub fn windowed_sinc(x: Float, radius: Float, tau: Float) -> Float {
|
||||||
if x.abs() > radius {
|
if x.abs() > radius {
|
||||||
return 0.;
|
return 0.;
|
||||||
}
|
}
|
||||||
sinc(x) * sinc(x / tau)
|
|
||||||
|
if x < 1e-5 {
|
||||||
|
1.0
|
||||||
|
} else {
|
||||||
|
sinc(x) * sinc(x / tau)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -261,7 +266,11 @@ pub fn quadratic(a: Float, b: Float, c: Float) -> Option<(Float, Float)> {
|
||||||
|
|
||||||
pub fn smooth_step(x: Float, a: Float, b: Float) -> Float {
|
pub fn smooth_step(x: Float, a: Float, b: Float) -> Float {
|
||||||
if a == b {
|
if a == b {
|
||||||
if x < a { return 0. } else { return 1. }
|
if x < a {
|
||||||
|
return 0.;
|
||||||
|
} else {
|
||||||
|
return 1.;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let t = clamp((x - a) / (b - a), 0., 1.);
|
let t = clamp((x - a) / (b - a), 0., 1.);
|
||||||
|
|
@ -760,27 +769,65 @@ pub fn inverse_radical_inverse(mut inverse: u64, base: u64, n_digits: u64) -> u6
|
||||||
|
|
||||||
// Digit scrambling
|
// Digit scrambling
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Default, Debug, Copy, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DeviceDigitPermutation {
|
pub struct DigitPermutation {
|
||||||
pub base: i32,
|
pub base: i32,
|
||||||
pub n_digits: u32,
|
pub n_digits: u32,
|
||||||
pub permutations: Ptr<u16>,
|
pub permutations: GVec<u16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeviceDigitPermutation {
|
impl Default for DigitPermutation {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
base: i32::default(),
|
||||||
|
n_digits: u32::default(),
|
||||||
|
permutations: gvec(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigitPermutation {
|
||||||
|
pub fn new(base: i32, seed: u64) -> Self {
|
||||||
|
assert!(base < 65536);
|
||||||
|
let mut n_digits: u32 = 0;
|
||||||
|
let inv_base = 1. / base as Float;
|
||||||
|
let mut inv_base_m = 1.;
|
||||||
|
|
||||||
|
while 1.0 - ((base as Float - 1.0) * inv_base_m) < 1.0 {
|
||||||
|
n_digits += 1;
|
||||||
|
inv_base_m *= inv_base;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut permutations = gvec_with_capacity(n_digits as usize * base as usize);
|
||||||
|
|
||||||
|
for digit_index in 0..n_digits {
|
||||||
|
let hash_input = [base as u64, digit_index as u64, seed];
|
||||||
|
let dseed = hash_buffer(&hash_input, 0);
|
||||||
|
|
||||||
|
for digit_value in 0..base {
|
||||||
|
let index = (digit_index as i32 * base + digit_value) as usize;
|
||||||
|
|
||||||
|
permutations[index] =
|
||||||
|
permutation_element(digit_value as u32, base as u32, dseed as u32) as u16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
base,
|
||||||
|
n_digits,
|
||||||
|
permutations,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn permute(&self, digit_index: i32, digit_value: i32) -> i32 {
|
pub fn permute(&self, digit_index: i32, digit_value: i32) -> i32 {
|
||||||
let idx = (digit_index * self.base as i32 + digit_value) as usize;
|
let idx = (digit_index * self.base as i32 + digit_value) as usize;
|
||||||
let permutation = unsafe { *self.permutations.add(idx.into()) };
|
let permutation = unsafe { *self.permutations.as_ptr().add(idx.into()) };
|
||||||
permutation as i32
|
permutation as i32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scrambled_radical_inverse(
|
pub fn scrambled_radical_inverse(base_index: u32, mut a: u64, perm: &DigitPermutation) -> Float {
|
||||||
base_index: u32,
|
|
||||||
mut a: u64,
|
|
||||||
perm: &DeviceDigitPermutation,
|
|
||||||
) -> Float {
|
|
||||||
let base = PRIMES[base_index as usize] as u64;
|
let base = PRIMES[base_index as usize] as u64;
|
||||||
|
|
||||||
let limit = (u64::MAX / base).saturating_sub(base);
|
let limit = (u64::MAX / base).saturating_sub(base);
|
||||||
|
|
@ -1443,7 +1490,11 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
let det: T = (0..N).map(|i| lum[i][i]).product();
|
let det: T = (0..N).map(|i| lum[i][i]).product();
|
||||||
if parity < 0 { -det } else { det }
|
if parity < 0 {
|
||||||
|
-det
|
||||||
|
} else {
|
||||||
|
det
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
use crate::Float;
|
|
||||||
use crate::core::geometry::{Normal3f, Point2f, Point3f, Vector3f};
|
|
||||||
use crate::utils::Ptr;
|
|
||||||
use crate::utils::Transform;
|
|
||||||
use crate::utils::sampling::DevicePiecewiseConstant2D;
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct DeviceTriangleMesh {
|
|
||||||
pub p: Ptr<Point3f>,
|
|
||||||
pub n: Ptr<Normal3f>,
|
|
||||||
pub s: Ptr<Vector3f>,
|
|
||||||
pub uv: Ptr<Point2f>,
|
|
||||||
pub vertex_indices: Ptr<i32>,
|
|
||||||
pub face_indices: Ptr<i32>,
|
|
||||||
pub n_triangles: u32,
|
|
||||||
pub n_vertices: u32,
|
|
||||||
pub reverse_orientation: bool,
|
|
||||||
pub transform_swaps_handedness: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct DeviceBilinearPatchMesh {
|
|
||||||
pub image_distribution: Ptr<DevicePiecewiseConstant2D>,
|
|
||||||
pub p: Ptr<Point3f>,
|
|
||||||
pub n: Ptr<Normal3f>,
|
|
||||||
pub uv: Ptr<Point2f>,
|
|
||||||
pub vertex_indices: Ptr<i32>,
|
|
||||||
pub n_patches: u32,
|
|
||||||
pub n_vertices: u32,
|
|
||||||
pub reverse_orientation: bool,
|
|
||||||
pub transform_swaps_handedness: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for DeviceTriangleMesh {}
|
|
||||||
unsafe impl Sync for DeviceTriangleMesh {}
|
|
||||||
unsafe impl Send for DeviceBilinearPatchMesh {}
|
|
||||||
unsafe impl Sync for DeviceBilinearPatchMesh {}
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
|
pub mod alloc;
|
||||||
pub mod complex;
|
pub mod complex;
|
||||||
pub mod containers;
|
pub mod containers;
|
||||||
pub mod hash;
|
pub mod hash;
|
||||||
pub mod interval;
|
pub mod interval;
|
||||||
pub mod math;
|
pub mod math;
|
||||||
pub mod mesh;
|
|
||||||
pub mod noise;
|
pub mod noise;
|
||||||
pub mod options;
|
pub mod options;
|
||||||
pub mod ptr;
|
pub mod ptr;
|
||||||
|
|
@ -17,15 +17,7 @@ pub mod transform;
|
||||||
pub use options::PBRTOptions;
|
pub use options::PBRTOptions;
|
||||||
pub use ptr::Ptr;
|
pub use ptr::Ptr;
|
||||||
pub use transform::{AnimatedTransform, Transform, TransformGeneric};
|
pub use transform::{AnimatedTransform, Transform, TransformGeneric};
|
||||||
|
pub use containers::Array2D;
|
||||||
use proc_macro::TokenStream;
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
|
||||||
use quote::{format_ident, quote};
|
|
||||||
use syn::{
|
|
||||||
parse_macro_input, Attribute, Data, DeriveInput, Expr, Fields, GenericArgument, Ident, Lit,
|
|
||||||
PathArguments, Type, Variant,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
use crate::Float;
|
use crate::Float;
|
||||||
use core::sync::atomic::{AtomicU32, Ordering};
|
use core::sync::atomic::{AtomicU32, Ordering};
|
||||||
|
|
@ -138,514 +130,3 @@ pub fn gpu_array_from_fn<T, const N: usize>(mut f: impl FnMut(usize) -> T) -> [T
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Enum variant attributes
|
|
||||||
///
|
|
||||||
/// | Attribute | Effect |
|
|
||||||
/// |-----------|--------|
|
|
||||||
/// | *(none)* | Inner type has `DeviceRepr`; auto-call `upload_value` |
|
|
||||||
/// | `#[device(clone)]` | Same type on both sides, just clone |
|
|
||||||
/// | `#[device(custom = "method")]` | You provide `fn method(inner: &T, arena) -> DeviceT` |
|
|
||||||
/// | `#[device(variant_type = "T")]` | Override the device-side variant's inner type |
|
|
||||||
///
|
|
||||||
/// # Container attribute
|
|
||||||
///
|
|
||||||
/// `#[device(name = "DeviceFoo")]` — override the generated type name (default: `Device{Name}`).
|
|
||||||
#[proc_macro_derive(Device, attributes(device))]
|
|
||||||
pub fn derive_device(input: TokenStream) -> TokenStream {
|
|
||||||
let input = parse_macro_input!(input as DeriveInput);
|
|
||||||
match derive_impl(input) {
|
|
||||||
Ok(tokens) => tokens.into(),
|
|
||||||
Err(e) => e.to_compile_error().into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn derive_impl(input: DeriveInput) -> syn::Result<TokenStream2> {
|
|
||||||
match &input.data {
|
|
||||||
Data::Struct(_) => derive_struct(input),
|
|
||||||
Data::Enum(_) => derive_enum(input),
|
|
||||||
Data::Union(_) => Err(syn::Error::new_spanned(
|
|
||||||
&input.ident,
|
|
||||||
"Device derive does not support unions",
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Struct derivation
|
|
||||||
|
|
||||||
fn derive_struct(input: DeriveInput) -> syn::Result<TokenStream2> {
|
|
||||||
let host_name = &input.ident;
|
|
||||||
let vis = &input.vis;
|
|
||||||
let device_name = get_device_name(&input.attrs, host_name)?;
|
|
||||||
|
|
||||||
let fields = match &input.data {
|
|
||||||
Data::Struct(s) => match &s.fields {
|
|
||||||
Fields::Named(named) => &named.named,
|
|
||||||
_ => {
|
|
||||||
return Err(syn::Error::new_spanned(
|
|
||||||
host_name,
|
|
||||||
"Device derive only supports structs with named fields",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut device_fields = Vec::new();
|
|
||||||
let mut upload_stmts = Vec::new();
|
|
||||||
let mut device_field_inits = Vec::new();
|
|
||||||
let mut spread_expr: Option<Expr> = None;
|
|
||||||
|
|
||||||
for field in fields {
|
|
||||||
let field_name = field.ident.as_ref().unwrap();
|
|
||||||
let attrs = parse_field_attrs(&field.attrs)?;
|
|
||||||
|
|
||||||
if attrs.skip {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(ref expr_str) = attrs.spread {
|
|
||||||
spread_expr = Some(syn::parse_str(expr_str).map_err(|e| {
|
|
||||||
syn::Error::new_spanned(field, format!("invalid device(spread): {}", e))
|
|
||||||
})?);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(expr_str) = &attrs.expr {
|
|
||||||
let expr: Expr = syn::parse_str(expr_str).map_err(|e| {
|
|
||||||
syn::Error::new_spanned(field, format!("invalid device(expr): {}", e))
|
|
||||||
})?;
|
|
||||||
let ty = &field.ty;
|
|
||||||
device_fields.push(quote! { pub #field_name: #ty });
|
|
||||||
upload_stmts.push(quote! { let #field_name = #expr; });
|
|
||||||
device_field_inits.push(quote! { #field_name });
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
match classify_type(&field.ty) {
|
|
||||||
FieldClass::VecCopy(inner_ty) => {
|
|
||||||
let len_name = format_ident!("{}_len", field_name);
|
|
||||||
device_fields.push(quote! { pub #field_name: Ptr<#inner_ty> });
|
|
||||||
device_fields.push(quote! { pub #len_name: usize });
|
|
||||||
upload_stmts.push(quote! {
|
|
||||||
let (#field_name, #len_name) = arena.alloc_slice(&self.#field_name);
|
|
||||||
});
|
|
||||||
device_field_inits.push(quote! { #field_name });
|
|
||||||
device_field_inits.push(quote! { #len_name });
|
|
||||||
}
|
|
||||||
FieldClass::VecUploadable(inner_ty) => {
|
|
||||||
let len_name = format_ident!("{}_len", field_name);
|
|
||||||
device_fields.push(quote! {
|
|
||||||
pub #field_name: Ptr<<#inner_ty as DeviceRepr>::Target>
|
|
||||||
});
|
|
||||||
device_fields.push(quote! { pub #len_name: usize });
|
|
||||||
upload_stmts.push(quote! {
|
|
||||||
let __up: Vec<<#inner_ty as DeviceRepr>::Target> = self.#field_name
|
|
||||||
.iter()
|
|
||||||
.map(|item| DeviceRepr::upload_value(item, arena))
|
|
||||||
.collect();
|
|
||||||
let (#field_name, #len_name) = arena.alloc_slice(&__up);
|
|
||||||
});
|
|
||||||
device_field_inits.push(quote! { #field_name });
|
|
||||||
device_field_inits.push(quote! { #len_name });
|
|
||||||
}
|
|
||||||
FieldClass::Option(inner_ty) => {
|
|
||||||
device_fields.push(quote! {
|
|
||||||
pub #field_name: Ptr<<#inner_ty as DeviceRepr>::Target>
|
|
||||||
});
|
|
||||||
upload_stmts.push(quote! {
|
|
||||||
let #field_name = match &self.#field_name {
|
|
||||||
Some(val) => DeviceRepr::upload(val, arena),
|
|
||||||
None => Ptr::null(),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
device_field_inits.push(quote! { #field_name });
|
|
||||||
}
|
|
||||||
FieldClass::Arc(inner_ty) => {
|
|
||||||
if attrs.flatten {
|
|
||||||
device_fields.push(quote! {
|
|
||||||
pub #field_name: <#inner_ty as DeviceRepr>::Target
|
|
||||||
});
|
|
||||||
upload_stmts.push(quote! {
|
|
||||||
let #field_name = DeviceRepr::upload_value(&*self.#field_name, arena);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
device_fields.push(quote! {
|
|
||||||
pub #field_name: Ptr<<#inner_ty as DeviceRepr>::Target>
|
|
||||||
});
|
|
||||||
upload_stmts.push(quote! {
|
|
||||||
let #field_name = DeviceRepr::upload(&*self.#field_name, arena);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
device_field_inits.push(quote! { #field_name });
|
|
||||||
}
|
|
||||||
FieldClass::Plain => {
|
|
||||||
let ty = &field.ty;
|
|
||||||
if attrs.copy_upload {
|
|
||||||
device_fields.push(quote! { pub #field_name: #ty });
|
|
||||||
upload_stmts.push(quote! {
|
|
||||||
let #field_name = self.#field_name.clone();
|
|
||||||
});
|
|
||||||
} else if attrs.flatten {
|
|
||||||
device_fields.push(quote! {
|
|
||||||
pub #field_name: <#ty as DeviceRepr>::Target
|
|
||||||
});
|
|
||||||
upload_stmts.push(quote! {
|
|
||||||
let #field_name = DeviceRepr::upload_value(&self.#field_name, arena);
|
|
||||||
});
|
|
||||||
} else if attrs.upload {
|
|
||||||
device_fields.push(quote! {
|
|
||||||
pub #field_name: Ptr<<#ty as DeviceRepr>::Target>
|
|
||||||
});
|
|
||||||
upload_stmts.push(quote! {
|
|
||||||
let #field_name = DeviceRepr::upload(&self.#field_name, arena);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
device_fields.push(quote! { pub #field_name: #ty });
|
|
||||||
upload_stmts.push(quote! {
|
|
||||||
let #field_name = self.#field_name;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
device_field_inits.push(quote! { #field_name });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let constructor = if let Some(spread) = spread_expr {
|
|
||||||
quote! {
|
|
||||||
#device_name {
|
|
||||||
#(#device_field_inits,)*
|
|
||||||
..#spread
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quote! {
|
|
||||||
#device_name {
|
|
||||||
#(#device_field_inits,)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(quote! {
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
#vis struct #device_name {
|
|
||||||
#(#device_fields,)*
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for #device_name {}
|
|
||||||
unsafe impl Sync for #device_name {}
|
|
||||||
|
|
||||||
impl DeviceRepr for #host_name {
|
|
||||||
type Target = #device_name;
|
|
||||||
|
|
||||||
fn upload_value<A: GpuAllocator>(&self, arena: &Arena<A>) -> Self::Target {
|
|
||||||
#(#upload_stmts)*
|
|
||||||
#constructor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enum derivation
|
|
||||||
fn derive_enum(input: DeriveInput) -> syn::Result<TokenStream2> {
|
|
||||||
let host_name = &input.ident;
|
|
||||||
let vis = &input.vis;
|
|
||||||
let device_name = get_device_name(&input.attrs, host_name)?;
|
|
||||||
|
|
||||||
let variants = match &input.data {
|
|
||||||
Data::Enum(e) => &e.variants,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut device_variants = Vec::new();
|
|
||||||
let mut match_arms = Vec::new();
|
|
||||||
|
|
||||||
for variant in variants {
|
|
||||||
let var_name = &variant.ident;
|
|
||||||
let var_attrs = parse_variant_attrs(&variant.attrs)?;
|
|
||||||
let inner_ty = get_variant_inner_type(variant)?;
|
|
||||||
|
|
||||||
// Determine the device-side inner type for this variant
|
|
||||||
let device_inner: Type = if let Some(ref ty_str) = var_attrs.variant_type {
|
|
||||||
syn::parse_str(ty_str).map_err(|e| {
|
|
||||||
syn::Error::new_spanned(variant, format!("invalid variant_type: {}", e))
|
|
||||||
})?
|
|
||||||
} else if var_attrs.clone_variant {
|
|
||||||
// clone: same type on both sides
|
|
||||||
inner_ty.clone()
|
|
||||||
} else {
|
|
||||||
// auto-upload: use DeviceRepr::Target
|
|
||||||
syn::parse_str(&format!("<{} as DeviceRepr>::Target", quote!(#inner_ty))).map_err(
|
|
||||||
|e| {
|
|
||||||
syn::Error::new_spanned(variant, format!("cannot construct Target type: {}", e))
|
|
||||||
},
|
|
||||||
)?
|
|
||||||
};
|
|
||||||
|
|
||||||
device_variants.push(quote! { #var_name(#device_inner) });
|
|
||||||
|
|
||||||
if var_attrs.clone_variant {
|
|
||||||
match_arms.push(quote! {
|
|
||||||
#host_name::#var_name(inner) => #device_name::#var_name(inner.clone())
|
|
||||||
});
|
|
||||||
} else if let Some(ref method) = var_attrs.custom {
|
|
||||||
let method_ident = format_ident!("{}", method);
|
|
||||||
match_arms.push(quote! {
|
|
||||||
#host_name::#var_name(inner) => {
|
|
||||||
#device_name::#var_name(Self::#method_ident(inner, arena))
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Default: inner implements DeviceRepr
|
|
||||||
match_arms.push(quote! {
|
|
||||||
#host_name::#var_name(inner) => {
|
|
||||||
#device_name::#var_name(DeviceRepr::upload_value(inner, arena))
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(quote! {
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
#vis enum #device_name {
|
|
||||||
#(#device_variants,)*
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for #device_name {}
|
|
||||||
unsafe impl Sync for #device_name {}
|
|
||||||
|
|
||||||
impl DeviceRepr for #host_name {
|
|
||||||
type Target = #device_name;
|
|
||||||
|
|
||||||
fn upload_value<A: GpuAllocator>(&self, arena: &Arena<A>) -> Self::Target {
|
|
||||||
match self {
|
|
||||||
#(#match_arms,)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_variant_inner_type(variant: &Variant) -> syn::Result<Type> {
|
|
||||||
match &variant.fields {
|
|
||||||
Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
|
|
||||||
Ok(fields.unnamed.first().unwrap().ty.clone())
|
|
||||||
}
|
|
||||||
Fields::Unit => Err(syn::Error::new_spanned(
|
|
||||||
variant,
|
|
||||||
"Device derive: enum variants must have exactly one field, e.g. Variant(Type)",
|
|
||||||
)),
|
|
||||||
_ => Err(syn::Error::new_spanned(
|
|
||||||
variant,
|
|
||||||
"Device derive: only single-field tuple variants supported, e.g. Variant(Type)",
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attribute parsing for variants
|
|
||||||
struct VariantAttrs {
|
|
||||||
clone_variant: bool,
|
|
||||||
custom: Option<String>,
|
|
||||||
variant_type: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_variant_attrs(attrs: &[Attribute]) -> syn::Result<VariantAttrs> {
|
|
||||||
let mut result = VariantAttrs {
|
|
||||||
clone_variant: false,
|
|
||||||
custom: None,
|
|
||||||
variant_type: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
for attr in attrs {
|
|
||||||
if !attr.path().is_ident("device") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
attr.parse_nested_meta(|meta| {
|
|
||||||
if meta.path.is_ident("clone") {
|
|
||||||
result.clone_variant = true;
|
|
||||||
Ok(())
|
|
||||||
} else if meta.path.is_ident("custom") {
|
|
||||||
let value = meta.value()?;
|
|
||||||
let lit: Lit = value.parse()?;
|
|
||||||
if let Lit::Str(s) = lit {
|
|
||||||
result.custom = Some(s.value());
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(meta.error("expected string literal"))
|
|
||||||
}
|
|
||||||
} else if meta.path.is_ident("variant_type") {
|
|
||||||
let value = meta.value()?;
|
|
||||||
let lit: Lit = value.parse()?;
|
|
||||||
if let Lit::Str(s) = lit {
|
|
||||||
result.variant_type = Some(s.value());
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(meta.error("expected string literal"))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(meta.error("unknown device variant attribute"))
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attribute parsing for fields
|
|
||||||
struct FieldAttrs {
|
|
||||||
skip: bool,
|
|
||||||
expr: Option<String>,
|
|
||||||
copy_upload: bool,
|
|
||||||
flatten: bool,
|
|
||||||
upload: bool,
|
|
||||||
spread: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_field_attrs(attrs: &[Attribute]) -> syn::Result<FieldAttrs> {
|
|
||||||
let mut result = FieldAttrs {
|
|
||||||
skip: false,
|
|
||||||
expr: None,
|
|
||||||
copy_upload: false,
|
|
||||||
flatten: false,
|
|
||||||
upload: false,
|
|
||||||
spread: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
for attr in attrs {
|
|
||||||
if !attr.path().is_ident("device") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
attr.parse_nested_meta(|meta| {
|
|
||||||
if meta.path.is_ident("skip") {
|
|
||||||
result.skip = true;
|
|
||||||
Ok(())
|
|
||||||
} else if meta.path.is_ident("expr") {
|
|
||||||
let value = meta.value()?;
|
|
||||||
let lit: Lit = value.parse()?;
|
|
||||||
if let Lit::Str(s) = lit {
|
|
||||||
result.expr = Some(s.value());
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(meta.error("expected string literal"))
|
|
||||||
}
|
|
||||||
} else if meta.path.is_ident("copy_upload") {
|
|
||||||
result.copy_upload = true;
|
|
||||||
Ok(())
|
|
||||||
} else if meta.path.is_ident("flatten") {
|
|
||||||
result.flatten = true;
|
|
||||||
Ok(())
|
|
||||||
} else if meta.path.is_ident("upload") {
|
|
||||||
result.upload = true;
|
|
||||||
Ok(())
|
|
||||||
} else if meta.path.is_ident("spread") {
|
|
||||||
let value = meta.value()?;
|
|
||||||
let lit: Lit = value.parse()?;
|
|
||||||
if let Lit::Str(s) = lit {
|
|
||||||
result.spread = Some(s.value());
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(meta.error("expected string literal"))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(meta.error("unknown device attribute"))
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Container-level name attribute
|
|
||||||
fn get_device_name(attrs: &[Attribute], host_name: &Ident) -> syn::Result<Ident> {
|
|
||||||
for attr in attrs {
|
|
||||||
if !attr.path().is_ident("device") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let mut name = None;
|
|
||||||
attr.parse_nested_meta(|meta| {
|
|
||||||
if meta.path.is_ident("name") {
|
|
||||||
let value = meta.value()?;
|
|
||||||
let lit: Lit = value.parse()?;
|
|
||||||
if let Lit::Str(s) = lit {
|
|
||||||
name = Some(format_ident!("{}", s.value()));
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(meta.error("expected string literal"))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
if let Some(n) = name {
|
|
||||||
return Ok(n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(format_ident!("Device{}", host_name))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type classification
|
|
||||||
enum FieldClass {
|
|
||||||
VecCopy(Type),
|
|
||||||
VecUploadable(Type),
|
|
||||||
Option(Type),
|
|
||||||
Arc(Type),
|
|
||||||
Plain,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn classify_type(ty: &Type) -> FieldClass {
|
|
||||||
if let Some(inner) = extract_generic_arg(ty, "Vec") {
|
|
||||||
if is_copy_primitive(&inner) {
|
|
||||||
FieldClass::VecCopy(inner)
|
|
||||||
} else {
|
|
||||||
FieldClass::VecUploadable(inner)
|
|
||||||
}
|
|
||||||
} else if let Some(inner) = extract_generic_arg(ty, "Option") {
|
|
||||||
FieldClass::Option(inner)
|
|
||||||
} else if let Some(inner) = extract_generic_arg(ty, "Arc") {
|
|
||||||
FieldClass::Arc(inner)
|
|
||||||
} else {
|
|
||||||
FieldClass::Plain
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extract_generic_arg(ty: &Type, wrapper: &str) -> Option<Type> {
|
|
||||||
if let Type::Path(type_path) = ty {
|
|
||||||
let seg = type_path.path.segments.last()?;
|
|
||||||
if seg.ident != wrapper {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
if let PathArguments::AngleBracketed(args) = &seg.arguments {
|
|
||||||
if let Some(GenericArgument::Type(inner)) = args.args.first() {
|
|
||||||
return Some(inner.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_copy_primitive(ty: &Type) -> bool {
|
|
||||||
if let Type::Path(type_path) = ty {
|
|
||||||
if let Some(seg) = type_path.path.segments.last() {
|
|
||||||
let name = seg.ident.to_string();
|
|
||||||
return matches!(
|
|
||||||
name.as_str(),
|
|
||||||
"f32"
|
|
||||||
| "f64"
|
|
||||||
| "u8"
|
|
||||||
| "u16"
|
|
||||||
| "u32"
|
|
||||||
| "u64"
|
|
||||||
| "i8"
|
|
||||||
| "i16"
|
|
||||||
| "i32"
|
|
||||||
| "i64"
|
|
||||||
| "usize"
|
|
||||||
| "isize"
|
|
||||||
| "bool"
|
|
||||||
| "Float"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,6 @@ impl<T> Ptr<T> {
|
||||||
unsafe { &*self.ptr }
|
unsafe { &*self.ptr }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get as Option - safe for optional fields
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn get<'a>(self) -> Option<&'a T> {
|
pub fn get<'a>(self) -> Option<&'a T> {
|
||||||
if self.is_null() {
|
if self.is_null() {
|
||||||
|
|
@ -141,3 +140,12 @@ impl<T> From<*const T> for Ptr<T> {
|
||||||
Self { ptr }
|
Self { ptr }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> From<Option<&T>> for Ptr<T> {
|
||||||
|
fn from(opt: Option<&T>) -> Self {
|
||||||
|
match opt {
|
||||||
|
Some(r) => Ptr::from(r),
|
||||||
|
None => Ptr::null(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
use crate::core::geometry::{
|
use crate::core::geometry::{
|
||||||
Bounds2f, Frame, Point2f, Point2i, Point3f, Vector2f, Vector2i, Vector3f, VectorLike,
|
Bounds2f, Frame, Point2f, Point2i, Point3f, Vector2f, Vector2i, Vector3f, VectorLike,
|
||||||
};
|
};
|
||||||
use crate::utils::containers::DeviceArray2D;
|
use crate::core::image::Image;
|
||||||
use crate::utils::find_interval;
|
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::rng::Rng;
|
use crate::utils::rng::Rng;
|
||||||
|
use crate::{gvec, gvec_from_slice, gvec_with_capacity, Array2D, GVec, Ptr};
|
||||||
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};
|
||||||
use num_traits::Float as NumFloat;
|
use num_traits::Float as NumFloat;
|
||||||
use num_traits::Num;
|
use num_traits::Num;
|
||||||
|
|
||||||
#[cfg(feature = "cpu_debug")]
|
#[cfg(feature = "cpu_debug")]
|
||||||
use crate::{RARE_EVENT_CONDITION_MET, RARE_EVENT_TOTAL_CALLS, check_rare};
|
use crate::{check_rare, RARE_EVENT_CONDITION_MET, RARE_EVENT_TOTAL_CALLS};
|
||||||
|
|
||||||
pub fn linear_pdf<T>(x: T, a: T, b: T) -> T
|
pub fn linear_pdf<T>(x: T, a: T, b: T) -> T
|
||||||
where
|
where
|
||||||
|
|
@ -703,132 +703,228 @@ pub struct PLSample {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DevicePiecewiseConstant1D {
|
pub struct PiecewiseConstant1D {
|
||||||
pub func: Ptr<Float>,
|
pub func: GVec<Float>,
|
||||||
pub cdf: Ptr<Float>,
|
pub cdf: GVec<Float>,
|
||||||
pub min: Float,
|
pub min: Float,
|
||||||
pub max: Float,
|
pub max: Float,
|
||||||
pub n: u32,
|
|
||||||
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 new(f: &[Float]) -> Self {
|
||||||
|
Self::new_with_bounds(f, 0.0, 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_bounds(f: &[Float], min: Float, max: Float) -> Self {
|
||||||
|
let n = f.len();
|
||||||
|
let mut cdf = gvec_with_capacity(n + 1);
|
||||||
|
cdf.push(0.0);
|
||||||
|
|
||||||
|
let delta = (max - min) / n as Float;
|
||||||
|
for i in 0..n {
|
||||||
|
cdf.push(cdf[i] + f[i] * delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
let func_integral = cdf[n];
|
||||||
|
if func_integral > 0.0 {
|
||||||
|
for c in &mut cdf {
|
||||||
|
*c /= func_integral;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
func: gvec_from_slice(f),
|
||||||
|
cdf,
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
func_integral,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 delta = (max - min) / n as Float;
|
||||||
|
let mut values = gvec_with_capacity(n);
|
||||||
|
for i in 0..n {
|
||||||
|
values.push(f(min + (i as Float + 0.5) * delta));
|
||||||
|
}
|
||||||
|
Self::new_with_bounds(&values, min, max)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn n(&self) -> usize {
|
||||||
|
self.func.len()
|
||||||
|
}
|
||||||
|
pub fn func(&self) -> &[Float] {
|
||||||
|
&self.func
|
||||||
|
}
|
||||||
|
pub fn cdf(&self) -> &[Float] {
|
||||||
|
&self.cdf
|
||||||
|
}
|
||||||
pub fn integral(&self) -> Float {
|
pub fn integral(&self) -> Float {
|
||||||
self.func_integral
|
self.func_integral
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn size(&self) -> u32 {
|
pub fn find_interval(&self, u: Float) -> usize {
|
||||||
self.n
|
let n = self.func.len();
|
||||||
}
|
let mut size = n;
|
||||||
|
let mut first = 0usize;
|
||||||
fn find_interval(&self, u: Float) -> usize {
|
|
||||||
let mut size = self.n as usize;
|
|
||||||
let mut first = 0;
|
|
||||||
|
|
||||||
while size > 0 {
|
while size > 0 {
|
||||||
let half = size >> 1;
|
let half = size >> 1;
|
||||||
let middle = first + half;
|
let middle = first + half;
|
||||||
|
if self.cdf[middle] <= u {
|
||||||
let cdf_val = unsafe { *self.cdf.add(middle) };
|
|
||||||
|
|
||||||
if cdf_val <= u {
|
|
||||||
first = middle + 1;
|
first = middle + 1;
|
||||||
size -= half + 1;
|
size -= half + 1;
|
||||||
} else {
|
} else {
|
||||||
size = half;
|
size = half;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
first.saturating_sub(1).min(n - 1)
|
||||||
(first - 1).clamp(0, self.n as usize - 1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sample(&self, u: Float) -> (Float, Float, usize) {
|
pub fn sample(&self, u: Float) -> (Float, Float, usize) {
|
||||||
// Find offset via binary search on CDF
|
|
||||||
let offset = self.find_interval(u);
|
let offset = self.find_interval(u);
|
||||||
|
let cdf_offset = self.cdf[offset];
|
||||||
let cdf_offset = unsafe { *self.cdf.add(offset) };
|
let cdf_next = self.cdf[offset + 1];
|
||||||
let cdf_next = unsafe { *self.cdf.add(offset + 1) };
|
|
||||||
|
|
||||||
let du = if cdf_next - cdf_offset > 0.0 {
|
let du = if cdf_next - cdf_offset > 0.0 {
|
||||||
(u - cdf_offset) / (cdf_next - cdf_offset)
|
(u - cdf_offset) / (cdf_next - cdf_offset)
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
};
|
};
|
||||||
|
let n = self.func.len();
|
||||||
let delta = (self.max - self.min) / self.n as Float;
|
let delta = (self.max - self.min) / n as Float;
|
||||||
let x = self.min + (offset as Float + du) * delta;
|
let x = self.min + (offset as Float + du) * delta;
|
||||||
|
|
||||||
let pdf = if self.func_integral > 0.0 {
|
let pdf = if self.func_integral > 0.0 {
|
||||||
(unsafe { *self.func.add(offset) }) / self.func_integral
|
self.func[offset] / self.func_integral
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
};
|
};
|
||||||
|
|
||||||
(x, pdf, offset)
|
(x, pdf, offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DevicePiecewiseConstant2D {
|
pub struct PiecewiseConstant2D {
|
||||||
pub conditionals: Ptr<DevicePiecewiseConstant1D>, // Array of n_v conditionals
|
pub conditionals: GVec<PiecewiseConstant1D>,
|
||||||
pub marginal: DevicePiecewiseConstant1D,
|
pub marginal: PiecewiseConstant1D,
|
||||||
pub n_u: u32,
|
pub n_u: u32,
|
||||||
pub n_v: u32,
|
pub n_v: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for DevicePiecewiseConstant2D {}
|
impl PiecewiseConstant2D {
|
||||||
unsafe impl Sync for DevicePiecewiseConstant2D {}
|
pub fn new(data: &Array2D<Float>) -> Self {
|
||||||
|
Self::new_with_bounds(data, Bounds2f::unit())
|
||||||
|
}
|
||||||
|
|
||||||
impl DevicePiecewiseConstant2D {
|
pub fn new_with_bounds(data: &Array2D<Float>, domain: Bounds2f) -> Self {
|
||||||
// pub fn resolution(&self) -> Point2i {
|
Self::from_slice(data.as_slice(), data.x_size(), data.y_size(), domain)
|
||||||
// Point2i::new(
|
}
|
||||||
// self.p_conditional_v[0u32].size() as i32,
|
|
||||||
// self.p_conditional_v[1u32].size() as i32,
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub fn integral(&self) -> f32 {
|
pub fn from_slice(data: &[Float], n_u: usize, n_v: usize, domain: Bounds2f) -> Self {
|
||||||
|
assert_eq!(data.len(), n_u * n_v);
|
||||||
|
|
||||||
|
let mut conditionals = gvec_with_capacity(n_v);
|
||||||
|
let mut marginal_func = gvec_with_capacity(n_v);
|
||||||
|
|
||||||
|
for v in 0..n_v {
|
||||||
|
let row = data[v * n_u..(v + 1) * n_u].to_vec();
|
||||||
|
let conditional =
|
||||||
|
PiecewiseConstant1D::new_with_bounds(&row, domain.p_min.x(), domain.p_max.x());
|
||||||
|
marginal_func.push(conditional.integral());
|
||||||
|
conditionals.push(conditional);
|
||||||
|
}
|
||||||
|
|
||||||
|
let marginal = PiecewiseConstant1D::new_with_bounds(
|
||||||
|
&marginal_func,
|
||||||
|
domain.p_min.y(),
|
||||||
|
domain.p_max.y(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
conditionals,
|
||||||
|
marginal,
|
||||||
|
n_u: n_u.try_into().unwrap(),
|
||||||
|
n_v: n_v.try_into().unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_image(image: &Image) -> Self {
|
||||||
|
let res = image.resolution();
|
||||||
|
let n_u = res.x() as usize;
|
||||||
|
let n_v = res.y() as usize;
|
||||||
|
|
||||||
|
let mut data = gvec_with_capacity(n_u * n_v);
|
||||||
|
for v in 0..n_v {
|
||||||
|
for u in 0..n_u {
|
||||||
|
data.push(image.get_channels_average(Point2i::new(u as i32, v as i32)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PiecewiseConstant2D::from_slice(&data, n_u, n_v, Bounds2f::unit())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn integral(&self) -> Float {
|
||||||
self.marginal.integral()
|
self.marginal.integral()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sample(&self, u: Point2f) -> (Point2f, f32, Point2i) {
|
pub fn pdf(&self, p: Point2f) -> Float {
|
||||||
let (d1, pdf1, off_y) = self.marginal.sample(u.y());
|
let u_offset = ((p.x() * self.n_u as Float) as usize).min(self.n_u as usize - 1);
|
||||||
let (d0, pdf0, off_x) = (unsafe { self.conditionals.add(off_y) }).sample(u.x());
|
let v_offset = ((p.y() * self.n_v as Float) as usize).min(self.n_v as usize - 1);
|
||||||
let pdf = pdf0 * pdf1;
|
let conditional = unsafe { &*self.conditionals.as_ptr().add(v_offset) };
|
||||||
let offset = Point2i::new(off_x as i32, off_y as i32);
|
let func_val = unsafe { *conditional.func.as_ptr().add(u_offset) };
|
||||||
(Point2f::new(d0, d1), pdf, offset)
|
func_val / self.integral()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pdf(&self, p: Point2f) -> Float {
|
pub fn sample(&self, u: Point2f) -> (Point2f, Float, Point2i) {
|
||||||
// Find which row
|
let (d1, pdf1, off_y) = self.marginal.sample(u.y());
|
||||||
// let delta_v = 1.0 / self.n_v as Float;
|
let (d0, pdf0, off_x) = self.conditionals[off_y].sample(u.x());
|
||||||
let v_offset = ((p.y() * self.n_v as Float) as usize).min(self.n_v as usize - 1);
|
let pdf = pdf0 * pdf1;
|
||||||
|
(
|
||||||
let conditional = unsafe { &*self.conditionals.add(v_offset) };
|
Point2f::new(d0, d1),
|
||||||
|
pdf,
|
||||||
// Find which column
|
Point2i::new(off_x as i32, off_y as i32),
|
||||||
// let delta_u = 1.0 / self.n_u as Float;
|
)
|
||||||
let u_offset = ((p.x() * self.n_u as Float) as usize).min(self.n_u as usize - 1);
|
|
||||||
|
|
||||||
let func_val = unsafe { *conditional.func.add(u_offset) };
|
|
||||||
|
|
||||||
func_val / self.marginal.func_integral
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DeviceSummedAreaTable {
|
pub struct SummedAreaTable {
|
||||||
pub sum: DeviceArray2D<f64>,
|
pub sum: Array2D<f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeviceSummedAreaTable {
|
impl SummedAreaTable {
|
||||||
//
|
pub fn new(values: &Array2D<Float>) -> Self {
|
||||||
|
let width = values.x_size() as i32;
|
||||||
|
let height = values.y_size() as i32;
|
||||||
|
|
||||||
|
let mut sum = Array2D::<f64>::new_dims(width, height);
|
||||||
|
sum[(0, 0)] = values[(0, 0)] as f64;
|
||||||
|
|
||||||
|
for x in 1..width {
|
||||||
|
sum[(x, 0)] = values[(x, 0)] as f64 + sum[(x - 1, 0)];
|
||||||
|
}
|
||||||
|
for y in 1..height {
|
||||||
|
sum[(0, y)] = values[(0, y)] as f64 + sum[(0, y - 1)];
|
||||||
|
}
|
||||||
|
for y in 1..height {
|
||||||
|
for x in 1..width {
|
||||||
|
sum[(x, y)] =
|
||||||
|
values[(x, y)] as f64 + sum[(x - 1, y)] + sum[(x, y - 1)] - sum[(x - 1, y - 1)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Self { sum }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn integral(&self, extent: Bounds2f) -> Float {
|
pub fn integral(&self, extent: Bounds2f) -> Float {
|
||||||
let s = self.lookup(extent.p_max.x(), extent.p_max.y())
|
let s = self.lookup(extent.p_max.x(), extent.p_max.y())
|
||||||
- self.lookup(extent.p_min.x(), extent.p_max.y())
|
- self.lookup(extent.p_min.x(), extent.p_max.y())
|
||||||
|
|
@ -874,13 +970,18 @@ impl DeviceSummedAreaTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DeviceWindowedPiecewiseConstant2D {
|
pub struct WindowedPiecewiseConstant2D {
|
||||||
pub sat: DeviceSummedAreaTable,
|
pub sat: SummedAreaTable,
|
||||||
pub func: DeviceArray2D<Float>,
|
pub func: Array2D<Float>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeviceWindowedPiecewiseConstant2D {
|
impl WindowedPiecewiseConstant2D {
|
||||||
|
pub fn new(func: Array2D<Float>) -> Self {
|
||||||
|
let sat = SummedAreaTable::new(&func);
|
||||||
|
Self { sat, func }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn sample(&self, u: Point2f, b: Bounds2f) -> Option<(Point2f, Float)> {
|
pub fn sample(&self, u: Point2f, b: Bounds2f) -> Option<(Point2f, Float)> {
|
||||||
let b_int = self.sat.integral(b);
|
let b_int = self.sat.integral(b);
|
||||||
if b_int == 0.0 {
|
if b_int == 0.0 {
|
||||||
|
|
@ -894,7 +995,8 @@ impl DeviceWindowedPiecewiseConstant2D {
|
||||||
};
|
};
|
||||||
|
|
||||||
let nx = self.func.x_size();
|
let nx = self.func.x_size();
|
||||||
let px_val = Self::sample_bisection(px, u.x(), b.p_min.x(), b.p_max.x(), nx);
|
let px_val =
|
||||||
|
Self::sample_bisection(px, u.x(), b.p_min.x(), b.p_max.x(), nx.try_into().unwrap());
|
||||||
|
|
||||||
let nx_f = nx as Float;
|
let nx_f = nx as Float;
|
||||||
|
|
||||||
|
|
@ -922,7 +1024,8 @@ impl DeviceWindowedPiecewiseConstant2D {
|
||||||
};
|
};
|
||||||
|
|
||||||
let ny = self.func.y_size();
|
let ny = self.func.y_size();
|
||||||
let py_val = Self::sample_bisection(py, u.y(), b.p_min.y(), b.p_max.y(), ny);
|
let py_val =
|
||||||
|
Self::sample_bisection(py, u.y(), b.p_min.y(), b.p_max.y(), ny.try_into().unwrap());
|
||||||
|
|
||||||
let p = Point2f::new(px_val, py_val);
|
let p = Point2f::new(px_val, py_val);
|
||||||
|
|
||||||
|
|
@ -990,81 +1093,250 @@ pub struct Bin {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AliasTable {
|
pub struct AliasTable {
|
||||||
pub bins: Ptr<Bin>,
|
pub bins: GVec<Bin>,
|
||||||
pub size: u32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for AliasTable {}
|
unsafe impl Send for AliasTable {}
|
||||||
unsafe impl Sync for AliasTable {}
|
unsafe impl Sync for AliasTable {}
|
||||||
|
|
||||||
impl AliasTable {
|
impl AliasTable {
|
||||||
#[inline(always)]
|
pub fn new(weights: &[Float]) -> Self {
|
||||||
fn bin(&self, idx: u32) -> Ptr<Bin> {
|
let n = weights.len();
|
||||||
unsafe { self.bins.add(idx as usize) }
|
if n == 0 {
|
||||||
|
return Self { bins: gvec() };
|
||||||
|
}
|
||||||
|
|
||||||
|
let sum: f64 = weights.iter().map(|&w| w as f64).sum();
|
||||||
|
assert!(sum > 0.0, "Sum of weights must be positive");
|
||||||
|
|
||||||
|
let mut bins = gvec_with_capacity(n);
|
||||||
|
for &w in weights {
|
||||||
|
bins.push(Bin {
|
||||||
|
p: (w as f64 / sum) as Float,
|
||||||
|
q: 0.0,
|
||||||
|
alias: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Outcome {
|
||||||
|
p_hat: f64,
|
||||||
|
index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut under = gvec_with_capacity(n);
|
||||||
|
let mut over = gvec_with_capacity(n);
|
||||||
|
|
||||||
|
for (i, bin) in bins.iter().enumerate() {
|
||||||
|
let p_hat = (bin.p as f64) * (n as f64);
|
||||||
|
if p_hat < 1.0 {
|
||||||
|
under.push(Outcome { p_hat, index: i });
|
||||||
|
} else {
|
||||||
|
over.push(Outcome { p_hat, index: i });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while !under.is_empty() && !over.is_empty() {
|
||||||
|
let un = under.pop().unwrap();
|
||||||
|
let ov = over.pop().unwrap();
|
||||||
|
bins[un.index].q = un.p_hat as Float;
|
||||||
|
bins[un.index].alias = ov.index as u32;
|
||||||
|
let p_excess = un.p_hat + ov.p_hat - 1.0;
|
||||||
|
if p_excess < 1.0 {
|
||||||
|
under.push(Outcome {
|
||||||
|
p_hat: p_excess,
|
||||||
|
index: ov.index,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
over.push(Outcome {
|
||||||
|
p_hat: p_excess,
|
||||||
|
index: ov.index,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(ov) = over.pop() {
|
||||||
|
bins[ov.index].q = 1.0;
|
||||||
|
bins[ov.index].alias = ov.index as u32;
|
||||||
|
}
|
||||||
|
while let Some(un) = under.pop() {
|
||||||
|
bins[un.index].q = 1.0;
|
||||||
|
bins[un.index].alias = un.index as u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
Self { bins }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn size(&self) -> u32 {
|
pub fn size(&self) -> u32 {
|
||||||
self.size
|
self.bins.len() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.bins.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pmf(&self, index: u32) -> Float {
|
pub fn pmf(&self, index: u32) -> Float {
|
||||||
if index >= self.size() {
|
if index >= self.size() {
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
self.bin(index).p
|
self.bins[index as usize].p
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sample(&self, u: Float) -> (u32, Float, Float) {
|
pub fn sample(&self, u: Float) -> (u32, Float, Float) {
|
||||||
if self.size == 0 {
|
if self.bins.is_empty() {
|
||||||
return (0, 0.0, 0.0);
|
return (0, 0.0, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let n = self.size as Float;
|
let n = self.bins.len() as Float;
|
||||||
|
|
||||||
let val = u * n;
|
let val = u * n;
|
||||||
let offset = (val.min(n - 1.0)) as u32;
|
let offset = (val.min(n - 1.0)) as u32;
|
||||||
|
let up = (val - offset as Float).min(ONE_MINUS_EPSILON);
|
||||||
|
|
||||||
let up = (val - (offset as Float)).min(ONE_MINUS_EPSILON);
|
let bin = &self.bins[offset as usize];
|
||||||
|
|
||||||
let bin = self.bin(offset);
|
|
||||||
|
|
||||||
if up < bin.q {
|
if up < bin.q {
|
||||||
debug_assert!(bin.p > 0.0);
|
|
||||||
|
|
||||||
let pmf = bin.p;
|
let pmf = bin.p;
|
||||||
|
|
||||||
let u_remapped = (up / bin.q).min(ONE_MINUS_EPSILON);
|
let u_remapped = (up / bin.q).min(ONE_MINUS_EPSILON);
|
||||||
|
|
||||||
(offset, pmf, u_remapped)
|
(offset, pmf, u_remapped)
|
||||||
} else {
|
} else {
|
||||||
let alias_idx = bin.alias;
|
let alias_idx = bin.alias;
|
||||||
|
let alias_p = self.bins[alias_idx as usize].p;
|
||||||
let alias_p = self.bin(alias_idx).p;
|
|
||||||
debug_assert!(alias_p > 0.0);
|
|
||||||
|
|
||||||
let u_remapped = ((up - bin.q) / (1.0 - bin.q)).min(ONE_MINUS_EPSILON);
|
let u_remapped = ((up - bin.q) / (1.0 - bin.q)).min(ONE_MINUS_EPSILON);
|
||||||
|
|
||||||
(alias_idx, alias_p, u_remapped)
|
(alias_idx, alias_p, u_remapped)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PiecewiseLinear2D<const N: usize> {
|
pub struct PiecewiseLinear2D<const N: usize> {
|
||||||
pub size: Vector2i,
|
pub size: Vector2i,
|
||||||
pub inv_patch_size: Vector2f,
|
pub inv_patch_size: Vector2f,
|
||||||
pub param_size: [u32; N],
|
pub param_size: [u32; N],
|
||||||
pub param_strides: [u32; N],
|
pub param_strides: [u32; N],
|
||||||
pub param_values: [Ptr<Float>; N],
|
pub param_values: [GVec<Float>; N],
|
||||||
pub data: Ptr<Float>,
|
pub data: GVec<Float>,
|
||||||
pub marginal_cdf: Ptr<Float>,
|
pub marginal_cdf: GVec<Float>,
|
||||||
pub conditional_cdf: Ptr<Float>,
|
pub conditional_cdf: GVec<Float>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const N: usize> PiecewiseLinear2D<N> {
|
impl<const N: usize> PiecewiseLinear2D<N> {
|
||||||
|
pub fn new(
|
||||||
|
data: &[Float],
|
||||||
|
x_size: i32,
|
||||||
|
y_size: i32,
|
||||||
|
param_res: [usize; N],
|
||||||
|
param_values: [&[Float]; N],
|
||||||
|
normalize: bool,
|
||||||
|
build_cdf: bool,
|
||||||
|
) -> Self {
|
||||||
|
if build_cdf && !normalize {
|
||||||
|
panic!("PiecewiseLinear2D: build_cdf implies normalize=true");
|
||||||
|
}
|
||||||
|
|
||||||
|
let size = Vector2i::new(x_size, y_size);
|
||||||
|
let inv_patch_size =
|
||||||
|
Vector2f::new(1.0 / (x_size - 1) as Float, 1.0 / (y_size - 1) as Float);
|
||||||
|
|
||||||
|
let mut param_size = [0u32; N];
|
||||||
|
let mut param_strides = [0u32; N];
|
||||||
|
let owned_param_values: [GVec<Float>; N] =
|
||||||
|
core::array::from_fn(|i| gvec_from_slice(param_values[i]));
|
||||||
|
|
||||||
|
let mut slices: u32 = 1;
|
||||||
|
for i in (0..N).rev() {
|
||||||
|
assert!(param_res[i] >= 1, "Parameter resolution must be >= 1");
|
||||||
|
param_size[i] = param_res[i] as u32;
|
||||||
|
param_strides[i] = if param_res[i] > 1 { slices } else { 0 };
|
||||||
|
slices *= param_size[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
let n_values = (x_size * y_size) as usize;
|
||||||
|
let mut new_data = gvec_with_capacity(slices as usize * n_values);
|
||||||
|
new_data.resize(slices as usize * n_values, 0.0);
|
||||||
|
|
||||||
|
let mut marginal_cdf = if build_cdf {
|
||||||
|
let mut v = gvec_with_capacity(slices as usize * y_size as usize);
|
||||||
|
v.resize(slices as usize * y_size as usize, 0.0);
|
||||||
|
v
|
||||||
|
} else {
|
||||||
|
gvec()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut conditional_cdf = if build_cdf {
|
||||||
|
let mut v = gvec_with_capacity(slices as usize * n_values);
|
||||||
|
v.resize(slices as usize * n_values, 0.0);
|
||||||
|
v
|
||||||
|
} else {
|
||||||
|
gvec()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut data_offset = 0;
|
||||||
|
for slice in 0..slices as usize {
|
||||||
|
let slice_offset = slice * n_values;
|
||||||
|
let current_data = &data[data_offset..data_offset + n_values];
|
||||||
|
let mut sum = 0.0_f64;
|
||||||
|
|
||||||
|
if normalize {
|
||||||
|
for y in 0..(y_size - 1) {
|
||||||
|
for x in 0..(x_size - 1) {
|
||||||
|
let i = (y * x_size + x) as usize;
|
||||||
|
let v00 = current_data[i] as f64;
|
||||||
|
let v10 = current_data[i + 1] as f64;
|
||||||
|
let v01 = current_data[i + x_size as usize] as f64;
|
||||||
|
let v11 = current_data[i + 1 + x_size as usize] as f64;
|
||||||
|
sum += 0.25 * (v00 + v10 + v01 + v11);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let normalization = if normalize && sum > 0.0 {
|
||||||
|
1.0 / sum as Float
|
||||||
|
} else {
|
||||||
|
1.0
|
||||||
|
};
|
||||||
|
for k in 0..n_values {
|
||||||
|
new_data[slice_offset + k] = current_data[k] * normalization;
|
||||||
|
}
|
||||||
|
|
||||||
|
if build_cdf {
|
||||||
|
let marginal_slice_offset = slice * y_size as usize;
|
||||||
|
for y in 0..y_size as usize {
|
||||||
|
let mut cdf_sum = 0.0;
|
||||||
|
let i_base = y * x_size as usize;
|
||||||
|
conditional_cdf[slice_offset + i_base] = 0.0;
|
||||||
|
for x in 0..(x_size - 1) as usize {
|
||||||
|
let i = i_base + x;
|
||||||
|
cdf_sum +=
|
||||||
|
0.5 * (new_data[slice_offset + i] + new_data[slice_offset + i + 1]);
|
||||||
|
conditional_cdf[slice_offset + i + 1] = cdf_sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
marginal_cdf[marginal_slice_offset] = 0.0;
|
||||||
|
let mut marginal_sum = 0.0;
|
||||||
|
for y in 0..(y_size - 1) as usize {
|
||||||
|
let cdf1 = conditional_cdf[slice_offset + (y + 1) * x_size as usize - 1];
|
||||||
|
let cdf2 = conditional_cdf[slice_offset + (y + 2) * x_size as usize - 1];
|
||||||
|
marginal_sum += 0.5 * (cdf1 + cdf2);
|
||||||
|
marginal_cdf[marginal_slice_offset + y + 1] = marginal_sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data_offset += n_values;
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
size,
|
||||||
|
inv_patch_size,
|
||||||
|
param_size,
|
||||||
|
param_strides,
|
||||||
|
data: new_data,
|
||||||
|
marginal_cdf,
|
||||||
|
conditional_cdf,
|
||||||
|
param_values: owned_param_values,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn sample(&self, mut sample: Point2f, params: [Float; N]) -> PLSample {
|
pub fn sample(&self, mut sample: Point2f, params: [Float; N]) -> PLSample {
|
||||||
sample = Point2f::new(
|
sample = Point2f::new(
|
||||||
sample.x().clamp(0.0, ONE_MINUS_EPSILON),
|
sample.x().clamp(0.0, ONE_MINUS_EPSILON),
|
||||||
|
|
@ -1079,7 +1351,7 @@ impl<const N: usize> PiecewiseLinear2D<N> {
|
||||||
let conditional_offset = slice_offset * conditional_size;
|
let conditional_offset = slice_offset * conditional_size;
|
||||||
let fetch_marginal = |idx: u32| {
|
let fetch_marginal = |idx: u32| {
|
||||||
self.lookup(
|
self.lookup(
|
||||||
self.marginal_cdf,
|
&self.marginal_cdf,
|
||||||
marginal_offset + idx,
|
marginal_offset + idx,
|
||||||
marginal_size,
|
marginal_size,
|
||||||
¶m_weights,
|
¶m_weights,
|
||||||
|
|
@ -1089,13 +1361,13 @@ impl<const N: usize> PiecewiseLinear2D<N> {
|
||||||
let marginal_cdf_row = fetch_marginal(row);
|
let marginal_cdf_row = fetch_marginal(row);
|
||||||
sample[1] -= marginal_cdf_row;
|
sample[1] -= marginal_cdf_row;
|
||||||
let r0 = self.lookup(
|
let r0 = self.lookup(
|
||||||
self.conditional_cdf,
|
&self.conditional_cdf,
|
||||||
conditional_offset + (row + 1) * self.size.x() as u32 - 1,
|
conditional_offset + (row + 1) * self.size.x() as u32 - 1,
|
||||||
conditional_size,
|
conditional_size,
|
||||||
¶m_weights,
|
¶m_weights,
|
||||||
);
|
);
|
||||||
let r1 = self.lookup(
|
let r1 = self.lookup(
|
||||||
self.conditional_cdf,
|
&self.conditional_cdf,
|
||||||
conditional_offset + (row + 2) * self.size.x() as u32 - 1,
|
conditional_offset + (row + 2) * self.size.x() as u32 - 1,
|
||||||
conditional_size,
|
conditional_size,
|
||||||
¶m_weights,
|
¶m_weights,
|
||||||
|
|
@ -1112,13 +1384,13 @@ impl<const N: usize> PiecewiseLinear2D<N> {
|
||||||
let conditional_row_offset = conditional_offset + row * self.size.x() as u32;
|
let conditional_row_offset = conditional_offset + row * self.size.x() as u32;
|
||||||
let fetch_conditional = |idx: u32| {
|
let fetch_conditional = |idx: u32| {
|
||||||
let v0 = self.lookup(
|
let v0 = self.lookup(
|
||||||
self.conditional_cdf,
|
&self.conditional_cdf,
|
||||||
conditional_row_offset + idx,
|
conditional_row_offset + idx,
|
||||||
conditional_size,
|
conditional_size,
|
||||||
¶m_weights,
|
¶m_weights,
|
||||||
);
|
);
|
||||||
let v1 = self.lookup(
|
let v1 = self.lookup(
|
||||||
self.conditional_cdf,
|
&self.conditional_cdf,
|
||||||
conditional_row_offset + idx + self.size.x() as u32,
|
conditional_row_offset + idx + self.size.x() as u32,
|
||||||
conditional_size,
|
conditional_size,
|
||||||
¶m_weights,
|
¶m_weights,
|
||||||
|
|
@ -1130,16 +1402,16 @@ impl<const N: usize> PiecewiseLinear2D<N> {
|
||||||
});
|
});
|
||||||
sample[0] -= fetch_conditional(col);
|
sample[0] -= fetch_conditional(col);
|
||||||
let offset = conditional_row_offset + col;
|
let offset = conditional_row_offset + col;
|
||||||
let v00 = self.lookup(self.data, offset, slice_size, ¶m_weights);
|
let v00 = self.lookup(&self.data, offset, slice_size, ¶m_weights);
|
||||||
let v10 = self.lookup(self.data, offset + 1, slice_size, ¶m_weights);
|
let v10 = self.lookup(&self.data, offset + 1, slice_size, ¶m_weights);
|
||||||
let v01 = self.lookup(
|
let v01 = self.lookup(
|
||||||
self.data,
|
&self.data,
|
||||||
offset + self.size.x() as u32,
|
offset + self.size.x() as u32,
|
||||||
slice_size,
|
slice_size,
|
||||||
¶m_weights,
|
¶m_weights,
|
||||||
);
|
);
|
||||||
let v11 = self.lookup(
|
let v11 = self.lookup(
|
||||||
self.data,
|
&self.data,
|
||||||
offset + self.size.x() as u32 + 1,
|
offset + self.size.x() as u32 + 1,
|
||||||
slice_size,
|
slice_size,
|
||||||
¶m_weights,
|
¶m_weights,
|
||||||
|
|
@ -1176,16 +1448,16 @@ impl<const N: usize> PiecewiseLinear2D<N> {
|
||||||
let frac = Point2f::new(p.x() - col as Float, p.y() - row as Float);
|
let frac = Point2f::new(p.x() - col as Float, p.y() - row as Float);
|
||||||
let slice_size = (self.size.x() * self.size.y()) as u32;
|
let slice_size = (self.size.x() * self.size.y()) as u32;
|
||||||
let offset = slice_offset * slice_size + (row * self.size.x() + col) as u32;
|
let offset = slice_offset * slice_size + (row * self.size.x() + col) as u32;
|
||||||
let v00 = self.lookup(self.data, offset, slice_size, ¶m_weights);
|
let v00 = self.lookup(&self.data, offset, slice_size, ¶m_weights);
|
||||||
let v10 = self.lookup(self.data, offset + 1, slice_size, ¶m_weights);
|
let v10 = self.lookup(&self.data, offset + 1, slice_size, ¶m_weights);
|
||||||
let v01 = self.lookup(
|
let v01 = self.lookup(
|
||||||
self.data,
|
&self.data,
|
||||||
offset + self.size.x() as u32,
|
offset + self.size.x() as u32,
|
||||||
slice_size,
|
slice_size,
|
||||||
¶m_weights,
|
¶m_weights,
|
||||||
);
|
);
|
||||||
let v11 = self.lookup(
|
let v11 = self.lookup(
|
||||||
self.data,
|
&self.data,
|
||||||
offset + self.size.x() as u32 + 1,
|
offset + self.size.x() as u32 + 1,
|
||||||
slice_size,
|
slice_size,
|
||||||
¶m_weights,
|
¶m_weights,
|
||||||
|
|
@ -1199,26 +1471,26 @@ impl<const N: usize> PiecewiseLinear2D<N> {
|
||||||
u[0] = w1.x() * (c0 + 0.5 * w1.x() * (c1 - c0));
|
u[0] = w1.x() * (c0 + 0.5 * w1.x() * (c1 - c0));
|
||||||
let conditional_row_offset = slice_offset * slice_size + (row * self.size.x()) as u32;
|
let conditional_row_offset = slice_offset * slice_size + (row * self.size.x()) as u32;
|
||||||
let v0 = self.lookup(
|
let v0 = self.lookup(
|
||||||
self.conditional_cdf,
|
&self.conditional_cdf,
|
||||||
conditional_row_offset + col as u32,
|
conditional_row_offset + col as u32,
|
||||||
slice_size,
|
slice_size,
|
||||||
¶m_weights,
|
¶m_weights,
|
||||||
);
|
);
|
||||||
let v1 = self.lookup(
|
let v1 = self.lookup(
|
||||||
self.conditional_cdf,
|
&self.conditional_cdf,
|
||||||
conditional_row_offset + col as u32 + self.size.x() as u32,
|
conditional_row_offset + col as u32 + self.size.x() as u32,
|
||||||
slice_size,
|
slice_size,
|
||||||
¶m_weights,
|
¶m_weights,
|
||||||
);
|
);
|
||||||
u[0] += (1.0 - u.y()) * v0 + u.y() * v1;
|
u[0] += (1.0 - u.y()) * v0 + u.y() * v1;
|
||||||
let r0 = self.lookup(
|
let r0 = self.lookup(
|
||||||
self.conditional_cdf,
|
&self.conditional_cdf,
|
||||||
conditional_row_offset + self.size.x() as u32 - 1,
|
conditional_row_offset + self.size.x() as u32 - 1,
|
||||||
slice_size,
|
slice_size,
|
||||||
¶m_weights,
|
¶m_weights,
|
||||||
);
|
);
|
||||||
let r1 = self.lookup(
|
let r1 = self.lookup(
|
||||||
self.conditional_cdf,
|
&self.conditional_cdf,
|
||||||
conditional_row_offset + self.size.x() as u32 * 2 - 1,
|
conditional_row_offset + self.size.x() as u32 * 2 - 1,
|
||||||
slice_size,
|
slice_size,
|
||||||
¶m_weights,
|
¶m_weights,
|
||||||
|
|
@ -1227,7 +1499,7 @@ impl<const N: usize> PiecewiseLinear2D<N> {
|
||||||
u[1] = w1.y() * (r0 + 0.5 * w1.y() * (r1 - r0));
|
u[1] = w1.y() * (r0 + 0.5 * w1.y() * (r1 - r0));
|
||||||
let marginal_offset = slice_offset * self.size.y() as u32 + row as u32;
|
let marginal_offset = slice_offset * self.size.y() as u32 + row as u32;
|
||||||
u[1] += self.lookup(
|
u[1] += self.lookup(
|
||||||
self.marginal_cdf,
|
&self.marginal_cdf,
|
||||||
marginal_offset,
|
marginal_offset,
|
||||||
self.size.y() as u32,
|
self.size.y() as u32,
|
||||||
¶m_weights,
|
¶m_weights,
|
||||||
|
|
@ -1251,16 +1523,16 @@ impl<const N: usize> PiecewiseLinear2D<N> {
|
||||||
let w0 = Point2f::new(1.0 - w1.x(), 1.0 - w1.y());
|
let w0 = Point2f::new(1.0 - w1.x(), 1.0 - w1.y());
|
||||||
let slice_size = (self.size.x() * self.size.y()) as u32;
|
let slice_size = (self.size.x() * self.size.y()) as u32;
|
||||||
let offset = slice_offset * slice_size + (row * self.size.x() + col) as u32;
|
let offset = slice_offset * slice_size + (row * self.size.x() + col) as u32;
|
||||||
let v00 = self.lookup(self.data, offset, slice_size, ¶m_weights);
|
let v00 = self.lookup(&self.data, offset, slice_size, ¶m_weights);
|
||||||
let v10 = self.lookup(self.data, offset + 1, slice_size, ¶m_weights);
|
let v10 = self.lookup(&self.data, offset + 1, slice_size, ¶m_weights);
|
||||||
let v01 = self.lookup(
|
let v01 = self.lookup(
|
||||||
self.data,
|
&self.data,
|
||||||
offset + self.size.x() as u32,
|
offset + self.size.x() as u32,
|
||||||
slice_size,
|
slice_size,
|
||||||
¶m_weights,
|
¶m_weights,
|
||||||
);
|
);
|
||||||
let v11 = self.lookup(
|
let v11 = self.lookup(
|
||||||
self.data,
|
&self.data,
|
||||||
offset + self.size.x() as u32 + 1,
|
offset + self.size.x() as u32 + 1,
|
||||||
slice_size,
|
slice_size,
|
||||||
¶m_weights,
|
¶m_weights,
|
||||||
|
|
@ -1272,8 +1544,7 @@ impl<const N: usize> PiecewiseLinear2D<N> {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn get_param_value(&self, dim: usize, idx: usize) -> Float {
|
fn get_param_value(&self, dim: usize, idx: usize) -> Float {
|
||||||
// Safety: Bounds checking against param_size ensures this is valid
|
unsafe { *self.param_values[dim].as_ptr().add(idx) }
|
||||||
unsafe { *self.param_values[dim].add(idx) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_slice_info(&self, params: [Float; N]) -> (u32, [(Float, Float); N]) {
|
fn get_slice_info(&self, params: [Float; N]) -> (u32, [(Float, Float); N]) {
|
||||||
|
|
@ -1313,7 +1584,7 @@ impl<const N: usize> PiecewiseLinear2D<N> {
|
||||||
|
|
||||||
fn lookup(
|
fn lookup(
|
||||||
&self,
|
&self,
|
||||||
data: Ptr<Float>,
|
data: &GVec<Float>,
|
||||||
i0: u32,
|
i0: u32,
|
||||||
size: u32,
|
size: u32,
|
||||||
param_weight: &[(Float, Float); N],
|
param_weight: &[(Float, Float); N],
|
||||||
|
|
@ -1335,7 +1606,7 @@ impl<const N: usize> PiecewiseLinear2D<N> {
|
||||||
current_mask >>= 1;
|
current_mask >>= 1;
|
||||||
}
|
}
|
||||||
let idx = (i0 + offset) as usize;
|
let idx = (i0 + offset) as usize;
|
||||||
let val = unsafe { *data.add(idx) };
|
let val = unsafe { *data.as_ptr().add(idx) };
|
||||||
result += weight * val;
|
result += weight * val;
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
pub mod perspective;
|
|
||||||
pub mod realistic;
|
|
||||||
pub mod spherical;
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
use crate::core::image::Image;
|
|
||||||
use shared::cameras::{EXIT_PUPIL_SAMPLES, LensElementInterface, RealisticCamera};
|
|
||||||
use shared::core::camera::CameraBase;
|
|
||||||
use shared::core::geometry::{Bounds2f, Point2f};
|
|
||||||
use shared::utils::math::square;
|
|
||||||
use shared::{Float, Ptr};
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct RealisticCameraData {
|
|
||||||
aperture_image: Arc<Image>,
|
|
||||||
element_interfaces: Vec<LensElementInterface>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct RealisticCameraHost {
|
|
||||||
device: RealisticCamera,
|
|
||||||
data: RealisticCameraData,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RealisticCameraHost {
|
|
||||||
pub fn device(&self) -> RealisticCamera {
|
|
||||||
self.device
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(
|
|
||||||
base: CameraBase,
|
|
||||||
lens_params: &[Float],
|
|
||||||
focus_distance: Float,
|
|
||||||
set_aperture_diameter: Float,
|
|
||||||
aperture_image: Arc<Image>,
|
|
||||||
) -> Self {
|
|
||||||
let film_ptr = base.film;
|
|
||||||
if film_ptr.is_null() {
|
|
||||||
panic!("Camera must have a film");
|
|
||||||
}
|
|
||||||
let film = &*film_ptr;
|
|
||||||
|
|
||||||
let aspect = film.full_resolution().x() as Float / film.full_resolution().y() as Float;
|
|
||||||
let diagonal = film.diagonal();
|
|
||||||
let x = (square(diagonal) / (1.0 + square(diagonal))).sqrt();
|
|
||||||
let y = x * aspect;
|
|
||||||
let physical_extent =
|
|
||||||
Bounds2f::from_points(Point2f::new(-x / 2., -y / 2.), Point2f::new(x / 2., y / 2.));
|
|
||||||
let mut element_interfaces: Vec<LensElementInterface> = Vec::new();
|
|
||||||
|
|
||||||
for i in (0..lens_params.len()).step_by(4) {
|
|
||||||
let curvature_radius = lens_params[i] / 1000.0;
|
|
||||||
let thickness = lens_params[i + 1] / 1000.0;
|
|
||||||
let eta = lens_params[i + 2];
|
|
||||||
let mut aperture_diameter = lens_params[i + 3] / 1000.0;
|
|
||||||
|
|
||||||
if curvature_radius == 0.0 {
|
|
||||||
aperture_diameter /= 1000.0;
|
|
||||||
if set_aperture_diameter > aperture_diameter {
|
|
||||||
println!("Aperture is larger than possible")
|
|
||||||
} else {
|
|
||||||
aperture_diameter = set_aperture_diameter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let el_int = LensElementInterface {
|
|
||||||
curvature_radius,
|
|
||||||
thickness,
|
|
||||||
eta,
|
|
||||||
aperture_radius: aperture_diameter / 2.0,
|
|
||||||
};
|
|
||||||
element_interfaces.push(el_int);
|
|
||||||
}
|
|
||||||
|
|
||||||
let half_diag = film.diagonal() / 2.0;
|
|
||||||
let mut exit_pupil_bounds = [Bounds2f::default(); EXIT_PUPIL_SAMPLES];
|
|
||||||
|
|
||||||
for i in 0..EXIT_PUPIL_SAMPLES {
|
|
||||||
let r0 = (i as Float / EXIT_PUPIL_SAMPLES as Float) * half_diag;
|
|
||||||
let r1 = ((i + 1) as Float / EXIT_PUPIL_SAMPLES as Float) * half_diag;
|
|
||||||
exit_pupil_bounds[i] =
|
|
||||||
RealisticCamera::compute_exit_pupil_bounds(&element_interfaces, r0, r1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let n_elements = element_interfaces.len();
|
|
||||||
|
|
||||||
let data = RealisticCameraData {
|
|
||||||
element_interfaces: element_interfaces.clone(),
|
|
||||||
aperture_image: aperture_image.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let device = RealisticCamera {
|
|
||||||
base,
|
|
||||||
focus_distance,
|
|
||||||
element_interfaces: Ptr::from(element_interfaces.as_ptr()),
|
|
||||||
n_elements,
|
|
||||||
physical_extent,
|
|
||||||
set_aperture_diameter,
|
|
||||||
aperture_image: Ptr::from(aperture_image.device()),
|
|
||||||
exit_pupil_bounds,
|
|
||||||
};
|
|
||||||
|
|
||||||
Self { device, data }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use shared::core::aggregates::{{DeviceBVHAggregate, LinearBVHNode};
|
use shared::core::aggregates::{BVHAggregate, LinearBVHNode};
|
||||||
use shared::core::geometry::{Bounds3f, Point3f, Ray, Vector3f};
|
use shared::core::geometry::{Bounds3f, Point3f, Ray, Vector3f};
|
||||||
use shared::core::primitive::{Primitive, PrimitiveTrait};
|
use shared::core::primitive::{Primitive, PrimitiveTrait};
|
||||||
use shared::core::shape::ShapeIntersection;
|
use shared::core::shape::ShapeIntersection;
|
||||||
|
|
@ -307,7 +307,7 @@ impl<P: PrimitiveTrait + Clone + Send + Sync> BVHAggregate<P> {
|
||||||
pub fn build_hlbvh(
|
pub fn build_hlbvh(
|
||||||
bvh_primitives: &[BVHPrimitiveInfo],
|
bvh_primitives: &[BVHPrimitiveInfo],
|
||||||
total_nodes: &AtomicUsize,
|
total_nodes: &AtomicUsize,
|
||||||
original_primitives: &[P],
|
_original_primitives: &[P],
|
||||||
max_prims_in_node: usize,
|
max_prims_in_node: usize,
|
||||||
) -> Box<BVHBuildNode> {
|
) -> Box<BVHBuildNode> {
|
||||||
let bounds = bvh_primitives
|
let bounds = bvh_primitives
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
use crate::cameras::realistic::RealisticCameraHost;
|
|
||||||
use crate::core::image::ImageMetadata;
|
use crate::core::image::ImageMetadata;
|
||||||
use crate::core::image::{Image, ImageIO};
|
use crate::core::image::{Image, ImageIO};
|
||||||
use crate::globals::get_options;
|
use crate::globals::get_options;
|
||||||
use crate::utils::read_float_file;
|
use crate::utils::read_float_file;
|
||||||
use crate::utils::{Arena, FileLoc, ParameterDictionary};
|
use crate::{Arena, FileLoc, ParameterDictionary};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use shared::cameras::*;
|
use shared::cameras::*;
|
||||||
use shared::core::camera::{Camera, CameraBase, CameraTrait, CameraTransform};
|
use shared::core::camera::{Camera, CameraBase, CameraTrait, CameraTransform};
|
||||||
|
|
@ -13,8 +12,7 @@ use shared::core::geometry::{Bounds2f, Point2f, Point2i, Vector2f, Vector3f};
|
||||||
use shared::core::image::PixelFormat;
|
use shared::core::image::PixelFormat;
|
||||||
use shared::core::medium::Medium;
|
use shared::core::medium::Medium;
|
||||||
use shared::utils::math::square;
|
use shared::utils::math::square;
|
||||||
use shared::Ptr;
|
use shared::{Float, Ptr, PI};
|
||||||
use shared::{Float, PI};
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|
@ -380,19 +378,19 @@ impl CameraFactory for Camera {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let camera = RealisticCameraHost::new(
|
let camera = RealisticCamera::new(
|
||||||
base,
|
base,
|
||||||
&lens_params,
|
&lens_params,
|
||||||
focal_distance,
|
focal_distance,
|
||||||
aperture_diameter,
|
aperture_diameter,
|
||||||
Arc::from(aperture_image.unwrap()),
|
Ptr::from(&*aperture_image.unwrap()),
|
||||||
);
|
);
|
||||||
|
|
||||||
// arena.alloc(camera);
|
arena.alloc(camera);
|
||||||
Ok(Camera::Realistic(camera.device()))
|
Ok(Camera::Realistic(camera))
|
||||||
}
|
}
|
||||||
"spherical" => {
|
"spherical" => {
|
||||||
let full_res = film.full_resolution();
|
let _full_res = film.full_resolution();
|
||||||
let camera_params =
|
let camera_params =
|
||||||
CameraBaseParameters::new(camera_transform, film, medium, params, loc)?;
|
CameraBaseParameters::new(camera_transform, film, medium, params, loc)?;
|
||||||
let base = CameraBase::create(camera_params);
|
let base = CameraBase::create(camera_params);
|
||||||
|
|
|
||||||
|
|
@ -1,50 +1,33 @@
|
||||||
use crate::utils::read_float_file;
|
use crate::utils::read_float_file;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use shared::core::color::{Coeffs, RES, RGBToSpectrumTable};
|
use shared::core::color::{Coeffs, RES, RGBToSpectrumTable};
|
||||||
use shared::{Float, Ptr};
|
use shared::{Float, Ptr, gvec_from_slice};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
pub struct RGBToSpectrumTableData {
|
pub trait CreateRGBToSpectrumTable {
|
||||||
_z_nodes: Vec<Float>,
|
fn from_data(z_nodes: &[Float], coeffs: &[Float]) -> Self;
|
||||||
_coeffs: Vec<Float>,
|
fn load(base_dir: &Path, name: &str) -> Result<Self> where Self: Sized;
|
||||||
|
|
||||||
pub view: RGBToSpectrumTable,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for RGBToSpectrumTableData {
|
impl CreateRGBToSpectrumTable for RGBToSpectrumTable {
|
||||||
type Target = RGBToSpectrumTable;
|
fn new(z_nodes: &[Float], coeffs: &[Float]) -> Self {
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.view
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RGBToSpectrumTableData {
|
|
||||||
pub fn new(z_nodes: Vec<Float>, coeffs: Vec<Float>) -> Self {
|
|
||||||
eprintln!("z_nodes.len() = {}, coeffs.len() = {}", z_nodes.len(), coeffs.len());
|
|
||||||
assert_eq!(z_nodes.len(), RES as usize);
|
assert_eq!(z_nodes.len(), RES as usize);
|
||||||
assert_eq!(coeffs.len(), (RES * RES * RES) as usize * 3 * 3);
|
assert_eq!(coeffs.len(), (RES * RES * RES) as usize * 3 * 3);
|
||||||
|
|
||||||
let view = RGBToSpectrumTable {
|
|
||||||
z_nodes: Ptr::from(z_nodes.as_ptr()),
|
|
||||||
coeffs: Ptr::from(coeffs.as_ptr() as *const Coeffs),
|
|
||||||
n_nodes: z_nodes.len() as u32,
|
|
||||||
};
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
_z_nodes: z_nodes,
|
z_nodes: gvec_from_slice(z_nodes),
|
||||||
_coeffs: coeffs,
|
coeffs: gvec_from_slice(unsafe {
|
||||||
view,
|
core::slice::from_raw_parts(
|
||||||
|
coeffs.as_ptr() as *const Coeffs,
|
||||||
|
coeffs.len() / 3,
|
||||||
|
)
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load(base_dir: &Path, name: &str) -> Result<Self> {
|
fn load(base_dir: &Path, name: &str) -> Result<Self> {
|
||||||
let z_path = base_dir.join(format!("{}_znodes.dat", name));
|
let scale = read_float_file(base_dir.join(format!("{}_scale.dat", name)).to_str().unwrap())?;
|
||||||
let c_path = base_dir.join(format!("{}_coeffs.dat", name));
|
let coeffs = read_float_file(base_dir.join(format!("{}_coeffs.dat", name)).to_str().unwrap())?;
|
||||||
|
Ok(Self::new(&scale, &coeffs))
|
||||||
let z_nodes = read_float_file(&z_path.to_str().unwrap())?;
|
|
||||||
let coeffs = read_float_file(&c_path.to_str().unwrap())?;
|
|
||||||
|
|
||||||
Ok(Self::new(z_nodes, coeffs))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
130
src/core/film.rs
130
src/core/film.rs
|
|
@ -1,53 +1,62 @@
|
||||||
use crate::Arena;
|
use crate::core::image::{HostImage, ImageChannelDesc, ImageChannelValues, ImageIO, ImageMetadata};
|
||||||
use crate::core::image::{Image, ImageChannelDesc, ImageChannelValues, ImageIO, ImageMetadata};
|
|
||||||
use crate::films::*;
|
use crate::films::*;
|
||||||
use crate::spectra::data::get_named_spectrum;
|
use crate::spectra::data::get_named_spectrum;
|
||||||
use crate::spectra::piecewise::PiecewiseLinearSpectrumBuffer;
|
use anyhow::{anyhow, Result};
|
||||||
use anyhow::{Result, anyhow};
|
|
||||||
use rayon::iter::ParallelIterator;
|
use rayon::iter::ParallelIterator;
|
||||||
use rayon::prelude::IntoParallelIterator;
|
use rayon::prelude::IntoParallelIterator;
|
||||||
use shared::core::camera::CameraTransform;
|
use shared::core::camera::CameraTransform;
|
||||||
use shared::core::color::{RGB, SRGB, XYZ, white_balance};
|
use shared::core::color::{white_balance, RGB, SRGB, XYZ};
|
||||||
use shared::core::film::{DevicePixelSensor, Film, FilmBase, GBufferFilm, RGBFilm, SpectralFilm};
|
use shared::core::film::{Film, FilmBase, GBufferFilm, PixelSensor, RGBFilm, SpectralFilm};
|
||||||
use shared::core::filter::{Filter, FilterTrait};
|
use shared::core::filter::{Filter, FilterTrait};
|
||||||
use shared::core::geometry::{Bounds2f, Bounds2i, Point2f, Point2i};
|
use shared::core::geometry::{Bounds2f, Bounds2i, Point2f, Point2i};
|
||||||
use shared::core::image::PixelFormat;
|
use shared::core::image::PixelFormat;
|
||||||
use shared::core::spectrum::Spectrum;
|
use shared::core::spectrum::Spectrum;
|
||||||
use shared::spectra::{RGBColorSpace, cie::SWATCHES_RAW};
|
use shared::spectra::{
|
||||||
use shared::utils::math::{SquareMatrix, linear_least_squares};
|
cie::SWATCHES_RAW, DenselySampledSpectrum, PiecewiseLinearSpectrum, RGBColorSpace,
|
||||||
|
};
|
||||||
|
use shared::utils::math::{linear_least_squares, SquareMatrix};
|
||||||
use shared::{Float, Ptr};
|
use shared::{Float, Ptr};
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::sync::{Arc, LazyLock};
|
use std::sync::{Arc, LazyLock};
|
||||||
|
|
||||||
use crate::spectra::{
|
use crate::spectra::{get_spectra_context, CIE_X_DATA, CIE_Y_DATA, CIE_Z_DATA};
|
||||||
CIE_X_DATA, CIE_Y_DATA, CIE_Z_DATA, DenselySampledSpectrumBuffer, get_spectra_context,
|
use crate::{Arena, FileLoc, ParameterDictionary};
|
||||||
};
|
|
||||||
use crate::utils::{FileLoc, ParameterDictionary};
|
|
||||||
|
|
||||||
const N_SWATCH_REFLECTANCES: usize = 24;
|
const N_SWATCH_REFLECTANCES: usize = 24;
|
||||||
const SWATCH_REFLECTANCES: LazyLock<[Spectrum; N_SWATCH_REFLECTANCES]> = LazyLock::new(|| {
|
const SWATCH_REFLECTANCES: LazyLock<[Spectrum; N_SWATCH_REFLECTANCES]> = LazyLock::new(|| {
|
||||||
std::array::from_fn(|i| {
|
std::array::from_fn(|i| {
|
||||||
let raw_data = SWATCHES_RAW[i];
|
let raw_data = SWATCHES_RAW[i];
|
||||||
let pls = PiecewiseLinearSpectrumBuffer::from_interleaved(raw_data, false);
|
let pls = PiecewiseLinearSpectrum::from_interleaved(raw_data, false);
|
||||||
Spectrum::Piecewise(pls.device)
|
Spectrum::Piecewise(pls)
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
pub trait CreatePixelSensor: Sized {
|
||||||
struct SensorStorage {
|
fn create(
|
||||||
r_bar: DenselySampledSpectrumBuffer,
|
params: &ParameterDictionary,
|
||||||
g_bar: DenselySampledSpectrumBuffer,
|
output_colorspace: Arc<RGBColorSpace>,
|
||||||
b_bar: DenselySampledSpectrumBuffer,
|
exposure_time: Float,
|
||||||
|
loc: &FileLoc,
|
||||||
|
) -> Result<Self>;
|
||||||
|
|
||||||
|
fn new(
|
||||||
|
r: &Spectrum,
|
||||||
|
g: &Spectrum,
|
||||||
|
b: &Spectrum,
|
||||||
|
output_colorspace: Arc<RGBColorSpace>,
|
||||||
|
sensor_illum: Option<&Spectrum>,
|
||||||
|
imaging_ratio: Float,
|
||||||
|
) -> Self;
|
||||||
|
|
||||||
|
fn new_with_white_balance(
|
||||||
|
output_colorspace: &RGBColorSpace,
|
||||||
|
sensor_illum: Option<&Spectrum>,
|
||||||
|
imaging_ratio: Float,
|
||||||
|
) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
impl CreatePixelSensor for PixelSensor {
|
||||||
pub struct PixelSensor {
|
fn create(
|
||||||
device: DevicePixelSensor,
|
|
||||||
data: SensorStorage,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PixelSensor {
|
|
||||||
pub fn create(
|
|
||||||
params: &ParameterDictionary,
|
params: &ParameterDictionary,
|
||||||
output_colorspace: Arc<RGBColorSpace>,
|
output_colorspace: Arc<RGBColorSpace>,
|
||||||
exposure_time: Float,
|
exposure_time: Float,
|
||||||
|
|
@ -65,9 +74,9 @@ impl PixelSensor {
|
||||||
let imaging_ratio = exposure_time * iso / 100.;
|
let imaging_ratio = exposure_time * iso / 100.;
|
||||||
|
|
||||||
let d_illum = if white_balance_temp == 0. {
|
let d_illum = if white_balance_temp == 0. {
|
||||||
DenselySampledSpectrumBuffer::generate_cie_d(6500.)
|
DenselySampledSpectrum::generate_cie_d(6500.)
|
||||||
} else {
|
} else {
|
||||||
DenselySampledSpectrumBuffer::generate_cie_d(white_balance_temp)
|
DenselySampledSpectrum::generate_cie_d(white_balance_temp)
|
||||||
};
|
};
|
||||||
|
|
||||||
let sensor_illum: Option<Arc<Spectrum>> = if white_balance_temp != 0. {
|
let sensor_illum: Option<Arc<Spectrum>> = if white_balance_temp != 0. {
|
||||||
|
|
@ -121,28 +130,25 @@ impl PixelSensor {
|
||||||
sensor_illum: Option<&Spectrum>,
|
sensor_illum: Option<&Spectrum>,
|
||||||
imaging_ratio: Float,
|
imaging_ratio: Float,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// As seen in usages of this constructos, sensor_illum can be null
|
|
||||||
// Going with the colorspace's own illuminant, but this might not be the right choice
|
|
||||||
// TODO: Test this
|
|
||||||
let illum: &Spectrum = match sensor_illum {
|
let illum: &Spectrum = match sensor_illum {
|
||||||
Some(arc_illum) => arc_illum,
|
Some(arc_illum) => arc_illum,
|
||||||
None => &Spectrum::Dense(output_colorspace.as_ref().illuminant),
|
None => &Spectrum::Dense(output_colorspace.as_ref().illuminant),
|
||||||
};
|
};
|
||||||
|
|
||||||
let r_bar = DenselySampledSpectrumBuffer::from_spectrum(r);
|
let r_bar = DenselySampledSpectrum::from_spectrum(r);
|
||||||
let g_bar = DenselySampledSpectrumBuffer::from_spectrum(g);
|
let g_bar = DenselySampledSpectrum::from_spectrum(g);
|
||||||
let b_bar = DenselySampledSpectrumBuffer::from_spectrum(b);
|
let b_bar = DenselySampledSpectrum::from_spectrum(b);
|
||||||
let mut rgb_camera = [[0.; 3]; N_SWATCH_REFLECTANCES];
|
let mut rgb_camera = [[0.; 3]; N_SWATCH_REFLECTANCES];
|
||||||
|
|
||||||
let swatches = Self::get_swatches();
|
let swatches = Self::get_swatches();
|
||||||
|
|
||||||
for i in 0..N_SWATCH_REFLECTANCES {
|
for i in 0..N_SWATCH_REFLECTANCES {
|
||||||
let rgb = DevicePixelSensor::project_reflectance::<RGB>(
|
let rgb = PixelSensor::project_reflectance::<RGB>(
|
||||||
&swatches[i],
|
&swatches[i],
|
||||||
illum,
|
illum,
|
||||||
&Spectrum::Dense(r_bar.device()),
|
&Spectrum::Dense(r_bar),
|
||||||
&Spectrum::Dense(g_bar.device()),
|
&Spectrum::Dense(g_bar),
|
||||||
&Spectrum::Dense(b_bar.device()),
|
&Spectrum::Dense(b_bar),
|
||||||
);
|
);
|
||||||
for c in 0..3 {
|
for c in 0..3 {
|
||||||
rgb_camera[i][c] = rgb[c];
|
rgb_camera[i][c] = rgb[c];
|
||||||
|
|
@ -151,11 +157,11 @@ impl PixelSensor {
|
||||||
|
|
||||||
let mut xyz_output = [[0.; 3]; N_SWATCH_REFLECTANCES];
|
let mut xyz_output = [[0.; 3]; N_SWATCH_REFLECTANCES];
|
||||||
let spectra = get_spectra_context();
|
let spectra = get_spectra_context();
|
||||||
let sensor_white_g = illum.inner_product(&Spectrum::Dense(g_bar.device()));
|
let sensor_white_g = illum.inner_product(&Spectrum::Dense(g_bar));
|
||||||
let sensor_white_y = illum.inner_product(&Spectrum::Dense(spectra.y));
|
let sensor_white_y = illum.inner_product(&Spectrum::Dense(spectra.y));
|
||||||
for i in 0..N_SWATCH_REFLECTANCES {
|
for i in 0..N_SWATCH_REFLECTANCES {
|
||||||
let s = swatches[i].clone();
|
let s = swatches[i].clone();
|
||||||
let xyz = DevicePixelSensor::project_reflectance::<XYZ>(
|
let xyz = PixelSensor::project_reflectance::<XYZ>(
|
||||||
&s,
|
&s,
|
||||||
illum,
|
illum,
|
||||||
&Spectrum::Dense(spectra.x),
|
&Spectrum::Dense(spectra.x),
|
||||||
|
|
@ -170,21 +176,13 @@ impl PixelSensor {
|
||||||
let xyz_from_sensor_rgb = linear_least_squares(rgb_camera, xyz_output)
|
let xyz_from_sensor_rgb = linear_least_squares(rgb_camera, xyz_output)
|
||||||
.expect("Could not convert sensor illuminance to XYZ space");
|
.expect("Could not convert sensor illuminance to XYZ space");
|
||||||
|
|
||||||
let data = SensorStorage {
|
PixelSensor {
|
||||||
r_bar: r_bar.clone(),
|
r_bar: r_bar.clone(),
|
||||||
g_bar: g_bar.clone(),
|
g_bar: g_bar.clone(),
|
||||||
b_bar: b_bar.clone(),
|
b_bar: b_bar.clone(),
|
||||||
};
|
|
||||||
|
|
||||||
let device = DevicePixelSensor {
|
|
||||||
r_bar: r_bar.device(),
|
|
||||||
g_bar: g_bar.device(),
|
|
||||||
b_bar: b_bar.device(),
|
|
||||||
imaging_ratio,
|
imaging_ratio,
|
||||||
xyz_from_sensor_rgb,
|
xyz_from_sensor_rgb,
|
||||||
};
|
}
|
||||||
|
|
||||||
Self { device, data }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_with_white_balance(
|
fn new_with_white_balance(
|
||||||
|
|
@ -206,25 +204,13 @@ impl PixelSensor {
|
||||||
xyz_from_sensor_rgb = SquareMatrix::<Float, 3>::default();
|
xyz_from_sensor_rgb = SquareMatrix::<Float, 3>::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = SensorStorage {
|
PixelSensor {
|
||||||
r_bar: r_bar.clone(),
|
r_bar: r_bar.clone(),
|
||||||
g_bar: g_bar.clone(),
|
g_bar: g_bar.clone(),
|
||||||
b_bar: b_bar.clone(),
|
b_bar: b_bar.clone(),
|
||||||
};
|
|
||||||
|
|
||||||
let device = DevicePixelSensor {
|
|
||||||
r_bar: r_bar.device(),
|
|
||||||
g_bar: g_bar.device(),
|
|
||||||
b_bar: b_bar.device(),
|
|
||||||
xyz_from_sensor_rgb,
|
xyz_from_sensor_rgb,
|
||||||
imaging_ratio,
|
imaging_ratio,
|
||||||
};
|
}
|
||||||
|
|
||||||
Self { data, device }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn device(&self) -> DevicePixelSensor {
|
|
||||||
self.device
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_swatches() -> Arc<[Spectrum; N_SWATCH_REFLECTANCES]> {
|
fn get_swatches() -> Arc<[Spectrum; N_SWATCH_REFLECTANCES]> {
|
||||||
|
|
@ -236,7 +222,7 @@ pub trait CreateFilmBase {
|
||||||
fn create(
|
fn create(
|
||||||
params: &ParameterDictionary,
|
params: &ParameterDictionary,
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
sensor: Option<&DevicePixelSensor>,
|
sensor: Option<&PixelSensor>,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
) -> Result<Self>
|
) -> Result<Self>
|
||||||
where
|
where
|
||||||
|
|
@ -247,7 +233,7 @@ impl CreateFilmBase for FilmBase {
|
||||||
fn create(
|
fn create(
|
||||||
params: &ParameterDictionary,
|
params: &ParameterDictionary,
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
sensor: Option<&DevicePixelSensor>,
|
sensor: Option<&PixelSensor>,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
) -> Result<Self>
|
) -> Result<Self>
|
||||||
where
|
where
|
||||||
|
|
@ -314,7 +300,7 @@ pub trait FilmTrait: Sync {
|
||||||
image.write(filename, metadata).expect("Something")
|
image.write(filename, metadata).expect("Something")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_image(&self, _metadata: &ImageMetadata, splat_scale: Float) -> Image {
|
fn get_image(&self, _metadata: &ImageMetadata, splat_scale: Float) -> HostImage {
|
||||||
let write_fp16 = true;
|
let write_fp16 = true;
|
||||||
let format = if write_fp16 {
|
let format = if write_fp16 {
|
||||||
PixelFormat::F16
|
PixelFormat::F16
|
||||||
|
|
@ -362,8 +348,8 @@ pub trait FilmTrait: Sync {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut image = Image::new(format, resolution, channel_names, SRGB.into());
|
let mut image = HostImage::new(format, resolution, channel_names, SRGB.into());
|
||||||
let rgb_desc = ImageChannelDesc::new(&[0, 1, 2]);
|
let _rgb_desc = ImageChannelDesc::new(&[0, 1, 2]);
|
||||||
|
|
||||||
for (iy, row_data) in processed_rows.into_iter().enumerate() {
|
for (iy, row_data) in processed_rows.into_iter().enumerate() {
|
||||||
for (ix, rgb_chunk) in row_data.chunks_exact(3).enumerate() {
|
for (ix, rgb_chunk) in row_data.chunks_exact(3).enumerate() {
|
||||||
|
|
@ -381,10 +367,6 @@ pub trait FilmTrait: Sync {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// self.base().pixel_bounds = pixel_bounds;
|
|
||||||
// self.base().full_resolution = resolution;
|
|
||||||
// self.colorspace = colorspace;
|
|
||||||
|
|
||||||
image
|
image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,28 @@
|
||||||
use crate::filters::*;
|
|
||||||
use crate::utils::containers::Array2D;
|
|
||||||
use crate::utils::sampling::PiecewiseConstant2D;
|
use crate::utils::sampling::PiecewiseConstant2D;
|
||||||
use crate::utils::DeviceRepr;
|
|
||||||
use crate::utils::{FileLoc, ParameterDictionary};
|
use crate::utils::{FileLoc, ParameterDictionary};
|
||||||
|
use crate::Arena;
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use shared::core::filter::{DeviceFilterSampler, Filter};
|
use shared::core::filter::Filter;
|
||||||
use shared::core::geometry::{Bounds2f, Point2f, Vector2f};
|
use shared::core::geometry::{Bounds2f, Point2f, Vector2f};
|
||||||
use shared::filters::*;
|
use shared::filters::*;
|
||||||
use shared::Float;
|
use shared::{Array2D, Float};
|
||||||
|
|
||||||
pub trait FilterFactory {
|
pub trait FilterFactory {
|
||||||
fn create(name: &str, params: &ParameterDictionary, loc: &FileLoc) -> Result<Filter>;
|
fn create(
|
||||||
|
name: &str,
|
||||||
|
params: &ParameterDictionary,
|
||||||
|
loc: &FileLoc,
|
||||||
|
arena: &Arena,
|
||||||
|
) -> Result<Filter>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FilterFactory for Filter {
|
impl FilterFactory for Filter {
|
||||||
fn create(name: &str, params: &ParameterDictionary, loc: &FileLoc) -> Result<Self> {
|
fn create(
|
||||||
|
name: &str,
|
||||||
|
params: &ParameterDictionary,
|
||||||
|
loc: &FileLoc,
|
||||||
|
arena: &Arena,
|
||||||
|
) -> Result<Self> {
|
||||||
match name {
|
match name {
|
||||||
"box" => {
|
"box" => {
|
||||||
let xw = params.get_one_float("xradius", 0.5)?;
|
let xw = params.get_one_float("xradius", 0.5)?;
|
||||||
|
|
@ -27,7 +35,6 @@ impl FilterFactory for Filter {
|
||||||
let yw = params.get_one_float("yradius", 1.5)?;
|
let yw = params.get_one_float("yradius", 1.5)?;
|
||||||
let sigma = params.get_one_float("sigma", 0.5)?;
|
let sigma = params.get_one_float("sigma", 0.5)?;
|
||||||
let filter = GaussianFilter::new(Vector2f::new(xw, yw), sigma);
|
let filter = GaussianFilter::new(Vector2f::new(xw, yw), sigma);
|
||||||
Ok(Filter::Gaussian(filter))
|
|
||||||
}
|
}
|
||||||
"mitchell" => {
|
"mitchell" => {
|
||||||
let xw = params.get_one_float("xradius", 2.)?;
|
let xw = params.get_one_float("xradius", 2.)?;
|
||||||
|
|
@ -54,52 +61,3 @@ impl FilterFactory for Filter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Debug, Copy)]
|
|
||||||
pub struct FilterSampler {
|
|
||||||
pub domain: Bounds2f,
|
|
||||||
pub distrib: PiecewiseConstant2D,
|
|
||||||
pub f: Array2D<Float>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FilterSampler {
|
|
||||||
pub fn new<F>(radius: Vector2f, func: F) -> Self
|
|
||||||
where
|
|
||||||
F: Fn(Point2f) -> Float,
|
|
||||||
{
|
|
||||||
let domain = Bounds2f::from_points(
|
|
||||||
Point2f::new(-radius.x(), -radius.y()),
|
|
||||||
Point2f::new(radius.x(), radius.y()),
|
|
||||||
);
|
|
||||||
|
|
||||||
let nx = (32.0 * radius.x()) as i32;
|
|
||||||
let ny = (32.0 * radius.y()) as i32;
|
|
||||||
let mut f = Array2D::new_dims(nx, ny);
|
|
||||||
for y in 0..f.y_size() {
|
|
||||||
for x in 0..f.x_size() {
|
|
||||||
let p = domain.lerp(Point2f::new(
|
|
||||||
(x as Float + 0.5) / f.x_size() as Float,
|
|
||||||
(y as Float + 0.5) / f.y_size() as Float,
|
|
||||||
));
|
|
||||||
f[(x as i32, y as i32)] = func(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let distrib = PiecewiseConstant2D::new_with_bounds(&f, domain);
|
|
||||||
|
|
||||||
Self { domain, distrib, f }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DeviceRepr for FilterSampler {
|
|
||||||
type Target = DeviceFilterSampler;
|
|
||||||
|
|
||||||
fn upload_value<A: GpuAllocator>(&self, arena: &Arena<A>) -> DeviceFilterSampler {
|
|
||||||
DeviceFilterSampler {
|
|
||||||
domain: self.domain,
|
|
||||||
distrib: self.distrib.upload_value(arena),
|
|
||||||
f: self.f.upload_value(arena),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,6 @@ use shared::spectra::RGBColorSpace;
|
||||||
use shared::utils::math::SquareMatrix;
|
use shared::utils::math::SquareMatrix;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
// use std::ops::{Deref, DerefMut};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct ImageChannelDesc {
|
pub struct ImageChannelDesc {
|
||||||
pub offset: Vec<usize>,
|
pub offset: Vec<usize>,
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use shared::Float;
|
||||||
use shared::Ptr;
|
use shared::Ptr;
|
||||||
use shared::core::color::{ColorEncoding, ColorEncodingTrait, LINEAR};
|
use shared::core::color::{ColorEncoding, ColorEncodingTrait, LINEAR};
|
||||||
use shared::core::geometry::{Bounds2f, Point2f, Point2i};
|
use shared::core::geometry::{Bounds2f, Point2f, Point2i};
|
||||||
use shared::core::image::{DeviceImage, ImageBase, PixelFormat, Pixels, WrapMode, WrapMode2D};
|
use shared::core::image::{Image, ImageBase, PixelFormat, Pixels, WrapMode, WrapMode2D};
|
||||||
use shared::utils::math::square;
|
use shared::utils::math::square;
|
||||||
use smallvec::{SmallVec, smallvec};
|
use smallvec::{SmallVec, smallvec};
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
@ -69,133 +69,66 @@ impl DerefMut for ImageChannelValues {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum PixelStorage {
|
pub struct HostImage {
|
||||||
U8(Vec<u8>),
|
pub inner: Image,
|
||||||
F16(Vec<f16>),
|
|
||||||
F32(Vec<f32>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PixelStorage {
|
|
||||||
pub fn as_pixels(&self) -> Pixels {
|
|
||||||
match self {
|
|
||||||
PixelStorage::U8(vec) => Pixels::new_u8(Ptr::from_raw(vec.as_ptr())),
|
|
||||||
PixelStorage::F16(vec) => Pixels::new_f16(Ptr::from_raw(vec.as_ptr() as *const u16)),
|
|
||||||
PixelStorage::F32(vec) => Pixels::new_f32(Ptr::from_raw(vec.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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Image {
|
|
||||||
pub pixels: PixelStorage,
|
|
||||||
pub channel_names: Vec<String>,
|
pub channel_names: Vec<String>,
|
||||||
pub device: DeviceImage,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl Deref for Image {
|
|
||||||
// type Target = DeviceImage;
|
|
||||||
// #[inline]
|
|
||||||
// fn deref(&self) -> &Self::Target {
|
|
||||||
// &self.device
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ImageAndMetadata {
|
pub struct ImageAndMetadata {
|
||||||
pub image: Image,
|
pub image: HostImage,
|
||||||
pub metadata: ImageMetadata,
|
pub metadata: ImageMetadata,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Image {
|
impl HostImage {
|
||||||
// Constructors
|
pub fn from_u8(
|
||||||
fn from_storage(
|
data: &[u8],
|
||||||
storage: PixelStorage,
|
|
||||||
resolution: Point2i,
|
resolution: Point2i,
|
||||||
channel_names: &[impl AsRef<str>],
|
channel_names: &[impl AsRef<str>],
|
||||||
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 channel_names = channel_names
|
|
||||||
.iter()
|
|
||||||
.map(|s| s.as_ref().to_string())
|
|
||||||
.collect();
|
|
||||||
assert_eq!(storage.len(), expected, "Pixel data size mismatch");
|
|
||||||
|
|
||||||
let device = DeviceImage {
|
|
||||||
base: ImageBase {
|
|
||||||
format: storage.format(),
|
|
||||||
encoding,
|
|
||||||
resolution,
|
|
||||||
n_channels,
|
|
||||||
},
|
|
||||||
pixels: storage.as_pixels(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
pixels: storage,
|
inner: Image::from_u8(data, resolution, n_channels, encoding),
|
||||||
channel_names,
|
channel_names: channel_names.iter().map(|s| s.as_ref().to_string()).collect(),
|
||||||
device,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_u8(
|
pub fn from_f32(
|
||||||
data: Vec<u8>,
|
data: &[f32],
|
||||||
resolution: Point2i,
|
resolution: Point2i,
|
||||||
channel_names: &[impl AsRef<str>],
|
channel_names: &[impl AsRef<str>],
|
||||||
encoding: ColorEncoding,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::from_storage(PixelStorage::U8(data), resolution, channel_names, encoding)
|
let n_channels = channel_names.len() as i32;
|
||||||
|
Self {
|
||||||
|
inner: Image::from_f32(data, resolution, n_channels),
|
||||||
|
channel_names: channel_names.iter().map(|s| s.as_ref().to_string()).collect(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_f16(
|
pub fn from_f16(
|
||||||
data: Vec<half::f16>,
|
data: &[u16],
|
||||||
resolution: Point2i,
|
resolution: Point2i,
|
||||||
channel_names: &[impl AsRef<str>],
|
channel_names: &[impl AsRef<str>],
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::from_storage(PixelStorage::F16(data), resolution, channel_names, LINEAR)
|
let n_channels = channel_names.len() as i32;
|
||||||
}
|
Self {
|
||||||
|
inner: Image::from_f16(data, resolution, n_channels),
|
||||||
pub fn from_f32(
|
channel_names: channel_names.iter().map(|s| s.as_ref().to_string()).collect(),
|
||||||
data: Vec<f32>,
|
}
|
||||||
resolution: Point2i,
|
|
||||||
channel_names: &[impl AsRef<str>],
|
|
||||||
) -> Self {
|
|
||||||
Self::from_storage(PixelStorage::F32(data), resolution, channel_names, LINEAR)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
format: PixelFormat,
|
format: PixelFormat,
|
||||||
resolution: Point2i,
|
resolution: Point2i,
|
||||||
channel_names: &[impl AsRef<str>],
|
channel_names: &[impl AsRef<str>],
|
||||||
encoding: Arc<ColorEncoding>,
|
encoding: ColorEncoding,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let n_channels = channel_names.len();
|
let n_channels = channel_names.len() as i32;
|
||||||
let pixel_count = (resolution.x() * resolution.y()) as usize * n_channels;
|
Self {
|
||||||
|
inner: Image::new(format, resolution, n_channels, encoding),
|
||||||
let storage = match format {
|
channel_names: channel_names.iter().map(|s| s.as_ref().to_string()).collect(),
|
||||||
PixelFormat::U8 => PixelStorage::U8(vec![0; pixel_count].into()),
|
}
|
||||||
PixelFormat::F16 => PixelStorage::F16(vec![f16::ZERO; pixel_count].into()),
|
|
||||||
PixelFormat::F32 => PixelStorage::F32(vec![0.0; pixel_count].into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
Self::from_storage(storage, resolution, channel_names, *encoding)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_constant(
|
pub fn new_constant(
|
||||||
|
|
@ -204,206 +137,29 @@ impl Image {
|
||||||
values: &[f32],
|
values: &[f32],
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let n_channels = channel_names.len();
|
let n_channels = channel_names.len();
|
||||||
if values.len() != n_channels {
|
assert_eq!(values.len(), n_channels, "values length must match channel count");
|
||||||
panic!(
|
|
||||||
"Image::new_constant: values length ({}) must match channel count ({})",
|
|
||||||
values.len(),
|
|
||||||
n_channels
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let n_pixels = (resolution.x() * resolution.y()) as usize;
|
let n_pixels = (resolution.x() * resolution.y()) as usize;
|
||||||
|
|
||||||
let mut data = Vec::with_capacity(n_pixels * n_channels);
|
let mut data = Vec::with_capacity(n_pixels * n_channels);
|
||||||
|
|
||||||
for _ in 0..n_pixels {
|
for _ in 0..n_pixels {
|
||||||
data.extend_from_slice(values);
|
data.extend_from_slice(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::from_f32(data, resolution, channel_names)
|
Self::from_f32(&data, resolution, channel_names)
|
||||||
}
|
|
||||||
|
|
||||||
// Access
|
|
||||||
pub fn device(&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.base().format
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn channel_names(&self) -> Vec<&str> {
|
pub fn channel_names(&self) -> Vec<&str> {
|
||||||
self.channel_names.iter().map(|s| s.as_str()).collect()
|
self.channel_names.iter().map(|s| s.as_str()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encoding(&self) -> ColorEncoding {
|
|
||||||
self.base().encoding
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pixel_offset(&self, p: Point2i) -> usize {
|
|
||||||
let width = self.resolution().x() as usize;
|
|
||||||
let idx = p.y() as usize * width + p.x() as usize;
|
|
||||||
idx * self.n_channels() as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read
|
|
||||||
pub fn as_f32_slice(&self) -> Option<&[f32]> {
|
|
||||||
match &self.pixels {
|
|
||||||
PixelStorage::F32(data) => Some(data.as_slice()),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
|
|
||||||
match &self.pixels {
|
|
||||||
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 nc = self.n_channels() as usize;
|
|
||||||
let mut values = SmallVec::with_capacity(nc);
|
|
||||||
|
|
||||||
match &self.pixels {
|
|
||||||
PixelStorage::U8(data) => {
|
|
||||||
for i in 0..nc {
|
|
||||||
values.push(self.device.base.encoding.to_linear_scalar(data[offset + i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PixelStorage::F16(data) => {
|
|
||||||
for i in 0..nc {
|
|
||||||
values.push(data[offset + i].to_f32());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PixelStorage::F32(data) => {
|
|
||||||
for i in 0..nc {
|
|
||||||
values.push(data[offset + i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageChannelValues(values)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write
|
|
||||||
pub fn set_channel(&mut self, p: Point2i, c: i32, mut value: Float) {
|
|
||||||
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.pixels {
|
|
||||||
PixelStorage::U8(data) => {
|
|
||||||
data[offset] = self.device.base.encoding.from_linear_scalar(value);
|
|
||||||
}
|
|
||||||
PixelStorage::F16(data) => {
|
|
||||||
data[offset] = f16::from_f32(value);
|
|
||||||
}
|
|
||||||
PixelStorage::F32(data) => {
|
|
||||||
data[offset] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_channels(&mut self, p: Point2i, values: &ImageChannelValues) {
|
|
||||||
for i in 0..values.len() {
|
|
||||||
self.set_channel(p, i.try_into().unwrap(), values[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Descriptions
|
|
||||||
pub fn get_channels_with_desc(
|
|
||||||
&self,
|
|
||||||
p: Point2i,
|
|
||||||
desc: &ImageChannelDesc,
|
|
||||||
wrap_mode: WrapMode2D,
|
|
||||||
) -> ImageChannelValues {
|
|
||||||
let mut pp = p;
|
|
||||||
if !self.device.base.remap_pixel_coords(&mut pp, wrap_mode) {
|
|
||||||
return ImageChannelValues(smallvec![0.0; desc.offset.len()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
let pixel_offset = self.pixel_offset(pp);
|
|
||||||
let mut values = SmallVec::with_capacity(desc.offset.len());
|
|
||||||
|
|
||||||
match &self.pixels {
|
|
||||||
PixelStorage::U8(data) => {
|
|
||||||
for &c in &desc.offset {
|
|
||||||
let raw = data[pixel_offset + c];
|
|
||||||
values.push(self.device.base.encoding.to_linear_scalar(raw));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PixelStorage::F16(data) => {
|
|
||||||
for &c in &desc.offset {
|
|
||||||
values.push(data[pixel_offset + c].to_f32());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PixelStorage::F32(data) => {
|
|
||||||
for &c in &desc.offset {
|
|
||||||
values.push(data[pixel_offset + c]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageChannelValues(values)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn channel_names_from_desc(&self, desc: &ImageChannelDesc) -> Vec<&str> {
|
|
||||||
desc.offset
|
|
||||||
.iter()
|
|
||||||
.map(|&i| self.channel_names[i].as_str())
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_channel_desc(
|
pub fn get_channel_desc(
|
||||||
&self,
|
&self,
|
||||||
requested_channels: &[impl AsRef<str> + std::fmt::Display],
|
requested: &[impl AsRef<str> + std::fmt::Display],
|
||||||
) -> Result<ImageChannelDesc> {
|
) -> Result<ImageChannelDesc> {
|
||||||
let mut offset = Vec::with_capacity(requested_channels.len());
|
let mut offset = Vec::with_capacity(requested.len());
|
||||||
|
for req in requested {
|
||||||
for req in requested_channels.iter() {
|
|
||||||
match self.channel_names.iter().position(|n| n == req.as_ref()) {
|
match self.channel_names.iter().position(|n| n == req.as_ref()) {
|
||||||
Some(idx) => {
|
Some(idx) => offset.push(idx),
|
||||||
offset.push(idx);
|
|
||||||
}
|
|
||||||
None => {
|
None => {
|
||||||
return Err(anyhow!(
|
return Err(anyhow!(
|
||||||
"Missing channel '{}'. Available: {:?}",
|
"Missing channel '{}'. Available: {:?}",
|
||||||
|
|
@ -413,81 +169,98 @@ impl Image {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ImageChannelDesc { offset })
|
Ok(ImageChannelDesc { offset })
|
||||||
}
|
}
|
||||||
|
|
||||||
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.inner.n_channels() as usize).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn channel_names_from_desc(&self, desc: &ImageChannelDesc) -> Vec<&str> {
|
||||||
|
desc.offset.iter().map(|&i| self.channel_names[i].as_str()).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
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.inner.remap_pixel_coords(&mut p, wrap_mode) {
|
||||||
|
return ImageChannelValues(SmallVec::from_elem(0.0, self.inner.n_channels() as usize));
|
||||||
|
}
|
||||||
|
let offset = self.inner.pixel_offset(p);
|
||||||
|
let nc = self.inner.n_channels() as usize;
|
||||||
|
let mut values = SmallVec::with_capacity(nc);
|
||||||
|
for i in 0..nc {
|
||||||
|
values.push(unsafe { self.inner.pixels.read(offset + i, &self.inner.encoding) });
|
||||||
|
}
|
||||||
|
ImageChannelValues(values)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_channels_with_desc(
|
||||||
|
&self,
|
||||||
|
p: Point2i,
|
||||||
|
desc: &ImageChannelDesc,
|
||||||
|
wrap_mode: WrapMode2D,
|
||||||
|
) -> ImageChannelValues {
|
||||||
|
let mut pp = p;
|
||||||
|
if !self.inner.remap_pixel_coords(&mut pp, wrap_mode) {
|
||||||
|
return ImageChannelValues(SmallVec::from_elem(0.0, desc.offset.len()));
|
||||||
|
}
|
||||||
|
let pixel_offset = self.inner.pixel_offset(pp);
|
||||||
|
let mut values = SmallVec::with_capacity(desc.offset.len());
|
||||||
|
for &c in &desc.offset {
|
||||||
|
values.push(unsafe { self.inner.pixels.read(pixel_offset + c, &self.inner.encoding) });
|
||||||
|
}
|
||||||
|
ImageChannelValues(values)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_channels(&mut self, p: Point2i, values: &ImageChannelValues) {
|
||||||
|
for i in 0..values.len() {
|
||||||
|
self.inner.set_channel(p, i as i32, values[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_channels(&self, desc: &ImageChannelDesc) -> Self {
|
pub fn select_channels(&self, desc: &ImageChannelDesc) -> Self {
|
||||||
let new_names: Vec<String> = desc
|
let new_names: Vec<String> = desc.offset.iter().map(|&i| self.channel_names[i].clone()).collect();
|
||||||
.offset
|
let res = self.inner.resolution();
|
||||||
.iter()
|
|
||||||
.map(|&i| self.channel_names[i].clone())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let res = self.resolution();
|
|
||||||
let pixel_count = (res.x() * res.y()) as usize;
|
let pixel_count = (res.x() * res.y()) as usize;
|
||||||
let src_nc = self.n_channels() as usize;
|
let src_nc = self.inner.n_channels() as usize;
|
||||||
let dst_nc = desc.offset.len();
|
let dst_nc = desc.offset.len();
|
||||||
|
|
||||||
let new_storage = match &self.pixels {
|
// Always produce f32 output for simplicity
|
||||||
PixelStorage::U8(src) => {
|
let mut dst = vec![0.0f32; pixel_count * dst_nc];
|
||||||
let mut dst = vec![0u8; pixel_count * dst_nc];
|
for i in 0..pixel_count {
|
||||||
for i in 0..pixel_count {
|
let src_offset = i * src_nc;
|
||||||
for (out_idx, &in_c) in desc.offset.iter().enumerate() {
|
for (out_idx, &in_c) in desc.offset.iter().enumerate() {
|
||||||
dst[i * dst_nc + out_idx] = src[i * src_nc + in_c];
|
dst[i * dst_nc + out_idx] = unsafe {
|
||||||
}
|
self.inner.pixels.read(src_offset + in_c, &self.inner.encoding)
|
||||||
}
|
};
|
||||||
PixelStorage::U8(dst)
|
|
||||||
}
|
}
|
||||||
PixelStorage::F16(src) => {
|
}
|
||||||
let mut dst = vec![f16::ZERO; pixel_count * dst_nc];
|
|
||||||
for i in 0..pixel_count {
|
|
||||||
for (out_idx, &in_c) in desc.offset.iter().enumerate() {
|
|
||||||
dst[i * dst_nc + out_idx] = src[i * src_nc + in_c];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PixelStorage::F16(dst)
|
|
||||||
}
|
|
||||||
PixelStorage::F32(src) => {
|
|
||||||
let mut dst = vec![0.0f32; pixel_count * dst_nc];
|
|
||||||
for i in 0..pixel_count {
|
|
||||||
for (out_idx, &in_c) in desc.offset.iter().enumerate() {
|
|
||||||
dst[i * dst_nc + out_idx] = src[i * src_nc + in_c];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PixelStorage::F32(dst)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Self::from_storage(new_storage, res, &new_names, self.encoding())
|
Self::from_f32(&dst, res, &new_names)
|
||||||
}
|
}
|
||||||
|
|
||||||
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>
|
||||||
where
|
where
|
||||||
F: Fn(Point2f) -> Float + Sync + Send,
|
F: Fn(Point2f) -> Float + Sync + Send,
|
||||||
{
|
{
|
||||||
let width = self.resolution().x();
|
let width = self.inner.resolution().x();
|
||||||
let height = self.resolution().y();
|
let height = self.inner.resolution().y();
|
||||||
|
|
||||||
let mut dist: Array2D<Float> = Array2D::new_dims(width, height);
|
let mut dist = Array2D::new_dims(width, height);
|
||||||
|
|
||||||
dist.values
|
dist.as_mut_slice()
|
||||||
.par_chunks_mut(width as usize)
|
.par_chunks_mut(width as usize)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.for_each(|(y, row)| {
|
.for_each(|(y, row)| {
|
||||||
let y = y as i32;
|
let y = y as i32;
|
||||||
|
|
||||||
for (x, out_val) in row.iter_mut().enumerate() {
|
for (x, out_val) in row.iter_mut().enumerate() {
|
||||||
let x = x as i32;
|
let x = x as i32;
|
||||||
|
|
||||||
let value = self.get_channels(Point2i::new(x, y)).average();
|
let value = self.get_channels(Point2i::new(x, y)).average();
|
||||||
|
|
||||||
let u = (x as Float + 0.5) / width as Float;
|
let u = (x as Float + 0.5) / width as Float;
|
||||||
let v = (y as Float + 0.5) / height as Float;
|
let v = (y as Float + 0.5) / height as Float;
|
||||||
let p = domain.lerp(Point2f::new(u, v));
|
let p = domain.lerp(Point2f::new(u, v));
|
||||||
|
|
@ -499,29 +272,36 @@ impl Image {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_sampling_distribution_uniform(&self) -> Array2D<Float> {
|
pub fn get_sampling_distribution_uniform(&self) -> Array2D<Float> {
|
||||||
let default_domain = Bounds2f::from_points(Point2f::new(0.0, 0.0), Point2f::new(1.0, 1.0));
|
let domain = Bounds2f::from_points(Point2f::new(0.0, 0.0), Point2f::new(1.0, 1.0));
|
||||||
|
self.get_sampling_distribution(|_| 1.0, domain)
|
||||||
|
}
|
||||||
|
|
||||||
self.get_sampling_distribution(|_| 1.0, default_domain)
|
pub fn has_any_infinite_pixels(&self) -> bool {
|
||||||
|
self.inner.has_any_infinite_pixels()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_any_nan_pixels(&self) -> bool {
|
||||||
|
self.inner.has_any_nan_pixels()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mse(
|
pub fn mse(
|
||||||
&self,
|
&self,
|
||||||
desc: ImageChannelDesc,
|
desc: &ImageChannelDesc,
|
||||||
ref_img: &Image,
|
ref_img: &HostImage,
|
||||||
generate_mse_image: bool,
|
generate_mse_image: bool,
|
||||||
) -> (ImageChannelValues, Option<Image>) {
|
) -> (ImageChannelValues, Option<HostImage>) {
|
||||||
let res = self.resolution();
|
let res = self.inner.resolution();
|
||||||
|
assert_eq!(res, ref_img.inner.resolution());
|
||||||
|
|
||||||
let mut sum_se: Vec<f64> = vec![0.; desc.size()];
|
|
||||||
let names_ref = self.channel_names_from_desc(&desc);
|
|
||||||
let ref_desc = ref_img
|
let ref_desc = ref_img
|
||||||
.get_channel_desc(&self.channel_names_from_desc(&desc))
|
.get_channel_desc(&self.channel_names_from_desc(desc))
|
||||||
.expect("Channels not found in image");
|
.expect("Channels not found in reference image");
|
||||||
assert_eq!(res, ref_img.resolution());
|
|
||||||
|
|
||||||
let width = res.x() as usize;
|
let width = res.x() as usize;
|
||||||
let height = res.y() as usize;
|
let height = res.y() as usize;
|
||||||
let n_channels = desc.offset.len();
|
let n_channels = desc.size();
|
||||||
|
|
||||||
|
let mut sum_se: Vec<f64> = vec![0.0; n_channels];
|
||||||
let mut mse_pixels = if generate_mse_image {
|
let mut mse_pixels = if generate_mse_image {
|
||||||
vec![0.0f32; width * height * n_channels]
|
vec![0.0f32; width * height * n_channels]
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -530,18 +310,15 @@ impl Image {
|
||||||
|
|
||||||
for y in 0..res.y() {
|
for y in 0..res.y() {
|
||||||
for x in 0..res.x() {
|
for x in 0..res.x() {
|
||||||
let v =
|
let v = self.get_channels_with_desc(
|
||||||
self.get_channels_with_desc(Point2i::new(x, y), &desc, WrapMode::Clamp.into());
|
Point2i::new(x, y), desc, WrapMode::Clamp.into(),
|
||||||
let v_ref = self.get_channels_with_desc(
|
|
||||||
Point2i::new(x, y),
|
|
||||||
&ref_desc,
|
|
||||||
WrapMode::Clamp.into(),
|
|
||||||
);
|
);
|
||||||
for c in 0..desc.size() {
|
let v_ref = ref_img.get_channels_with_desc(
|
||||||
|
Point2i::new(x, y), &ref_desc, WrapMode::Clamp.into(),
|
||||||
|
);
|
||||||
|
for c in 0..n_channels {
|
||||||
let se = square(v[c] as f64 - v_ref[c] as f64);
|
let se = square(v[c] as f64 - v_ref[c] as f64);
|
||||||
if se.is_infinite() {
|
if se.is_infinite() { continue; }
|
||||||
continue;
|
|
||||||
}
|
|
||||||
sum_se[c] += se;
|
sum_se[c] += se;
|
||||||
if generate_mse_image {
|
if generate_mse_image {
|
||||||
let idx = (y as usize * width + x as usize) * n_channels + c;
|
let idx = (y as usize * width + x as usize) * n_channels + c;
|
||||||
|
|
@ -556,7 +333,8 @@ impl Image {
|
||||||
sum_se.iter().map(|&s| (s / pixel_count) as Float).collect();
|
sum_se.iter().map(|&s| (s / pixel_count) as Float).collect();
|
||||||
|
|
||||||
let mse_image = if generate_mse_image {
|
let mse_image = if generate_mse_image {
|
||||||
Some(Image::new(PixelFormat::F32, res, &names_ref, LINEAR.into()))
|
let names = self.channel_names_from_desc(desc);
|
||||||
|
Some(HostImage::from_f32(&mse_pixels, res, &names))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
@ -564,53 +342,11 @@ impl Image {
|
||||||
(ImageChannelValues(mse_values), mse_image)
|
(ImageChannelValues(mse_values), mse_image)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_view_pointers(&mut self) {
|
pub fn image(&self) -> &Image {
|
||||||
self.device.pixels = match &self.pixels {
|
&self.inner
|
||||||
PixelStorage::U8(vec) => Pixels::new_u8(Ptr::from_raw(vec.as_ptr())),
|
|
||||||
PixelStorage::F16(vec) => Pixels::new_f16(Ptr::from_raw(vec.as_ptr() as *const u16)),
|
|
||||||
PixelStorage::F32(vec) => Pixels::new_f32(Ptr::from_raw(vec.as_ptr())),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_any_infinite_pixels(&self) -> bool {
|
pub fn into_image(self) -> Image {
|
||||||
if self.format() == PixelFormat::F32 {
|
self.inner
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn has_any_nan_pixels(&self) -> bool {
|
|
||||||
if self.format() == PixelFormat::F32 {
|
|
||||||
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_nan() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::Deref for Image {
|
|
||||||
type Target = DeviceImage;
|
|
||||||
|
|
||||||
fn deref(&self) -> &DeviceImage {
|
|
||||||
&self.device
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
use super::Image;
|
use super::HostImage;
|
||||||
use crate::core::image::PixelStorage;
|
|
||||||
use crate::core::image::pixel::PixelStorageTrait;
|
use crate::core::image::pixel::PixelStorageTrait;
|
||||||
|
use crate::core::image::PixelStorage;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use shared::Float;
|
|
||||||
use shared::core::color::ColorEncoding;
|
use shared::core::color::ColorEncoding;
|
||||||
use shared::core::geometry::{Bounds2i, Point2i};
|
use shared::core::geometry::{Bounds2i, Point2i};
|
||||||
use shared::core::image::{PixelFormat, WrapMode, WrapMode2D};
|
use shared::core::image::{PixelFormat, WrapMode, WrapMode2D};
|
||||||
use shared::utils::math::windowed_sinc;
|
use shared::utils::math::windowed_sinc;
|
||||||
|
use shared::Float;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
@ -15,71 +15,105 @@ pub struct ResampleWeight {
|
||||||
pub weight: [Float; 4],
|
pub weight: [Float; 4],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Image {
|
impl HostImage {
|
||||||
pub fn flip_y(&mut self) {
|
pub fn flip_y(&mut self) {
|
||||||
let res = self.resolution();
|
let res = self.inner.resolution;
|
||||||
let nc = self.n_channels() as usize;
|
let nc = self.inner.n_channels as usize;
|
||||||
|
|
||||||
match &mut self.pixels {
|
match self.inner.format {
|
||||||
PixelStorage::U8(d) => flip_y_kernel(d, res, nc),
|
PixelFormat::U8 => flip_y_kernel(self.inner.pixels.as_u8_mut(), res, nc),
|
||||||
PixelStorage::F16(d) => flip_y_kernel(d, res, nc),
|
PixelFormat::F16 => flip_y_kernel(self.inner.pixels.as_f16_mut(), res, nc),
|
||||||
PixelStorage::F32(d) => flip_y_kernel(d, res, nc),
|
PixelFormat::F32 => flip_y_kernel(self.inner.pixels.as_f32_slice_mut(), res, nc),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn crop(&self, bounds: Bounds2i) -> Image {
|
pub fn crop(&self, bounds: Bounds2i) -> HostImage {
|
||||||
let res = self.resolution();
|
let n_channels = self.inner.n_channels as usize;
|
||||||
let n_channels = self.n_channels() as usize;
|
|
||||||
|
|
||||||
let new_res = Point2i::new(
|
let new_res = Point2i::new(
|
||||||
bounds.p_max.x() - bounds.p_min.x(),
|
bounds.p_max.x() - bounds.p_min.x(),
|
||||||
bounds.p_max.y() - bounds.p_min.y(),
|
bounds.p_max.y() - bounds.p_min.y(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut new_image = Image::new(
|
let new_names = self.channel_names.clone();
|
||||||
self.format(),
|
let mut new_image =
|
||||||
new_res,
|
HostImage::new(self.inner.format, new_res, &new_names, self.inner.encoding);
|
||||||
&self.channel_names,
|
|
||||||
self.encoding().into(),
|
|
||||||
);
|
|
||||||
|
|
||||||
match (&self.pixels, &mut new_image.pixels) {
|
match self.inner.format {
|
||||||
(PixelStorage::U8(src), PixelStorage::U8(dst)) => {
|
PixelFormat::U8 => crop_kernel(
|
||||||
crop_kernel(src, dst, res, bounds, n_channels)
|
self.inner.pixels.as_u8(),
|
||||||
}
|
new_image.inner.pixels.as_u8_mut(),
|
||||||
(PixelStorage::F16(src), PixelStorage::F16(dst)) => {
|
self.inner.resolution,
|
||||||
crop_kernel(src, dst, res, bounds, n_channels)
|
bounds,
|
||||||
}
|
n_channels,
|
||||||
(PixelStorage::F32(src), PixelStorage::F32(dst)) => {
|
),
|
||||||
crop_kernel(src, dst, res, bounds, n_channels)
|
PixelFormat::F16 => crop_kernel(
|
||||||
}
|
self.inner.pixels.as_f16(),
|
||||||
_ => panic!("Format mismatch in crop"),
|
new_image.inner.pixels.as_f16_mut(),
|
||||||
|
self.inner.resolution,
|
||||||
|
bounds,
|
||||||
|
n_channels,
|
||||||
|
),
|
||||||
|
PixelFormat::F32 => crop_kernel(
|
||||||
|
self.inner.pixels.as_f32_slice(),
|
||||||
|
new_image.inner.pixels.as_f32_slice_mut(),
|
||||||
|
self.inner.resolution,
|
||||||
|
bounds,
|
||||||
|
n_channels,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
new_image
|
new_image
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_rect_out(&self, extent: Bounds2i, buf: &mut [Float], wrap: WrapMode2D) {
|
pub fn copy_rect_out(&self, extent: Bounds2i, buf: &mut [Float], wrap: WrapMode2D) {
|
||||||
match &self.pixels {
|
match self.inner.format {
|
||||||
PixelStorage::U8(d) => copy_rect_out_kernel(d, self, extent, buf, wrap),
|
PixelFormat::U8 => {
|
||||||
PixelStorage::F16(d) => copy_rect_out_kernel(d, self, extent, buf, wrap),
|
copy_rect_out_kernel(self.inner.pixels.as_u8(), self, extent, buf, wrap)
|
||||||
PixelStorage::F32(d) => copy_rect_out_kernel(d, self, extent, buf, wrap),
|
}
|
||||||
|
PixelFormat::F16 => {
|
||||||
|
copy_rect_out_kernel(self.inner.pixels.as_f16(), self, extent, buf, wrap)
|
||||||
|
}
|
||||||
|
PixelFormat::F32 => {
|
||||||
|
copy_rect_out_kernel(self.inner.pixels.as_f32_slice(), self, extent, buf, wrap)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_rect_in(&mut self, extent: Bounds2i, buf: &[Float]) {
|
pub fn copy_rect_in(&mut self, extent: Bounds2i, buf: &[Float]) {
|
||||||
let res = self.resolution();
|
let res = self.inner.resolution;
|
||||||
let n_channels = self.n_channels() as usize;
|
let n_channels = self.inner.n_channels as usize;
|
||||||
let encoding = self.encoding();
|
let encoding = self.inner.encoding;
|
||||||
|
let format = self.inner.format;
|
||||||
|
|
||||||
match &mut self.pixels {
|
match format {
|
||||||
PixelStorage::U8(d) => copy_rect_in_kernel(d, res, n_channels, encoding, extent, buf),
|
PixelFormat::U8 => copy_rect_in_kernel(
|
||||||
PixelStorage::F16(d) => copy_rect_in_kernel(d, res, n_channels, encoding, extent, buf),
|
self.inner.pixels.as_u8_mut(),
|
||||||
PixelStorage::F32(d) => copy_rect_in_kernel(d, res, n_channels, encoding, extent, buf),
|
res,
|
||||||
|
n_channels,
|
||||||
|
encoding,
|
||||||
|
extent,
|
||||||
|
buf,
|
||||||
|
),
|
||||||
|
PixelFormat::F16 => copy_rect_in_kernel(
|
||||||
|
self.inner.pixels.as_f16_mut(),
|
||||||
|
res,
|
||||||
|
n_channels,
|
||||||
|
encoding,
|
||||||
|
extent,
|
||||||
|
buf,
|
||||||
|
),
|
||||||
|
PixelFormat::F32 => copy_rect_in_kernel(
|
||||||
|
self.inner.pixels.as_f32_slice_mut(),
|
||||||
|
res,
|
||||||
|
n_channels,
|
||||||
|
encoding,
|
||||||
|
extent,
|
||||||
|
buf,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn float_resize_up(&self, new_res: Point2i, wrap_mode: WrapMode2D) -> Image {
|
pub fn float_resize_up(&self, new_res: Point2i, wrap_mode: WrapMode2D) -> HostImage {
|
||||||
let res = self.resolution();
|
let res = self.resolution();
|
||||||
assert!(new_res.x() >= res.x() && new_res.y() >= res.y());
|
assert!(new_res.x() >= res.x() && new_res.y() >= res.y());
|
||||||
assert!(
|
assert!(
|
||||||
|
|
@ -87,7 +121,7 @@ impl Image {
|
||||||
"ResizeUp requires Float format"
|
"ResizeUp requires Float format"
|
||||||
);
|
);
|
||||||
|
|
||||||
let resampled_image = Arc::new(Mutex::new(Image::new(
|
let resampled_image = Arc::new(Mutex::new(HostImage::new(
|
||||||
PixelFormat::F32,
|
PixelFormat::F32,
|
||||||
new_res,
|
new_res,
|
||||||
&self.channel_names,
|
&self.channel_names,
|
||||||
|
|
@ -132,7 +166,7 @@ impl Image {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_pyramid(base: Image, _wrap: WrapMode) -> Vec<Image> {
|
pub fn generate_pyramid(base: HostImage, _wrap: WrapMode) -> Vec<HostImage> {
|
||||||
let mut levels = vec![base];
|
let mut levels = vec![base];
|
||||||
let internal_wrap = WrapMode2D {
|
let internal_wrap = WrapMode2D {
|
||||||
uv: [WrapMode::Clamp; 2],
|
uv: [WrapMode::Clamp; 2],
|
||||||
|
|
@ -146,17 +180,32 @@ impl Image {
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_res = Point2i::new((old.x() / 2).max(1), (old.y() / 2).max(1));
|
let new_res = Point2i::new((old.x() / 2).max(1), (old.y() / 2).max(1));
|
||||||
let mut next = Image::new(
|
let mut next = HostImage::new(
|
||||||
prev.format(),
|
prev.inner.format,
|
||||||
new_res,
|
new_res,
|
||||||
&prev.channel_names,
|
&prev.channel_names,
|
||||||
prev.encoding().into(),
|
prev.inner.encoding,
|
||||||
);
|
);
|
||||||
|
|
||||||
match &mut next.pixels {
|
match next.inner.format {
|
||||||
PixelStorage::U8(d) => downsample_kernel(d, new_res, prev, internal_wrap),
|
PixelFormat::U8 => downsample_kernel(
|
||||||
PixelStorage::F16(d) => downsample_kernel(d, new_res, prev, internal_wrap),
|
next.inner.pixels.as_u8_mut(),
|
||||||
PixelStorage::F32(d) => downsample_kernel(d, new_res, prev, internal_wrap),
|
new_res,
|
||||||
|
&prev.inner,
|
||||||
|
internal_wrap,
|
||||||
|
),
|
||||||
|
PixelFormat::F16 => downsample_kernel(
|
||||||
|
next.inner.pixels.as_f16_mut(),
|
||||||
|
new_res,
|
||||||
|
&prev.inner,
|
||||||
|
internal_wrap,
|
||||||
|
),
|
||||||
|
PixelFormat::F32 => downsample_kernel(
|
||||||
|
next.inner.pixels.as_f32_slice_mut(),
|
||||||
|
new_res,
|
||||||
|
&prev.inner,
|
||||||
|
internal_wrap,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
levels.push(next);
|
levels.push(next);
|
||||||
}
|
}
|
||||||
|
|
@ -202,7 +251,7 @@ fn crop_kernel<T: PixelStorageTrait>(
|
||||||
|
|
||||||
fn copy_rect_out_kernel<T: PixelStorageTrait>(
|
fn copy_rect_out_kernel<T: PixelStorageTrait>(
|
||||||
src: &[T],
|
src: &[T],
|
||||||
image: &Image,
|
image: &HostImage,
|
||||||
extent: Bounds2i,
|
extent: Bounds2i,
|
||||||
buf: &mut [Float],
|
buf: &mut [Float],
|
||||||
wrap: WrapMode2D,
|
wrap: WrapMode2D,
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,26 @@
|
||||||
use crate::core::spectrum::SPECTRUM_CACHE;
|
use crate::core::spectrum::SPECTRUM_CACHE;
|
||||||
use crate::core::texture::FloatTexture;
|
use crate::core::texture::FloatTexture;
|
||||||
use crate::spectra::DenselySampledSpectrumBuffer;
|
use crate::{Arena, FileLoc, ParameterDictionary};
|
||||||
use crate::utils::{Arena, FileLoc, ParameterDictionary};
|
use anyhow::{anyhow, Result};
|
||||||
use anyhow::{Result, anyhow};
|
|
||||||
use shared::core::camera::CameraTransform;
|
use shared::core::camera::CameraTransform;
|
||||||
use shared::core::light::Light;
|
use shared::core::light::Light;
|
||||||
use shared::core::medium::Medium;
|
use shared::core::medium::Medium;
|
||||||
use shared::core::shape::Shape;
|
use shared::core::shape::Shape;
|
||||||
|
use shared::spectra::DenselySampledSpectrum;
|
||||||
use shared::core::spectrum::Spectrum;
|
use shared::core::spectrum::Spectrum;
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::RGBColorSpace;
|
||||||
use shared::utils::Transform;
|
use shared::Transform;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub fn lookup_spectrum(s: &Spectrum) -> Arc<DenselySampledSpectrumBuffer> {
|
pub fn lookup_spectrum(s: &Spectrum) -> Arc<DenselySampledSpectrum> {
|
||||||
let cache = &SPECTRUM_CACHE;
|
let cache = &SPECTRUM_CACHE;
|
||||||
let dense_spectrum = DenselySampledSpectrumBuffer::from_spectrum(s);
|
let dense_spectrum = DenselySampledSpectrum::from_spectrum(s);
|
||||||
cache.lookup(dense_spectrum).into()
|
cache.lookup(dense_spectrum).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Placeholders for non-area lights that never inspect these arguments.
|
// Placeholders for non-area lights that never inspect these arguments.
|
||||||
// TODO: refactor each light's create to only take what it actually needs,
|
// TODO: refactor each light to only take what it actually needs,
|
||||||
// then delete these.
|
// then delete this bullshit
|
||||||
fn dummy_shape() -> Shape {
|
fn dummy_shape() -> Shape {
|
||||||
Shape::default()
|
Shape::default()
|
||||||
}
|
}
|
||||||
|
|
@ -29,7 +29,6 @@ fn dummy_alpha() -> FloatTexture {
|
||||||
FloatTexture::default()
|
FloatTexture::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a non-area light from a scene file directive.
|
|
||||||
pub fn create_light(
|
pub fn create_light(
|
||||||
name: &str,
|
name: &str,
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
|
|
@ -110,8 +109,7 @@ pub fn create_light(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a diffuse area light bound to a specific shape.
|
/// Create a diffuse area light bound to a specific shape
|
||||||
/// Called once per sub-shape (e.g. once per triangle in a mesh).
|
|
||||||
pub fn create_area_light(
|
pub fn create_area_light(
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
medium: Option<Medium>,
|
medium: Option<Medium>,
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,9 @@
|
||||||
use crate::spectra::dense::DenselySampledSpectrumBuffer;
|
|
||||||
use shared::core::geometry::{Bounds3f, Point3i};
|
use shared::core::geometry::{Bounds3f, Point3i};
|
||||||
use shared::core::medium::{GridMedium, HGPhaseFunction, HomogeneousMedium, RGBGridMedium};
|
use shared::core::medium::{GridMedium, HGPhaseFunction, HomogeneousMedium, RGBGridMedium};
|
||||||
use shared::core::spectrum::{Spectrum, SpectrumTrait};
|
use shared::core::spectrum::{Spectrum, SpectrumTrait};
|
||||||
use shared::spectra::{RGBIlluminantSpectrum, RGBUnboundedSpectrum};
|
use shared::spectra::{RGBIlluminantSpectrum, RGBUnboundedSpectrum, DenselySampledSpectrum};
|
||||||
use shared::utils::Transform;
|
|
||||||
use shared::utils::containers::SampledGrid;
|
use shared::utils::containers::SampledGrid;
|
||||||
use shared::{Float, core::medium::MajorantGrid};
|
use shared::{Float, Transform, core::medium::MajorantGrid};
|
||||||
|
|
||||||
pub struct MajorantGridHost {
|
|
||||||
pub device: MajorantGrid,
|
|
||||||
voxels: Vec<Float>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MajorantGridHost {
|
|
||||||
pub fn new(bounds: Bounds3f, res: Point3i) -> Self {
|
|
||||||
let n = (res.x() * res.y() * res.z()) as usize;
|
|
||||||
let voxels = vec![0.0; n];
|
|
||||||
|
|
||||||
let device = MajorantGrid {
|
|
||||||
bounds,
|
|
||||||
res,
|
|
||||||
voxels: std::ptr::null_mut(),
|
|
||||||
n_voxels: n as u32,
|
|
||||||
};
|
|
||||||
|
|
||||||
Self { device, voxels }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait RGBGridMediumCreator {
|
pub trait RGBGridMediumCreator {
|
||||||
fn new(
|
fn new(
|
||||||
|
|
@ -53,7 +30,7 @@ impl RGBGridMediumCreator for RGBGridMedium {
|
||||||
le_grid: SampledGrid<RGBIlluminantSpectrum>,
|
le_grid: SampledGrid<RGBIlluminantSpectrum>,
|
||||||
le_scale: Float,
|
le_scale: Float,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut majorant_grid = MajorantGridHost::new(*bounds, Point3i::new(16, 16, 16)).device;
|
let mut majorant_grid = MajorantGrid::new(*bounds, Point3i::new(16, 16, 16));
|
||||||
for z in 0..majorant_grid.res.x() {
|
for z in 0..majorant_grid.res.x() {
|
||||||
for y in 0..majorant_grid.res.y() {
|
for y in 0..majorant_grid.res.y() {
|
||||||
for x in 0..majorant_grid.res.x() {
|
for x in 0..majorant_grid.res.x() {
|
||||||
|
|
@ -109,15 +86,15 @@ impl GridMediumCreator for GridMedium {
|
||||||
le: &Spectrum,
|
le: &Spectrum,
|
||||||
le_scale: SampledGrid<Float>,
|
le_scale: SampledGrid<Float>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut sigma_a_spec = DenselySampledSpectrumBuffer::from_spectrum(sigma_a);
|
let mut sigma_a_spec = DenselySampledSpectrum::from_spectrum(sigma_a);
|
||||||
let mut sigma_s_spec = DenselySampledSpectrumBuffer::from_spectrum(sigma_s);
|
let mut sigma_s_spec = DenselySampledSpectrum::from_spectrum(sigma_s);
|
||||||
|
|
||||||
sigma_a_spec.scale(sigma_scale);
|
sigma_a_spec.scale(sigma_scale);
|
||||||
sigma_s_spec.scale(sigma_scale);
|
sigma_s_spec.scale(sigma_scale);
|
||||||
|
|
||||||
let le_spec = DenselySampledSpectrumBuffer::from_spectrum(le);
|
let le_spec = DenselySampledSpectrum::from_spectrum(le);
|
||||||
|
|
||||||
let mut majorant_grid = MajorantGridHost::new(*bounds, Point3i::new(16, 16, 16)).device;
|
let mut majorant_grid = MajorantGrid::new(*bounds, Point3i::new(16, 16, 16)).device;
|
||||||
let is_emissive = if temperature_grid.is_some() {
|
let is_emissive = if temperature_grid.is_some() {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -169,9 +146,9 @@ impl HomogeneousMediumCreator for HomogeneousMedium {
|
||||||
le_scale: Float,
|
le_scale: Float,
|
||||||
g: Float,
|
g: Float,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut sigma_a_spec = DenselySampledSpectrumBuffer::from_spectrum(&sigma_a);
|
let mut sigma_a_spec = DenselySampledSpectrum::from_spectrum(&sigma_a);
|
||||||
let mut sigma_s_spec = DenselySampledSpectrumBuffer::from_spectrum(&sigma_s);
|
let mut sigma_s_spec = DenselySampledSpectrum::from_spectrum(&sigma_s);
|
||||||
let mut le_spec = DenselySampledSpectrumBuffer::from_spectrum(&le);
|
let mut le_spec = DenselySampledSpectrum::from_spectrum(&le);
|
||||||
|
|
||||||
sigma_a_spec.scale(sigma_scale);
|
sigma_a_spec.scale(sigma_scale);
|
||||||
sigma_s_spec.scale(sigma_scale);
|
sigma_s_spec.scale(sigma_scale);
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use crate::core::shape::{ShapeFactory, ShapeWithContext};
|
||||||
use crate::core::texture::{FloatTexture, SpectrumTexture};
|
use crate::core::texture::{FloatTexture, SpectrumTexture};
|
||||||
use crate::utils::parallel::{run_async, AsyncJob};
|
use crate::utils::parallel::{run_async, AsyncJob};
|
||||||
use crate::utils::parameters::{NamedTextures, ParameterDictionary, TextureParameterDictionary};
|
use crate::utils::parameters::{NamedTextures, ParameterDictionary, TextureParameterDictionary};
|
||||||
use crate::utils::{resolve_filename, Upload};
|
use crate::utils::resolve_filename;
|
||||||
use crate::{Arena, FileLoc};
|
use crate::{Arena, FileLoc};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
@ -121,7 +121,7 @@ impl BasicScene {
|
||||||
sampler: SceneEntity,
|
sampler: SceneEntity,
|
||||||
integ: SceneEntity,
|
integ: SceneEntity,
|
||||||
accel: SceneEntity,
|
accel: SceneEntity,
|
||||||
arena: Arc<Arena>,
|
arena: &Arena,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
*self.integrator.lock() = Some(integ);
|
*self.integrator.lock() = Some(integ);
|
||||||
*self.accelerator.lock() = Some(accel);
|
*self.accelerator.lock() = Some(accel);
|
||||||
|
|
@ -130,7 +130,7 @@ impl BasicScene {
|
||||||
*self.film_colorspace.lock() = Some(Arc::clone(cs));
|
*self.film_colorspace.lock() = Some(Arc::clone(cs));
|
||||||
}
|
}
|
||||||
|
|
||||||
let filter = Filter::create(&filter.name, &filter.parameters, &filter.loc)
|
let filter = Filter::create(&filter.name, &filter.parameters, &filter.loc, &arena)
|
||||||
.map_err(|e| anyhow!("Failed to create filter: {}", e))?;
|
.map_err(|e| anyhow!("Failed to create filter: {}", e))?;
|
||||||
let shutter_close = camera.base.parameters.get_one_float("shutterclose", 1.)?;
|
let shutter_close = camera.base.parameters.get_one_float("shutterclose", 1.)?;
|
||||||
let shutter_open = camera.base.parameters.get_one_float("shutteropen", 0.)?;
|
let shutter_open = camera.base.parameters.get_one_float("shutteropen", 0.)?;
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,19 @@
|
||||||
use crate::spectra::{DenselySampledSpectrumBuffer, cie_y};
|
use crate::spectra::cie_y;
|
||||||
use crate::utils::containers::InternCache;
|
use crate::utils::containers::InternCache;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
use shared::core::spectrum::Spectrum;
|
use shared::core::spectrum::Spectrum;
|
||||||
|
use shared::spectra::DenselySampledSpectrum;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
pub static SPECTRUM_CACHE: LazyLock<InternCache<DenselySampledSpectrumBuffer>> =
|
pub static SPECTRUM_CACHE: LazyLock<InternCache<DenselySampledSpectrum>> =
|
||||||
LazyLock::new(InternCache::new);
|
LazyLock::new(InternCache::new);
|
||||||
|
|
||||||
pub static SPECTRUM_FILE_CACHE: LazyLock<Mutex<HashMap<String, Spectrum>>> =
|
pub static SPECTRUM_FILE_CACHE: LazyLock<Mutex<HashMap<String, Spectrum>>> =
|
||||||
LazyLock::new(|| Mutex::new(HashMap::new()));
|
LazyLock::new(|| Mutex::new(HashMap::new()));
|
||||||
|
|
||||||
pub fn get_spectrum_cache() -> &'static InternCache<DenselySampledSpectrumBuffer> {
|
pub fn get_spectrum_cache() -> &'static InternCache<DenselySampledSpectrum> {
|
||||||
&SPECTRUM_CACHE
|
&SPECTRUM_CACHE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,21 @@
|
||||||
use crate::textures::*;
|
use crate::textures::*;
|
||||||
use crate::utils::mipmap::MIPMap;
|
use crate::utils::mipmap::{MIPMapFilterOptions, MIPMap};
|
||||||
use crate::utils::mipmap::MIPMapFilterOptions;
|
use crate::utils::TextureParameterDictionary;
|
||||||
use crate::utils::{Arena, FileLoc, TextureParameterDictionary};
|
use crate::{Arena, FileLoc};
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{anyhow, Result};
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
use shared::Float;
|
|
||||||
use shared::core::color::ColorEncoding;
|
use shared::core::color::ColorEncoding;
|
||||||
use shared::core::geometry::Vector3f;
|
use shared::core::geometry::Vector3f;
|
||||||
use shared::core::image::WrapMode;
|
use shared::core::image::WrapMode;
|
||||||
use shared::core::texture::SpectrumType;
|
use shared::core::texture::SpectrumType;
|
||||||
use shared::core::texture::{
|
use shared::core::texture::{
|
||||||
CylindricalMapping, PlanarMapping, SphericalMapping, TextureEvalContext, TextureMapping2D,
|
CylindricalMapping, PlanarMapping, SphericalMapping, TextureEvalContext, TextureMapping2D,
|
||||||
UVMapping,
|
UVMapping, GPUFloatTexture, GPUSpectrumTexture
|
||||||
};
|
};
|
||||||
use shared::spectra::{SampledSpectrum, SampledWavelengths};
|
use shared::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use shared::textures::*;
|
use shared::textures::*;
|
||||||
use shared::utils::Transform;
|
use shared::utils::Transform;
|
||||||
|
use shared::Float;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex, OnceLock};
|
use std::sync::{Arc, Mutex, OnceLock};
|
||||||
|
|
||||||
|
|
@ -29,20 +29,30 @@ pub trait SpectrumTextureTrait {
|
||||||
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum;
|
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch(FloatTextureTrait)]
|
#[derive(Clone, Debug)]
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum FloatTexture {
|
pub enum FloatTexture {
|
||||||
Constant(FloatConstantTexture),
|
Constant(FloatConstantTexture),
|
||||||
Image(FloatImageTexture),
|
|
||||||
Mix(FloatMixTexture),
|
|
||||||
DirectionMix(FloatDirectionMixTexture),
|
|
||||||
Scaled(FloatScaledTexture),
|
|
||||||
Bilerp(FloatBilerpTexture),
|
|
||||||
Checkerboard(FloatCheckerboardTexture),
|
Checkerboard(FloatCheckerboardTexture),
|
||||||
Dots(FloatDotsTexture),
|
Dots(FloatDotsTexture),
|
||||||
FBm(FBmTexture),
|
FBm(FBmTexture),
|
||||||
Windy(WindyTexture),
|
Windy(WindyTexture),
|
||||||
Wrinkled(WrinkledTexture),
|
Wrinkled(WrinkledTexture),
|
||||||
|
Mix(FloatMixTexture),
|
||||||
|
DirectionMix(FloatDirectionMixTexture),
|
||||||
|
// #[device(custom = "upload_image", variant_type = "GPUFloatImageTexture")]
|
||||||
|
Image(FloatImageTexture),
|
||||||
|
Bilerp(FloatBilerpTexture),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatTexture {
|
||||||
|
fn upload_image(inner: &FloatImageTexture, _arena: &Arena) -> GPUFloatImageTexture {
|
||||||
|
GPUFloatImageTexture {
|
||||||
|
mapping: inner.base.mapping,
|
||||||
|
tex_obj: inner.base.mipmap.texture_object(),
|
||||||
|
scale: inner.base.scale,
|
||||||
|
invert: inner.base.invert,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FloatTexture {
|
impl Default for FloatTexture {
|
||||||
|
|
@ -95,21 +105,43 @@ impl FloatTexture {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
#[enum_dispatch(SpectrumTextureTrait)]
|
|
||||||
pub enum SpectrumTexture {
|
pub enum SpectrumTexture {
|
||||||
// RGBConstant(RGBConstantTexture),
|
|
||||||
// RGBReflectanceConstant(RGBReflectanceConstantTexture),
|
|
||||||
Constant(SpectrumConstantTexture),
|
Constant(SpectrumConstantTexture),
|
||||||
Bilerp(SpectrumBilerpTexture),
|
|
||||||
Checkerboard(SpectrumCheckerboardTexture),
|
Checkerboard(SpectrumCheckerboardTexture),
|
||||||
|
Dots(SpectrumDotsTexture),
|
||||||
|
// #[device(
|
||||||
|
// custom = "upload_spectrum_image",
|
||||||
|
// variant_type = "GPUSpectrumImageTexture"
|
||||||
|
// )]
|
||||||
Image(SpectrumImageTexture),
|
Image(SpectrumImageTexture),
|
||||||
|
Bilerp(SpectrumBilerpTexture),
|
||||||
|
Scaled(SpectrumScaledTexture),
|
||||||
Marble(MarbleTexture),
|
Marble(MarbleTexture),
|
||||||
Mix(SpectrumMixTexture),
|
Mix(SpectrumMixTexture),
|
||||||
DirectionMix(SpectrumDirectionMixTexture),
|
DirectionMix(SpectrumDirectionMixTexture),
|
||||||
Dots(SpectrumDotsTexture),
|
}
|
||||||
// Ptex(SpectrumPtexTexture),
|
|
||||||
Scaled(SpectrumScaledTexture),
|
impl SpectrumTexture {
|
||||||
|
fn upload_spectrum_image(
|
||||||
|
inner: &SpectrumImageTexture,
|
||||||
|
arena: &Arena,
|
||||||
|
) -> GPUSpectrumImageTexture {
|
||||||
|
let tex_obj = arena.get_texture_object(&inner.base.mipmap);
|
||||||
|
GPUSpectrumImageTexture {
|
||||||
|
mapping: inner.base.mapping,
|
||||||
|
tex_obj,
|
||||||
|
scale: inner.base.scale,
|
||||||
|
invert: inner.base.invert,
|
||||||
|
is_single_channel: inner.base.mipmap.is_single_channel(),
|
||||||
|
color_space: inner
|
||||||
|
.base
|
||||||
|
.mipmap
|
||||||
|
.color_space
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(crate::spectra::default_colorspace),
|
||||||
|
spectrum_type: inner.spectrum_type,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait CreateSpectrumTexture {
|
pub trait CreateSpectrumTexture {
|
||||||
|
|
|
||||||
|
|
@ -8,44 +8,6 @@ use shared::spectra::RGBColorSpace;
|
||||||
use shared::utils::AnimatedTransform;
|
use shared::utils::AnimatedTransform;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
pub struct GBufferFilmHost {
|
|
||||||
pub device: GBufferFilm,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GBufferFilmHost {
|
|
||||||
pub fn new(
|
|
||||||
base: &FilmBase,
|
|
||||||
output_from_render: &AnimatedTransform,
|
|
||||||
apply_inverse: bool,
|
|
||||||
colorspace: &RGBColorSpace,
|
|
||||||
max_component_value: Float,
|
|
||||||
write_fp16: bool,
|
|
||||||
) -> Self {
|
|
||||||
assert!(!base.pixel_bounds.is_empty());
|
|
||||||
let sensor_ptr = base.sensor;
|
|
||||||
if sensor_ptr.is_null() {
|
|
||||||
panic!("Film must have a sensor");
|
|
||||||
}
|
|
||||||
let sensor = &*sensor_ptr;
|
|
||||||
let output_rgbf_from_sensor_rgb = colorspace.rgb_from_xyz * sensor.xyz_from_sensor_rgb;
|
|
||||||
let filter_integral = base.filter.integral();
|
|
||||||
let pixels = Array2D::new(base.pixel_bounds);
|
|
||||||
|
|
||||||
let device = GBufferFilm {
|
|
||||||
base: base.clone(),
|
|
||||||
output_from_render: *output_from_render,
|
|
||||||
apply_inverse,
|
|
||||||
pixels: pixels.device,
|
|
||||||
colorspace: colorspace.clone(),
|
|
||||||
max_component_value,
|
|
||||||
write_fp16,
|
|
||||||
filter_integral,
|
|
||||||
output_rgbf_from_sensor_rgb,
|
|
||||||
};
|
|
||||||
|
|
||||||
Self { device }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CreateFilm for GBufferFilm {
|
impl CreateFilm for GBufferFilm {
|
||||||
fn create(
|
fn create(
|
||||||
|
|
@ -85,7 +47,7 @@ impl CreateFilm for GBufferFilm {
|
||||||
.into());
|
.into());
|
||||||
};
|
};
|
||||||
|
|
||||||
let film = GBufferFilmHost::new(
|
let film = GBufferFilm::new(
|
||||||
&film_base,
|
&film_base,
|
||||||
&output_from_render,
|
&output_from_render,
|
||||||
apply_inverse,
|
apply_inverse,
|
||||||
|
|
@ -94,6 +56,6 @@ impl CreateFilm for GBufferFilm {
|
||||||
write_fp16,
|
write_fp16,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(Film::GBuffer(film.device))
|
Ok(Film::GBuffer(film))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,58 +8,6 @@ use shared::core::film::{Film, FilmBase, RGBFilm, RGBPixel};
|
||||||
use shared::core::filter::FilterTrait;
|
use shared::core::filter::FilterTrait;
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::RGBColorSpace;
|
||||||
|
|
||||||
struct RGBFilmStorage {
|
|
||||||
pixels: Array2D<RGBPixel>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct RGBFilmHost {
|
|
||||||
pub device: RGBFilm,
|
|
||||||
storage: RGBFilmStorage,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RGBFilmHost {
|
|
||||||
pub fn new(
|
|
||||||
base: FilmBase,
|
|
||||||
colorspace: &RGBColorSpace,
|
|
||||||
max_component_value: Float,
|
|
||||||
write_fp16: bool,
|
|
||||||
) -> Self {
|
|
||||||
let sensor_ptr = base.sensor;
|
|
||||||
if sensor_ptr.is_null() {
|
|
||||||
panic!("Film must have a sensor");
|
|
||||||
}
|
|
||||||
let sensor = &*sensor_ptr;
|
|
||||||
let filter_integral = base.filter.integral();
|
|
||||||
let sensor_matrix = sensor.xyz_from_sensor_rgb;
|
|
||||||
let output_rgbf_from_sensor_rgb = colorspace.rgb_from_xyz * sensor_matrix;
|
|
||||||
|
|
||||||
let width = base.pixel_bounds.p_max.x() - base.pixel_bounds.p_min.x();
|
|
||||||
let height = base.pixel_bounds.p_max.y() - base.pixel_bounds.p_min.y();
|
|
||||||
let count = (width * height) as usize;
|
|
||||||
|
|
||||||
let mut pixel_vec = Vec::with_capacity(count);
|
|
||||||
for _ in 0..count {
|
|
||||||
pixel_vec.push(RGBPixel::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
let pixels: Array2D<RGBPixel> = Array2D::new(base.pixel_bounds);
|
|
||||||
|
|
||||||
let device_pixels = pixels.device.clone();
|
|
||||||
let storage = RGBFilmStorage { pixels };
|
|
||||||
|
|
||||||
let device = RGBFilm {
|
|
||||||
base,
|
|
||||||
max_component_value,
|
|
||||||
write_fp16,
|
|
||||||
filter_integral,
|
|
||||||
output_rgbf_from_sensor_rgb,
|
|
||||||
pixels: device_pixels,
|
|
||||||
};
|
|
||||||
|
|
||||||
Self { device, storage }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CreateFilm for RGBFilm {
|
impl CreateFilm for RGBFilm {
|
||||||
fn create(
|
fn create(
|
||||||
params: &ParameterDictionary,
|
params: &ParameterDictionary,
|
||||||
|
|
@ -74,7 +22,7 @@ impl CreateFilm for RGBFilm {
|
||||||
let write_fp16 = params.get_one_bool("savefp16", true)?;
|
let write_fp16 = params.get_one_bool("savefp16", true)?;
|
||||||
let sensor = PixelSensor::create(params, colorspace.clone(), exposure_time, loc)?;
|
let sensor = PixelSensor::create(params, colorspace.clone(), exposure_time, loc)?;
|
||||||
let film_base = FilmBase::create(params, filter, Some(&sensor.device()), loc)?;
|
let film_base = FilmBase::create(params, filter, Some(&sensor.device()), loc)?;
|
||||||
let film = RGBFilmHost::new(film_base, &colorspace, max_component_value, write_fp16);
|
let film = RGBFilm::new(film_base, &colorspace, max_component_value, write_fp16);
|
||||||
Ok(Film::RGB(film.device))
|
Ok(Film::RGB(film))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,91 +1,15 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::core::film::{CreateFilmBase, PixelSensor};
|
use crate::core::film::{CreateFilmBase, PixelSensor};
|
||||||
use crate::utils::containers::Array2D;
|
|
||||||
use crate::{Arena, FileLoc, ParameterDictionary};
|
use crate::{Arena, FileLoc, ParameterDictionary};
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{Result, anyhow};
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
use shared::core::camera::CameraTransform;
|
use shared::core::camera::CameraTransform;
|
||||||
use shared::core::film::{FilmBase, SpectralFilm, SpectralPixel};
|
use shared::core::film::{FilmBase, SpectralFilm};
|
||||||
use shared::core::filter::FilterTrait;
|
use shared::core::filter::FilterTrait;
|
||||||
use shared::spectra::{LAMBDA_MAX, LAMBDA_MIN, RGBColorSpace};
|
use shared::spectra::{LAMBDA_MAX, LAMBDA_MIN};
|
||||||
use shared::utils::AtomicFloat;
|
|
||||||
use shared::utils::containers::DeviceArray2D;
|
|
||||||
use shared::utils::math::SquareMatrix;
|
use shared::utils::math::SquareMatrix;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
struct SpectralFilmStorage {
|
|
||||||
pixels: DeviceArray2D<SpectralPixel>,
|
|
||||||
bucket_sums: Vec<f64>,
|
|
||||||
weight_sums: Vec<f64>,
|
|
||||||
bucket_splats: Vec<AtomicFloat>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SpectralFilmHost {
|
|
||||||
pub device: SpectralFilm,
|
|
||||||
storage: Arc<SpectralFilmStorage>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpectralFilmHost {
|
|
||||||
pub fn new(
|
|
||||||
base: &FilmBase,
|
|
||||||
lambda_min: Float,
|
|
||||||
lambda_max: Float,
|
|
||||||
n_buckets: usize,
|
|
||||||
colorspace: &RGBColorSpace,
|
|
||||||
max_component_value: Float,
|
|
||||||
write_fp16: bool,
|
|
||||||
) -> Self {
|
|
||||||
let n_pixels = base.pixel_bounds.area() as usize;
|
|
||||||
let total_buckets = n_pixels * n_buckets;
|
|
||||||
|
|
||||||
let bucket_sums = vec![0.0; total_buckets];
|
|
||||||
let weight_sums = vec![0.0; total_buckets];
|
|
||||||
|
|
||||||
let mut bucket_splats = Vec::with_capacity(total_buckets);
|
|
||||||
for _ in 0..total_buckets {
|
|
||||||
bucket_splats.push(AtomicFloat::new(0.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut pixels = Array2D::<SpectralPixel>::new(base.pixel_bounds);
|
|
||||||
|
|
||||||
for i in 0..n_pixels {
|
|
||||||
let pixel = pixels.get_linear_mut(i);
|
|
||||||
pixel.bucket_offset = i * n_buckets;
|
|
||||||
}
|
|
||||||
|
|
||||||
let storage = Arc::new(SpectralFilmStorage {
|
|
||||||
pixels: pixels.device,
|
|
||||||
bucket_sums,
|
|
||||||
weight_sums,
|
|
||||||
bucket_splats,
|
|
||||||
});
|
|
||||||
|
|
||||||
let device = SpectralFilm {
|
|
||||||
base: *base,
|
|
||||||
colorspace: colorspace.clone(),
|
|
||||||
lambda_min,
|
|
||||||
lambda_max,
|
|
||||||
n_buckets,
|
|
||||||
max_component_value,
|
|
||||||
write_fp16,
|
|
||||||
filter_integral: base.filter.integral(),
|
|
||||||
output_rgbf_from_sensor_rgb: SquareMatrix::identity(),
|
|
||||||
|
|
||||||
pixels: DeviceArray2D {
|
|
||||||
values: pixels.values.as_mut_ptr(),
|
|
||||||
extent: base.pixel_bounds,
|
|
||||||
stride: base.pixel_bounds.p_max.x() - base.pixel_bounds.p_min.x(),
|
|
||||||
},
|
|
||||||
|
|
||||||
bucket_sums: storage.bucket_sums.as_ptr() as *mut f64,
|
|
||||||
weight_sums: storage.weight_sums.as_ptr() as *mut f64,
|
|
||||||
bucket_splats: storage.bucket_splats.as_ptr() as *mut AtomicFloat,
|
|
||||||
};
|
|
||||||
|
|
||||||
Self { device, storage }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CreateFilm for SpectralFilm {
|
impl CreateFilm for SpectralFilm {
|
||||||
fn create(
|
fn create(
|
||||||
|
|
@ -96,6 +20,7 @@ impl CreateFilm for SpectralFilm {
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
_arena: &Arena,
|
_arena: &Arena,
|
||||||
) -> Result<Film> {
|
) -> Result<Film> {
|
||||||
|
// Missing default illuminant, use srgb
|
||||||
let colorspace = params.color_space.as_ref().unwrap();
|
let colorspace = params.color_space.as_ref().unwrap();
|
||||||
let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY)?;
|
let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY)?;
|
||||||
let write_fp16 = params.get_one_bool("savefp16", true)?;
|
let write_fp16 = params.get_one_bool("savefp16", true)?;
|
||||||
|
|
@ -117,7 +42,7 @@ impl CreateFilm for SpectralFilm {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let film = SpectralFilmHost::new(
|
let film = SpectralFilm::new(
|
||||||
&film_base,
|
&film_base,
|
||||||
lambda_min,
|
lambda_min,
|
||||||
lambda_max,
|
lambda_max,
|
||||||
|
|
@ -127,6 +52,6 @@ impl CreateFilm for SpectralFilm {
|
||||||
write_fp16,
|
write_fp16,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(Film::Spectral(film.device))
|
Ok(Film::Spectral(film))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
pub trait BoxFilterCreator {}
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
use crate::core::filter::CreateFilterSampler;
|
|
||||||
use shared::Float;
|
|
||||||
use shared::core::filter::FilterSampler;
|
|
||||||
use shared::core::geometry::{Point2f, Vector2f};
|
|
||||||
use shared::filters::GaussianFilter;
|
|
||||||
use shared::utils::math::gaussian;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Device)]
|
|
||||||
#[device(name = "GaussianFilter")]
|
|
||||||
pub struct GaussianFilterHost {
|
|
||||||
pub radius: Vector2f,
|
|
||||||
pub sigma: Float,
|
|
||||||
pub exp_x: Float,
|
|
||||||
pub exp_y: Float,
|
|
||||||
#[device(flatten)]
|
|
||||||
pub sampler: FilterSampler,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GaussianFilterHost {
|
|
||||||
pub fn new(radius: Vector2f, sigma: Float) -> Self {
|
|
||||||
let exp_x = gaussian(radius.x(), 0.0, sigma);
|
|
||||||
let exp_y = gaussian(radius.y(), 0.0, sigma);
|
|
||||||
let sampler = FilterSampler::new(radius, move |p: Point2f| {
|
|
||||||
let gx = (gaussian(p.x(), 0.0, sigma) - exp_x).max(0.0);
|
|
||||||
let gy = (gaussian(p.y(), 0.0, sigma) - exp_y).max(0.0);
|
|
||||||
gx * gy
|
|
||||||
});
|
|
||||||
Self { radius, sigma, exp_x, exp_y, sampler }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
use crate::core::filter::CreateFilterSampler;
|
|
||||||
use rand::Rng;
|
|
||||||
use shared::Float;
|
|
||||||
use shared::core::filter::FilterSampler;
|
|
||||||
use shared::core::geometry::{Point2f, Vector2f};
|
|
||||||
use shared::filters::LanczosSincFilter;
|
|
||||||
use shared::utils::math::{lerp, windowed_sinc};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Device)]
|
|
||||||
#[device(name = "LanczosSincFilter")]
|
|
||||||
pub struct LanczosSincFilterHost {
|
|
||||||
pub radius: Vector2f,
|
|
||||||
pub tau: Float,
|
|
||||||
#[device(flatten)]
|
|
||||||
pub sampler: FilterSampler,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LanczosSincFilterHost {
|
|
||||||
pub fn new(radius: Vector2f, tau: Float) -> Self {
|
|
||||||
let sampler = FilterSampler::new(radius, move |p: Point2f| {
|
|
||||||
windowed_sinc(p.x(), radius.x(), tau) * windowed_sinc(p.y(), radius.y(), tau)
|
|
||||||
});
|
|
||||||
Self { radius, tau, sampler }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn windowed_sinc(x: Float, radius: Float, tau: Float) -> Float {
|
|
||||||
use std::f32::consts::PI;
|
|
||||||
let x = x.abs();
|
|
||||||
if x > radius {
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
if x < 1e-5 {
|
|
||||||
1.0
|
|
||||||
} else {
|
|
||||||
let xpi = x * PI;
|
|
||||||
let xpit = xpi * tau;
|
|
||||||
(xpi.sin() / xpi) * (xpit.sin() / xpit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
use crate::core::filter::CreateFilterSampler;
|
|
||||||
use shared::Float;
|
|
||||||
use shared::core::filter::FilterSampler;
|
|
||||||
use shared::core::geometry::{Point2f, Vector2f};
|
|
||||||
use shared::filters::MitchellFilter;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Device)]
|
|
||||||
#[device(name = "MitchellFilter")]
|
|
||||||
pub struct MitchellFilterHost {
|
|
||||||
pub radius: Vector2f,
|
|
||||||
pub b: Float,
|
|
||||||
pub c: Float,
|
|
||||||
#[device(flatten)]
|
|
||||||
pub sampler: FilterSampler,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MitchellFilterHost {
|
|
||||||
pub fn new(radius: Vector2f, b: Float, c: Float) -> Self {
|
|
||||||
let sampler = FilterSampler::new(radius, move |p: Point2f| {
|
|
||||||
mitchell_1d(p.x() / radius.x(), b, c) * mitchell_1d(p.y() / radius.y(), b, c)
|
|
||||||
});
|
|
||||||
Self { radius, b, c, sampler }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mitchell_1d(x: Float, b: Float, c: Float) -> Float {
|
|
||||||
let x = (2.0 * x).abs();
|
|
||||||
if x <= 1.0 {
|
|
||||||
((12.0 - 9.0 * b - 6.0 * c) * x * x * x
|
|
||||||
+ (-18.0 + 12.0 * b + 6.0 * c) * x * x
|
|
||||||
+ (6.0 - 2.0 * b))
|
|
||||||
/ 6.0
|
|
||||||
} else if x <= 2.0 {
|
|
||||||
((-b - 6.0 * c) * x * x * x
|
|
||||||
+ (6.0 * b + 30.0 * c) * x * x
|
|
||||||
+ (-12.0 * b - 48.0 * c) * x
|
|
||||||
+ (8.0 * b + 24.0 * c))
|
|
||||||
/ 6.0
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
pub mod boxf;
|
|
||||||
pub mod gaussian;
|
|
||||||
pub mod lanczos;
|
|
||||||
pub mod mitchell;
|
|
||||||
pub mod triangle;
|
|
||||||
|
|
||||||
pub use boxf::*;
|
|
||||||
pub use gaussian::*;
|
|
||||||
pub use lanczos::*;
|
|
||||||
pub use mitchell::*;
|
|
||||||
// pub use triangle::*;
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::core::color::RGBToSpectrumTableData;
|
use crate::core::color::CreateRGBToSpectrumTable;
|
||||||
use shared::core::color::RES;
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
use shared::core::color::{RGBToSpectrumTable, RES};
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
use shared::PBRTOptions;
|
use shared::PBRTOptions;
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
|
|
@ -30,7 +30,6 @@ fn aligned_cast(bytes: &[u8]) -> &[Float] {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static SRGB_SCALE_BYTES: &[u8] = include_bytes!("../data/srgb_scale.dat");
|
static SRGB_SCALE_BYTES: &[u8] = include_bytes!("../data/srgb_scale.dat");
|
||||||
static SRGB_COEFFS_BYTES: &[u8] = include_bytes!("../data/srgb_coeffs.dat");
|
static SRGB_COEFFS_BYTES: &[u8] = include_bytes!("../data/srgb_coeffs.dat");
|
||||||
static DCI_P3_SCALE_BYTES: &[u8] = include_bytes!("../data/dcip3_scale.dat");
|
static DCI_P3_SCALE_BYTES: &[u8] = include_bytes!("../data/dcip3_scale.dat");
|
||||||
|
|
@ -40,7 +39,6 @@ static ACES_COEFFS_BYTES: &[u8] = include_bytes!("../data/aces_coeffs.dat");
|
||||||
static REC2020_SCALE_BYTES: &[u8] = include_bytes!("../data/rec2020_scale.dat");
|
static REC2020_SCALE_BYTES: &[u8] = include_bytes!("../data/rec2020_scale.dat");
|
||||||
static REC2020_COEFFS_BYTES: &[u8] = include_bytes!("../data/rec2020_coeffs.dat");
|
static REC2020_COEFFS_BYTES: &[u8] = include_bytes!("../data/rec2020_coeffs.dat");
|
||||||
|
|
||||||
|
|
||||||
fn strip_to_len(bytes: &[u8], expected_len: usize) -> &'static [Float] {
|
fn strip_to_len(bytes: &[u8], expected_len: usize) -> &'static [Float] {
|
||||||
let all: Vec<Float> = bytemuck::pod_collect_to_vec(bytes);
|
let all: Vec<Float> = bytemuck::pod_collect_to_vec(bytes);
|
||||||
let skip = all.len() - expected_len;
|
let skip = all.len() - expected_len;
|
||||||
|
|
@ -53,20 +51,24 @@ const COEFFS_LEN: usize = (RES * RES * RES) as usize * 3 * 3;
|
||||||
pub static SRGB_SCALE: Lazy<&[Float]> = Lazy::new(|| strip_to_len(SRGB_SCALE_BYTES, RES as usize));
|
pub static SRGB_SCALE: Lazy<&[Float]> = Lazy::new(|| strip_to_len(SRGB_SCALE_BYTES, RES as usize));
|
||||||
pub static SRGB_COEFFS: Lazy<&[Float]> = Lazy::new(|| strip_to_len(SRGB_COEFFS_BYTES, COEFFS_LEN));
|
pub static SRGB_COEFFS: Lazy<&[Float]> = Lazy::new(|| strip_to_len(SRGB_COEFFS_BYTES, COEFFS_LEN));
|
||||||
|
|
||||||
pub static DCI_P3_SCALE: Lazy<&[Float]> = Lazy::new(|| strip_to_len(DCI_P3_SCALE_BYTES, RES as usize));
|
pub static DCI_P3_SCALE: Lazy<&[Float]> =
|
||||||
pub static DCI_P3_COEFFS: Lazy<&[Float]> = Lazy::new(|| strip_to_len(DCI_P3_COEFFS_BYTES, COEFFS_LEN));
|
Lazy::new(|| strip_to_len(DCI_P3_SCALE_BYTES, RES as usize));
|
||||||
|
pub static DCI_P3_COEFFS: Lazy<&[Float]> =
|
||||||
|
Lazy::new(|| strip_to_len(DCI_P3_COEFFS_BYTES, COEFFS_LEN));
|
||||||
|
|
||||||
pub static ACES_SCALE: Lazy<&[Float]> = Lazy::new(|| strip_to_len(ACES_SCALE_BYTES, RES as usize));
|
pub static ACES_SCALE: Lazy<&[Float]> = Lazy::new(|| strip_to_len(ACES_SCALE_BYTES, RES as usize));
|
||||||
pub static ACES_COEFFS: Lazy<&[Float]> = Lazy::new(|| strip_to_len(ACES_COEFFS_BYTES, COEFFS_LEN));
|
pub static ACES_COEFFS: Lazy<&[Float]> = Lazy::new(|| strip_to_len(ACES_COEFFS_BYTES, COEFFS_LEN));
|
||||||
|
|
||||||
pub static REC2020_SCALE: Lazy<&[Float]> = Lazy::new(|| strip_to_len(REC2020_SCALE_BYTES, RES as usize));
|
pub static REC2020_SCALE: Lazy<&[Float]> =
|
||||||
pub static REC2020_COEFFS: Lazy<&[Float]> = Lazy::new(|| strip_to_len(REC2020_COEFFS_BYTES, COEFFS_LEN));
|
Lazy::new(|| strip_to_len(REC2020_SCALE_BYTES, RES as usize));
|
||||||
|
pub static REC2020_COEFFS: Lazy<&[Float]> =
|
||||||
|
Lazy::new(|| strip_to_len(REC2020_COEFFS_BYTES, COEFFS_LEN));
|
||||||
|
|
||||||
pub static SRGB_TABLE: Lazy<RGBToSpectrumTableData> =
|
pub static SRGB_TABLE: Lazy<RGBToSpectrumTable> =
|
||||||
Lazy::new(|| RGBToSpectrumTableData::new(SRGB_SCALE.to_vec(), SRGB_COEFFS.to_vec()));
|
Lazy::new(|| RGBToSpectrumTableData::new(SRGB_SCALE, SRGB_COEFFS));
|
||||||
pub static DCI_P3_TABLE: Lazy<RGBToSpectrumTableData> =
|
pub static DCI_P3_TABLE: Lazy<RGBToSpectrumTable> =
|
||||||
Lazy::new(|| RGBToSpectrumTableData::new(DCI_P3_SCALE.to_vec(), DCI_P3_COEFFS.to_vec()));
|
Lazy::new(|| RGBToSpectrumTableData::new(DCI_P3_SCALE.to_vec(), DCI_P3_COEFFS.to_vec()));
|
||||||
pub static REC2020_TABLE: Lazy<RGBToSpectrumTableData> =
|
pub static REC2020_TABLE: Lazy<RGBToSpectrumTable> =
|
||||||
Lazy::new(|| RGBToSpectrumTableData::new(REC2020_SCALE.to_vec(), REC2020_COEFFS.to_vec()));
|
Lazy::new(|| RGBToSpectrumTableData::new(REC2020_SCALE.to_vec(), REC2020_COEFFS.to_vec()));
|
||||||
pub static ACES_TABLE: Lazy<RGBToSpectrumTableData> =
|
pub static ACES_TABLE: Lazy<RGBToSpectrumTable> =
|
||||||
Lazy::new(|| RGBToSpectrumTableData::new(ACES_SCALE.to_vec(), ACES_COEFFS.to_vec()));
|
Lazy::new(|| RGBToSpectrumTableData::new(ACES_SCALE.to_vec(), ACES_COEFFS.to_vec()));
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
#![feature(f16)]
|
#![feature(f16)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub mod cameras;
|
|
||||||
pub mod core;
|
pub mod core;
|
||||||
pub mod films;
|
pub mod films;
|
||||||
pub mod filters;
|
pub mod filters;
|
||||||
|
|
@ -16,5 +15,4 @@ pub mod utils;
|
||||||
|
|
||||||
#[cfg(feature = "cuda")]
|
#[cfg(feature = "cuda")]
|
||||||
pub mod gpu;
|
pub mod gpu;
|
||||||
|
|
||||||
pub use utils::{Arena, FileLoc, ParameterDictionary};
|
pub use utils::{Arena, FileLoc, ParameterDictionary};
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,9 @@ use crate::core::image::{Image, ImageIO};
|
||||||
use crate::core::light::lookup_spectrum;
|
use crate::core::light::lookup_spectrum;
|
||||||
use crate::core::spectrum::spectrum_to_photometric;
|
use crate::core::spectrum::spectrum_to_photometric;
|
||||||
use crate::core::texture::FloatTexture;
|
use crate::core::texture::FloatTexture;
|
||||||
use crate::utils::{Arena, FileLoc, ParameterDictionary, Upload, resolve_filename};
|
use crate::utils::resolve_filename;
|
||||||
use anyhow::{Result, anyhow};
|
use crate::{Arena, FileLoc, ParameterDictionary};
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
use shared::core::geometry::Point2i;
|
use shared::core::geometry::Point2i;
|
||||||
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};
|
||||||
|
|
@ -110,7 +111,7 @@ pub fn create(
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (light_type, stored_alpha) = if is_constant_zero {
|
let (light_type, _) = if is_constant_zero {
|
||||||
(LightType::DeltaPosition, None)
|
(LightType::DeltaPosition, None)
|
||||||
} else {
|
} else {
|
||||||
(LightType::Area, Some(alpha))
|
(LightType::Area, Some(alpha))
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ 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 anyhow::Result;
|
use anyhow::Result;
|
||||||
use shared::Float;
|
|
||||||
use shared::core::geometry::{Point3f, VectorLike};
|
use shared::core::geometry::{Point3f, 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, MediumInterface};
|
||||||
|
|
@ -13,8 +12,9 @@ use shared::core::texture::SpectrumType;
|
||||||
use shared::lights::DistantLight;
|
use shared::lights::DistantLight;
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::RGBColorSpace;
|
||||||
use shared::utils::{Ptr, Transform};
|
use shared::utils::{Ptr, Transform};
|
||||||
|
use shared::Float;
|
||||||
|
|
||||||
pub trait CreateDistantLight {
|
trait CreateDistantLight {
|
||||||
fn new(render_from_light: Transform, le: Spectrum, scale: Float) -> Self;
|
fn new(render_from_light: Transform, le: Spectrum, scale: Float) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@ use crate::core::light::lookup_spectrum;
|
||||||
use crate::core::spectrum::spectrum_to_photometric;
|
use crate::core::spectrum::spectrum_to_photometric;
|
||||||
use crate::core::texture::FloatTexture;
|
use crate::core::texture::FloatTexture;
|
||||||
use crate::utils::sampling::PiecewiseConstant2D;
|
use crate::utils::sampling::PiecewiseConstant2D;
|
||||||
use crate::utils::{Arena, FileLoc, ParameterDictionary, Upload, resolve_filename};
|
use crate::utils::resolve_filename;
|
||||||
|
use crate::{Arena, FileLoc, ParameterDictionary};
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{Result, anyhow};
|
||||||
use shared::core::geometry::Point2i;
|
use shared::core::geometry::Point2i;
|
||||||
use shared::core::light::{Light, LightBase, LightType};
|
use shared::core::light::{Light, LightBase, LightType};
|
||||||
|
|
@ -80,7 +81,7 @@ pub fn create(
|
||||||
];
|
];
|
||||||
let t =
|
let t =
|
||||||
Transform::from_flat(&swap_yz).expect("Could not create transform for GoniometricLight");
|
Transform::from_flat(&swap_yz).expect("Could not create transform for GoniometricLight");
|
||||||
let final_render_from_light = render_from_light * t;
|
let _final_render_from_light = render_from_light * t;
|
||||||
|
|
||||||
let mi = match medium {
|
let mi = match medium {
|
||||||
Some(m) => {
|
Some(m) => {
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
use crate::core::image::{Image, ImageIO};
|
use crate::core::image::{HostImage, ImageIO};
|
||||||
use crate::core::light::lookup_spectrum;
|
use crate::core::light::lookup_spectrum;
|
||||||
use crate::core::spectrum::spectrum_to_photometric;
|
use crate::core::spectrum::spectrum_to_photometric;
|
||||||
use crate::spectra::get_spectra_context;
|
use crate::spectra::get_spectra_context;
|
||||||
|
use crate::utils::resolve_filename;
|
||||||
use crate::utils::sampling::{PiecewiseConstant2D, WindowedPiecewiseConstant2D};
|
use crate::utils::sampling::{PiecewiseConstant2D, WindowedPiecewiseConstant2D};
|
||||||
use crate::utils::{resolve_filename, FileLoc, ParameterDictionary, Upload};
|
use crate::{Arena, FileLoc, ParameterDictionary};
|
||||||
use crate::Arena;
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use shared::core::camera::CameraTransform;
|
use shared::core::camera::CameraTransform;
|
||||||
use shared::core::geometry::{cos_theta, Bounds2f, Frame, Point2f, Point2i, Point3f, VectorLike};
|
use shared::core::geometry::{cos_theta, Bounds2f, Frame, Point2f, Point2i, Point3f, VectorLike};
|
||||||
use shared::core::image::{DeviceImage, WrapMode};
|
use shared::core::image::WrapMode;
|
||||||
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};
|
||||||
use shared::core::spectrum::Spectrum;
|
use shared::core::spectrum::Spectrum;
|
||||||
|
|
@ -17,111 +17,10 @@ use shared::core::texture::SpectrumType;
|
||||||
use shared::lights::{ImageInfiniteLight, PortalInfiniteLight, UniformInfiniteLight};
|
use shared::lights::{ImageInfiniteLight, PortalInfiniteLight, UniformInfiniteLight};
|
||||||
use shared::spectra::{DenselySampledSpectrum, RGBColorSpace};
|
use shared::spectra::{DenselySampledSpectrum, RGBColorSpace};
|
||||||
use shared::utils::math::{equal_area_sphere_to_square, equal_area_square_to_sphere};
|
use shared::utils::math::{equal_area_sphere_to_square, equal_area_square_to_sphere};
|
||||||
use shared::utils::sampling::{DevicePiecewiseConstant2D, DeviceWindowedPiecewiseConstant2D};
|
use shared::utils::sampling::{PiecewiseConstant2D, WindowedPiecewiseConstant2D};
|
||||||
use shared::utils::{Ptr, Transform};
|
use shared::{Float, Ptr, Transform, PI};
|
||||||
use shared::{Float, PI};
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
pub trait CreateImageInfiniteLight {
|
|
||||||
fn new(
|
|
||||||
render_from_light: Transform,
|
|
||||||
scale: Float,
|
|
||||||
image: Ptr<DeviceImage>,
|
|
||||||
image_color_space: Ptr<RGBColorSpace>,
|
|
||||||
distrib: Ptr<DevicePiecewiseConstant2D>,
|
|
||||||
compensated_distrib: Ptr<DevicePiecewiseConstant2D>,
|
|
||||||
) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CreateImageInfiniteLight for ImageInfiniteLight {
|
|
||||||
fn new(
|
|
||||||
render_from_light: Transform,
|
|
||||||
scale: Float,
|
|
||||||
image: Ptr<DeviceImage>,
|
|
||||||
image_color_space: Ptr<RGBColorSpace>,
|
|
||||||
distrib: Ptr<DevicePiecewiseConstant2D>,
|
|
||||||
compensated_distrib: Ptr<DevicePiecewiseConstant2D>,
|
|
||||||
) -> Self {
|
|
||||||
let base = LightBase::new(
|
|
||||||
LightType::Infinite,
|
|
||||||
render_from_light,
|
|
||||||
MediumInterface::default(),
|
|
||||||
);
|
|
||||||
Self {
|
|
||||||
base,
|
|
||||||
image,
|
|
||||||
image_color_space,
|
|
||||||
scale,
|
|
||||||
distrib,
|
|
||||||
compensated_distrib,
|
|
||||||
scene_center: Point3f::default(),
|
|
||||||
scene_radius: 0.0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait CreatePortalInfiniteLight {
|
|
||||||
fn new(
|
|
||||||
render_from_light: Transform,
|
|
||||||
scale: Float,
|
|
||||||
image: Ptr<DeviceImage>,
|
|
||||||
image_color_space: Ptr<RGBColorSpace>,
|
|
||||||
portal: [Point3f; 4],
|
|
||||||
portal_frame: Frame,
|
|
||||||
distribution: Ptr<DeviceWindowedPiecewiseConstant2D>,
|
|
||||||
) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CreatePortalInfiniteLight for PortalInfiniteLight {
|
|
||||||
fn new(
|
|
||||||
render_from_light: Transform,
|
|
||||||
scale: Float,
|
|
||||||
image: Ptr<DeviceImage>,
|
|
||||||
image_color_space: Ptr<RGBColorSpace>,
|
|
||||||
portal: [Point3f; 4],
|
|
||||||
portal_frame: Frame,
|
|
||||||
distribution: Ptr<DeviceWindowedPiecewiseConstant2D>,
|
|
||||||
) -> Self {
|
|
||||||
let base = LightBase::new(
|
|
||||||
LightType::Infinite,
|
|
||||||
render_from_light,
|
|
||||||
MediumInterface::default(),
|
|
||||||
);
|
|
||||||
Self {
|
|
||||||
base,
|
|
||||||
image,
|
|
||||||
image_color_space,
|
|
||||||
scale,
|
|
||||||
portal,
|
|
||||||
portal_frame,
|
|
||||||
distribution: *distribution,
|
|
||||||
scene_center: Point3f::default(),
|
|
||||||
scene_radius: 0.0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait CreateUniformInfiniteLight {
|
|
||||||
fn new(render_from_light: Transform, scale: Float, lemit: Ptr<DenselySampledSpectrum>) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CreateUniformInfiniteLight for UniformInfiniteLight {
|
|
||||||
fn new(render_from_light: Transform, scale: Float, lemit: Ptr<DenselySampledSpectrum>) -> Self {
|
|
||||||
let base = LightBase::new(
|
|
||||||
LightType::Infinite,
|
|
||||||
render_from_light,
|
|
||||||
MediumInterface::default(),
|
|
||||||
);
|
|
||||||
Self {
|
|
||||||
base,
|
|
||||||
lemit,
|
|
||||||
scale,
|
|
||||||
scene_center: Point3f::default(),
|
|
||||||
scene_radius: 0.0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create(
|
pub fn create(
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
_medium: Option<Medium>,
|
_medium: Option<Medium>,
|
||||||
|
|
|
||||||
|
|
@ -6,10 +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 distant::CreateDistantLight;
|
|
||||||
pub use infinite::{
|
|
||||||
CreateImageInfiniteLight, CreatePortalInfiniteLight, CreateUniformInfiniteLight,
|
|
||||||
};
|
|
||||||
pub use point::CreatePointLight;
|
|
||||||
pub use spot::CreateSpotLight;
|
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,7 @@ use shared::core::spectrum::Spectrum;
|
||||||
use shared::core::texture::SpectrumType;
|
use shared::core::texture::SpectrumType;
|
||||||
use shared::lights::PointLight;
|
use shared::lights::PointLight;
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::RGBColorSpace;
|
||||||
use shared::utils::{Ptr, Transform};
|
use shared::{Float, PI, Ptr, Transform};
|
||||||
use shared::{Float, PI};
|
|
||||||
|
|
||||||
pub trait CreatePointLight {
|
pub trait CreatePointLight {
|
||||||
fn new(
|
fn new(
|
||||||
|
|
@ -20,6 +19,7 @@ pub trait CreatePointLight {
|
||||||
medium_interface: MediumInterface,
|
medium_interface: MediumInterface,
|
||||||
le: Spectrum,
|
le: Spectrum,
|
||||||
scale: Float,
|
scale: Float,
|
||||||
|
arena: &Arena,
|
||||||
) -> Self;
|
) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -29,6 +29,7 @@ impl CreatePointLight for PointLight {
|
||||||
medium_interface: MediumInterface,
|
medium_interface: MediumInterface,
|
||||||
le: Spectrum,
|
le: Spectrum,
|
||||||
scale: Float,
|
scale: Float,
|
||||||
|
arena: &Arena,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let base = LightBase::new(
|
let base = LightBase::new(
|
||||||
LightType::DeltaPosition,
|
LightType::DeltaPosition,
|
||||||
|
|
@ -36,12 +37,12 @@ impl CreatePointLight for PointLight {
|
||||||
medium_interface,
|
medium_interface,
|
||||||
);
|
);
|
||||||
let iemit = lookup_spectrum(&le);
|
let iemit = lookup_spectrum(&le);
|
||||||
let i = Ptr::from(&iemit.device());
|
let i = arena.alloc((*iemit).clone());
|
||||||
|
|
||||||
Self { base, scale, i }
|
Self { base, scale, i }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn create(
|
pub fn create(
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
medium: Option<Medium>,
|
medium: Option<Medium>,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ use crate::core::image::{Image, ImageIO};
|
||||||
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::sampling::PiecewiseConstant2D;
|
use crate::utils::sampling::PiecewiseConstant2D;
|
||||||
use crate::utils::{Arena, FileLoc, ParameterDictionary, Upload, resolve_filename};
|
use crate::utils::resolve_filename;
|
||||||
|
use crate::{Arena, FileLoc, ParameterDictionary};
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{Result, anyhow};
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
use shared::core::geometry::{
|
use shared::core::geometry::{
|
||||||
|
|
@ -77,7 +78,7 @@ pub fn create(
|
||||||
}
|
}
|
||||||
|
|
||||||
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 mi = match medium {
|
let mi = match medium {
|
||||||
Some(m) => {
|
Some(m) => {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::utils::sampling::AliasTableHost;
|
|
||||||
use crate::Arena;
|
use crate::Arena;
|
||||||
use shared::core::light::{Light, LightTrait};
|
use shared::core::light::{Light, LightTrait};
|
||||||
use shared::lights::sampler::PowerLightSampler;
|
use shared::lights::sampler::PowerLightSampler;
|
||||||
|
use shared::utils::sampling::AliasTable;
|
||||||
use shared::spectra::{SampledSpectrum, SampledWavelengths};
|
use shared::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
@ -9,7 +9,7 @@ use std::sync::Arc;
|
||||||
pub struct PowerSamplerHost {
|
pub struct PowerSamplerHost {
|
||||||
pub lights: Vec<Arc<Light>>,
|
pub lights: Vec<Arc<Light>>,
|
||||||
pub light_to_index: HashMap<usize, usize>,
|
pub light_to_index: HashMap<usize, usize>,
|
||||||
pub alias_table: AliasTableHost,
|
pub alias_table: AliasTable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PowerSamplerHost {
|
impl PowerSamplerHost {
|
||||||
|
|
@ -18,7 +18,7 @@ impl PowerSamplerHost {
|
||||||
return Self {
|
return Self {
|
||||||
lights: Vec::new(),
|
lights: Vec::new(),
|
||||||
light_to_index: HashMap::new(),
|
light_to_index: HashMap::new(),
|
||||||
alias_table: AliasTableHost::new(&[]),
|
alias_table: AliasTable::new(&[]),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,7 +38,7 @@ impl PowerSamplerHost {
|
||||||
light_power.push(phi.average());
|
light_power.push(phi.average());
|
||||||
}
|
}
|
||||||
|
|
||||||
let alias_table = AliasTableHost::new(&light_power);
|
let alias_table = AliasTable::new(&light_power);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
lights: lights_vec,
|
lights: lights_vec,
|
||||||
|
|
@ -46,16 +46,15 @@ impl PowerSamplerHost {
|
||||||
alias_table,
|
alias_table,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// pub fn to_device(&self, arena: &Arena) -> PowerLightSampler {
|
||||||
pub fn to_device(&self, arena: &Arena) -> PowerLightSampler {
|
// let device_lights: Vec<Light> = self.lights.iter().map(|l| (**l).clone()).collect();
|
||||||
let device_lights: Vec<Light> = self.lights.iter().map(|l| (**l).clone()).collect();
|
// let (lights_ptr, _) = arena.alloc_slice(&device_lights);
|
||||||
let (lights_ptr, _) = arena.alloc_slice(&device_lights);
|
// let alias_device = self.alias_table.to_device(arena);
|
||||||
let alias_device = self.alias_table.to_device(arena);
|
//
|
||||||
|
// PowerLightSampler {
|
||||||
PowerLightSampler {
|
// lights: lights_ptr,
|
||||||
lights: lights_ptr,
|
// lights_len: self.lights.len() as u32,
|
||||||
lights_len: self.lights.len() as u32,
|
// alias_table: alias_device,
|
||||||
alias_table: alias_device,
|
// }
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,14 @@ use shared::lights::SpotLight;
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::RGBColorSpace;
|
||||||
use shared::utils::math::radians;
|
use shared::utils::math::radians;
|
||||||
use shared::utils::{Ptr, Transform};
|
use shared::utils::{Ptr, Transform};
|
||||||
use shared::{Float, PI};
|
use shared::{Float, Ptr, Transform, PI};
|
||||||
|
|
||||||
pub trait CreateSpotLight {
|
trait CreateSpotLight {
|
||||||
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: Float,
|
||||||
cos_falloff_start: Float,
|
cos_falloff_start: Float,
|
||||||
total_width: Float,
|
total_width: Float,
|
||||||
) -> Self;
|
) -> Self;
|
||||||
|
|
@ -31,7 +31,7 @@ impl CreateSpotLight for SpotLight {
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
_medium_interface: MediumInterface,
|
_medium_interface: MediumInterface,
|
||||||
le: Spectrum,
|
le: Spectrum,
|
||||||
scale: shared::Float,
|
scale: Float,
|
||||||
cos_falloff_start: Float,
|
cos_falloff_start: Float,
|
||||||
total_width: Float,
|
total_width: Float,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,9 @@ use crate::core::material::CreateMaterial;
|
||||||
use crate::core::texture::SpectrumTexture;
|
use crate::core::texture::SpectrumTexture;
|
||||||
use crate::globals::get_options;
|
use crate::globals::get_options;
|
||||||
use crate::spectra::data::get_named_spectrum;
|
use crate::spectra::data::get_named_spectrum;
|
||||||
use crate::utils::{Arena, FileLoc, TextureParameterDictionary, Upload};
|
use crate::utils::TextureParameterDictionary;
|
||||||
use anyhow::{Result, bail};
|
use crate::{Arena, FileLoc};
|
||||||
|
use anyhow::{bail, Result};
|
||||||
use shared::core::material::Material;
|
use shared::core::material::Material;
|
||||||
use shared::core::spectrum::Spectrum;
|
use shared::core::spectrum::Spectrum;
|
||||||
use shared::core::texture::SpectrumType;
|
use shared::core::texture::SpectrumType;
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,14 @@ use crate::core::image::Image;
|
||||||
use crate::core::material::CreateMaterial;
|
use crate::core::material::CreateMaterial;
|
||||||
use crate::core::texture::SpectrumTexture;
|
use crate::core::texture::SpectrumTexture;
|
||||||
use crate::spectra::get_colorspace_device;
|
use crate::spectra::get_colorspace_device;
|
||||||
use crate::utils::{Arena, FileLoc, TextureParameterDictionary, Upload};
|
use crate::{Arena, FileLoc};
|
||||||
|
use crate::utils::TextureParameterDictionary;
|
||||||
use shared::bxdfs::HairBxDF;
|
use shared::bxdfs::HairBxDF;
|
||||||
use shared::core::material::Material;
|
use shared::core::material::Material;
|
||||||
use shared::core::spectrum::Spectrum;
|
use shared::core::spectrum::Spectrum;
|
||||||
use shared::core::texture::SpectrumType;
|
use shared::core::texture::SpectrumType;
|
||||||
use shared::materials::complex::*;
|
use shared::materials::complex::*;
|
||||||
// use shared::spectra::SampledWavelengths;
|
|
||||||
use shared::textures::SpectrumConstantTexture;
|
use shared::textures::SpectrumConstantTexture;
|
||||||
// use shared::utils::Ptr;
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
use crate::core::image::{Image, ImageIO};
|
use crate::core::image::{Image, ImageIO};
|
||||||
use crate::core::shape::{ALL_BILINEAR_MESHES, CreateShape};
|
use crate::core::shape::{CreateShape, ALL_BILINEAR_MESHES};
|
||||||
use crate::core::texture::FloatTexture;
|
use crate::core::texture::FloatTexture;
|
||||||
use crate::shapes::mesh::BilinearPatchMesh;
|
use crate::shapes::mesh::BilinearPatchMesh;
|
||||||
use crate::utils::sampling::PiecewiseConstant2D;
|
use crate::utils::sampling::PiecewiseConstant2D;
|
||||||
use crate::utils::{Arena, FileLoc, ParameterDictionary};
|
use crate::{Arena, FileLoc, ParameterDictionary};
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{anyhow, Result};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use shared::core::shape::Shape;
|
use shared::core::shape::Shape;
|
||||||
use shared::shapes::BilinearPatchShape;
|
use shared::shapes::BilinearPatchShape;
|
||||||
use shared::utils::{Ptr, Transform};
|
use shared::{Ptr, Transform};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,17 @@
|
||||||
use crate::Arena;
|
|
||||||
use crate::core::shape::CreateShape;
|
use crate::core::shape::CreateShape;
|
||||||
use crate::core::texture::FloatTexture;
|
use crate::core::texture::FloatTexture;
|
||||||
use crate::utils::{FileLoc, ParameterDictionary};
|
use crate::{Arena, FileLoc, ParameterDictionary};
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{anyhow, Result};
|
||||||
use shared::Float;
|
use log::warn;
|
||||||
use shared::core::geometry::{Normal3f, Point3f};
|
use shared::core::geometry::{Normal3f, Point3f};
|
||||||
use shared::core::shape::Shape;
|
use shared::core::shape::Shape;
|
||||||
use shared::shapes::{CurveCommon, CurveShape, CurveType};
|
use shared::shapes::{CurveCommon, CurveShape, CurveType};
|
||||||
use shared::utils::Transform;
|
|
||||||
use shared::utils::math::lerp;
|
use shared::utils::math::lerp;
|
||||||
use shared::utils::splines::{
|
use shared::utils::splines::{
|
||||||
cubic_bspline_to_bezier, elevate_quadratic_bezier_to_cubic, quadratic_bspline_to_bezier,
|
cubic_bspline_to_bezier, elevate_quadratic_bezier_to_cubic, quadratic_bspline_to_bezier,
|
||||||
};
|
};
|
||||||
use shared::Ptr;
|
use shared::Float;
|
||||||
|
use shared::{Ptr, Transform};
|
||||||
use log::warn;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|
@ -199,7 +196,7 @@ impl CreateShape for CurveShape {
|
||||||
curve_type,
|
curve_type,
|
||||||
seg_normals.expect("Could not determine normals to curve segments"),
|
seg_normals.expect("Could not determine normals to curve segments"),
|
||||||
split_depth.try_into().unwrap(),
|
split_depth.try_into().unwrap(),
|
||||||
arena
|
arena,
|
||||||
);
|
);
|
||||||
|
|
||||||
curves.extend(new_curves);
|
curves.extend(new_curves);
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
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::{Arena, FileLoc, ParameterDictionary};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use shared::core::shape::Shape;
|
use shared::core::shape::Shape;
|
||||||
use shared::shapes::CylinderShape;
|
use shared::shapes::CylinderShape;
|
||||||
use shared::utils::Transform;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use shared::Ptr;
|
use shared::{Transform, Ptr};
|
||||||
|
|
||||||
impl CreateShape for CylinderShape {
|
impl CreateShape for CylinderShape {
|
||||||
fn create(
|
fn create(
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
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::{Arena, FileLoc, ParameterDictionary};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use shared::core::shape::Shape;
|
use shared::core::shape::Shape;
|
||||||
use shared::shapes::DiskShape;
|
use shared::shapes::DiskShape;
|
||||||
use shared::utils::Transform;
|
use shared::{Ptr, Transform};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use shared::Ptr;
|
|
||||||
|
|
||||||
impl CreateShape for DiskShape {
|
impl CreateShape for DiskShape {
|
||||||
fn create(
|
fn create(
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,14 @@
|
||||||
// use crate::Arena;
|
|
||||||
use crate::utils::sampling::PiecewiseConstant2D;
|
use crate::utils::sampling::PiecewiseConstant2D;
|
||||||
|
use crate::Arena;
|
||||||
use anyhow::{bail, Context, Result as AnyResult};
|
use anyhow::{bail, Context, Result as AnyResult};
|
||||||
use ply_rs::parser::Parser;
|
use ply_rs::parser::Parser;
|
||||||
use ply_rs::ply::{DefaultElement, Property};
|
use ply_rs::ply::{DefaultElement, Property};
|
||||||
use shared::core::geometry::{Normal3f, Point2f, Point3f, Vector3f, VectorLike};
|
use shared::core::geometry::{Normal3f, Point2f, Point3f, Vector3f, VectorLike};
|
||||||
use shared::utils::mesh::{DeviceBilinearPatchMesh, DeviceTriangleMesh};
|
use shared::shapes::mesh::{BilinearPatchMesh, TriangleMesh};
|
||||||
use shared::utils::{Ptr, Transform};
|
use shared::{Ptr, Transform};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::ops::Deref;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
/// Intermediate mesh from PLY
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct TriQuadMesh {
|
pub struct TriQuadMesh {
|
||||||
pub p: Vec<Point3f>,
|
pub p: Vec<Point3f>,
|
||||||
|
|
@ -21,235 +19,7 @@ pub struct TriQuadMesh {
|
||||||
pub quad_indices: Vec<i32>,
|
pub quad_indices: Vec<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(DeviceRepr)]
|
|
||||||
#[device(name = "DeviceTriangleMesh")]
|
|
||||||
pub struct TriangleMeshStorage {
|
|
||||||
pub vertex_indices: Vec<i32>, // → Ptr<i32> + len (always present)
|
|
||||||
pub p: Vec<Point3f>, // → Ptr<Point3f> + len (always present)
|
|
||||||
pub n: Vec<Normal3f>, // → Ptr<Normal3f> + len (empty → null Ptr, len 0)
|
|
||||||
pub s: Vec<Vector3f>, // → Ptr<Vector3f> + len
|
|
||||||
pub uv: Vec<Point2f>, // → Ptr<Point2f> + len
|
|
||||||
pub face_indices: Vec<i32>, // → Ptr<i32> + len
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) struct BilinearMeshStorage {
|
|
||||||
pub vertex_indices: Vec<i32>,
|
|
||||||
pub p: Vec<Point3f>,
|
|
||||||
pub n: Vec<Normal3f>,
|
|
||||||
pub uv: Vec<Point2f>,
|
|
||||||
pub image_distribution: Option<PiecewiseConstant2D>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct TriangleMesh {
|
|
||||||
pub storage: Box<TriangleMeshStorage>,
|
|
||||||
pub device: DeviceTriangleMesh,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct BilinearPatchMesh {
|
|
||||||
pub storage: Box<BilinearMeshStorage>,
|
|
||||||
pub device: DeviceBilinearPatchMesh,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for TriangleMesh {
|
|
||||||
type Target = DeviceTriangleMesh;
|
|
||||||
#[inline]
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.device
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for BilinearPatchMesh {
|
|
||||||
type Target = DeviceBilinearPatchMesh;
|
|
||||||
#[inline]
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.device
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TriangleMesh {
|
|
||||||
pub fn new(
|
|
||||||
render_from_object: &Transform,
|
|
||||||
reverse_orientation: bool,
|
|
||||||
vertex_indices: Vec<i32>,
|
|
||||||
mut p: Vec<Point3f>,
|
|
||||||
mut n: Vec<Normal3f>,
|
|
||||||
mut s: Vec<Vector3f>,
|
|
||||||
uv: Vec<Point2f>,
|
|
||||||
face_indices: Vec<i32>,
|
|
||||||
) -> Self {
|
|
||||||
let n_triangles = vertex_indices.len() / 3;
|
|
||||||
let n_vertices = p.len();
|
|
||||||
|
|
||||||
// Transform positions to render space
|
|
||||||
for pt in p.iter_mut() {
|
|
||||||
*pt = render_from_object.apply_to_point(*pt);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transform and optionally flip normals
|
|
||||||
if !n.is_empty() {
|
|
||||||
assert_eq!(n_vertices, n.len(), "Normal count mismatch");
|
|
||||||
for nn in n.iter_mut() {
|
|
||||||
*nn = render_from_object.apply_to_normal(*nn);
|
|
||||||
if reverse_orientation {
|
|
||||||
*nn = -*nn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transform tangents
|
|
||||||
if !s.is_empty() {
|
|
||||||
assert_eq!(n_vertices, s.len(), "Tangent count mismatch");
|
|
||||||
for ss in s.iter_mut() {
|
|
||||||
*ss = render_from_object.apply_to_vector(*ss);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate UVs
|
|
||||||
if !uv.is_empty() {
|
|
||||||
assert_eq!(n_vertices, uv.len(), "UV count mismatch");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate face indices
|
|
||||||
if !face_indices.is_empty() {
|
|
||||||
assert_eq!(n_triangles, face_indices.len(), "Face index count mismatch");
|
|
||||||
}
|
|
||||||
|
|
||||||
let transform_swaps_handedness = render_from_object.swaps_handedness();
|
|
||||||
|
|
||||||
let storage = Box::new(TriangleMeshStorage {
|
|
||||||
vertex_indices,
|
|
||||||
p,
|
|
||||||
n,
|
|
||||||
s,
|
|
||||||
uv,
|
|
||||||
face_indices,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Build device struct with pointers into storage
|
|
||||||
let device = DeviceTriangleMesh {
|
|
||||||
n_triangles: n_triangles as u32,
|
|
||||||
n_vertices: n_vertices as u32,
|
|
||||||
vertex_indices: storage.vertex_indices.as_ptr().into(),
|
|
||||||
p: storage.p.as_ptr().into(),
|
|
||||||
n: if storage.n.is_empty() {
|
|
||||||
Ptr::null()
|
|
||||||
} else {
|
|
||||||
storage.n.as_ptr().into()
|
|
||||||
},
|
|
||||||
s: if storage.s.is_empty() {
|
|
||||||
Ptr::null()
|
|
||||||
} else {
|
|
||||||
storage.s.as_ptr().into()
|
|
||||||
},
|
|
||||||
uv: if storage.uv.is_empty() {
|
|
||||||
Ptr::null()
|
|
||||||
} else {
|
|
||||||
storage.uv.as_ptr().into()
|
|
||||||
},
|
|
||||||
face_indices: if storage.face_indices.is_empty() {
|
|
||||||
Ptr::null()
|
|
||||||
} else {
|
|
||||||
storage.face_indices.as_ptr().into()
|
|
||||||
},
|
|
||||||
reverse_orientation,
|
|
||||||
transform_swaps_handedness,
|
|
||||||
};
|
|
||||||
|
|
||||||
Self { storage, device }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn positions(&self) -> &[Point3f] {
|
|
||||||
&self.storage.p
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn indices(&self) -> &[i32] {
|
|
||||||
&self.storage.vertex_indices
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn normals(&self) -> &[Normal3f] {
|
|
||||||
&self.storage.n
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn uvs(&self) -> &[Point2f] {
|
|
||||||
&self.storage.uv
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BilinearPatchMesh {
|
|
||||||
pub fn new(
|
|
||||||
render_from_object: &Transform,
|
|
||||||
reverse_orientation: bool,
|
|
||||||
vertex_indices: Vec<i32>,
|
|
||||||
mut p: Vec<Point3f>,
|
|
||||||
mut n: Vec<Normal3f>,
|
|
||||||
uv: Vec<Point2f>,
|
|
||||||
image_distribution: Option<PiecewiseConstant2D>,
|
|
||||||
) -> Self {
|
|
||||||
let n_patches = vertex_indices.len() / 4;
|
|
||||||
let n_vertices = p.len();
|
|
||||||
|
|
||||||
for pt in p.iter_mut() {
|
|
||||||
*pt = render_from_object.apply_to_point(*pt);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !n.is_empty() {
|
|
||||||
assert_eq!(n_vertices, n.len(), "Normal count mismatch");
|
|
||||||
for nn in n.iter_mut() {
|
|
||||||
*nn = render_from_object.apply_to_normal(*nn);
|
|
||||||
if reverse_orientation {
|
|
||||||
*nn = -*nn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !uv.is_empty() {
|
|
||||||
assert_eq!(n_vertices, uv.len(), "UV count mismatch");
|
|
||||||
}
|
|
||||||
|
|
||||||
let transform_swaps_handedness = render_from_object.swaps_handedness();
|
|
||||||
|
|
||||||
let storage = Box::new(BilinearMeshStorage {
|
|
||||||
vertex_indices,
|
|
||||||
p,
|
|
||||||
n,
|
|
||||||
uv,
|
|
||||||
image_distribution,
|
|
||||||
});
|
|
||||||
|
|
||||||
let device = DeviceBilinearPatchMesh {
|
|
||||||
n_patches: n_patches as u32,
|
|
||||||
n_vertices: n_vertices as u32,
|
|
||||||
vertex_indices: storage.vertex_indices.as_ptr().into(),
|
|
||||||
p: storage.p.as_ptr().into(),
|
|
||||||
n: if storage.n.is_empty() {
|
|
||||||
Ptr::null()
|
|
||||||
} else {
|
|
||||||
storage.n.as_ptr().into()
|
|
||||||
},
|
|
||||||
uv: if storage.uv.is_empty() {
|
|
||||||
Ptr::null()
|
|
||||||
} else {
|
|
||||||
storage.uv.as_ptr().into()
|
|
||||||
},
|
|
||||||
reverse_orientation,
|
|
||||||
transform_swaps_handedness,
|
|
||||||
image_distribution: storage
|
|
||||||
.image_distribution
|
|
||||||
.as_ref()
|
|
||||||
.map(|d| Ptr::from(&d.device))
|
|
||||||
.unwrap_or(Ptr::null()),
|
|
||||||
};
|
|
||||||
|
|
||||||
Self { storage, device }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PLY Helper Functions
|
// PLY Helper Functions
|
||||||
|
|
||||||
fn get_float(elem: &DefaultElement, key: &str) -> AnyResult<f32> {
|
fn get_float(elem: &DefaultElement, key: &str) -> AnyResult<f32> {
|
||||||
match elem.get(key) {
|
match elem.get(key) {
|
||||||
Some(Property::Float(v)) => Ok(*v),
|
Some(Property::Float(v)) => Ok(*v),
|
||||||
|
|
@ -290,14 +60,12 @@ fn get_list_uint(elem: &DefaultElement, key: &str) -> AnyResult<Vec<u32>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TriQuadMesh Implementation
|
|
||||||
|
|
||||||
impl TriQuadMesh {
|
impl TriQuadMesh {
|
||||||
pub fn read_ply<P: AsRef<Path>>(filename: P) -> AnyResult<Self> {
|
pub fn read_ply<P: AsRef<Path>>(filename: P) -> AnyResult<Self> {
|
||||||
let path = filename.as_ref();
|
let path = filename.as_ref();
|
||||||
let filename_display = path.display().to_string();
|
let filename_display = path.display().to_string();
|
||||||
|
|
||||||
let mut f = File::open(path)
|
let f = File::open(path)
|
||||||
.with_context(|| format!("Couldn't open PLY file \"{}\"", filename_display))?;
|
.with_context(|| format!("Couldn't open PLY file \"{}\"", filename_display))?;
|
||||||
|
|
||||||
let p = Parser::<DefaultElement>::new();
|
let p = Parser::<DefaultElement>::new();
|
||||||
|
|
@ -313,7 +81,6 @@ impl TriQuadMesh {
|
||||||
|
|
||||||
let mut mesh = TriQuadMesh::default();
|
let mut mesh = TriQuadMesh::default();
|
||||||
|
|
||||||
// Parse vertices
|
|
||||||
if let Some(vertices) = ply.payload.get("vertex") {
|
if let Some(vertices) = ply.payload.get("vertex") {
|
||||||
if vertices.is_empty() {
|
if vertices.is_empty() {
|
||||||
bail!("{}: PLY file has no vertices", filename_display);
|
bail!("{}: PLY file has no vertices", filename_display);
|
||||||
|
|
@ -360,7 +127,6 @@ impl TriQuadMesh {
|
||||||
bail!("{}: PLY file has no vertex elements", filename_display);
|
bail!("{}: PLY file has no vertex elements", filename_display);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse faces
|
|
||||||
if let Some(faces) = ply.payload.get("face") {
|
if let Some(faces) = ply.payload.get("face") {
|
||||||
mesh.tri_indices.reserve(faces.len() * 3);
|
mesh.tri_indices.reserve(faces.len() * 3);
|
||||||
mesh.quad_indices.reserve(faces.len() * 4);
|
mesh.quad_indices.reserve(faces.len() * 4);
|
||||||
|
|
@ -390,7 +156,6 @@ impl TriQuadMesh {
|
||||||
bail!("{}: PLY file has no face elements", filename_display);
|
bail!("{}: PLY file has no face elements", filename_display);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate indices
|
|
||||||
let vertex_count = mesh.p.len() as i32;
|
let vertex_count = mesh.p.len() as i32;
|
||||||
for &idx in &mesh.tri_indices {
|
for &idx in &mesh.tri_indices {
|
||||||
if idx >= vertex_count {
|
if idx >= vertex_count {
|
||||||
|
|
@ -421,18 +186,13 @@ impl TriQuadMesh {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Each quad becomes 2 triangles
|
|
||||||
self.tri_indices.reserve(self.quad_indices.len() / 4 * 6);
|
self.tri_indices.reserve(self.quad_indices.len() / 4 * 6);
|
||||||
|
|
||||||
for quad in self.quad_indices.chunks_exact(4) {
|
for quad in self.quad_indices.chunks_exact(4) {
|
||||||
let (v0, v1, v2, v3) = (quad[0], quad[1], quad[2], quad[3]);
|
let (v0, v1, v2, v3) = (quad[0], quad[1], quad[2], quad[3]);
|
||||||
|
|
||||||
// Triangle 1: v0, v1, v2
|
|
||||||
self.tri_indices.push(v0);
|
self.tri_indices.push(v0);
|
||||||
self.tri_indices.push(v1);
|
self.tri_indices.push(v1);
|
||||||
self.tri_indices.push(v2);
|
self.tri_indices.push(v2);
|
||||||
|
|
||||||
// Triangle 2: v0, v2, v3
|
|
||||||
self.tri_indices.push(v0);
|
self.tri_indices.push(v0);
|
||||||
self.tri_indices.push(v2);
|
self.tri_indices.push(v2);
|
||||||
self.tri_indices.push(v3);
|
self.tri_indices.push(v3);
|
||||||
|
|
@ -442,10 +202,8 @@ impl TriQuadMesh {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compute_normals(&mut self) {
|
pub fn compute_normals(&mut self) {
|
||||||
// Initialize normals to zero
|
|
||||||
self.n = vec![Normal3f::new(0.0, 0.0, 0.0); self.p.len()];
|
self.n = vec![Normal3f::new(0.0, 0.0, 0.0); self.p.len()];
|
||||||
|
|
||||||
// Accumulate face normals for triangles
|
|
||||||
for tri in self.tri_indices.chunks_exact(3) {
|
for tri in self.tri_indices.chunks_exact(3) {
|
||||||
let (i0, i1, i2) = (tri[0] as usize, tri[1] as usize, tri[2] as usize);
|
let (i0, i1, i2) = (tri[0] as usize, tri[1] as usize, tri[2] as usize);
|
||||||
|
|
||||||
|
|
@ -457,13 +215,11 @@ impl TriQuadMesh {
|
||||||
let v20 = p2 - p0;
|
let v20 = p2 - p0;
|
||||||
let face_normal = v10.cross(v20);
|
let face_normal = v10.cross(v20);
|
||||||
|
|
||||||
// Accumulate (will normalize later)
|
|
||||||
self.n[i0] = self.n[i0] + Normal3f::from(face_normal);
|
self.n[i0] = self.n[i0] + Normal3f::from(face_normal);
|
||||||
self.n[i1] = self.n[i1] + Normal3f::from(face_normal);
|
self.n[i1] = self.n[i1] + Normal3f::from(face_normal);
|
||||||
self.n[i2] = self.n[i2] + Normal3f::from(face_normal);
|
self.n[i2] = self.n[i2] + Normal3f::from(face_normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accumulate face normals for quads
|
|
||||||
for quad in self.quad_indices.chunks_exact(4) {
|
for quad in self.quad_indices.chunks_exact(4) {
|
||||||
let indices: Vec<usize> = quad.iter().map(|&i| i as usize).collect();
|
let indices: Vec<usize> = quad.iter().map(|&i| i as usize).collect();
|
||||||
|
|
||||||
|
|
@ -480,7 +236,6 @@ impl TriQuadMesh {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize all normals
|
|
||||||
for normal in &mut self.n {
|
for normal in &mut self.n {
|
||||||
let len_sq = normal.norm_squared();
|
let len_sq = normal.norm_squared();
|
||||||
if len_sq > 0.0 {
|
if len_sq > 0.0 {
|
||||||
|
|
@ -489,7 +244,6 @@ impl TriQuadMesh {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert to a TriangleMesh, consuming self
|
|
||||||
pub fn into_triangle_mesh(
|
pub fn into_triangle_mesh(
|
||||||
mut self,
|
mut self,
|
||||||
render_from_object: &Transform,
|
render_from_object: &Transform,
|
||||||
|
|
@ -507,15 +261,13 @@ impl TriQuadMesh {
|
||||||
self.tri_indices,
|
self.tri_indices,
|
||||||
self.p,
|
self.p,
|
||||||
self.n,
|
self.n,
|
||||||
Vec::new(), // s (tangents)
|
Vec::new(),
|
||||||
self.uv,
|
self.uv,
|
||||||
self.face_indices,
|
self.face_indices,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create from PLY directly
|
|
||||||
|
|
||||||
impl TriangleMesh {
|
impl TriangleMesh {
|
||||||
pub fn from_ply<P: AsRef<Path>>(
|
pub fn from_ply<P: AsRef<Path>>(
|
||||||
filename: P,
|
filename: P,
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
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::{Arena, FileLoc, ParameterDictionary};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use shared::core::shape::Shape;
|
use shared::core::shape::Shape;
|
||||||
use shared::shapes::SphereShape;
|
use shared::shapes::SphereShape;
|
||||||
use shared::utils::Transform;
|
use shared::{Ptr, Transform};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use shared::Ptr;
|
|
||||||
|
|
||||||
impl CreateShape for SphereShape {
|
impl CreateShape for SphereShape {
|
||||||
fn create(
|
fn create(
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
use crate::core::shape::{ALL_TRIANGLE_MESHES, CreateShape};
|
use crate::core::shape::{CreateShape, ALL_TRIANGLE_MESHES};
|
||||||
use crate::core::texture::FloatTexture;
|
use crate::core::texture::FloatTexture;
|
||||||
use crate::shapes::mesh::TriangleMesh;
|
use crate::shapes::mesh::TriangleMesh;
|
||||||
use crate::utils::{Arena, FileLoc, ParameterDictionary};
|
use crate::{Arena, FileLoc, ParameterDictionary};
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{bail, Result};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use shared::core::shape::Shape;
|
use shared::core::shape::Shape;
|
||||||
use shared::shapes::TriangleShape;
|
use shared::shapes::TriangleShape;
|
||||||
use shared::utils::{Ptr, Transform};
|
use shared::{Ptr, Transform};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,39 @@
|
||||||
use crate::spectra::get_spectra_context;
|
use crate::spectra::get_spectra_context;
|
||||||
|
use shared::core::color::{RGBToSpectrumTable, RGB, XYZ};
|
||||||
use super::DenselySampledSpectrumBuffer;
|
|
||||||
use shared::core::color::{RGB, RGBToSpectrumTable, XYZ};
|
|
||||||
use shared::core::geometry::Point2f;
|
use shared::core::geometry::Point2f;
|
||||||
use shared::core::spectrum::Spectrum;
|
use shared::core::spectrum::Spectrum;
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::{DenselySampledSpectrum, RGBColorSpace};
|
||||||
use shared::utils::math::SquareMatrix;
|
use shared::utils::math::SquareMatrix;
|
||||||
use shared::utils::ptr::Ptr;
|
use shared::Ptr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
pub trait CreateRGBColorSpace {
|
||||||
pub struct RGBColorSpaceData {
|
fn new(
|
||||||
illuminant: Arc<DenselySampledSpectrumBuffer>,
|
|
||||||
pub view: RGBColorSpace,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::Deref for RGBColorSpaceData {
|
|
||||||
type Target = RGBColorSpace;
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.view
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RGBColorSpaceData {
|
|
||||||
pub fn new(
|
|
||||||
r: Point2f,
|
r: Point2f,
|
||||||
g: Point2f,
|
g: Point2f,
|
||||||
b: Point2f,
|
b: Point2f,
|
||||||
illuminant: Arc<DenselySampledSpectrumBuffer>,
|
illuminant: Arc<DenselySampledSpectrum>,
|
||||||
|
rgb_to_spectrum_table: Ptr<RGBToSpectrumTable>,
|
||||||
|
) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CreateRGBColorSpace for RGBColorSpace {
|
||||||
|
fn new(
|
||||||
|
r: Point2f,
|
||||||
|
g: Point2f,
|
||||||
|
b: Point2f,
|
||||||
|
illuminant: Arc<DenselySampledSpectrum>,
|
||||||
rgb_to_spectrum_table: Ptr<RGBToSpectrumTable>,
|
rgb_to_spectrum_table: Ptr<RGBToSpectrumTable>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let stdspec = get_spectra_context();
|
let stdspec = get_spectra_context();
|
||||||
let w_xyz: XYZ = Spectrum::Dense(illuminant.device()).to_xyz(&stdspec);
|
let illum_spectrum = Spectrum::Dense(illuminant.as_ref().clone());
|
||||||
|
let w_xyz: XYZ = illum_spectrum.to_xyz(&stdspec);
|
||||||
let w = w_xyz.xy();
|
let w = w_xyz.xy();
|
||||||
|
|
||||||
let r_xyz = XYZ::from_xyy(r, Some(1.0));
|
let r_xyz = XYZ::from_xyy(r, Some(1.0));
|
||||||
let g_xyz = XYZ::from_xyy(g, Some(1.0));
|
let g_xyz = XYZ::from_xyy(g, Some(1.0));
|
||||||
let b_xyz = XYZ::from_xyy(b, Some(1.0));
|
let b_xyz = XYZ::from_xyy(b, Some(1.0));
|
||||||
|
|
||||||
let rgb_values = [
|
let rgb_values = [
|
||||||
[r_xyz.x(), g_xyz.x(), b_xyz.x()],
|
[r_xyz.x(), g_xyz.x(), b_xyz.x()],
|
||||||
[r_xyz.y(), g_xyz.y(), b_xyz.y()],
|
[r_xyz.y(), g_xyz.y(), b_xyz.y()],
|
||||||
|
|
@ -44,23 +42,17 @@ impl RGBColorSpaceData {
|
||||||
let rgb = SquareMatrix::new(rgb_values);
|
let rgb = SquareMatrix::new(rgb_values);
|
||||||
let c: RGB = rgb.inverse().unwrap() * w_xyz;
|
let c: RGB = rgb.inverse().unwrap() * w_xyz;
|
||||||
let xyz_from_rgb = rgb * SquareMatrix::diag(&[c.r, c.g, c.b]);
|
let xyz_from_rgb = rgb * SquareMatrix::diag(&[c.r, c.g, c.b]);
|
||||||
let rgb_from_xyz = xyz_from_rgb
|
let rgb_from_xyz = xyz_from_rgb.inverse().expect("singular");
|
||||||
.inverse()
|
|
||||||
.expect("XYZ from RGB matrix is singular");
|
RGBColorSpace {
|
||||||
let view = RGBColorSpace {
|
|
||||||
r,
|
r,
|
||||||
g,
|
g,
|
||||||
b,
|
b,
|
||||||
w,
|
w,
|
||||||
illuminant: illuminant.device(),
|
illuminant: Ptr::from(illuminant.as_ref()),
|
||||||
|
rgb_to_spectrum_table,
|
||||||
xyz_from_rgb,
|
xyz_from_rgb,
|
||||||
rgb_from_xyz,
|
rgb_from_xyz,
|
||||||
rgb_to_spectrum_table,
|
|
||||||
};
|
|
||||||
|
|
||||||
Self {
|
|
||||||
illuminant: illuminant.into(),
|
|
||||||
view,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use crate::spectra::{DenselySampledSpectrumBuffer, piecewise::PiecewiseLinearSpectrumBuffer};
|
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
use shared::core::spectrum::Spectrum;
|
use shared::core::spectrum::Spectrum;
|
||||||
use shared::spectra::cie::*;
|
use shared::spectra::cie::*;
|
||||||
|
use shared::spectra::{PiecewiseLinearSpectrum, DenselySampledSpectrum};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
pub fn create_cie_buffer(data: &[Float]) -> DenselySampledSpectrumBuffer {
|
pub fn create_cie(data: &[Float]) -> DenselySampledSpectrum {
|
||||||
let (start_lambda, step) = match data.len() {
|
let (start_lambda, step) = match data.len() {
|
||||||
471 => (360.0, 1.0),
|
471 => (360.0, 1.0),
|
||||||
95 => (300.0, 5.0),
|
95 => (300.0, 5.0),
|
||||||
|
|
@ -16,9 +16,9 @@ pub fn create_cie_buffer(data: &[Float]) -> DenselySampledSpectrumBuffer {
|
||||||
.map(|i| start_lambda + i as Float * step)
|
.map(|i| start_lambda + i as Float * step)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let buffer = PiecewiseLinearSpectrumBuffer::new(lambdas, data.to_vec());
|
let buffer = PiecewiseLinearSpectrum::new(lambdas, data.to_vec());
|
||||||
let spec = Spectrum::Piecewise(buffer.device);
|
let spec = Spectrum::Piecewise(buffer);
|
||||||
DenselySampledSpectrumBuffer::from_spectrum(&spec)
|
DenselySampledSpectrum::from_spectrum(&spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static NAMED_SPECTRA: LazyLock<HashMap<String, Spectrum>> = LazyLock::new(|| {
|
pub static NAMED_SPECTRA: LazyLock<HashMap<String, Spectrum>> = LazyLock::new(|| {
|
||||||
|
|
@ -26,8 +26,8 @@ pub static NAMED_SPECTRA: LazyLock<HashMap<String, Spectrum>> = LazyLock::new(||
|
||||||
|
|
||||||
macro_rules! add {
|
macro_rules! add {
|
||||||
($name:expr, $data:expr, $norm:expr) => {
|
($name:expr, $data:expr, $norm:expr) => {
|
||||||
let buffer = PiecewiseLinearSpectrumBuffer::from_interleaved($data, $norm);
|
let buffer = PiecewiseLinearSpectrum::from_interleaved($data, $norm);
|
||||||
let spectrum = Spectrum::Piecewise(*buffer);
|
let spectrum = Spectrum::Piecewise(buffer);
|
||||||
m.insert($name.to_string(), spectrum);
|
m.insert($name.to_string(), spectrum);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue