Migrating to a more GPU friendly architecture, refactored texture handling, working on images and scene creation. God have mercy on my soul
This commit is contained in:
parent
8bcc2fb0c8
commit
2e9d3c7301
61 changed files with 1734 additions and 1716 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -3,3 +3,5 @@
|
||||||
*.log
|
*.log
|
||||||
*.bak
|
*.bak
|
||||||
flip.rs
|
flip.rs
|
||||||
|
.vscode
|
||||||
|
rust-analyzer.json
|
||||||
|
|
|
||||||
13
Cargo.toml
13
Cargo.toml
|
|
@ -11,10 +11,6 @@ use_nvtx = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.100"
|
anyhow = "1.0.100"
|
||||||
bitflags = "2.10.0"
|
|
||||||
bumpalo = "3.19.0"
|
|
||||||
bytemuck = { version = "1.24.0", features = ["derive"] }
|
|
||||||
enum_dispatch = "0.3.13"
|
|
||||||
exr = "1.73.0"
|
exr = "1.73.0"
|
||||||
flate2 = "1.1.5"
|
flate2 = "1.1.5"
|
||||||
gpu = "0.2.3"
|
gpu = "0.2.3"
|
||||||
|
|
@ -24,22 +20,19 @@ indicatif = "0.18.3"
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
log = "0.4.29"
|
log = "0.4.29"
|
||||||
memmap2 = "0.9.9"
|
memmap2 = "0.9.9"
|
||||||
num = "0.4.3"
|
|
||||||
num-integer = "0.1.46"
|
|
||||||
num-traits = "0.2.19"
|
|
||||||
nvtx = "1.3.0"
|
nvtx = "1.3.0"
|
||||||
once_cell = "1.21.3"
|
|
||||||
parking_lot = "0.12.5"
|
parking_lot = "0.12.5"
|
||||||
paste = "1.0.15"
|
paste = "1.0.15"
|
||||||
qoi = "0.4.1"
|
qoi = "0.4.1"
|
||||||
rand = "0.9.2"
|
rand = "0.9.2"
|
||||||
rayon = "1.11.0"
|
rayon = "1.11.0"
|
||||||
smallvec = "1.15.1"
|
|
||||||
thiserror = "2.0.17"
|
thiserror = "2.0.17"
|
||||||
unicode-normalization = "0.1.25"
|
unicode-normalization = "0.1.25"
|
||||||
wgpu = "27.0.1"
|
wgpu = "27.0.1"
|
||||||
|
|
||||||
shared = { path = "shared" }
|
shared = { path = "shared" }
|
||||||
kernel = { path = "kernels" }
|
kernels = { path = "kernels" }
|
||||||
|
|
||||||
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 }
|
||||||
cust = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", default-features = false, features = ["glam"], optional = true }
|
cust = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", default-features = false, features = ["glam"], optional = true }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cuda_std = { git = "https://github.com/rust-gpu/rust-cuda", rev = "7fa76f3d717038a92c90bf4a482b0b8dd3259344" }
|
cuda_std = { git = "https://github.com/rust-gpu/rust-cuda", rev = "7fa76f3d717038a92c90bf4a482b0b8dd3259344" }
|
||||||
|
shared = { path = "../shared" }
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
|
||||||
|
|
@ -4,3 +4,16 @@ version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bitflags = "2.10.0"
|
||||||
|
bumpalo = "3.19.1"
|
||||||
|
bytemuck = { version = "1.24.0", features = ["derive"] }
|
||||||
|
enum_dispatch = "0.3.13"
|
||||||
|
log = "0.4.29"
|
||||||
|
num = "0.4.3"
|
||||||
|
num-integer = "0.1.46"
|
||||||
|
num-traits = "0.2.19"
|
||||||
|
once_cell = "1.21.3"
|
||||||
|
smallvec = "1.15.1"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
cuda = []
|
||||||
|
|
|
||||||
9
shared/src/cameras/mod.rs
Normal file
9
shared/src/cameras/mod.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
mod orthographic;
|
||||||
|
mod perspective;
|
||||||
|
mod realistic;
|
||||||
|
mod spherical;
|
||||||
|
|
||||||
|
pub use orthographic::OrthographicCamera;
|
||||||
|
pub use perspective::PerspectiveCamera;
|
||||||
|
pub use realistic::RealisticCamera;
|
||||||
|
pub use spherical::SphericalCamera;
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
use super::{CameraBase, CameraBaseParameters, CameraRay, CameraTrait, CameraTransform};
|
use crate::core::camera::{
|
||||||
|
CameraBase, CameraBaseParameters, CameraRay, CameraTrait, CameraTransform,
|
||||||
|
};
|
||||||
use crate::core::film::{Film, FilmTrait};
|
use crate::core::film::{Film, FilmTrait};
|
||||||
use crate::core::geometry::{
|
use crate::core::geometry::{
|
||||||
Bounds2f, Point2f, Point3f, Ray, RayDifferential, Vector2f, Vector3f, VectorLike,
|
Bounds2f, Point2f, Point3f, Ray, RayDifferential, Vector2f, Vector3f, VectorLike,
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use rand::distr::uniform::SampleUniform;
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::Not;
|
use std::ops::Not;
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,3 @@
|
||||||
mod orthographic;
|
|
||||||
mod perspective;
|
|
||||||
mod realistic;
|
|
||||||
mod spherical;
|
|
||||||
|
|
||||||
pub use orthographic::OrthographicCamera;
|
|
||||||
pub use perspective::PerspectiveCamera;
|
|
||||||
pub use realistic::RealisticCamera;
|
|
||||||
pub use spherical::SphericalCamera;
|
|
||||||
|
|
||||||
use crate::core::film::{Film, FilmTrait};
|
use crate::core::film::{Film, FilmTrait};
|
||||||
use crate::core::geometry::{
|
use crate::core::geometry::{
|
||||||
Normal3f, Point2f, Point2i, Point3f, Ray, RayDifferential, Vector3f, VectorLike,
|
Normal3f, Point2f, Point2i, Point3f, Ray, RayDifferential, Vector3f, VectorLike,
|
||||||
|
|
@ -17,7 +7,7 @@ use crate::core::medium::Medium;
|
||||||
use crate::core::options::RenderingCoordinateSystem;
|
use crate::core::options::RenderingCoordinateSystem;
|
||||||
use crate::core::pbrt::Float;
|
use crate::core::pbrt::Float;
|
||||||
use crate::core::sampler::CameraSample;
|
use crate::core::sampler::CameraSample;
|
||||||
use crate::image::ImageMetadata;
|
use crate::images::ImageMetadata;
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::error::FileLoc;
|
use crate::utils::error::FileLoc;
|
||||||
use crate::utils::math::lerp;
|
use crate::utils::math::lerp;
|
||||||
|
|
@ -147,7 +137,7 @@ impl CameraBaseParameters {
|
||||||
shutter_open,
|
shutter_open,
|
||||||
shutter_close,
|
shutter_close,
|
||||||
film,
|
film,
|
||||||
medium: Some(medium),
|
medium_id: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -16,14 +16,14 @@ use crate::spectra::{
|
||||||
};
|
};
|
||||||
use crate::utils::AtomicFloat;
|
use crate::utils::AtomicFloat;
|
||||||
use crate::utils::containers::Array2D;
|
use crate::utils::containers::Array2D;
|
||||||
use crate::utils::error::FileLoc;
|
|
||||||
use crate::utils::math::linear_least_squares;
|
use crate::utils::math::linear_least_squares;
|
||||||
use crate::utils::math::{SquareMatrix, wrap_equal_area_square};
|
use crate::utils::math::{SquareMatrix, wrap_equal_area_square};
|
||||||
use crate::utils::parameters::ParameterDictionary;
|
use crate::utils::parameters::ParameterDictionary;
|
||||||
use crate::utils::sampling::VarianceEstimator;
|
use crate::utils::sampling::VarianceEstimator;
|
||||||
use crate::utils::transform::AnimatedTransform;
|
use crate::utils::transform::AnimatedTransform;
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
use rayon::prelude::*;
|
use pbrt::utils::error::FileLoc;
|
||||||
|
// use rayon::prelude::*;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::{Arc, OnceLock, atomic::AtomicUsize, atomic::Ordering};
|
use std::sync::{Arc, OnceLock, atomic::AtomicUsize, atomic::Ordering};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,6 @@ use crate::utils::parameters::ParameterDictionary;
|
||||||
use crate::utils::sampling::PiecewiseConstant2D;
|
use crate::utils::sampling::PiecewiseConstant2D;
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
|
|
||||||
use rand::Rng;
|
|
||||||
use std::hash::Hash;
|
|
||||||
|
|
||||||
pub struct FilterSample {
|
pub struct FilterSample {
|
||||||
pub p: Point2f,
|
pub p: Point2f,
|
||||||
pub weight: Float,
|
pub weight: Float,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
use crate::core::pbrt::Float;
|
use crate::core::pbrt::Float;
|
||||||
use crate::utils::interval::Interval;
|
use crate::utils::interval::Interval;
|
||||||
use crate::utils::math::{next_float_down, next_float_up};
|
use crate::utils::math::{next_float_down, next_float_up};
|
||||||
use num_integer::Roots;
|
|
||||||
use num_traits::{Float as NumFloat, FloatConst, Num, One, Signed, Zero};
|
use num_traits::{Float as NumFloat, FloatConst, Num, One, Signed, Zero};
|
||||||
use std::ops::{Add, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub};
|
use std::ops::{Add, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub};
|
||||||
|
|
||||||
|
|
|
||||||
889
shared/src/core/light.rs
Normal file
889
shared/src/core/light.rs
Normal file
|
|
@ -0,0 +1,889 @@
|
||||||
|
use crate::core::geometry::{
|
||||||
|
Bounds2f, Bounds3f, DirectionCone, Normal3f, Point2f, Point2i, Point3f, Point3fi, Ray,
|
||||||
|
Vector3f, VectorLike, cos_theta,
|
||||||
|
};
|
||||||
|
use crate::core::interaction::{
|
||||||
|
Interaction, InteractionTrait, MediumInteraction, SimpleInteraction, SurfaceInteraction,
|
||||||
|
};
|
||||||
|
use crate::core::medium::MediumInterface;
|
||||||
|
use crate::core::pbrt::{Float, PI};
|
||||||
|
use crate::images::Image;
|
||||||
|
use crate::spectra::{
|
||||||
|
DenselySampledSpectrum, LAMBDA_MAX, LAMBDA_MIN, RGB, RGBColorSpace, RGBIlluminantSpectrum,
|
||||||
|
SampledSpectrum, SampledWavelengths, Spectrum, SpectrumProvider,
|
||||||
|
};
|
||||||
|
use crate::utils::containers::InternCache;
|
||||||
|
use crate::utils::math::{equal_area_sphere_to_square, radians, safe_sqrt, smooth_step, square};
|
||||||
|
use crate::utils::sampling::PiecewiseConstant2D;
|
||||||
|
use crate::utils::transform::TransformGeneric;
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
|
use enum_dispatch::enum_dispatch;
|
||||||
|
use std::sync::{Arc, OnceLock};
|
||||||
|
|
||||||
|
use crate::lights::diffuse::DiffuseAreaLight;
|
||||||
|
use crate::lights::infinite::{InfiniteImageLight, InfinitePortalLight, InfiniteUniformLight};
|
||||||
|
|
||||||
|
use crate::lights::sampler::{LightSampler, LightSamplerTrait};
|
||||||
|
|
||||||
|
static SPECTRUM_CACHE: OnceLock<InternCache<DenselySampledSpectrum>> = OnceLock::new();
|
||||||
|
|
||||||
|
fn get_spectrum_cache() -> &'static InternCache<DenselySampledSpectrum> {
|
||||||
|
SPECTRUM_CACHE.get_or_init(InternCache::new)
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct LightType: u32 {
|
||||||
|
const DeltaPosition = 1;
|
||||||
|
const DeltaDirection = 2;
|
||||||
|
const Area = 4;
|
||||||
|
const Infinite = 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightType {
|
||||||
|
pub fn is_infinite(&self) -> bool {
|
||||||
|
self.contains(LightType::Infinite)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_delta_light(&self) -> bool {
|
||||||
|
self.contains(LightType::DeltaPosition) || self.contains(LightType::DeltaDirection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct LightLeSample {
|
||||||
|
l: SampledSpectrum,
|
||||||
|
ray: Ray,
|
||||||
|
intr: Option<Interaction>,
|
||||||
|
pdf_pos: Float,
|
||||||
|
pdf_dir: Float,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Default, Clone)]
|
||||||
|
pub struct LightSampleContext {
|
||||||
|
pub pi: Point3fi,
|
||||||
|
pub n: Normal3f,
|
||||||
|
pub ns: Normal3f,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightSampleContext {
|
||||||
|
pub fn new(pi: Point3fi, n: Normal3f, ns: Normal3f) -> Self {
|
||||||
|
Self { pi, n, ns }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn p(&self) -> Point3f {
|
||||||
|
self.pi.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&SurfaceInteraction> for LightSampleContext {
|
||||||
|
fn from(si: &SurfaceInteraction) -> Self {
|
||||||
|
Self {
|
||||||
|
pi: si.common.pi,
|
||||||
|
n: si.common.n,
|
||||||
|
ns: si.shading.n,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&MediumInteraction> for LightSampleContext {
|
||||||
|
fn from(mi: &MediumInteraction) -> Self {
|
||||||
|
Self {
|
||||||
|
pi: mi.common.pi,
|
||||||
|
n: Normal3f::default(),
|
||||||
|
ns: Normal3f::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Interaction> for LightSampleContext {
|
||||||
|
fn from(intr: &Interaction) -> Self {
|
||||||
|
match intr {
|
||||||
|
Interaction::Surface(si) => Self {
|
||||||
|
pi: si.common.pi,
|
||||||
|
n: si.common.n,
|
||||||
|
ns: si.shading.n,
|
||||||
|
},
|
||||||
|
|
||||||
|
Interaction::Medium(mi) => Self {
|
||||||
|
pi: mi.common.pi,
|
||||||
|
n: mi.common.n,
|
||||||
|
ns: mi.common.n,
|
||||||
|
},
|
||||||
|
|
||||||
|
Interaction::Simple(sim) => Self {
|
||||||
|
pi: sim.common.pi,
|
||||||
|
n: sim.common.n,
|
||||||
|
ns: sim.common.n,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct LightLiSample {
|
||||||
|
pub l: SampledSpectrum,
|
||||||
|
pub wi: Vector3f,
|
||||||
|
pub pdf: Float,
|
||||||
|
pub p_light: Arc<Interaction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightLiSample {
|
||||||
|
pub fn new(l: SampledSpectrum, wi: Vector3f, pdf: Float, p_light: Interaction) -> Self {
|
||||||
|
Self {
|
||||||
|
l,
|
||||||
|
wi,
|
||||||
|
pdf,
|
||||||
|
p_light: Arc::new(p_light),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct LightBaseData {
|
||||||
|
pub render_from_light: Transform,
|
||||||
|
pub light_type: u32,
|
||||||
|
pub mi_inside: i32,
|
||||||
|
pub mi_outside: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightBase {
|
||||||
|
pub fn new(
|
||||||
|
light_type: LightType,
|
||||||
|
render_from_light: &TransformGeneric<Float>,
|
||||||
|
medium_interface: &MediumInterface,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
light_type,
|
||||||
|
render_from_light: *render_from_light,
|
||||||
|
medium_interface: medium_interface.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn l(
|
||||||
|
&self,
|
||||||
|
_p: Point3f,
|
||||||
|
_n: Normal3f,
|
||||||
|
_uv: Point2f,
|
||||||
|
_w: Vector3f,
|
||||||
|
_lambda: &SampledWavelengths,
|
||||||
|
) -> SampledSpectrum {
|
||||||
|
SampledSpectrum::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn light_type(&self) -> LightType {
|
||||||
|
self.light_type
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
|
SampledSpectrum::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lookup_spectrum(s: &Spectrum) -> Arc<DenselySampledSpectrum> {
|
||||||
|
let cache = SPECTRUM_CACHE.get_or_init(InternCache::new);
|
||||||
|
let dense_spectrum = DenselySampledSpectrum::from_spectrum(s);
|
||||||
|
cache.lookup(dense_spectrum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct LightBounds {
|
||||||
|
bounds: Bounds3f,
|
||||||
|
phi: Float,
|
||||||
|
w: Vector3f,
|
||||||
|
cos_theta_o: Float,
|
||||||
|
cos_theta_e: Float,
|
||||||
|
two_sided: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightBounds {
|
||||||
|
pub fn new(
|
||||||
|
bounds: &Bounds3f,
|
||||||
|
w: Vector3f,
|
||||||
|
phi: Float,
|
||||||
|
cos_theta_o: Float,
|
||||||
|
cos_theta_e: Float,
|
||||||
|
two_sided: bool,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
bounds: *bounds,
|
||||||
|
phi,
|
||||||
|
w,
|
||||||
|
cos_theta_o,
|
||||||
|
cos_theta_e,
|
||||||
|
two_sided,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn centroid(&self) -> Point3f {
|
||||||
|
self.bounds.p_min + Vector3f::from(self.bounds.p_max) / 2.
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn importance(&self, p: Point3f, n: Normal3f) -> Float {
|
||||||
|
// Compute clamped squared distance to reference point
|
||||||
|
let pc = self.centroid();
|
||||||
|
let d2_raw = p.distance_squared(pc);
|
||||||
|
let d2 = d2_raw.max(self.bounds.diagonal().norm()) / 2.;
|
||||||
|
let cos_sub_clamped = |sin_theta_a: Float,
|
||||||
|
cos_theta_a: Float,
|
||||||
|
sin_theta_b: Float,
|
||||||
|
cos_theta_b: Float|
|
||||||
|
-> Float {
|
||||||
|
if cos_theta_a > cos_theta_b {
|
||||||
|
return 1.;
|
||||||
|
}
|
||||||
|
cos_theta_a * cos_theta_b + sin_theta_a * sin_theta_b
|
||||||
|
};
|
||||||
|
|
||||||
|
let sin_sub_clamped = |sin_theta_a: Float,
|
||||||
|
cos_theta_a: Float,
|
||||||
|
sin_theta_b: Float,
|
||||||
|
cos_theta_b: Float|
|
||||||
|
-> Float {
|
||||||
|
if cos_theta_a > cos_theta_b {
|
||||||
|
return 1.;
|
||||||
|
}
|
||||||
|
sin_theta_a * cos_theta_b - cos_theta_a * sin_theta_b
|
||||||
|
};
|
||||||
|
|
||||||
|
let wi = (p - pc).normalize();
|
||||||
|
let mut cos_theta_w = self.w.dot(wi);
|
||||||
|
if self.two_sided {
|
||||||
|
cos_theta_w = cos_theta_w.abs();
|
||||||
|
}
|
||||||
|
let sin_theta_w = safe_sqrt(1. - square(cos_theta_w));
|
||||||
|
let cos_theta_b = DirectionCone::bound_subtended_directions(&self.bounds, p).cos_theta;
|
||||||
|
let sin_theta_b = safe_sqrt(1. - square(cos_theta_b));
|
||||||
|
let sin_theta_o = safe_sqrt(1. - square(self.cos_theta_o));
|
||||||
|
let cos_theta_x = cos_sub_clamped(sin_theta_w, cos_theta_w, sin_theta_o, self.cos_theta_o);
|
||||||
|
let sin_theta_x = sin_sub_clamped(sin_theta_w, cos_theta_w, sin_theta_o, self.cos_theta_o);
|
||||||
|
let cos_theta_p = cos_sub_clamped(sin_theta_x, cos_theta_x, sin_theta_b, cos_theta_b);
|
||||||
|
|
||||||
|
if cos_theta_p <= self.cos_theta_e {
|
||||||
|
return 0.;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut importance = self.phi * cos_theta_p / d2;
|
||||||
|
if n != Normal3f::new(0., 0., 0.) {
|
||||||
|
let cos_theta_i = wi.abs_dot(n.into());
|
||||||
|
let sin_theta_i = safe_sqrt(1. - square(cos_theta_i));
|
||||||
|
let cos_thetap_i = cos_sub_clamped(sin_theta_i, cos_theta_i, sin_theta_b, cos_theta_b);
|
||||||
|
importance *= cos_thetap_i;
|
||||||
|
}
|
||||||
|
importance
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn union(a: &Self, b: &Self) -> Self {
|
||||||
|
if a.phi == 0. {
|
||||||
|
return a.clone();
|
||||||
|
}
|
||||||
|
if b.phi == 0. {
|
||||||
|
return b.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
let a_cone = DirectionCone::new(a.w, a.cos_theta_o);
|
||||||
|
let b_cone = DirectionCone::new(b.w, b.cos_theta_o);
|
||||||
|
let cone = DirectionCone::union(&a_cone, &b_cone);
|
||||||
|
let cos_theta_o = cone.cos_theta;
|
||||||
|
let cos_theta_e = a.cos_theta_e.min(b.cos_theta_e);
|
||||||
|
LightBounds::new(
|
||||||
|
&a.bounds.union(b.bounds),
|
||||||
|
cone.w,
|
||||||
|
a.phi + b.phi,
|
||||||
|
cos_theta_o,
|
||||||
|
cos_theta_e,
|
||||||
|
a.two_sided || b.two_sided,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[enum_dispatch]
|
||||||
|
pub trait LightTrait: Send + Sync + std::fmt::Debug {
|
||||||
|
fn base(&self) -> &LightBase;
|
||||||
|
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum;
|
||||||
|
fn sample_li(
|
||||||
|
&self,
|
||||||
|
ctx: &LightSampleContext,
|
||||||
|
u: Point2f,
|
||||||
|
lambda: &SampledWavelengths,
|
||||||
|
allow_incomplete_pdf: bool,
|
||||||
|
) -> Option<LightLiSample>;
|
||||||
|
fn pdf_li(&self, ctx: &LightSampleContext, wi: Vector3f, allow_incomplete_pdf: bool) -> Float;
|
||||||
|
fn l(
|
||||||
|
&self,
|
||||||
|
p: Point3f,
|
||||||
|
n: Normal3f,
|
||||||
|
uv: Point2f,
|
||||||
|
w: Vector3f,
|
||||||
|
lambda: &SampledWavelengths,
|
||||||
|
) -> SampledSpectrum;
|
||||||
|
fn le(&self, ray: &Ray, lambda: &SampledWavelengths) -> SampledSpectrum;
|
||||||
|
fn preprocess(&mut self, scene_bounds: &Bounds3f);
|
||||||
|
fn bounds(&self) -> Option<LightBounds>;
|
||||||
|
fn light_type(&self) -> LightType {
|
||||||
|
self.base().light_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[enum_dispatch(LightTrait)]
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
|
pub enum Light {
|
||||||
|
DiffuseArea(DiffuseAreaLight),
|
||||||
|
Distant(DistantLight),
|
||||||
|
Goniometric(GoniometricLight),
|
||||||
|
InfiniteUniform(InfiniteUniformLight),
|
||||||
|
InfiniteImage(InfiniteImageLight),
|
||||||
|
InfinitePortal(InfinitePortalLight),
|
||||||
|
Point(PointLight),
|
||||||
|
Projection(ProjectionLight),
|
||||||
|
Spot(SpotLight),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct DistantLightData {
|
||||||
|
pub base: LightBaseData,
|
||||||
|
pub lemit_coeffs: [Float; 32],
|
||||||
|
pub scale: Float,
|
||||||
|
pub scene_center: Point3f,
|
||||||
|
pub scene_radius: Float,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DistantLightData {
|
||||||
|
pub fn sample_li(
|
||||||
|
&self,
|
||||||
|
ctx_p: Point3f,
|
||||||
|
lambda: &SampledWavelengths,
|
||||||
|
) -> (SampledSpectrum, Vector3f, Float, Point3f) {
|
||||||
|
let wi = self
|
||||||
|
.base
|
||||||
|
.render_from_light
|
||||||
|
.apply_to_vector(Vector3f::new(0., 0., 1.))
|
||||||
|
.normalize();
|
||||||
|
let p_outside = ctx_p + wi * 2. * self.scene_radius;
|
||||||
|
let spectrum = DenselySampledSpectrum::from_array(&self.lemit_coeffs);
|
||||||
|
let li = self.scale * spectrum.sample(lambda);
|
||||||
|
(li, wi, 1.0, p_outside)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightTrait for DistantLight {
|
||||||
|
fn base(&self) -> &LightBase {
|
||||||
|
&self.base
|
||||||
|
}
|
||||||
|
|
||||||
|
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
|
||||||
|
self.scale * self.lemit.sample(&lambda) * PI * self.scene_radius.sqrt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sample_li(
|
||||||
|
&self,
|
||||||
|
ctx: &LightSampleContext,
|
||||||
|
_u: Point2f,
|
||||||
|
lambda: &SampledWavelengths,
|
||||||
|
_allow_incomplete_pdf: bool,
|
||||||
|
) -> Option<LightLiSample> {
|
||||||
|
let wi = self
|
||||||
|
.base
|
||||||
|
.render_from_light
|
||||||
|
.apply_to_vector(Vector3f::new(0., 0., 1.))
|
||||||
|
.normalize();
|
||||||
|
let p_outside = ctx.p() + wi * 2. * self.scene_radius;
|
||||||
|
|
||||||
|
let li = self.scale * self.lemit.sample(lambda);
|
||||||
|
let intr = SimpleInteraction::new(
|
||||||
|
Point3fi::new_from_point(p_outside),
|
||||||
|
0.0,
|
||||||
|
Some(self.base.medium_interface.clone()),
|
||||||
|
);
|
||||||
|
|
||||||
|
Some(LightLiSample::new(li, wi, 1., Interaction::Simple(intr)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pdf_li(
|
||||||
|
&self,
|
||||||
|
_ctx: &LightSampleContext,
|
||||||
|
_wi: Vector3f,
|
||||||
|
_allow_incomplete_pdf: bool,
|
||||||
|
) -> Float {
|
||||||
|
0.
|
||||||
|
}
|
||||||
|
|
||||||
|
fn l(
|
||||||
|
&self,
|
||||||
|
_p: Point3f,
|
||||||
|
_n: Normal3f,
|
||||||
|
_uv: Point2f,
|
||||||
|
_w: Vector3f,
|
||||||
|
_lambda: &SampledWavelengths,
|
||||||
|
) -> SampledSpectrum {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn preprocess(&mut self, scene_bounds: &Bounds3f) {
|
||||||
|
let (center, radius) = scene_bounds.bounding_sphere();
|
||||||
|
self.scene_center = center;
|
||||||
|
self.scene_radius = radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bounds(&self) -> Option<LightBounds> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct GoniometricLight {
|
||||||
|
pub base: LightBase,
|
||||||
|
iemit: Arc<DenselySampledSpectrum>,
|
||||||
|
scale: Float,
|
||||||
|
image: Image,
|
||||||
|
distrib: PiecewiseConstant2D,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GoniometricLight {
|
||||||
|
pub fn new(
|
||||||
|
render_from_light: &TransformGeneric<Float>,
|
||||||
|
medium_interface: &MediumInterface,
|
||||||
|
iemit: Spectrum,
|
||||||
|
scale: Float,
|
||||||
|
image: Image,
|
||||||
|
) -> Self {
|
||||||
|
let base = LightBase::new(
|
||||||
|
LightType::DeltaPosition,
|
||||||
|
render_from_light,
|
||||||
|
medium_interface,
|
||||||
|
);
|
||||||
|
|
||||||
|
let i_interned = LightBase::lookup_spectrum(&iemit);
|
||||||
|
let d = image.get_sampling_distribution_uniform();
|
||||||
|
let distrib = PiecewiseConstant2D::new_with_data(&d);
|
||||||
|
Self {
|
||||||
|
base,
|
||||||
|
iemit: i_interned,
|
||||||
|
scale,
|
||||||
|
image,
|
||||||
|
distrib,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn i(&self, w: Vector3f, lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
|
let uv = equal_area_sphere_to_square(w);
|
||||||
|
self.scale * self.iemit.sample(lambda) * self.image.lookup_nearest_channel(uv, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightTrait for GoniometricLight {
|
||||||
|
fn base(&self) -> &LightBase {
|
||||||
|
&self.base
|
||||||
|
}
|
||||||
|
|
||||||
|
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
|
||||||
|
let mut sum_y = 0.;
|
||||||
|
for y in 0..self.image.resolution.y() {
|
||||||
|
for x in 0..self.image.resolution.x() {
|
||||||
|
sum_y += self.image.get_channel(Point2i::new(x, y), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.scale * self.iemit.sample(&lambda) * 4. * PI * sum_y
|
||||||
|
/ (self.image.resolution.x() * self.image.resolution.y()) as Float
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sample_li(
|
||||||
|
&self,
|
||||||
|
_ctx: &LightSampleContext,
|
||||||
|
_u: Point2f,
|
||||||
|
_lambda: &SampledWavelengths,
|
||||||
|
_allow_incomplete_pdf: bool,
|
||||||
|
) -> Option<LightLiSample> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pdf_li(
|
||||||
|
&self,
|
||||||
|
_ctx: &LightSampleContext,
|
||||||
|
_wi: Vector3f,
|
||||||
|
_allow_incomplete_pdf: bool,
|
||||||
|
) -> Float {
|
||||||
|
0.
|
||||||
|
}
|
||||||
|
|
||||||
|
fn l(
|
||||||
|
&self,
|
||||||
|
_p: Point3f,
|
||||||
|
_n: Normal3f,
|
||||||
|
_uv: Point2f,
|
||||||
|
_w: Vector3f,
|
||||||
|
_lambda: &SampledWavelengths,
|
||||||
|
) -> SampledSpectrum {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn bounds(&self) -> Option<LightBounds> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct PointLightData {
|
||||||
|
pub base: LightBaseData,
|
||||||
|
pub i_coeffs: [Float; 32],
|
||||||
|
pub scale: Float,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PointLightData {
|
||||||
|
pub fn sample_li(
|
||||||
|
&self,
|
||||||
|
ctx_p: Point3f,
|
||||||
|
lambda: &SampledWavelengths,
|
||||||
|
) -> (SampledSpectrum, Vector3f, Float, Point3fi) {
|
||||||
|
let pi = self
|
||||||
|
.base
|
||||||
|
.render_from_light
|
||||||
|
.apply_to_interval(&Point3fi::default());
|
||||||
|
let p: Point3f = pi.into();
|
||||||
|
let wi = (p - ctx_p).normalize();
|
||||||
|
let spectrum = DenselySampledSpectrum::from_array(&self.i_coeffs);
|
||||||
|
let li = self.scale * spectrum.sample(lambda) / p.distance_squared(ctx_p);
|
||||||
|
(li, wi, 1.0, pi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightTrait for PointLight {
|
||||||
|
fn base(&self) -> &LightBase {
|
||||||
|
&self.base
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sample_li(
|
||||||
|
&self,
|
||||||
|
ctx: &LightSampleContext,
|
||||||
|
_u: Point2f,
|
||||||
|
lambda: &SampledWavelengths,
|
||||||
|
_allow_incomplete_pdf: bool,
|
||||||
|
) -> Option<LightLiSample> {
|
||||||
|
let pi = self
|
||||||
|
.base
|
||||||
|
.render_from_light
|
||||||
|
.apply_to_interval(&Point3fi::default());
|
||||||
|
let p: Point3f = pi.into();
|
||||||
|
let wi = (p - ctx.p()).normalize();
|
||||||
|
let li = self.scale * self.i.sample(lambda) / p.distance_squared(ctx.p());
|
||||||
|
let intr = SimpleInteraction::new(pi, 0.0, Some(self.base.medium_interface.clone()));
|
||||||
|
Some(LightLiSample::new(li, wi, 1., Interaction::Simple(intr)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
|
||||||
|
4. * PI * self.scale * self.i.sample(&lambda)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pdf_li(
|
||||||
|
&self,
|
||||||
|
_ctx: &LightSampleContext,
|
||||||
|
_wi: Vector3f,
|
||||||
|
_allow_incomplete_pdf: bool,
|
||||||
|
) -> Float {
|
||||||
|
0.
|
||||||
|
}
|
||||||
|
|
||||||
|
fn l(
|
||||||
|
&self,
|
||||||
|
_p: Point3f,
|
||||||
|
_n: Normal3f,
|
||||||
|
_uv: Point2f,
|
||||||
|
_w: Vector3f,
|
||||||
|
_lambda: &SampledWavelengths,
|
||||||
|
) -> SampledSpectrum {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn bounds(&self) -> Option<LightBounds> {
|
||||||
|
let p = self
|
||||||
|
.base
|
||||||
|
.render_from_light
|
||||||
|
.apply_to_point(Point3f::new(0., 0., 0.));
|
||||||
|
let phi = 4. * PI * self.scale * self.i.max_value();
|
||||||
|
Some(LightBounds::new(
|
||||||
|
&Bounds3f::from_points(p, p),
|
||||||
|
Vector3f::new(0., 0., 1.),
|
||||||
|
phi,
|
||||||
|
PI.cos(),
|
||||||
|
(PI / 2.).cos(),
|
||||||
|
false,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct ProjectionLightPOD {
|
||||||
|
pub base: LightBasePOD,
|
||||||
|
pub scale: Float,
|
||||||
|
pub hither: Float,
|
||||||
|
pub screen_from_light: Transform,
|
||||||
|
pub screen_bounds: Bounds2f,
|
||||||
|
pub image_id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProjectionLight {
|
||||||
|
pub fn new(
|
||||||
|
render_from_light: TransformGeneric<Float>,
|
||||||
|
medium_interface: MediumInterface,
|
||||||
|
image: Image,
|
||||||
|
image_color_space: Arc<RGBColorSpace>,
|
||||||
|
scale: Float,
|
||||||
|
fov: Float,
|
||||||
|
) -> Self {
|
||||||
|
let base = LightBase::new(
|
||||||
|
LightType::DeltaPosition,
|
||||||
|
&render_from_light,
|
||||||
|
&medium_interface,
|
||||||
|
);
|
||||||
|
let aspect = image.resolution().x() as Float / image.resolution().y() as Float;
|
||||||
|
let screen_bounds = if aspect > 1. {
|
||||||
|
Bounds2f::from_points(Point2f::new(-aspect, -1.), Point2f::new(aspect, 1.))
|
||||||
|
} else {
|
||||||
|
Bounds2f::from_points(
|
||||||
|
Point2f::new(-1., 1. / aspect),
|
||||||
|
Point2f::new(1., 1. / aspect),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let hither = 1e-3;
|
||||||
|
let screen_from_light = TransformGeneric::perspective(fov, hither, 1e30).unwrap();
|
||||||
|
let light_from_screen = screen_from_light.inverse();
|
||||||
|
let opposite = (radians(fov) / 2.).tan();
|
||||||
|
let aspect_ratio = if aspect > 1. { aspect } else { 1. / aspect };
|
||||||
|
let a = 4. * square(opposite) * aspect_ratio;
|
||||||
|
let dwda = |p: Point2f| {
|
||||||
|
let w =
|
||||||
|
Vector3f::from(light_from_screen.apply_to_point(Point3f::new(p.x(), p.y(), 0.)));
|
||||||
|
cos_theta(w.normalize()).powi(3)
|
||||||
|
};
|
||||||
|
|
||||||
|
let d = image.get_sampling_distribution(dwda, screen_bounds);
|
||||||
|
let distrib = PiecewiseConstant2D::new_with_bounds(&d, screen_bounds);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
base,
|
||||||
|
image,
|
||||||
|
image_color_space,
|
||||||
|
screen_bounds,
|
||||||
|
screen_from_light,
|
||||||
|
light_from_screen,
|
||||||
|
scale,
|
||||||
|
hither,
|
||||||
|
a,
|
||||||
|
distrib,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn i(&self, w: Vector3f, lambda: SampledWavelengths) -> SampledSpectrum {
|
||||||
|
if w.z() < self.hither {
|
||||||
|
return SampledSpectrum::new(0.);
|
||||||
|
}
|
||||||
|
let ps = self.screen_from_light.apply_to_point(w.into());
|
||||||
|
if !self.screen_bounds.contains(Point2f::new(ps.x(), ps.y())) {
|
||||||
|
return SampledSpectrum::new(0.);
|
||||||
|
}
|
||||||
|
let uv = Point2f::from(self.screen_bounds.offset(&Point2f::new(ps.x(), ps.y())));
|
||||||
|
let mut rgb = RGB::default();
|
||||||
|
for c in 0..3 {
|
||||||
|
rgb[c] = self.image.lookup_nearest_channel(uv, c);
|
||||||
|
}
|
||||||
|
let s = RGBIlluminantSpectrum::new(self.image_color_space.as_ref(), RGB::clamp_zero(rgb));
|
||||||
|
self.scale * s.sample(&lambda)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightTrait for ProjectionLight {
|
||||||
|
fn base(&self) -> &LightBase {
|
||||||
|
&self.base
|
||||||
|
}
|
||||||
|
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
|
||||||
|
let mut sum = SampledSpectrum::new(0.);
|
||||||
|
for y in 0..self.image.resolution.y() {
|
||||||
|
for x in 0..self.image.resolution.x() {
|
||||||
|
let ps = self.screen_bounds.lerp(Point2f::new(
|
||||||
|
(x as Float + 0.5) / self.image.resolution.x() as Float,
|
||||||
|
(y as Float + 0.5) / self.image.resolution.y() as Float,
|
||||||
|
));
|
||||||
|
let w_raw = Vector3f::from(self.light_from_screen.apply_to_point(Point3f::new(
|
||||||
|
ps.x(),
|
||||||
|
ps.y(),
|
||||||
|
0.,
|
||||||
|
)));
|
||||||
|
let w = w_raw.normalize();
|
||||||
|
let dwda = cos_theta(w).powi(3);
|
||||||
|
let mut rgb = RGB::default();
|
||||||
|
for c in 0..3 {
|
||||||
|
rgb[c] = self.image.get_channel(Point2i::new(x, y), c);
|
||||||
|
}
|
||||||
|
|
||||||
|
let s = RGBIlluminantSpectrum::new(
|
||||||
|
self.image_color_space.as_ref(),
|
||||||
|
RGB::clamp_zero(rgb),
|
||||||
|
);
|
||||||
|
sum += s.sample(&lambda) * dwda;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.scale * self.a * sum / (self.image.resolution.x() * self.image.resolution.y()) as Float
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sample_li(
|
||||||
|
&self,
|
||||||
|
_ctx: &LightSampleContext,
|
||||||
|
_u: Point2f,
|
||||||
|
_lambda: &SampledWavelengths,
|
||||||
|
_allow_incomplete_pdf: bool,
|
||||||
|
) -> Option<LightLiSample> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn pdf_li(
|
||||||
|
&self,
|
||||||
|
_ctx: &LightSampleContext,
|
||||||
|
_wi: Vector3f,
|
||||||
|
_allow_incomplete_pdf: bool,
|
||||||
|
) -> Float {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn l(
|
||||||
|
&self,
|
||||||
|
_p: Point3f,
|
||||||
|
_n: Normal3f,
|
||||||
|
_uv: Point2f,
|
||||||
|
_w: Vector3f,
|
||||||
|
_lambda: &SampledWavelengths,
|
||||||
|
) -> SampledSpectrum {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn bounds(&self) -> Option<LightBounds> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct SpotLightData {
|
||||||
|
pub base: LightBaseData,
|
||||||
|
pub iemit_coeffs: [Float; 32],
|
||||||
|
pub scale: Float,
|
||||||
|
pub cos_falloff_start: Float,
|
||||||
|
pub cos_falloff_end: Float,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpotLightData {
|
||||||
|
pub fn i(&self, w: Vector3f, lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
|
let cos_theta = w.z(); // assuming normalized in light space
|
||||||
|
let falloff = crate::utils::math::smooth_step(
|
||||||
|
cos_theta,
|
||||||
|
self.cos_falloff_end,
|
||||||
|
self.cos_falloff_start,
|
||||||
|
);
|
||||||
|
let spectrum = DenselySampledSpectrum::from_array(&self.iemit_coeffs);
|
||||||
|
falloff * self.scale * spectrum.sample(lambda)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightTrait for SpotLight {
|
||||||
|
fn base(&self) -> &LightBase {
|
||||||
|
&self.base
|
||||||
|
}
|
||||||
|
fn sample_li(
|
||||||
|
&self,
|
||||||
|
ctx: &LightSampleContext,
|
||||||
|
_u: Point2f,
|
||||||
|
lambda: &SampledWavelengths,
|
||||||
|
_allow_incomplete_pdf: bool,
|
||||||
|
) -> Option<LightLiSample> {
|
||||||
|
let pi = self
|
||||||
|
.base
|
||||||
|
.render_from_light
|
||||||
|
.apply_to_interval(&Point3fi::default());
|
||||||
|
let p: Point3f = pi.into();
|
||||||
|
let wi = (p - ctx.p()).normalize();
|
||||||
|
let w_light = self.base.render_from_light.apply_inverse_vector(-wi);
|
||||||
|
let li = self.i(w_light, *lambda) / p.distance_squared(ctx.p());
|
||||||
|
|
||||||
|
let intr = SimpleInteraction::new(pi, 0.0, Some(self.base.medium_interface.clone()));
|
||||||
|
Some(LightLiSample::new(li, wi, 1., Interaction::Simple(intr)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
|
||||||
|
self.scale
|
||||||
|
* self.iemit.sample(&lambda)
|
||||||
|
* 2.
|
||||||
|
* PI
|
||||||
|
* ((1. - self.cos_fallof_start) + (self.cos_fallof_start - self.cos_fallof_end) / 2.)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pdf_li(
|
||||||
|
&self,
|
||||||
|
_ctx: &LightSampleContext,
|
||||||
|
_wi: Vector3f,
|
||||||
|
_allow_incomplete_pdf: bool,
|
||||||
|
) -> Float {
|
||||||
|
0.
|
||||||
|
}
|
||||||
|
|
||||||
|
fn l(
|
||||||
|
&self,
|
||||||
|
_p: Point3f,
|
||||||
|
_n: Normal3f,
|
||||||
|
_uv: Point2f,
|
||||||
|
_w: Vector3f,
|
||||||
|
_lambda: &SampledWavelengths,
|
||||||
|
) -> SampledSpectrum {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bounds(&self) -> Option<LightBounds> {
|
||||||
|
let p = self
|
||||||
|
.base
|
||||||
|
.render_from_light
|
||||||
|
.apply_to_point(Point3f::default());
|
||||||
|
let w = self
|
||||||
|
.base
|
||||||
|
.render_from_light
|
||||||
|
.apply_to_vector(Vector3f::new(0., 0., 1.))
|
||||||
|
.normalize();
|
||||||
|
let phi = self.scale * self.iemit.max_value() * 4. * PI;
|
||||||
|
let cos_theta_e = (self.cos_fallof_end.acos() - self.cos_fallof_start.acos()).cos();
|
||||||
|
Some(LightBounds::new(
|
||||||
|
&Bounds3f::from_points(p, p),
|
||||||
|
w,
|
||||||
|
phi,
|
||||||
|
self.cos_fallof_start,
|
||||||
|
cos_theta_e,
|
||||||
|
false,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -884,11 +884,10 @@ impl MaterialTrait for MixMaterial {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_displacement(&self) -> Option<FloatTexture> {
|
fn get_displacement(&self) -> Option<FloatTexture> {
|
||||||
None
|
panic!(
|
||||||
// panic!(
|
"MixMaterial::get_displacement() shouldn't be called. \
|
||||||
// "MixMaterial::get_displacement() shouldn't be called. \
|
Displacement is not supported on Mix materials directly."
|
||||||
// Displacement is not supported on Mix materials directly."
|
);
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_surface_scattering(&self) -> bool {
|
fn has_surface_scattering(&self) -> bool {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
pub mod aggregates;
|
pub mod aggregates;
|
||||||
pub mod bssrdf;
|
pub mod bssrdf;
|
||||||
pub mod bxdf;
|
pub mod bxdf;
|
||||||
|
pub mod camera;
|
||||||
pub mod film;
|
pub mod film;
|
||||||
pub mod filter;
|
pub mod filter;
|
||||||
pub mod geometry;
|
pub mod geometry;
|
||||||
pub mod interaction;
|
pub mod interaction;
|
||||||
|
pub mod light;
|
||||||
pub mod material;
|
pub mod material;
|
||||||
pub mod medium;
|
pub mod medium;
|
||||||
pub mod options;
|
pub mod options;
|
||||||
|
|
@ -12,4 +14,5 @@ pub mod pbrt;
|
||||||
pub mod primitive;
|
pub mod primitive;
|
||||||
pub mod sampler;
|
pub mod sampler;
|
||||||
pub mod scattering;
|
pub mod scattering;
|
||||||
|
pub mod spectrum;
|
||||||
pub mod texture;
|
pub mod texture;
|
||||||
|
|
|
||||||
|
|
@ -141,30 +141,22 @@ pub fn gamma(n: i32) -> Float {
|
||||||
pub static RARE_EVENT_TOTAL_CALLS: AtomicU64 = AtomicU64::new(0);
|
pub static RARE_EVENT_TOTAL_CALLS: AtomicU64 = AtomicU64::new(0);
|
||||||
pub static RARE_EVENT_CONDITION_MET: AtomicU64 = AtomicU64::new(0);
|
pub static RARE_EVENT_CONDITION_MET: AtomicU64 = AtomicU64::new(0);
|
||||||
|
|
||||||
// A macro to encapsulate the logic
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! check_rare {
|
macro_rules! check_rare {
|
||||||
($frequency_threshold:expr, $condition:expr) => {
|
($frequency_threshold:expr, $condition:expr) => {
|
||||||
// We only run the check periodically to reduce overhead.
|
|
||||||
// PBRT's implementation does something similar.
|
|
||||||
const CHECK_INTERVAL: u64 = 4096;
|
const CHECK_INTERVAL: u64 = 4096;
|
||||||
|
|
||||||
// Atomically increment the total call counter.
|
|
||||||
// Ordering::Relaxed is fine because we don't need to synchronize memory.
|
|
||||||
let total_calls = RARE_EVENT_TOTAL_CALLS.fetch_add(1, SyncOrdering::Relaxed);
|
let total_calls = RARE_EVENT_TOTAL_CALLS.fetch_add(1, SyncOrdering::Relaxed);
|
||||||
|
|
||||||
if $condition {
|
if $condition {
|
||||||
// If the rare condition is met, increment that counter.
|
|
||||||
RARE_EVENT_CONDITION_MET.fetch_add(1, SyncOrdering::Relaxed);
|
RARE_EVENT_CONDITION_MET.fetch_add(1, SyncOrdering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only perform the expensive division and check periodically.
|
|
||||||
if (total_calls + 1) % CHECK_INTERVAL == 0 {
|
if (total_calls + 1) % CHECK_INTERVAL == 0 {
|
||||||
let met_count = RARE_EVENT_CONDITION_MET.load(SyncOrdering::Relaxed);
|
let met_count = RARE_EVENT_CONDITION_MET.load(SyncOrdering::Relaxed);
|
||||||
if met_count > 0 {
|
if met_count > 0 {
|
||||||
let frequency = met_count as f64 / (total_calls + 1) as f64;
|
let frequency = met_count as f64 / (total_calls + 1) as f64;
|
||||||
if frequency > $frequency_threshold {
|
if frequency > $frequency_threshold {
|
||||||
// In a real scenario, you might log an error or panic.
|
|
||||||
panic!(
|
panic!(
|
||||||
"Rare event occurred with frequency {} which is > threshold {}",
|
"Rare event occurred with frequency {} which is > threshold {}",
|
||||||
frequency, $frequency_threshold
|
frequency, $frequency_threshold
|
||||||
|
|
|
||||||
0
shared/src/core/spectrum.rs
Normal file
0
shared/src/core/spectrum.rs
Normal file
|
|
@ -4,23 +4,16 @@ use crate::core::geometry::{
|
||||||
use crate::core::interaction::{Interaction, InteractionTrait, SurfaceInteraction};
|
use crate::core::interaction::{Interaction, InteractionTrait, SurfaceInteraction};
|
||||||
use crate::core::pbrt::Float;
|
use crate::core::pbrt::Float;
|
||||||
use crate::core::pbrt::{INV_2_PI, INV_PI, PI};
|
use crate::core::pbrt::{INV_2_PI, INV_PI, PI};
|
||||||
use crate::image::WrapMode;
|
use crate::images::WrapMode;
|
||||||
use crate::spectra::color::ColorEncoding;
|
use crate::spectra::color::ColorEncoding;
|
||||||
use crate::spectra::{
|
use crate::spectra::{
|
||||||
RGB, RGBAlbedoSpectrum, RGBIlluminantSpectrum, RGBUnboundedSpectrum, SampledSpectrum,
|
RGB, RGBAlbedoSpectrum, RGBIlluminantSpectrum, RGBUnboundedSpectrum, SampledSpectrum,
|
||||||
SampledWavelengths, Spectrum, SpectrumProvider,
|
SampledWavelengths, Spectrum, SpectrumProvider,
|
||||||
};
|
};
|
||||||
use crate::utils::Transform;
|
use crate::utils::Transform;
|
||||||
use crate::utils::error::FileLoc;
|
|
||||||
use crate::utils::math::square;
|
use crate::utils::math::square;
|
||||||
use crate::utils::mipmap::MIPMap;
|
|
||||||
use crate::utils::mipmap::{MIPMapFilterOptions, MIPMapSample};
|
|
||||||
use crate::utils::parameters::TextureParameterDictionary;
|
|
||||||
use crate::utils::transform::TransformGeneric;
|
use crate::utils::transform::TransformGeneric;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::sync::{Arc, Mutex, OnceLock};
|
|
||||||
|
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
|
@ -46,46 +39,6 @@ pub enum TextureMapping2D {
|
||||||
Planar(PlanarMapping),
|
Planar(PlanarMapping),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextureMapping2D {
|
|
||||||
pub fn create(
|
|
||||||
params: &TextureParameterDictionary,
|
|
||||||
render_from_texture: &Transform,
|
|
||||||
loc: &FileLoc,
|
|
||||||
) -> Self {
|
|
||||||
let mtype = params.get_one_string("mapping", "uv");
|
|
||||||
match mtype.as_str() {
|
|
||||||
"uv" => {
|
|
||||||
let su = params.get_one_float("uscale", 1.);
|
|
||||||
let sv = params.get_one_float("vscale", 1.);
|
|
||||||
let du = params.get_one_float("udelta", 0.);
|
|
||||||
let dv = params.get_one_float("vdelta", 0.);
|
|
||||||
let mapping = UVMapping::new(su, sv, du, dv);
|
|
||||||
TextureMapping2D::UV(mapping)
|
|
||||||
}
|
|
||||||
"spherical" => {
|
|
||||||
let mapping = SphericalMapping::new(&render_from_texture.inverse());
|
|
||||||
TextureMapping2D::Spherical(mapping)
|
|
||||||
}
|
|
||||||
"cylindrical" => {
|
|
||||||
let mapping = CylindricalMapping::new(&render_from_texture.inverse());
|
|
||||||
TextureMapping2D::Cylindrical(mapping)
|
|
||||||
}
|
|
||||||
"planar" => {
|
|
||||||
let vs = params.get_one_vector3f("v1", Vector3f::new(1., 0., 0.));
|
|
||||||
let vt = params.get_one_vector3f("v2", Vector3f::new(0., 1., 0.));
|
|
||||||
let ds = params.get_one_float("udelta", 0.);
|
|
||||||
let dt = params.get_one_float("vdelta", 0.);
|
|
||||||
let mapping = PlanarMapping::new(&render_from_texture.inverse(), vs, vt, ds, dt);
|
|
||||||
TextureMapping2D::Planar(mapping)
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
log::error!("{}: 2D texture mapping unknown {}", loc, mtype);
|
|
||||||
TextureMapping2D::UV(UVMapping::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct UVMapping {
|
pub struct UVMapping {
|
||||||
su: Float,
|
su: Float,
|
||||||
|
|
@ -377,342 +330,6 @@ pub trait FloatTextureTrait: Send + Sync + std::fmt::Debug {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch(FloatTextureTrait)]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum FloatTexture {
|
|
||||||
FloatConstant(FloatConstantTexture),
|
|
||||||
GPUFloatImage(GPUFloatImageTexture),
|
|
||||||
FloatImage(FloatImageTexture),
|
|
||||||
FloatMix(FloatMixTexture),
|
|
||||||
FloatDirectionMix(FloatDirectionMixTexture),
|
|
||||||
FloatScaled(FloatScaledTexture),
|
|
||||||
FloatBilerp(FloatBilerpTexture),
|
|
||||||
FloatCheckerboard(FloatCheckerboardTexture),
|
|
||||||
FloatDots(FloatDotsTexture),
|
|
||||||
FBm(FBmTexture),
|
|
||||||
FloatPtex(FloatPtexTexture),
|
|
||||||
GPUFLoatPtex(GPUFloatPtexTexture),
|
|
||||||
Windy(WindyTexture),
|
|
||||||
Wrinkled(WrinkledTexture),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FloatTexture {
|
|
||||||
pub fn create(
|
|
||||||
name: &str,
|
|
||||||
render_from_texture: &Transform,
|
|
||||||
params: &TextureParameterDictionary,
|
|
||||||
loc: &FileLoc,
|
|
||||||
gpu: bool,
|
|
||||||
) -> Result<Self, String> {
|
|
||||||
match name {
|
|
||||||
"constant" => {
|
|
||||||
let tex = FloatConstantTexture::create(render_from_texture, params, loc);
|
|
||||||
Ok(FloatTexture::FloatConstant(tex))
|
|
||||||
}
|
|
||||||
"scale" => Ok(FloatScaledTexture::create(render_from_texture, params, loc)),
|
|
||||||
"mix" => {
|
|
||||||
let tex = FloatMixTexture::create(render_from_texture, params, loc);
|
|
||||||
Ok(FloatTexture::FloatMix(tex))
|
|
||||||
}
|
|
||||||
"directionmix" => {
|
|
||||||
let tex = FloatDirectionMixTexture::create(render_from_texture, params, loc);
|
|
||||||
Ok(FloatTexture::FloatDirectionMix(tex))
|
|
||||||
}
|
|
||||||
"bilerp" => {
|
|
||||||
let tex = FloatBilerpTexture::create(render_from_texture, params, loc);
|
|
||||||
Ok(FloatTexture::FloatBilerp(tex))
|
|
||||||
}
|
|
||||||
"imagemap" => {
|
|
||||||
if gpu {
|
|
||||||
let tex = GPUFloatImageTexture::create(render_from_texture, params, loc);
|
|
||||||
Ok(FloatTexture::GPUFloatImage(tex))
|
|
||||||
} else {
|
|
||||||
let tex = FloatImageTexture::create(render_from_texture, params, loc);
|
|
||||||
Ok(FloatTexture::FloatImage(tex))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"checkerboard" => {
|
|
||||||
let tex = FloatCheckerboardTexture::create(render_from_texture, params, loc);
|
|
||||||
Ok(FloatTexture::FloatCheckerboard(tex))
|
|
||||||
}
|
|
||||||
"dots" => {
|
|
||||||
let tex = FloatDotsTexture::create(render_from_texture, params, loc);
|
|
||||||
Ok(FloatTexture::FloatDots(tex))
|
|
||||||
}
|
|
||||||
"fbm" => {
|
|
||||||
let tex = FBmTexture::create(render_from_texture, params, loc);
|
|
||||||
Ok(FloatTexture::FBm(tex))
|
|
||||||
}
|
|
||||||
"wrinkled" => {
|
|
||||||
let tex = WrinkledTexture::create(render_from_texture, params, loc);
|
|
||||||
Ok(FloatTexture::Wrinkled(tex))
|
|
||||||
}
|
|
||||||
"windy" => {
|
|
||||||
let tex = WindyTexture::create(render_from_texture, params, loc);
|
|
||||||
Ok(FloatTexture::Windy(tex))
|
|
||||||
}
|
|
||||||
"ptex" => {
|
|
||||||
if gpu {
|
|
||||||
let tex = GPUFloatPtexTexture::create(render_from_texture, params, loc);
|
|
||||||
Ok(FloatTexture::GPUFLoatPtex(tex))
|
|
||||||
} else {
|
|
||||||
let tex = FloatPtexTexture::create(render_from_texture, params, loc);
|
|
||||||
Ok(FloatTexture::FloatPtex(tex))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
_ => Err(format!("Float texture type '{}' unknown at {}", name, loc)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct FloatConstantTexture {
|
|
||||||
value: Float,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FloatConstantTexture {
|
|
||||||
pub fn new(value: Float) -> Self {
|
|
||||||
Self { value }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create(
|
|
||||||
_render_from_texture: &Transform,
|
|
||||||
params: &TextureParameterDictionary,
|
|
||||||
_loc: &FileLoc,
|
|
||||||
) -> Self {
|
|
||||||
Self::new(params.get_one_float("value", 1.0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FloatTextureTrait for FloatConstantTexture {
|
|
||||||
fn evaluate(&self, _ctx: &TextureEvalContext) -> Float {
|
|
||||||
self.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct GPUFloatImageTexture {
|
|
||||||
mapping: TextureMapping2D,
|
|
||||||
filename: String,
|
|
||||||
scale: Float,
|
|
||||||
invert: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FloatTextureTrait for GPUFloatImageTexture {}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct FloatImageTexture {
|
|
||||||
base: ImageTextureBase,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FloatImageTexture {
|
|
||||||
pub fn new(
|
|
||||||
mapping: TextureMapping2D,
|
|
||||||
filename: String,
|
|
||||||
filter_options: MIPMapFilterOptions,
|
|
||||||
wrap_mode: WrapMode,
|
|
||||||
scale: Float,
|
|
||||||
invert: bool,
|
|
||||||
encoding: ColorEncoding,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
base: ImageTextureBase::new(
|
|
||||||
mapping,
|
|
||||||
filename,
|
|
||||||
filter_options,
|
|
||||||
wrap_mode,
|
|
||||||
scale,
|
|
||||||
invert,
|
|
||||||
encoding,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FloatTextureTrait for FloatImageTexture {}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct FloatMixTexture {
|
|
||||||
tex1: Arc<FloatTexture>,
|
|
||||||
tex2: Arc<FloatTexture>,
|
|
||||||
amount: Arc<FloatTexture>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FloatMixTexture {
|
|
||||||
pub fn new(
|
|
||||||
tex1: Arc<FloatTexture>,
|
|
||||||
tex2: Arc<FloatTexture>,
|
|
||||||
amount: Arc<FloatTexture>,
|
|
||||||
) -> Self {
|
|
||||||
Self { tex1, tex2, amount }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create(
|
|
||||||
_render_from_texture: &Transform,
|
|
||||||
params: &TextureParameterDictionary,
|
|
||||||
_loc: &FileLoc,
|
|
||||||
) -> Self {
|
|
||||||
let tex1 = params.get_float_texture("tex1", 0.);
|
|
||||||
let tex2 = params.get_float_texture("tex2", 1.);
|
|
||||||
let amount = params.get_float_texture("amount", 0.5);
|
|
||||||
Self::new(tex1, tex2, amount)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FloatTextureTrait for FloatMixTexture {
|
|
||||||
fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
|
|
||||||
let amt = self.amount.evaluate(ctx);
|
|
||||||
let mut t1 = 0.;
|
|
||||||
let mut t2 = 0.;
|
|
||||||
if amt != 1. {
|
|
||||||
t1 = self.tex1.evaluate(ctx);
|
|
||||||
}
|
|
||||||
if amt != 0. {
|
|
||||||
t2 = self.tex2.evaluate(ctx);
|
|
||||||
}
|
|
||||||
(1. - amt) * t1 + amt * t2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct FloatDirectionMixTexture {
|
|
||||||
tex1: Arc<FloatTexture>,
|
|
||||||
tex2: Arc<FloatTexture>,
|
|
||||||
dir: Vector3f,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FloatDirectionMixTexture {
|
|
||||||
pub fn new(tex1: Arc<FloatTexture>, tex2: Arc<FloatTexture>, dir: Vector3f) -> Self {
|
|
||||||
Self { tex1, tex2, dir }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create(
|
|
||||||
render_from_texture: &Transform,
|
|
||||||
params: &TextureParameterDictionary,
|
|
||||||
_loc: &FileLoc,
|
|
||||||
) -> Self {
|
|
||||||
let dir_raw = params.get_one_vector3f("dir", Vector3f::new(0., 1., 0.));
|
|
||||||
let dir = render_from_texture.apply_to_vector(dir_raw).normalize();
|
|
||||||
let tex1 = params.get_float_texture("tex1", 0.);
|
|
||||||
let tex2 = params.get_float_texture("tex2", 1.);
|
|
||||||
Self::new(tex1, tex2, dir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FloatTextureTrait for FloatDirectionMixTexture {}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct FloatScaledTexture {
|
|
||||||
tex: Arc<FloatTexture>,
|
|
||||||
scale: Arc<FloatTexture>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FloatScaledTexture {
|
|
||||||
pub fn new(tex: Arc<FloatTexture>, scale: Arc<FloatTexture>) -> Self {
|
|
||||||
Self { tex, scale }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create(
|
|
||||||
_render_from_texture: &Transform,
|
|
||||||
params: &TextureParameterDictionary,
|
|
||||||
_loc: &FileLoc,
|
|
||||||
) -> FloatTexture {
|
|
||||||
let mut tex = params.get_float_texture("tex", 1.);
|
|
||||||
let mut scale = params.get_float_texture("scale", 1.);
|
|
||||||
|
|
||||||
for _ in 0..2 {
|
|
||||||
if let FloatTexture::FloatConstant(c_tex) = &*scale {
|
|
||||||
let cs = c_tex.value;
|
|
||||||
if cs == 1.0 {
|
|
||||||
return (*tex).clone();
|
|
||||||
} else if let FloatTexture::FloatImage(img_tex) = &*tex {
|
|
||||||
let mut image_copy = img_tex.clone();
|
|
||||||
image_copy.base.multiply_scale(cs);
|
|
||||||
return FloatTexture::FloatImage(image_copy).into();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::mem::swap(&mut tex, &mut scale);
|
|
||||||
}
|
|
||||||
std::mem::swap(&mut tex, &mut scale);
|
|
||||||
FloatTexture::FloatScaled(FloatScaledTexture::new(tex, scale))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FloatTextureTrait for FloatScaledTexture {
|
|
||||||
fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
|
|
||||||
let sc = self.scale.evaluate(ctx);
|
|
||||||
if sc == 0. {
|
|
||||||
return 0.;
|
|
||||||
}
|
|
||||||
self.tex.evaluate(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct FloatBilerpTexture {
|
|
||||||
mapping: TextureMapping2D,
|
|
||||||
v00: Float,
|
|
||||||
v01: Float,
|
|
||||||
v10: Float,
|
|
||||||
v11: Float,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FloatBilerpTexture {
|
|
||||||
pub fn new(mapping: TextureMapping2D, v00: Float, v01: Float, v10: Float, v11: Float) -> Self {
|
|
||||||
Self {
|
|
||||||
mapping,
|
|
||||||
v00,
|
|
||||||
v01,
|
|
||||||
v10,
|
|
||||||
v11,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create(
|
|
||||||
render_from_texture: &Transform,
|
|
||||||
params: &TextureParameterDictionary,
|
|
||||||
loc: &FileLoc,
|
|
||||||
) -> Self {
|
|
||||||
let mapping = TextureMapping2D::create(params, render_from_texture, loc);
|
|
||||||
let v00 = params.get_one_float("v00", 0.);
|
|
||||||
let v01 = params.get_one_float("v01", 1.);
|
|
||||||
let v10 = params.get_one_float("v10", 0.);
|
|
||||||
let v11 = params.get_one_float("v11", 1.);
|
|
||||||
Self::new(mapping, v00, v01, v10, v11)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FloatTextureTrait for FloatBilerpTexture {}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct FloatCheckerboardTexture;
|
|
||||||
impl FloatTextureTrait for FloatCheckerboardTexture {}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct FloatDotsTexture;
|
|
||||||
impl FloatTextureTrait for FloatDotsTexture {}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct FBmTexture;
|
|
||||||
impl FloatTextureTrait for FBmTexture {}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct FloatPtexTexture;
|
|
||||||
impl FloatTextureTrait for FloatPtexTexture {}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct GPUFloatPtexTexture;
|
|
||||||
impl FloatTextureTrait for GPUFloatPtexTexture {}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct WindyTexture;
|
|
||||||
impl FloatTextureTrait for WindyTexture {}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct WrinkledTexture;
|
|
||||||
impl FloatTextureTrait for WrinkledTexture {}
|
|
||||||
|
|
||||||
#[enum_dispatch]
|
#[enum_dispatch]
|
||||||
pub trait SpectrumTextureTrait: Send + Sync + std::fmt::Debug {
|
pub trait SpectrumTextureTrait: Send + Sync + std::fmt::Debug {
|
||||||
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
|
|
@ -720,72 +337,20 @@ pub trait SpectrumTextureTrait: Send + Sync + std::fmt::Debug {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub enum GPUFloatTexture {
|
||||||
|
Constant(Float),
|
||||||
|
Ptex(GPUFloatPtexTexture),
|
||||||
|
Image(GPUFloatImageTexture),
|
||||||
|
Mix(GPUFloatMixTexture),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
#[enum_dispatch(SpectrumTextureTrait)]
|
#[enum_dispatch(SpectrumTextureTrait)]
|
||||||
pub enum SpectrumTexture {
|
pub enum SpectrumTexture {
|
||||||
RGBConstant(RGBConstantTexture),
|
Image(GPUSpectrumImageTexture),
|
||||||
RGBReflectanceConstant(RGBReflectanceConstantTexture),
|
Ptex(GPUSpectrumPtexTexture),
|
||||||
SpectrumConstant(SpectrumConstantTexture),
|
Mix(GPUSpectrumMixTexture),
|
||||||
SpectrumBilerp(SpectrumBilerpTexture),
|
|
||||||
SpectrumCheckerboard(SpectrumCheckerboardTexture),
|
|
||||||
SpectrumImage(SpectrumImageTexture),
|
|
||||||
GPUSpectrumImage(GPUSpectrumImageTexture),
|
|
||||||
Marble(MarbleTexture),
|
|
||||||
SpectrumMix(SpectrumMixTexture),
|
|
||||||
SpectrumDirectionMix(SpectrumDirectionMixTexture),
|
|
||||||
SpectrumDots(SpectrumDotsTexture),
|
|
||||||
SpectrumPtex(SpectrumPtexTexture),
|
|
||||||
GPUSpectrumPtex(GPUSpectrumPtexTexture),
|
|
||||||
SpectrumScaled(SpectrumScaledTexture),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct RGBConstantTexture;
|
|
||||||
impl SpectrumTextureTrait for RGBConstantTexture {
|
|
||||||
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct RGBReflectanceConstantTexture;
|
|
||||||
impl SpectrumTextureTrait for RGBReflectanceConstantTexture {
|
|
||||||
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct SpectrumConstantTexture {
|
|
||||||
value: Spectrum,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpectrumConstantTexture {
|
|
||||||
pub fn new(value: Spectrum) -> Self {
|
|
||||||
Self { value }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpectrumTextureTrait for SpectrumConstantTexture {
|
|
||||||
fn evaluate(&self, _ctx: &TextureEvalContext, lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
self.value.sample(lambda)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct SpectrumBilerpTexture;
|
|
||||||
impl SpectrumTextureTrait for SpectrumBilerpTexture {
|
|
||||||
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct SpectrumCheckerboardTexture;
|
|
||||||
impl SpectrumTextureTrait for SpectrumCheckerboardTexture {
|
|
||||||
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
|
@ -794,237 +359,6 @@ pub enum SpectrumType {
|
||||||
Albedo,
|
Albedo,
|
||||||
Unbounded,
|
Unbounded,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct SpectrumImageTexture {
|
|
||||||
base: ImageTextureBase,
|
|
||||||
spectrum_type: SpectrumType,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpectrumImageTexture {
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn new(
|
|
||||||
mapping: TextureMapping2D,
|
|
||||||
filename: String,
|
|
||||||
filter_options: MIPMapFilterOptions,
|
|
||||||
wrap_mode: WrapMode,
|
|
||||||
scale: Float,
|
|
||||||
invert: bool,
|
|
||||||
encoding: ColorEncoding,
|
|
||||||
spectrum_type: SpectrumType,
|
|
||||||
) -> Self {
|
|
||||||
let base = ImageTextureBase::new(
|
|
||||||
mapping,
|
|
||||||
filename,
|
|
||||||
filter_options,
|
|
||||||
wrap_mode,
|
|
||||||
scale,
|
|
||||||
invert,
|
|
||||||
encoding,
|
|
||||||
);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
base,
|
|
||||||
spectrum_type,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpectrumTextureTrait for SpectrumImageTexture {
|
|
||||||
fn evaluate(&self, ctx: &TextureEvalContext, lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
let mut c = self.base.mapping.map(ctx);
|
|
||||||
c.st[1] = 1. - c.st[1];
|
|
||||||
let dst0 = Vector2f::new(c.dsdx, c.dtdx);
|
|
||||||
let dst1 = Vector2f::new(c.dsdy, c.dtdy);
|
|
||||||
let rgb_unclamp = self.base.scale * self.base.mipmap.filter::<RGB>(c.st, dst0, dst1);
|
|
||||||
let rgb = RGB::clamp_zero(rgb_unclamp);
|
|
||||||
if let Some(cs) = self.base.mipmap.get_rgb_colorspace() {
|
|
||||||
match self.spectrum_type {
|
|
||||||
SpectrumType::Unbounded => {
|
|
||||||
return RGBUnboundedSpectrum::new(&cs, rgb).sample(lambda);
|
|
||||||
}
|
|
||||||
SpectrumType::Albedo => {
|
|
||||||
return RGBAlbedoSpectrum::new(&cs, rgb).sample(lambda);
|
|
||||||
}
|
|
||||||
_ => return RGBIlluminantSpectrum::new(&cs, rgb).sample(lambda),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert!(rgb[0] == rgb[1] && rgb[1] == rgb[2]);
|
|
||||||
SampledSpectrum::new(rgb[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct GPUSpectrumImageTexture;
|
|
||||||
impl SpectrumTextureTrait for GPUSpectrumImageTexture {
|
|
||||||
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct MarbleTexture;
|
|
||||||
impl SpectrumTextureTrait for MarbleTexture {
|
|
||||||
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct SpectrumMixTexture;
|
|
||||||
impl SpectrumTextureTrait for SpectrumMixTexture {
|
|
||||||
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct SpectrumDirectionMixTexture {
|
|
||||||
tex1: Box<SpectrumTexture>,
|
|
||||||
tex2: Box<SpectrumTexture>,
|
|
||||||
dir: Vector3f,
|
|
||||||
}
|
|
||||||
impl SpectrumTextureTrait for SpectrumDirectionMixTexture {
|
|
||||||
fn evaluate(&self, ctx: &TextureEvalContext, lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
let amt = ctx.n.abs_dot(self.dir.into());
|
|
||||||
let mut t1 = SampledSpectrum::default();
|
|
||||||
let mut t2 = SampledSpectrum::default();
|
|
||||||
if amt != 0. {
|
|
||||||
t1 = self.tex1.evaluate(ctx, lambda);
|
|
||||||
}
|
|
||||||
if amt != 1. {
|
|
||||||
t2 = self.tex2.evaluate(ctx, lambda);
|
|
||||||
}
|
|
||||||
amt * t1 + (1. - amt) * t2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct SpectrumDotsTexture;
|
|
||||||
impl SpectrumTextureTrait for SpectrumDotsTexture {
|
|
||||||
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct SpectrumPtexTexture;
|
|
||||||
impl SpectrumTextureTrait for SpectrumPtexTexture {
|
|
||||||
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct GPUSpectrumPtexTexture;
|
|
||||||
impl SpectrumTextureTrait for GPUSpectrumPtexTexture {
|
|
||||||
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct SpectrumScaledTexture {
|
|
||||||
tex: Box<SpectrumTexture>,
|
|
||||||
scale: Box<FloatTexture>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpectrumTextureTrait for SpectrumScaledTexture {
|
|
||||||
fn evaluate(&self, ctx: &TextureEvalContext, lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
let sc = self.scale.evaluate(ctx);
|
|
||||||
if sc == 0. {
|
|
||||||
return SampledSpectrum::new(0.);
|
|
||||||
}
|
|
||||||
self.tex.evaluate(ctx, lambda) * sc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
|
|
||||||
struct TexInfo {
|
|
||||||
filename: String,
|
|
||||||
filter_options: MIPMapFilterOptions,
|
|
||||||
wrap_mode: WrapMode,
|
|
||||||
encoding: ColorEncoding,
|
|
||||||
}
|
|
||||||
|
|
||||||
static TEXTURE_CACHE: OnceLock<Mutex<HashMap<TexInfo, Arc<MIPMap>>>> = OnceLock::new();
|
|
||||||
|
|
||||||
fn get_texture_cache() -> &'static Mutex<HashMap<TexInfo, Arc<MIPMap>>> {
|
|
||||||
TEXTURE_CACHE.get_or_init(|| Mutex::new(HashMap::new()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct ImageTextureBase {
|
|
||||||
mapping: TextureMapping2D,
|
|
||||||
filename: String,
|
|
||||||
scale: Float,
|
|
||||||
invert: bool,
|
|
||||||
mipmap: Arc<MIPMap>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ImageTextureBase {
|
|
||||||
pub fn new(
|
|
||||||
mapping: TextureMapping2D,
|
|
||||||
filename: String,
|
|
||||||
filter_options: MIPMapFilterOptions,
|
|
||||||
wrap_mode: WrapMode,
|
|
||||||
scale: Float,
|
|
||||||
invert: bool,
|
|
||||||
encoding: ColorEncoding,
|
|
||||||
) -> Self {
|
|
||||||
let tex_info = TexInfo {
|
|
||||||
filename: filename.clone(),
|
|
||||||
filter_options,
|
|
||||||
wrap_mode,
|
|
||||||
encoding,
|
|
||||||
};
|
|
||||||
|
|
||||||
let cache_mutex = get_texture_cache();
|
|
||||||
|
|
||||||
{
|
|
||||||
let cache = cache_mutex.lock().unwrap();
|
|
||||||
if let Some(mipmap) = cache.get(&tex_info) {
|
|
||||||
return Self {
|
|
||||||
mapping,
|
|
||||||
filename,
|
|
||||||
scale,
|
|
||||||
invert,
|
|
||||||
mipmap: mipmap.clone(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let path = Path::new(&filename);
|
|
||||||
let mipmap_raw = MIPMap::create_from_file(path, filter_options, wrap_mode, encoding)
|
|
||||||
.expect("Failed to create MIPMap from file");
|
|
||||||
|
|
||||||
let mipmap_arc = Arc::new(mipmap_raw);
|
|
||||||
|
|
||||||
{
|
|
||||||
let mut cache = cache_mutex.lock().unwrap();
|
|
||||||
|
|
||||||
let stored_mipmap = cache.entry(tex_info).or_insert(mipmap_arc);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
mapping,
|
|
||||||
filename,
|
|
||||||
scale,
|
|
||||||
invert,
|
|
||||||
mipmap: stored_mipmap.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear_cache() {
|
|
||||||
let mut cache = get_texture_cache().lock().unwrap();
|
|
||||||
cache.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn multiply_scale(&mut self, s: Float) {
|
|
||||||
self.scale *= s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait TextureEvaluator: Send + Sync {
|
pub trait TextureEvaluator: Send + Sync {
|
||||||
fn evaluate_float(&self, tex: &FloatTexture, ctx: &TextureEvalContext) -> Float;
|
fn evaluate_float(&self, tex: &FloatTexture, ctx: &TextureEvalContext) -> Float;
|
||||||
fn evaluate_spectrum(
|
fn evaluate_spectrum(
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,15 @@
|
||||||
#![feature(float_erf)]
|
#![feature(float_erf)]
|
||||||
#![feature(f16)]
|
#![feature(f16)]
|
||||||
|
|
||||||
mod camera;
|
mod cameras;
|
||||||
mod core;
|
mod core;
|
||||||
mod data;
|
mod data;
|
||||||
mod image;
|
mod images;
|
||||||
mod integrators;
|
mod integrators;
|
||||||
mod lights;
|
mod lights;
|
||||||
mod shapes;
|
mod shapes;
|
||||||
mod spectra;
|
mod spectra;
|
||||||
|
mod textures;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
pub use core::pbrt::*;
|
pub use core::pbrt::*;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::core::geometry::*;
|
use crate::core::geometry::*;
|
||||||
use crate::core::pbrt::Float;
|
use crate::core::pbrt::Float;
|
||||||
use crate::core::spectra::*;
|
// use crate::core::texture::GpuFloatTextureHandle;
|
||||||
use crate::shapes::GpuShapeHandle;
|
// use crate::shapes::GpuShapeHandle;
|
||||||
use crate::textures::GpuFloatTextureHandle;
|
use crate::spectra::*;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Default)]
|
#[derive(Clone, Copy, Default)]
|
||||||
|
|
|
||||||
|
|
@ -18,17 +18,18 @@ use crate::image::{PixelFormat, WrapMode};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Arc, Bounds2f, Bounds3f, DenselySampledSpectrum, Float, Image, Interaction, LightBase,
|
Arc, Bounds2f, Bounds3f, DenselySampledSpectrum, Float, Image, Interaction, LightBaseData,
|
||||||
LightBounds, LightLiSample, LightSampleContext, LightTrait, LightType, MediumInterface,
|
LightBounds, LightLiSample, LightSampleContext, LightTrait, LightType, MediumInterface,
|
||||||
Normal3f, PI, Point2f, Point2i, Point3f, Ray, SampledSpectrum, SampledWavelengths,
|
Normal3f, PI, Point2f, Point2i, Point3f, Ray, SampledSpectrum, SampledWavelengths,
|
||||||
SimpleInteraction, Spectrum, TransformGeneric, Vector3f, VectorLike,
|
SimpleInteraction, Spectrum, TransformGeneric, Vector3f, VectorLike,
|
||||||
equal_area_sphere_to_square, square,
|
equal_area_sphere_to_square, square,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct InfiniteUniformLight {
|
pub struct InfiniteUniformLight {
|
||||||
base: LightBase,
|
base: LightBaseData,
|
||||||
lemit: Arc<DenselySampledSpectrum>,
|
lemit: u32,
|
||||||
scale: Float,
|
scale: Float,
|
||||||
scene_center: Point3f,
|
scene_center: Point3f,
|
||||||
scene_radius: Float,
|
scene_radius: Float,
|
||||||
|
|
@ -124,7 +125,7 @@ impl LightTrait for InfiniteUniformLight {
|
||||||
pub struct InfiniteImageLight {
|
pub struct InfiniteImageLight {
|
||||||
base: LightBase,
|
base: LightBase,
|
||||||
image: Image,
|
image: Image,
|
||||||
image_color_space: Arc<RGBColorSpace>,
|
image_color_space: u32,
|
||||||
scale: Float,
|
scale: Float,
|
||||||
scene_radius: Float,
|
scene_radius: Float,
|
||||||
scene_center: Point3f,
|
scene_center: Point3f,
|
||||||
|
|
@ -313,7 +314,7 @@ impl LightTrait for InfiniteImageLight {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct InfinitePortalLight {
|
pub struct InfinitePortalLight {
|
||||||
pub base: LightBase,
|
pub base: LightBaseData,
|
||||||
pub image: Image,
|
pub image: Image,
|
||||||
pub image_color_space: Arc<RGBColorSpace>,
|
pub image_color_space: Arc<RGBColorSpace>,
|
||||||
pub scale: Float,
|
pub scale: Float,
|
||||||
|
|
|
||||||
|
|
@ -1,919 +1,3 @@
|
||||||
pub mod diffuse;
|
pub mod diffuse;
|
||||||
pub mod infinite;
|
pub mod infinite;
|
||||||
pub mod sampler;
|
pub mod sampler;
|
||||||
|
|
||||||
use crate::core::geometry::{
|
|
||||||
Bounds2f, Bounds3f, DirectionCone, Normal3f, Point2f, Point2i, Point3f, Point3fi, Ray,
|
|
||||||
Vector3f, VectorLike, cos_theta,
|
|
||||||
};
|
|
||||||
use crate::core::interaction::{
|
|
||||||
Interaction, InteractionTrait, MediumInteraction, SimpleInteraction, SurfaceInteraction,
|
|
||||||
};
|
|
||||||
use crate::core::medium::MediumInterface;
|
|
||||||
use crate::core::pbrt::{Float, PI};
|
|
||||||
use crate::image::Image;
|
|
||||||
use crate::spectra::{
|
|
||||||
DenselySampledSpectrum, LAMBDA_MAX, LAMBDA_MIN, RGB, RGBColorSpace, RGBIlluminantSpectrum,
|
|
||||||
SampledSpectrum, SampledWavelengths, Spectrum, SpectrumProvider,
|
|
||||||
};
|
|
||||||
use crate::utils::containers::InternCache;
|
|
||||||
use crate::utils::math::{equal_area_sphere_to_square, radians, safe_sqrt, smooth_step, square};
|
|
||||||
use crate::utils::sampling::PiecewiseConstant2D;
|
|
||||||
use crate::utils::transform::TransformGeneric;
|
|
||||||
use bitflags::bitflags;
|
|
||||||
|
|
||||||
use enum_dispatch::enum_dispatch;
|
|
||||||
use std::sync::{Arc, OnceLock};
|
|
||||||
|
|
||||||
use diffuse::DiffuseAreaLight;
|
|
||||||
use infinite::{InfiniteImageLight, InfinitePortalLight, InfiniteUniformLight};
|
|
||||||
|
|
||||||
pub use sampler::{LightSampler, LightSamplerTrait};
|
|
||||||
|
|
||||||
static SPECTRUM_CACHE: OnceLock<InternCache<DenselySampledSpectrum>> = OnceLock::new();
|
|
||||||
|
|
||||||
fn get_spectrum_cache() -> &'static InternCache<DenselySampledSpectrum> {
|
|
||||||
SPECTRUM_CACHE.get_or_init(InternCache::new)
|
|
||||||
}
|
|
||||||
|
|
||||||
bitflags! {
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub struct LightType: u32 {
|
|
||||||
const DeltaPosition = 1;
|
|
||||||
const DeltaDirection = 2;
|
|
||||||
const Area = 4;
|
|
||||||
const Infinite = 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LightType {
|
|
||||||
pub fn is_infinite(&self) -> bool {
|
|
||||||
self.contains(LightType::Infinite)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_delta_light(&self) -> bool {
|
|
||||||
self.contains(LightType::DeltaPosition) || self.contains(LightType::DeltaDirection)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct LightLeSample {
|
|
||||||
l: SampledSpectrum,
|
|
||||||
ray: Ray,
|
|
||||||
intr: Option<Interaction>,
|
|
||||||
pdf_pos: Float,
|
|
||||||
pdf_dir: Float,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Default, Clone)]
|
|
||||||
pub struct LightSampleContext {
|
|
||||||
pub pi: Point3fi,
|
|
||||||
pub n: Normal3f,
|
|
||||||
pub ns: Normal3f,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LightSampleContext {
|
|
||||||
pub fn new(pi: Point3fi, n: Normal3f, ns: Normal3f) -> Self {
|
|
||||||
Self { pi, n, ns }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn p(&self) -> Point3f {
|
|
||||||
self.pi.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&SurfaceInteraction> for LightSampleContext {
|
|
||||||
fn from(si: &SurfaceInteraction) -> Self {
|
|
||||||
Self {
|
|
||||||
pi: si.common.pi,
|
|
||||||
n: si.common.n,
|
|
||||||
ns: si.shading.n,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&MediumInteraction> for LightSampleContext {
|
|
||||||
fn from(mi: &MediumInteraction) -> Self {
|
|
||||||
Self {
|
|
||||||
pi: mi.common.pi,
|
|
||||||
n: Normal3f::default(),
|
|
||||||
ns: Normal3f::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&Interaction> for LightSampleContext {
|
|
||||||
fn from(intr: &Interaction) -> Self {
|
|
||||||
match intr {
|
|
||||||
Interaction::Surface(si) => Self {
|
|
||||||
pi: si.common.pi,
|
|
||||||
n: si.common.n,
|
|
||||||
ns: si.shading.n,
|
|
||||||
},
|
|
||||||
|
|
||||||
Interaction::Medium(mi) => Self {
|
|
||||||
pi: mi.common.pi,
|
|
||||||
n: mi.common.n,
|
|
||||||
ns: mi.common.n,
|
|
||||||
},
|
|
||||||
|
|
||||||
Interaction::Simple(sim) => Self {
|
|
||||||
pi: sim.common.pi,
|
|
||||||
n: sim.common.n,
|
|
||||||
ns: sim.common.n,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct LightLiSample {
|
|
||||||
pub l: SampledSpectrum,
|
|
||||||
pub wi: Vector3f,
|
|
||||||
pub pdf: Float,
|
|
||||||
pub p_light: Arc<Interaction>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LightLiSample {
|
|
||||||
pub fn new(l: SampledSpectrum, wi: Vector3f, pdf: Float, p_light: Interaction) -> Self {
|
|
||||||
Self {
|
|
||||||
l,
|
|
||||||
wi,
|
|
||||||
pdf,
|
|
||||||
p_light: Arc::new(p_light),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct LightBase {
|
|
||||||
pub light_type: LightType,
|
|
||||||
pub render_from_light: TransformGeneric<Float>,
|
|
||||||
pub medium_interface: MediumInterface,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LightBase {
|
|
||||||
pub fn new(
|
|
||||||
light_type: LightType,
|
|
||||||
render_from_light: &TransformGeneric<Float>,
|
|
||||||
medium_interface: &MediumInterface,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
light_type,
|
|
||||||
render_from_light: *render_from_light,
|
|
||||||
medium_interface: medium_interface.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn l(
|
|
||||||
&self,
|
|
||||||
_p: Point3f,
|
|
||||||
_n: Normal3f,
|
|
||||||
_uv: Point2f,
|
|
||||||
_w: Vector3f,
|
|
||||||
_lambda: &SampledWavelengths,
|
|
||||||
) -> SampledSpectrum {
|
|
||||||
SampledSpectrum::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn light_type(&self) -> LightType {
|
|
||||||
self.light_type
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
SampledSpectrum::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lookup_spectrum(s: &Spectrum) -> Arc<DenselySampledSpectrum> {
|
|
||||||
let cache = SPECTRUM_CACHE.get_or_init(InternCache::new);
|
|
||||||
let dense_spectrum = DenselySampledSpectrum::from_spectrum(s);
|
|
||||||
cache.lookup(dense_spectrum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct LightBounds {
|
|
||||||
bounds: Bounds3f,
|
|
||||||
phi: Float,
|
|
||||||
w: Vector3f,
|
|
||||||
cos_theta_o: Float,
|
|
||||||
cos_theta_e: Float,
|
|
||||||
two_sided: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LightBounds {
|
|
||||||
pub fn new(
|
|
||||||
bounds: &Bounds3f,
|
|
||||||
w: Vector3f,
|
|
||||||
phi: Float,
|
|
||||||
cos_theta_o: Float,
|
|
||||||
cos_theta_e: Float,
|
|
||||||
two_sided: bool,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
bounds: *bounds,
|
|
||||||
phi,
|
|
||||||
w,
|
|
||||||
cos_theta_o,
|
|
||||||
cos_theta_e,
|
|
||||||
two_sided,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn centroid(&self) -> Point3f {
|
|
||||||
self.bounds.p_min + Vector3f::from(self.bounds.p_max) / 2.
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn importance(&self, p: Point3f, n: Normal3f) -> Float {
|
|
||||||
// Compute clamped squared distance to reference point
|
|
||||||
let pc = self.centroid();
|
|
||||||
let d2_raw = p.distance_squared(pc);
|
|
||||||
let d2 = d2_raw.max(self.bounds.diagonal().norm()) / 2.;
|
|
||||||
let cos_sub_clamped = |sin_theta_a: Float,
|
|
||||||
cos_theta_a: Float,
|
|
||||||
sin_theta_b: Float,
|
|
||||||
cos_theta_b: Float|
|
|
||||||
-> Float {
|
|
||||||
if cos_theta_a > cos_theta_b {
|
|
||||||
return 1.;
|
|
||||||
}
|
|
||||||
cos_theta_a * cos_theta_b + sin_theta_a * sin_theta_b
|
|
||||||
};
|
|
||||||
|
|
||||||
let sin_sub_clamped = |sin_theta_a: Float,
|
|
||||||
cos_theta_a: Float,
|
|
||||||
sin_theta_b: Float,
|
|
||||||
cos_theta_b: Float|
|
|
||||||
-> Float {
|
|
||||||
if cos_theta_a > cos_theta_b {
|
|
||||||
return 1.;
|
|
||||||
}
|
|
||||||
sin_theta_a * cos_theta_b - cos_theta_a * sin_theta_b
|
|
||||||
};
|
|
||||||
|
|
||||||
let wi = (p - pc).normalize();
|
|
||||||
let mut cos_theta_w = self.w.dot(wi);
|
|
||||||
if self.two_sided {
|
|
||||||
cos_theta_w = cos_theta_w.abs();
|
|
||||||
}
|
|
||||||
let sin_theta_w = safe_sqrt(1. - square(cos_theta_w));
|
|
||||||
let cos_theta_b = DirectionCone::bound_subtended_directions(&self.bounds, p).cos_theta;
|
|
||||||
let sin_theta_b = safe_sqrt(1. - square(cos_theta_b));
|
|
||||||
let sin_theta_o = safe_sqrt(1. - square(self.cos_theta_o));
|
|
||||||
let cos_theta_x = cos_sub_clamped(sin_theta_w, cos_theta_w, sin_theta_o, self.cos_theta_o);
|
|
||||||
let sin_theta_x = sin_sub_clamped(sin_theta_w, cos_theta_w, sin_theta_o, self.cos_theta_o);
|
|
||||||
let cos_theta_p = cos_sub_clamped(sin_theta_x, cos_theta_x, sin_theta_b, cos_theta_b);
|
|
||||||
|
|
||||||
if cos_theta_p <= self.cos_theta_e {
|
|
||||||
return 0.;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut importance = self.phi * cos_theta_p / d2;
|
|
||||||
if n != Normal3f::new(0., 0., 0.) {
|
|
||||||
let cos_theta_i = wi.abs_dot(n.into());
|
|
||||||
let sin_theta_i = safe_sqrt(1. - square(cos_theta_i));
|
|
||||||
let cos_thetap_i = cos_sub_clamped(sin_theta_i, cos_theta_i, sin_theta_b, cos_theta_b);
|
|
||||||
importance *= cos_thetap_i;
|
|
||||||
}
|
|
||||||
importance
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn union(a: &Self, b: &Self) -> Self {
|
|
||||||
if a.phi == 0. {
|
|
||||||
return a.clone();
|
|
||||||
}
|
|
||||||
if b.phi == 0. {
|
|
||||||
return b.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
let a_cone = DirectionCone::new(a.w, a.cos_theta_o);
|
|
||||||
let b_cone = DirectionCone::new(b.w, b.cos_theta_o);
|
|
||||||
let cone = DirectionCone::union(&a_cone, &b_cone);
|
|
||||||
let cos_theta_o = cone.cos_theta;
|
|
||||||
let cos_theta_e = a.cos_theta_e.min(b.cos_theta_e);
|
|
||||||
LightBounds::new(
|
|
||||||
&a.bounds.union(b.bounds),
|
|
||||||
cone.w,
|
|
||||||
a.phi + b.phi,
|
|
||||||
cos_theta_o,
|
|
||||||
cos_theta_e,
|
|
||||||
a.two_sided || b.two_sided,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[enum_dispatch]
|
|
||||||
pub trait LightTrait: Send + Sync + std::fmt::Debug {
|
|
||||||
fn base(&self) -> &LightBase;
|
|
||||||
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum;
|
|
||||||
fn sample_li(
|
|
||||||
&self,
|
|
||||||
ctx: &LightSampleContext,
|
|
||||||
u: Point2f,
|
|
||||||
lambda: &SampledWavelengths,
|
|
||||||
allow_incomplete_pdf: bool,
|
|
||||||
) -> Option<LightLiSample>;
|
|
||||||
fn pdf_li(&self, ctx: &LightSampleContext, wi: Vector3f, allow_incomplete_pdf: bool) -> Float;
|
|
||||||
fn l(
|
|
||||||
&self,
|
|
||||||
p: Point3f,
|
|
||||||
n: Normal3f,
|
|
||||||
uv: Point2f,
|
|
||||||
w: Vector3f,
|
|
||||||
lambda: &SampledWavelengths,
|
|
||||||
) -> SampledSpectrum;
|
|
||||||
fn le(&self, ray: &Ray, lambda: &SampledWavelengths) -> SampledSpectrum;
|
|
||||||
fn preprocess(&mut self, scene_bounds: &Bounds3f);
|
|
||||||
fn bounds(&self) -> Option<LightBounds>;
|
|
||||||
fn light_type(&self) -> LightType {
|
|
||||||
self.base().light_type()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
#[enum_dispatch(LightTrait)]
|
|
||||||
#[allow(clippy::large_enum_variant)]
|
|
||||||
pub enum Light {
|
|
||||||
DiffuseArea(DiffuseAreaLight),
|
|
||||||
Distant(DistantLight),
|
|
||||||
Goniometric(GoniometricLight),
|
|
||||||
InfiniteUniform(InfiniteUniformLight),
|
|
||||||
InfiniteImage(InfiniteImageLight),
|
|
||||||
InfinitePortal(InfinitePortalLight),
|
|
||||||
Point(PointLight),
|
|
||||||
Projection(ProjectionLight),
|
|
||||||
Spot(SpotLight),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct DistantLight {
|
|
||||||
pub base: LightBase,
|
|
||||||
lemit: Arc<DenselySampledSpectrum>,
|
|
||||||
scale: Float,
|
|
||||||
scene_center: Point3f,
|
|
||||||
scene_radius: Float,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DistantLight {
|
|
||||||
pub fn new(render_from_light: &TransformGeneric<Float>, lemit: Spectrum, scale: Float) -> Self {
|
|
||||||
let base = LightBase::new(
|
|
||||||
LightType::DeltaDirection,
|
|
||||||
render_from_light,
|
|
||||||
&MediumInterface::default(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let l_interned = LightBase::lookup_spectrum(&lemit);
|
|
||||||
Self {
|
|
||||||
base,
|
|
||||||
lemit: l_interned,
|
|
||||||
scale,
|
|
||||||
scene_center: Point3f::default(),
|
|
||||||
scene_radius: 0.,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LightTrait for DistantLight {
|
|
||||||
fn base(&self) -> &LightBase {
|
|
||||||
&self.base
|
|
||||||
}
|
|
||||||
|
|
||||||
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
|
|
||||||
self.scale * self.lemit.sample(&lambda) * PI * self.scene_radius.sqrt()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sample_li(
|
|
||||||
&self,
|
|
||||||
ctx: &LightSampleContext,
|
|
||||||
_u: Point2f,
|
|
||||||
lambda: &SampledWavelengths,
|
|
||||||
_allow_incomplete_pdf: bool,
|
|
||||||
) -> Option<LightLiSample> {
|
|
||||||
let wi = self
|
|
||||||
.base
|
|
||||||
.render_from_light
|
|
||||||
.apply_to_vector(Vector3f::new(0., 0., 1.))
|
|
||||||
.normalize();
|
|
||||||
let p_outside = ctx.p() + wi * 2. * self.scene_radius;
|
|
||||||
|
|
||||||
let li = self.scale * self.lemit.sample(lambda);
|
|
||||||
let intr = SimpleInteraction::new(
|
|
||||||
Point3fi::new_from_point(p_outside),
|
|
||||||
0.0,
|
|
||||||
Some(self.base.medium_interface.clone()),
|
|
||||||
);
|
|
||||||
|
|
||||||
Some(LightLiSample::new(li, wi, 1., Interaction::Simple(intr)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pdf_li(
|
|
||||||
&self,
|
|
||||||
_ctx: &LightSampleContext,
|
|
||||||
_wi: Vector3f,
|
|
||||||
_allow_incomplete_pdf: bool,
|
|
||||||
) -> Float {
|
|
||||||
0.
|
|
||||||
}
|
|
||||||
|
|
||||||
fn l(
|
|
||||||
&self,
|
|
||||||
_p: Point3f,
|
|
||||||
_n: Normal3f,
|
|
||||||
_uv: Point2f,
|
|
||||||
_w: Vector3f,
|
|
||||||
_lambda: &SampledWavelengths,
|
|
||||||
) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn preprocess(&mut self, scene_bounds: &Bounds3f) {
|
|
||||||
let (center, radius) = scene_bounds.bounding_sphere();
|
|
||||||
self.scene_center = center;
|
|
||||||
self.scene_radius = radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bounds(&self) -> Option<LightBounds> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct GoniometricLight {
|
|
||||||
pub base: LightBase,
|
|
||||||
iemit: Arc<DenselySampledSpectrum>,
|
|
||||||
scale: Float,
|
|
||||||
image: Image,
|
|
||||||
distrib: PiecewiseConstant2D,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GoniometricLight {
|
|
||||||
pub fn new(
|
|
||||||
render_from_light: &TransformGeneric<Float>,
|
|
||||||
medium_interface: &MediumInterface,
|
|
||||||
iemit: Spectrum,
|
|
||||||
scale: Float,
|
|
||||||
image: Image,
|
|
||||||
) -> Self {
|
|
||||||
let base = LightBase::new(
|
|
||||||
LightType::DeltaPosition,
|
|
||||||
render_from_light,
|
|
||||||
medium_interface,
|
|
||||||
);
|
|
||||||
|
|
||||||
let i_interned = LightBase::lookup_spectrum(&iemit);
|
|
||||||
let d = image.get_sampling_distribution_uniform();
|
|
||||||
let distrib = PiecewiseConstant2D::new_with_data(&d);
|
|
||||||
Self {
|
|
||||||
base,
|
|
||||||
iemit: i_interned,
|
|
||||||
scale,
|
|
||||||
image,
|
|
||||||
distrib,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn i(&self, w: Vector3f, lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
let uv = equal_area_sphere_to_square(w);
|
|
||||||
self.scale * self.iemit.sample(lambda) * self.image.lookup_nearest_channel(uv, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LightTrait for GoniometricLight {
|
|
||||||
fn base(&self) -> &LightBase {
|
|
||||||
&self.base
|
|
||||||
}
|
|
||||||
|
|
||||||
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
|
|
||||||
let mut sum_y = 0.;
|
|
||||||
for y in 0..self.image.resolution.y() {
|
|
||||||
for x in 0..self.image.resolution.x() {
|
|
||||||
sum_y += self.image.get_channel(Point2i::new(x, y), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.scale * self.iemit.sample(&lambda) * 4. * PI * sum_y
|
|
||||||
/ (self.image.resolution.x() * self.image.resolution.y()) as Float
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sample_li(
|
|
||||||
&self,
|
|
||||||
_ctx: &LightSampleContext,
|
|
||||||
_u: Point2f,
|
|
||||||
_lambda: &SampledWavelengths,
|
|
||||||
_allow_incomplete_pdf: bool,
|
|
||||||
) -> Option<LightLiSample> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pdf_li(
|
|
||||||
&self,
|
|
||||||
_ctx: &LightSampleContext,
|
|
||||||
_wi: Vector3f,
|
|
||||||
_allow_incomplete_pdf: bool,
|
|
||||||
) -> Float {
|
|
||||||
0.
|
|
||||||
}
|
|
||||||
|
|
||||||
fn l(
|
|
||||||
&self,
|
|
||||||
_p: Point3f,
|
|
||||||
_n: Normal3f,
|
|
||||||
_uv: Point2f,
|
|
||||||
_w: Vector3f,
|
|
||||||
_lambda: &SampledWavelengths,
|
|
||||||
) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn bounds(&self) -> Option<LightBounds> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct PointLight {
|
|
||||||
base: LightBase,
|
|
||||||
i: Arc<DenselySampledSpectrum>,
|
|
||||||
scale: Float,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PointLight {
|
|
||||||
pub fn new(
|
|
||||||
render_from_light: TransformGeneric<Float>,
|
|
||||||
medium_interface: MediumInterface,
|
|
||||||
i: &Spectrum,
|
|
||||||
scale: Float,
|
|
||||||
) -> Self {
|
|
||||||
let base = LightBase::new(
|
|
||||||
LightType::DeltaPosition,
|
|
||||||
&render_from_light,
|
|
||||||
&medium_interface,
|
|
||||||
);
|
|
||||||
|
|
||||||
let i_interned = LightBase::lookup_spectrum(i);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
base,
|
|
||||||
i: i_interned,
|
|
||||||
scale,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LightTrait for PointLight {
|
|
||||||
fn base(&self) -> &LightBase {
|
|
||||||
&self.base
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sample_li(
|
|
||||||
&self,
|
|
||||||
ctx: &LightSampleContext,
|
|
||||||
_u: Point2f,
|
|
||||||
lambda: &SampledWavelengths,
|
|
||||||
_allow_incomplete_pdf: bool,
|
|
||||||
) -> Option<LightLiSample> {
|
|
||||||
let pi = self
|
|
||||||
.base
|
|
||||||
.render_from_light
|
|
||||||
.apply_to_interval(&Point3fi::default());
|
|
||||||
let p: Point3f = pi.into();
|
|
||||||
let wi = (p - ctx.p()).normalize();
|
|
||||||
let li = self.scale * self.i.sample(lambda) / p.distance_squared(ctx.p());
|
|
||||||
let intr = SimpleInteraction::new(pi, 0.0, Some(self.base.medium_interface.clone()));
|
|
||||||
Some(LightLiSample::new(li, wi, 1., Interaction::Simple(intr)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
|
|
||||||
4. * PI * self.scale * self.i.sample(&lambda)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pdf_li(
|
|
||||||
&self,
|
|
||||||
_ctx: &LightSampleContext,
|
|
||||||
_wi: Vector3f,
|
|
||||||
_allow_incomplete_pdf: bool,
|
|
||||||
) -> Float {
|
|
||||||
0.
|
|
||||||
}
|
|
||||||
|
|
||||||
fn l(
|
|
||||||
&self,
|
|
||||||
_p: Point3f,
|
|
||||||
_n: Normal3f,
|
|
||||||
_uv: Point2f,
|
|
||||||
_w: Vector3f,
|
|
||||||
_lambda: &SampledWavelengths,
|
|
||||||
) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn bounds(&self) -> Option<LightBounds> {
|
|
||||||
let p = self
|
|
||||||
.base
|
|
||||||
.render_from_light
|
|
||||||
.apply_to_point(Point3f::new(0., 0., 0.));
|
|
||||||
let phi = 4. * PI * self.scale * self.i.max_value();
|
|
||||||
Some(LightBounds::new(
|
|
||||||
&Bounds3f::from_points(p, p),
|
|
||||||
Vector3f::new(0., 0., 1.),
|
|
||||||
phi,
|
|
||||||
PI.cos(),
|
|
||||||
(PI / 2.).cos(),
|
|
||||||
false,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct ProjectionLight {
|
|
||||||
base: LightBase,
|
|
||||||
image: Image,
|
|
||||||
image_color_space: Arc<RGBColorSpace>,
|
|
||||||
scale: Float,
|
|
||||||
screen_bounds: Bounds2f,
|
|
||||||
hither: Float,
|
|
||||||
screen_from_light: TransformGeneric<Float>,
|
|
||||||
light_from_screen: TransformGeneric<Float>,
|
|
||||||
a: Float,
|
|
||||||
distrib: PiecewiseConstant2D,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ProjectionLight {
|
|
||||||
pub fn new(
|
|
||||||
render_from_light: TransformGeneric<Float>,
|
|
||||||
medium_interface: MediumInterface,
|
|
||||||
image: Image,
|
|
||||||
image_color_space: Arc<RGBColorSpace>,
|
|
||||||
scale: Float,
|
|
||||||
fov: Float,
|
|
||||||
) -> Self {
|
|
||||||
let base = LightBase::new(
|
|
||||||
LightType::DeltaPosition,
|
|
||||||
&render_from_light,
|
|
||||||
&medium_interface,
|
|
||||||
);
|
|
||||||
let aspect = image.resolution().x() as Float / image.resolution().y() as Float;
|
|
||||||
let screen_bounds = if aspect > 1. {
|
|
||||||
Bounds2f::from_points(Point2f::new(-aspect, -1.), Point2f::new(aspect, 1.))
|
|
||||||
} else {
|
|
||||||
Bounds2f::from_points(
|
|
||||||
Point2f::new(-1., 1. / aspect),
|
|
||||||
Point2f::new(1., 1. / aspect),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let hither = 1e-3;
|
|
||||||
let screen_from_light = TransformGeneric::perspective(fov, hither, 1e30).unwrap();
|
|
||||||
let light_from_screen = screen_from_light.inverse();
|
|
||||||
let opposite = (radians(fov) / 2.).tan();
|
|
||||||
let aspect_ratio = if aspect > 1. { aspect } else { 1. / aspect };
|
|
||||||
let a = 4. * square(opposite) * aspect_ratio;
|
|
||||||
let dwda = |p: Point2f| {
|
|
||||||
let w =
|
|
||||||
Vector3f::from(light_from_screen.apply_to_point(Point3f::new(p.x(), p.y(), 0.)));
|
|
||||||
cos_theta(w.normalize()).powi(3)
|
|
||||||
};
|
|
||||||
|
|
||||||
let d = image.get_sampling_distribution(dwda, screen_bounds);
|
|
||||||
let distrib = PiecewiseConstant2D::new_with_bounds(&d, screen_bounds);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
base,
|
|
||||||
image,
|
|
||||||
image_color_space,
|
|
||||||
screen_bounds,
|
|
||||||
screen_from_light,
|
|
||||||
light_from_screen,
|
|
||||||
scale,
|
|
||||||
hither,
|
|
||||||
a,
|
|
||||||
distrib,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn i(&self, w: Vector3f, lambda: SampledWavelengths) -> SampledSpectrum {
|
|
||||||
if w.z() < self.hither {
|
|
||||||
return SampledSpectrum::new(0.);
|
|
||||||
}
|
|
||||||
let ps = self.screen_from_light.apply_to_point(w.into());
|
|
||||||
if !self.screen_bounds.contains(Point2f::new(ps.x(), ps.y())) {
|
|
||||||
return SampledSpectrum::new(0.);
|
|
||||||
}
|
|
||||||
let uv = Point2f::from(self.screen_bounds.offset(&Point2f::new(ps.x(), ps.y())));
|
|
||||||
let mut rgb = RGB::default();
|
|
||||||
for c in 0..3 {
|
|
||||||
rgb[c] = self.image.lookup_nearest_channel(uv, c);
|
|
||||||
}
|
|
||||||
let s = RGBIlluminantSpectrum::new(self.image_color_space.as_ref(), RGB::clamp_zero(rgb));
|
|
||||||
self.scale * s.sample(&lambda)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LightTrait for ProjectionLight {
|
|
||||||
fn base(&self) -> &LightBase {
|
|
||||||
&self.base
|
|
||||||
}
|
|
||||||
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
|
|
||||||
let mut sum = SampledSpectrum::new(0.);
|
|
||||||
for y in 0..self.image.resolution.y() {
|
|
||||||
for x in 0..self.image.resolution.x() {
|
|
||||||
let ps = self.screen_bounds.lerp(Point2f::new(
|
|
||||||
(x as Float + 0.5) / self.image.resolution.x() as Float,
|
|
||||||
(y as Float + 0.5) / self.image.resolution.y() as Float,
|
|
||||||
));
|
|
||||||
let w_raw = Vector3f::from(self.light_from_screen.apply_to_point(Point3f::new(
|
|
||||||
ps.x(),
|
|
||||||
ps.y(),
|
|
||||||
0.,
|
|
||||||
)));
|
|
||||||
let w = w_raw.normalize();
|
|
||||||
let dwda = cos_theta(w).powi(3);
|
|
||||||
let mut rgb = RGB::default();
|
|
||||||
for c in 0..3 {
|
|
||||||
rgb[c] = self.image.get_channel(Point2i::new(x, y), c);
|
|
||||||
}
|
|
||||||
|
|
||||||
let s = RGBIlluminantSpectrum::new(
|
|
||||||
self.image_color_space.as_ref(),
|
|
||||||
RGB::clamp_zero(rgb),
|
|
||||||
);
|
|
||||||
sum += s.sample(&lambda) * dwda;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.scale * self.a * sum / (self.image.resolution.x() * self.image.resolution.y()) as Float
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sample_li(
|
|
||||||
&self,
|
|
||||||
_ctx: &LightSampleContext,
|
|
||||||
_u: Point2f,
|
|
||||||
_lambda: &SampledWavelengths,
|
|
||||||
_allow_incomplete_pdf: bool,
|
|
||||||
) -> Option<LightLiSample> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn pdf_li(
|
|
||||||
&self,
|
|
||||||
_ctx: &LightSampleContext,
|
|
||||||
_wi: Vector3f,
|
|
||||||
_allow_incomplete_pdf: bool,
|
|
||||||
) -> Float {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn l(
|
|
||||||
&self,
|
|
||||||
_p: Point3f,
|
|
||||||
_n: Normal3f,
|
|
||||||
_uv: Point2f,
|
|
||||||
_w: Vector3f,
|
|
||||||
_lambda: &SampledWavelengths,
|
|
||||||
) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn bounds(&self) -> Option<LightBounds> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct SpotLight {
|
|
||||||
base: LightBase,
|
|
||||||
iemit: Arc<DenselySampledSpectrum>,
|
|
||||||
scale: Float,
|
|
||||||
cos_fallof_start: Float,
|
|
||||||
cos_fallof_end: Float,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpotLight {
|
|
||||||
pub fn new(
|
|
||||||
render_from_light: &TransformGeneric<Float>,
|
|
||||||
medium_interface: &MediumInterface,
|
|
||||||
iemit: Spectrum,
|
|
||||||
scale: Float,
|
|
||||||
total_width: Float,
|
|
||||||
fallof_start: Float,
|
|
||||||
) -> Self {
|
|
||||||
let base = LightBase::new(
|
|
||||||
LightType::DeltaPosition,
|
|
||||||
render_from_light,
|
|
||||||
medium_interface,
|
|
||||||
);
|
|
||||||
|
|
||||||
let i_interned = LightBase::lookup_spectrum(&iemit);
|
|
||||||
let cos_fallof_end = radians(total_width).cos();
|
|
||||||
let cos_fallof_start = radians(fallof_start).cos();
|
|
||||||
assert!(fallof_start < total_width);
|
|
||||||
Self {
|
|
||||||
base,
|
|
||||||
iemit: i_interned,
|
|
||||||
scale,
|
|
||||||
cos_fallof_start,
|
|
||||||
cos_fallof_end,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn i(&self, w: Vector3f, lambda: SampledWavelengths) -> SampledSpectrum {
|
|
||||||
smooth_step(cos_theta(w), self.cos_fallof_end, self.cos_fallof_start)
|
|
||||||
* self.scale
|
|
||||||
* self.iemit.sample(&lambda)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LightTrait for SpotLight {
|
|
||||||
fn base(&self) -> &LightBase {
|
|
||||||
&self.base
|
|
||||||
}
|
|
||||||
fn sample_li(
|
|
||||||
&self,
|
|
||||||
ctx: &LightSampleContext,
|
|
||||||
_u: Point2f,
|
|
||||||
lambda: &SampledWavelengths,
|
|
||||||
_allow_incomplete_pdf: bool,
|
|
||||||
) -> Option<LightLiSample> {
|
|
||||||
let pi = self
|
|
||||||
.base
|
|
||||||
.render_from_light
|
|
||||||
.apply_to_interval(&Point3fi::default());
|
|
||||||
let p: Point3f = pi.into();
|
|
||||||
let wi = (p - ctx.p()).normalize();
|
|
||||||
let w_light = self.base.render_from_light.apply_inverse_vector(-wi);
|
|
||||||
let li = self.i(w_light, *lambda) / p.distance_squared(ctx.p());
|
|
||||||
|
|
||||||
let intr = SimpleInteraction::new(pi, 0.0, Some(self.base.medium_interface.clone()));
|
|
||||||
Some(LightLiSample::new(li, wi, 1., Interaction::Simple(intr)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
|
|
||||||
self.scale
|
|
||||||
* self.iemit.sample(&lambda)
|
|
||||||
* 2.
|
|
||||||
* PI
|
|
||||||
* ((1. - self.cos_fallof_start) + (self.cos_fallof_start - self.cos_fallof_end) / 2.)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pdf_li(
|
|
||||||
&self,
|
|
||||||
_ctx: &LightSampleContext,
|
|
||||||
_wi: Vector3f,
|
|
||||||
_allow_incomplete_pdf: bool,
|
|
||||||
) -> Float {
|
|
||||||
0.
|
|
||||||
}
|
|
||||||
|
|
||||||
fn l(
|
|
||||||
&self,
|
|
||||||
_p: Point3f,
|
|
||||||
_n: Normal3f,
|
|
||||||
_uv: Point2f,
|
|
||||||
_w: Vector3f,
|
|
||||||
_lambda: &SampledWavelengths,
|
|
||||||
) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bounds(&self) -> Option<LightBounds> {
|
|
||||||
let p = self
|
|
||||||
.base
|
|
||||||
.render_from_light
|
|
||||||
.apply_to_point(Point3f::default());
|
|
||||||
let w = self
|
|
||||||
.base
|
|
||||||
.render_from_light
|
|
||||||
.apply_to_vector(Vector3f::new(0., 0., 1.))
|
|
||||||
.normalize();
|
|
||||||
let phi = self.scale * self.iemit.max_value() * 4. * PI;
|
|
||||||
let cos_theta_e = (self.cos_fallof_end.acos() - self.cos_fallof_start.acos()).cos();
|
|
||||||
Some(LightBounds::new(
|
|
||||||
&Bounds3f::from_points(p, p),
|
|
||||||
w,
|
|
||||||
phi,
|
|
||||||
self.cos_fallof_start,
|
|
||||||
cos_theta_e,
|
|
||||||
false,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
36
shared/src/textures/image.rs
Normal file
36
shared/src/textures/image.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
use crate::{
|
||||||
|
core::texture::{
|
||||||
|
FloatTextureTrait, ImageTextureBase, SpectrumTextureTrait, SpectrumType, TextureMapping2D,
|
||||||
|
},
|
||||||
|
spectra::RGBColorSpace,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* GPU heavy code, dont know if this will ever work the way Im doing things.
|
||||||
|
* Leaving it here isolated, for careful handling */
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct GPUSpectrumImageTexture {
|
||||||
|
pub mapping: TextureMapping2D,
|
||||||
|
pub tex_obj: u64,
|
||||||
|
pub scale: Float,
|
||||||
|
pub invert: bool,
|
||||||
|
pub color_space: RGBColorSpace,
|
||||||
|
pub spectrum_type: SpectrumType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpectrumTextureTrait for GPUSpectrumImageTexture {
|
||||||
|
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct GPUFloatImageTexture {
|
||||||
|
pub mapping: TextureMapping2D,
|
||||||
|
pub tex_obj: u64,
|
||||||
|
pub scale: Float,
|
||||||
|
pub invert: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatTextureTrait for GPUFloatImageTexture {}
|
||||||
14
shared/src/textures/mix.rs
Normal file
14
shared/src/textures/mix.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct GPUFloatMixTexture {
|
||||||
|
tex1: GPUFloatTexture,
|
||||||
|
tex2: GPUFloatTexture,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct GPUFloatDirectionMixTexture {
|
||||||
|
tex1: GPUFloatTexture,
|
||||||
|
tex2: GPUFloatTexture,
|
||||||
|
dir: Vector3f,
|
||||||
|
}
|
||||||
3
shared/src/textures/mod.rs
Normal file
3
shared/src/textures/mod.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod image;
|
||||||
|
pub mod mix;
|
||||||
|
pub mod ptex;
|
||||||
16
shared/src/textures/ptex.rs
Normal file
16
shared/src/textures/ptex.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
use crate::core::texture::{FloatTextureTrait, SpectrumTextureTrait, TextureEvalContext};
|
||||||
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
|
|
||||||
|
/* GPU heavy code, have to see how to best approach this
|
||||||
|
*/
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct GPUSpectrumPtexTexture;
|
||||||
|
impl SpectrumTextureTrait for GPUSpectrumPtexTexture {
|
||||||
|
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct GPUFloatPtexTexture;
|
||||||
|
impl FloatTextureTrait for GPUFloatPtexTexture {}
|
||||||
|
|
@ -10,15 +10,15 @@ use crate::core::geometry::{
|
||||||
Bounds2i, Bounds3f, Bounds3i, Point2i, Point3f, Point3i, Vector2i, Vector3f, Vector3i,
|
Bounds2i, Bounds3f, Bounds3i, Point2i, Point3f, Point3i, Vector2i, Vector3f, Vector3i,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait Interpolatable:
|
// pub trait Interpolatable:
|
||||||
Copy + Default + Add<Output = Self> + Sub<Output = Self> + Mul<Float, Output = Self>
|
// Copy + Default + Add<Output = Self> + Sub<Output = Self> + Mul<Float, Output = Self>
|
||||||
{
|
// {
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
impl<T> Interpolatable for T where
|
// impl<T> Interpolatable for T where
|
||||||
T: Copy + Default + Add<Output = T> + Sub<Output = T> + Mul<Float, Output = T>
|
// T: Copy + Default + Add<Output = T> + Sub<Output = T> + Mul<Float, Output = T>
|
||||||
{
|
// {
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Array2D<T> {
|
pub struct Array2D<T> {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::fmt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::image::PixelFormat;
|
use crate::images::PixelFormat;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum LlsError {
|
pub enum LlsError {
|
||||||
|
|
|
||||||
|
|
@ -2,24 +2,17 @@ use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
|
|
||||||
pub mod containers;
|
pub mod containers;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod file;
|
|
||||||
pub mod hash;
|
pub mod hash;
|
||||||
pub mod interval;
|
pub mod interval;
|
||||||
pub mod math;
|
pub mod math;
|
||||||
pub mod mesh;
|
pub mod mesh;
|
||||||
pub mod mipmap;
|
|
||||||
pub mod parameters;
|
|
||||||
pub mod parser;
|
|
||||||
pub mod quaternion;
|
pub mod quaternion;
|
||||||
pub mod rng;
|
pub mod rng;
|
||||||
pub mod sampling;
|
pub mod sampling;
|
||||||
pub mod sobol;
|
pub mod sobol;
|
||||||
pub mod splines;
|
pub mod splines;
|
||||||
pub mod strings;
|
|
||||||
pub mod transform;
|
pub mod transform;
|
||||||
|
|
||||||
pub use file::{read_float_file, resolve_filename};
|
|
||||||
pub use strings::*;
|
|
||||||
pub use transform::{AnimatedTransform, Transform, TransformGeneric};
|
pub use transform::{AnimatedTransform, Transform, TransformGeneric};
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
||||||
2
src/core/mod.rs
Normal file
2
src/core/mod.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod scene;
|
||||||
|
pub mod texture;
|
||||||
|
|
@ -1,23 +1,23 @@
|
||||||
use crate::Float;
|
|
||||||
use crate::camera::{Camera, CameraTransform};
|
|
||||||
use crate::core::film::{Film, FilmTrait};
|
|
||||||
use crate::core::filter::Filter;
|
|
||||||
use crate::core::geometry::{Point3f, Vector3f};
|
|
||||||
use crate::core::medium::Medium;
|
|
||||||
use crate::core::options::RenderingCoordinateSystem;
|
|
||||||
use crate::core::sampler::Sampler;
|
|
||||||
use crate::core::texture::{FloatTexture, SpectrumTexture};
|
|
||||||
use crate::image::Image;
|
|
||||||
use crate::lights::Light;
|
|
||||||
use crate::spectra::RGBColorSpace;
|
|
||||||
use crate::utils::error::FileLoc;
|
|
||||||
use crate::utils::math::SquareMatrix;
|
|
||||||
use crate::utils::parameters::{ParameterDictionary, ParsedParameterVector};
|
use crate::utils::parameters::{ParameterDictionary, ParsedParameterVector};
|
||||||
use crate::utils::parser::ParserTarget;
|
|
||||||
use crate::utils::transform::look_at;
|
|
||||||
use crate::utils::transform::{AnimatedTransform, TransformGeneric};
|
|
||||||
use crate::utils::{normalize_utf8, resolve_filename};
|
use crate::utils::{normalize_utf8, resolve_filename};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
use shared::Float;
|
||||||
|
use shared::core::camera::{Camera, CameraTransform};
|
||||||
|
use shared::core::film::{Film, FilmTrait};
|
||||||
|
use shared::core::filter::Filter;
|
||||||
|
use shared::core::geometry::{Point3f, Vector3f};
|
||||||
|
use shared::core::lights::Light;
|
||||||
|
use shared::core::medium::Medium;
|
||||||
|
use shared::core::options::RenderingCoordinateSystem;
|
||||||
|
use shared::core::sampler::Sampler;
|
||||||
|
use shared::core::texture::{FloatTexture, SpectrumTexture};
|
||||||
|
use shared::images::Image;
|
||||||
|
use shared::spectra::RGBColorSpace;
|
||||||
|
use shared::utils::error::FileLoc;
|
||||||
|
use shared::utils::math::SquareMatrix;
|
||||||
|
use shared::utils::parser::ParserTarget;
|
||||||
|
use shared::utils::transform::look_at;
|
||||||
|
use shared::utils::transform::{AnimatedTransform, TransformGeneric};
|
||||||
use std::char::MAX;
|
use std::char::MAX;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::ops::{Index as IndexTrait, IndexMut as IndexMutTrait};
|
use std::ops::{Index as IndexTrait, IndexMut as IndexMutTrait};
|
||||||
225
src/core/texture.rs
Normal file
225
src/core/texture.rs
Normal file
|
|
@ -0,0 +1,225 @@
|
||||||
|
use crate::textures::*;
|
||||||
|
use crate::utils::error::FileLoc;
|
||||||
|
use crate::utils::mipmap::MIPMapFilterOptions;
|
||||||
|
use crate::utils::parameters::TextureParameterDictionary;
|
||||||
|
use shared::utils::Transform;
|
||||||
|
use shared::images::
|
||||||
|
use std::sync::{Arc, Mutex, OnceLock};
|
||||||
|
|
||||||
|
#[enum_dispatch(FloatTextureTrait)]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum FloatTexture {
|
||||||
|
Constant(FloatConstantTexture),
|
||||||
|
Image(FloatImageTexture),
|
||||||
|
Mix(FloatMixTexture),
|
||||||
|
DirectionMix(FloatDirectionMixTexture),
|
||||||
|
Scaled(FloatScaledTexture),
|
||||||
|
Bilerp(FloatBilerpTexture),
|
||||||
|
Checkerboard(FloatCheckerboardTexture),
|
||||||
|
Dots(FloatDotsTexture),
|
||||||
|
FBm(FBmTexture),
|
||||||
|
Ptex(FloatPtexTexture),
|
||||||
|
Windy(WindyTexture),
|
||||||
|
Wrinkled(WrinkledTexture),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatTexture {
|
||||||
|
pub fn create(
|
||||||
|
name: &str,
|
||||||
|
render_from_texture: &Transform,
|
||||||
|
params: &TextureParameterDictionary,
|
||||||
|
loc: &FileLoc,
|
||||||
|
) -> Result<Self, String> {
|
||||||
|
match name {
|
||||||
|
"constant" => {
|
||||||
|
let tex = FloatConstantTexture::create(render_from_texture, params, loc);
|
||||||
|
Ok(FloatTexture::Constant(tex))
|
||||||
|
}
|
||||||
|
"scale" => Ok(FloatScaledTexture::create(render_from_texture, params, loc)),
|
||||||
|
"mix" => {
|
||||||
|
let tex = FloatMixTexture::create(render_from_texture, params, loc);
|
||||||
|
Ok(FloatTexture::Mix(tex))
|
||||||
|
}
|
||||||
|
"directionmix" => {
|
||||||
|
let tex = FloatDirectionMixTexture::create(render_from_texture, params, loc);
|
||||||
|
Ok(FloatTexture::DirectionMix(tex))
|
||||||
|
}
|
||||||
|
"bilerp" => {
|
||||||
|
let tex = FloatBilerpTexture::create(render_from_texture, params, loc);
|
||||||
|
Ok(FloatTexture::Bilerp(tex))
|
||||||
|
}
|
||||||
|
"imagemap" => {
|
||||||
|
let tex = FloatImageTexture::create(render_from_texture, params, loc);
|
||||||
|
Ok(loatTexture::Image(tex))
|
||||||
|
}
|
||||||
|
"checkerboard" => {
|
||||||
|
let tex = FloatCheckerboardTexture::create(render_from_texture, params, loc);
|
||||||
|
Ok(FloatTexture::Checkerboard(tex))
|
||||||
|
}
|
||||||
|
"dots" => {
|
||||||
|
let tex = FloatDotsTexture::create(render_from_texture, params, loc);
|
||||||
|
Ok(FloatTexture::Dots(tex))
|
||||||
|
}
|
||||||
|
"fbm" => {
|
||||||
|
let tex = FBmTexture::create(render_from_texture, params, loc);
|
||||||
|
Ok(FloatTexture::FBm(tex))
|
||||||
|
}
|
||||||
|
"wrinkled" => {
|
||||||
|
let tex = WrinkledTexture::create(render_from_texture, params, loc);
|
||||||
|
Ok(FloatTexture::Wrinkled(tex))
|
||||||
|
}
|
||||||
|
"windy" => {
|
||||||
|
let tex = WindyTexture::create(render_from_texture, params, loc);
|
||||||
|
Ok(FloatTexture::Windy(tex))
|
||||||
|
}
|
||||||
|
"ptex" => {
|
||||||
|
let tex = FloatPtexTexture::create(render_from_texture, params, loc);
|
||||||
|
Ok(FloatTexture::Ptex(tex))
|
||||||
|
}
|
||||||
|
_ => Err(format!("Float texture type '{}' unknown at {}", name, loc)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
#[enum_dispatch(SpectrumTextureTrait)]
|
||||||
|
pub enum SpectrumTexture {
|
||||||
|
RGBConstant(RGBConstantTexture),
|
||||||
|
RGBReflectanceConstant(RGBReflectanceConstantTexture),
|
||||||
|
Constant(SpectrumConstantTexture),
|
||||||
|
Bilerp(SpectrumBilerpTexture),
|
||||||
|
Checkerboard(SpectrumCheckerboardTexture),
|
||||||
|
Image(SpectrumImageTexture),
|
||||||
|
Marble(MarbleTexture),
|
||||||
|
Mix(SpectrumMixTexture),
|
||||||
|
DirectionMix(SpectrumDirectionMixTexture),
|
||||||
|
Dots(SpectrumDotsTexture),
|
||||||
|
Ptex(SpectrumPtexTexture),
|
||||||
|
Scaled(SpectrumScaledTexture),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextureMapping2D {
|
||||||
|
pub fn create(
|
||||||
|
params: &TextureParameterDictionary,
|
||||||
|
render_from_texture: &Transform,
|
||||||
|
loc: &FileLoc,
|
||||||
|
) -> Self {
|
||||||
|
let mtype = params.get_one_string("mapping", "uv");
|
||||||
|
match mtype.as_str() {
|
||||||
|
"uv" => {
|
||||||
|
let su = params.get_one_float("uscale", 1.);
|
||||||
|
let sv = params.get_one_float("vscale", 1.);
|
||||||
|
let du = params.get_one_float("udelta", 0.);
|
||||||
|
let dv = params.get_one_float("vdelta", 0.);
|
||||||
|
let mapping = UVMapping::new(su, sv, du, dv);
|
||||||
|
TextureMapping2D::UV(mapping)
|
||||||
|
}
|
||||||
|
"spherical" => {
|
||||||
|
let mapping = SphericalMapping::new(&render_from_texture.inverse());
|
||||||
|
TextureMapping2D::Spherical(mapping)
|
||||||
|
}
|
||||||
|
"cylindrical" => {
|
||||||
|
let mapping = CylindricalMapping::new(&render_from_texture.inverse());
|
||||||
|
TextureMapping2D::Cylindrical(mapping)
|
||||||
|
}
|
||||||
|
"planar" => {
|
||||||
|
let vs = params.get_one_vector3f("v1", Vector3f::new(1., 0., 0.));
|
||||||
|
let vt = params.get_one_vector3f("v2", Vector3f::new(0., 1., 0.));
|
||||||
|
let ds = params.get_one_float("udelta", 0.);
|
||||||
|
let dt = params.get_one_float("vdelta", 0.);
|
||||||
|
let mapping = PlanarMapping::new(&render_from_texture.inverse(), vs, vt, ds, dt);
|
||||||
|
TextureMapping2D::Planar(mapping)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
log::error!("{}: 2D texture mapping unknown {}", loc, mtype);
|
||||||
|
TextureMapping2D::UV(UVMapping::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static TEXTURE_CACHE: OnceLock<Mutex<HashMap<TexInfo, Arc<MIPMap>>>> = OnceLock::new();
|
||||||
|
|
||||||
|
fn get_texture_cache() -> &'static Mutex<HashMap<TexInfo, Arc<MIPMap>>> {
|
||||||
|
TEXTURE_CACHE.get_or_init(|| Mutex::new(HashMap::new()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ImageTextureBase {
|
||||||
|
pub mapping: TextureMapping2D,
|
||||||
|
pub filename: String,
|
||||||
|
pub scale: Float,
|
||||||
|
pub invert: bool,
|
||||||
|
pub mipmap: Arc<MIPMap>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImageTextureBase {
|
||||||
|
pub fn new(
|
||||||
|
mapping: TextureMapping2D,
|
||||||
|
filename: String,
|
||||||
|
filter_options: MIPMapFilterOptions,
|
||||||
|
wrap_mode: WrapMode,
|
||||||
|
scale: Float,
|
||||||
|
invert: bool,
|
||||||
|
encoding: ColorEncoding,
|
||||||
|
) -> Self {
|
||||||
|
let tex_info = TexInfo {
|
||||||
|
filename: filename.clone(),
|
||||||
|
filter_options,
|
||||||
|
wrap_mode,
|
||||||
|
encoding,
|
||||||
|
};
|
||||||
|
|
||||||
|
let cache_mutex = get_texture_cache();
|
||||||
|
|
||||||
|
{
|
||||||
|
let cache = cache_mutex.lock().unwrap();
|
||||||
|
if let Some(mipmap) = cache.get(&tex_info) {
|
||||||
|
return Self {
|
||||||
|
mapping,
|
||||||
|
filename,
|
||||||
|
scale,
|
||||||
|
invert,
|
||||||
|
mipmap: mipmap.clone(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = Path::new(&filename);
|
||||||
|
let mipmap_raw = MIPMap::create_from_file(path, filter_options, wrap_mode, encoding)
|
||||||
|
.expect("Failed to create MIPMap from file");
|
||||||
|
|
||||||
|
let mipmap_arc = Arc::new(mipmap_raw);
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cache = cache_mutex.lock().unwrap();
|
||||||
|
|
||||||
|
let stored_mipmap = cache.entry(tex_info).or_insert(mipmap_arc);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
mapping,
|
||||||
|
filename,
|
||||||
|
scale,
|
||||||
|
invert,
|
||||||
|
mipmap: stored_mipmap.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_cache() {
|
||||||
|
let mut cache = get_texture_cache().lock().unwrap();
|
||||||
|
cache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn multiply_scale(&mut self, s: Float) {
|
||||||
|
self.scale *= s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
|
||||||
|
struct TexInfo {
|
||||||
|
filename: String,
|
||||||
|
filter_options: MIPMapFilterOptions,
|
||||||
|
wrap_mode: WrapMode,
|
||||||
|
encoding: ColorEncoding,
|
||||||
|
}
|
||||||
|
|
@ -1,2 +1,4 @@
|
||||||
pub mod scene;
|
pub mod core;
|
||||||
|
pub mod lights;
|
||||||
|
pub mod textures;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,23 @@
|
||||||
use super::{
|
|
||||||
DenselySampledSpectrum, LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait,
|
|
||||||
LightType, RGB, RGBColorSpace, RGBIlluminantSpectrum, SampledSpectrum, SampledWavelengths,
|
|
||||||
Spectrum, SpectrumTrait,
|
|
||||||
};
|
|
||||||
use crate::core::geometry::{
|
|
||||||
Bounds3f, Normal3f, Point2f, Point2fi, Point2i, Point3f, Point3fi, Ray, Vector3f, VectorLike,
|
|
||||||
};
|
|
||||||
use crate::core::interaction::{
|
|
||||||
Interaction, InteractionTrait, SimpleInteraction, SurfaceInteraction,
|
|
||||||
};
|
|
||||||
use crate::core::medium::MediumInterface;
|
|
||||||
use crate::core::pbrt::{Float, PI};
|
|
||||||
use crate::core::texture::{
|
use crate::core::texture::{
|
||||||
FloatTexture, FloatTextureTrait, TextureEvalContext, TextureEvaluator,
|
FloatTexture, FloatTextureTrait, TextureEvalContext, TextureEvaluator,
|
||||||
UniversalTextureEvaluator,
|
UniversalTextureEvaluator,
|
||||||
};
|
};
|
||||||
use crate::image::Image;
|
|
||||||
use crate::shapes::{Shape, ShapeSample, ShapeSampleContext, ShapeTrait};
|
use crate::shapes::{Shape, ShapeSample, ShapeSampleContext, ShapeTrait};
|
||||||
use crate::utils::hash::hash_float;
|
use crate::utils::hash::hash_float;
|
||||||
use crate::utils::transform::TransformGeneric;
|
use shared::core::geometry::{
|
||||||
|
Bounds3f, Normal3f, Point2f, Point2fi, Point2i, Point3f, Point3fi, Ray, Vector3f, VectorLike,
|
||||||
|
};
|
||||||
|
use shared::core::interaction::{
|
||||||
|
Interaction, InteractionTrait, SimpleInteraction, SurfaceInteraction,
|
||||||
|
};
|
||||||
|
use shared::core::medium::MediumInterface;
|
||||||
|
use shared::images::Image;
|
||||||
|
use shared::spectra::{
|
||||||
|
DenselySampledSpectrum, LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait,
|
||||||
|
LightType, RGB, RGBColorSpace, RGBIlluminantSpectrum, SampledSpectrum, SampledWavelengths,
|
||||||
|
Spectrum, SpectrumTrait,
|
||||||
|
};
|
||||||
|
use shared::{Float, PI};
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|
@ -38,7 +37,7 @@ pub struct DiffuseAreaLight {
|
||||||
impl DiffuseAreaLight {
|
impl DiffuseAreaLight {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
render_from_light: TransformGeneric<Float>,
|
render_from_light: Transform,
|
||||||
medium_interface: MediumInterface,
|
medium_interface: MediumInterface,
|
||||||
le: Spectrum,
|
le: Spectrum,
|
||||||
scale: Float,
|
scale: Float,
|
||||||
|
|
|
||||||
1
src/lights/mod.rs
Normal file
1
src/lights/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
pub mod diffuse;
|
||||||
48
src/textures/bilerp.rs
Normal file
48
src/textures/bilerp.rs
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
use crate::core::texture::{
|
||||||
|
FloatTextureTrait, SpectrumTextureTrait, TextureEvalContext, TextureMapping2D,
|
||||||
|
};
|
||||||
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FloatBilerpTexture {
|
||||||
|
mapping: TextureMapping2D,
|
||||||
|
v00: Float,
|
||||||
|
v01: Float,
|
||||||
|
v10: Float,
|
||||||
|
v11: Float,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatBilerpTexture {
|
||||||
|
pub fn new(mapping: TextureMapping2D, v00: Float, v01: Float, v10: Float, v11: Float) -> Self {
|
||||||
|
Self {
|
||||||
|
mapping,
|
||||||
|
v00,
|
||||||
|
v01,
|
||||||
|
v10,
|
||||||
|
v11,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create(
|
||||||
|
render_from_texture: &Transform,
|
||||||
|
params: &TextureParameterDictionary,
|
||||||
|
loc: &FileLoc,
|
||||||
|
) -> Self {
|
||||||
|
let mapping = TextureMapping2D::create(params, render_from_texture, loc);
|
||||||
|
let v00 = params.get_one_float("v00", 0.);
|
||||||
|
let v01 = params.get_one_float("v01", 1.);
|
||||||
|
let v10 = params.get_one_float("v10", 0.);
|
||||||
|
let v11 = params.get_one_float("v11", 1.);
|
||||||
|
Self::new(mapping, v00, v01, v10, v11)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatTextureTrait for FloatBilerpTexture {}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct SpectrumBilerpTexture;
|
||||||
|
impl SpectrumTextureTrait for SpectrumBilerpTexture {
|
||||||
|
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/textures/checkerboard.rs
Normal file
14
src/textures/checkerboard.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
use crate::core::texture::{FloatTextureTrait, SpectrumTextureTrait, TextureEvalContext};
|
||||||
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FloatCheckerboardTexture;
|
||||||
|
impl FloatTextureTrait for FloatCheckerboardTexture {}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct SpectrumCheckerboardTexture;
|
||||||
|
impl SpectrumTextureTrait for SpectrumCheckerboardTexture {
|
||||||
|
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
60
src/textures/constant.rs
Normal file
60
src/textures/constant.rs
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
use crate::Float;
|
||||||
|
use crate::core::texture::{FloatTextureTrait, SpectrumTextureTrait, TextureEvalContext};
|
||||||
|
use crate::spectra::{SampledSpectrum, SampledWavelengths, Spectrum};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FloatConstantTexture {
|
||||||
|
value: Float,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatConstantTexture {
|
||||||
|
pub fn new(value: Float) -> Self {
|
||||||
|
Self { value }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create(
|
||||||
|
_render_from_texture: &Transform,
|
||||||
|
params: &TextureParameterDictionary,
|
||||||
|
_loc: &FileLoc,
|
||||||
|
) -> Self {
|
||||||
|
Self::new(params.get_one_float("value", 1.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatTextureTrait for FloatConstantTexture {
|
||||||
|
fn evaluate(&self, _ctx: &TextureEvalContext) -> Float {
|
||||||
|
self.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct RGBConstantTexture;
|
||||||
|
impl SpectrumTextureTrait for RGBConstantTexture {
|
||||||
|
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct RGBReflectanceConstantTexture;
|
||||||
|
impl SpectrumTextureTrait for RGBReflectanceConstantTexture {
|
||||||
|
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct SpectrumConstantTexture {
|
||||||
|
value: Spectrum,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpectrumConstantTexture {
|
||||||
|
pub fn new(value: Spectrum) -> Self {
|
||||||
|
Self { value }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpectrumTextureTrait for SpectrumConstantTexture {
|
||||||
|
fn evaluate(&self, _ctx: &TextureEvalContext, lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
|
self.value.sample(lambda)
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/textures/dots.rs
Normal file
11
src/textures/dots.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct SpectrumDotsTexture;
|
||||||
|
impl SpectrumTextureTrait for SpectrumDotsTexture {
|
||||||
|
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FloatDotsTexture;
|
||||||
|
impl FloatTextureTrait for FloatDotsTexture {}
|
||||||
3
src/textures/fbm.rs
Normal file
3
src/textures/fbm.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FBmTexture;
|
||||||
|
impl FloatTextureTrait for FBmTexture {}
|
||||||
91
src/textures/image.rs
Normal file
91
src/textures/image.rs
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
use crate::texture::ImageTextureBase;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct SpectrumImageTexture {
|
||||||
|
base: ImageTextureBase,
|
||||||
|
spectrum_type: SpectrumType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpectrumImageTexture {
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn new(
|
||||||
|
mapping: TextureMapping2D,
|
||||||
|
filename: String,
|
||||||
|
filter_options: MIPMapFilterOptions,
|
||||||
|
wrap_mode: WrapMode,
|
||||||
|
scale: Float,
|
||||||
|
invert: bool,
|
||||||
|
encoding: ColorEncoding,
|
||||||
|
spectrum_type: SpectrumType,
|
||||||
|
) -> Self {
|
||||||
|
let base = ImageTextureBase::new(
|
||||||
|
mapping,
|
||||||
|
filename,
|
||||||
|
filter_options,
|
||||||
|
wrap_mode,
|
||||||
|
scale,
|
||||||
|
invert,
|
||||||
|
encoding,
|
||||||
|
);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
base,
|
||||||
|
spectrum_type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpectrumTextureTrait for SpectrumImageTexture {
|
||||||
|
fn evaluate(&self, ctx: &TextureEvalContext, lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
|
let mut c = self.base.mapping.map(ctx);
|
||||||
|
c.st[1] = 1. - c.st[1];
|
||||||
|
let dst0 = Vector2f::new(c.dsdx, c.dtdx);
|
||||||
|
let dst1 = Vector2f::new(c.dsdy, c.dtdy);
|
||||||
|
let rgb_unclamp = self.base.scale * self.base.mipmap.filter::<RGB>(c.st, dst0, dst1);
|
||||||
|
let rgb = RGB::clamp_zero(rgb_unclamp);
|
||||||
|
if let Some(cs) = self.base.mipmap.get_rgb_colorspace() {
|
||||||
|
match self.spectrum_type {
|
||||||
|
SpectrumType::Unbounded => {
|
||||||
|
return RGBUnboundedSpectrum::new(&cs, rgb).sample(lambda);
|
||||||
|
}
|
||||||
|
SpectrumType::Albedo => {
|
||||||
|
return RGBAlbedoSpectrum::new(&cs, rgb).sample(lambda);
|
||||||
|
}
|
||||||
|
_ => return RGBIlluminantSpectrum::new(&cs, rgb).sample(lambda),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(rgb[0] == rgb[1] && rgb[1] == rgb[2]);
|
||||||
|
SampledSpectrum::new(rgb[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FloatImageTexture {
|
||||||
|
base: ImageTextureBase,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatImageTexture {
|
||||||
|
pub fn new(
|
||||||
|
mapping: TextureMapping2D,
|
||||||
|
filename: String,
|
||||||
|
filter_options: MIPMapFilterOptions,
|
||||||
|
wrap_mode: WrapMode,
|
||||||
|
scale: Float,
|
||||||
|
invert: bool,
|
||||||
|
encoding: ColorEncoding,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
base: ImageTextureBase::new(
|
||||||
|
mapping,
|
||||||
|
filename,
|
||||||
|
filter_options,
|
||||||
|
wrap_mode,
|
||||||
|
scale,
|
||||||
|
invert,
|
||||||
|
encoding,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatTextureTrait for FloatImageTexture {}
|
||||||
7
src/textures/marble.rs
Normal file
7
src/textures/marble.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct MarbleTexture;
|
||||||
|
impl SpectrumTextureTrait for MarbleTexture {
|
||||||
|
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
77
src/textures/mix.rs
Normal file
77
src/textures/mix.rs
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FloatMixTexture {
|
||||||
|
tex1: Arc<FloatTexture>,
|
||||||
|
tex2: Arc<FloatTexture>,
|
||||||
|
amount: Arc<FloatTexture>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatMixTexture {
|
||||||
|
pub fn new(
|
||||||
|
tex1: Arc<FloatTexture>,
|
||||||
|
tex2: Arc<FloatTexture>,
|
||||||
|
amount: Arc<FloatTexture>,
|
||||||
|
) -> Self {
|
||||||
|
Self { tex1, tex2, amount }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create(
|
||||||
|
_render_from_texture: &Transform,
|
||||||
|
params: &TextureParameterDictionary,
|
||||||
|
_loc: &FileLoc,
|
||||||
|
) -> Self {
|
||||||
|
let tex1 = params.get_float_texture("tex1", 0.);
|
||||||
|
let tex2 = params.get_float_texture("tex2", 1.);
|
||||||
|
let amount = params.get_float_texture("amount", 0.5);
|
||||||
|
Self::new(tex1, tex2, amount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatTextureTrait for FloatMixTexture {
|
||||||
|
fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
|
||||||
|
let amt = self.amount.evaluate(ctx);
|
||||||
|
let mut t1 = 0.;
|
||||||
|
let mut t2 = 0.;
|
||||||
|
if amt != 1. {
|
||||||
|
t1 = self.tex1.evaluate(ctx);
|
||||||
|
}
|
||||||
|
if amt != 0. {
|
||||||
|
t2 = self.tex2.evaluate(ctx);
|
||||||
|
}
|
||||||
|
(1. - amt) * t1 + amt * t2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FloatDirectionMixTexture {
|
||||||
|
tex1: Arc<FloatTexture>,
|
||||||
|
tex2: Arc<FloatTexture>,
|
||||||
|
dir: Vector3f,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatDirectionMixTexture {
|
||||||
|
pub fn new(tex1: Arc<FloatTexture>, tex2: Arc<FloatTexture>, dir: Vector3f) -> Self {
|
||||||
|
Self { tex1, tex2, dir }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create(
|
||||||
|
render_from_texture: &Transform,
|
||||||
|
params: &TextureParameterDictionary,
|
||||||
|
_loc: &FileLoc,
|
||||||
|
) -> Self {
|
||||||
|
let dir_raw = params.get_one_vector3f("dir", Vector3f::new(0., 1., 0.));
|
||||||
|
let dir = render_from_texture.apply_to_vector(dir_raw).normalize();
|
||||||
|
let tex1 = params.get_float_texture("tex1", 0.);
|
||||||
|
let tex2 = params.get_float_texture("tex2", 1.);
|
||||||
|
Self::new(tex1, tex2, dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatTextureTrait for FloatDirectionMixTexture {}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SpectrumMixTexture;
|
||||||
|
impl SpectrumTextureTrait for SpectrumMixTexture {}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SpectrumDirectionMixTexture;
|
||||||
|
impl SpectrumTextureTrait for SpectrumDirectionMixTexture {}
|
||||||
25
src/textures/mod.rs
Normal file
25
src/textures/mod.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
pub mod bilerp;
|
||||||
|
pub mod checkerboard;
|
||||||
|
pub mod constant;
|
||||||
|
pub mod dots;
|
||||||
|
pub mod fbm;
|
||||||
|
pub mod image;
|
||||||
|
pub mod marble;
|
||||||
|
pub mod mix;
|
||||||
|
pub mod ptex;
|
||||||
|
pub mod scale;
|
||||||
|
pub mod windy;
|
||||||
|
pub mod wrinkled;
|
||||||
|
|
||||||
|
pub use bilerp::*;
|
||||||
|
pub use checkerboard::*;
|
||||||
|
pub use constant::*;
|
||||||
|
pub use dots::*;
|
||||||
|
pub use fbm::*;
|
||||||
|
pub use image::*;
|
||||||
|
pub use marble::*;
|
||||||
|
pub use mix::*;
|
||||||
|
pub use ptex::*;
|
||||||
|
pub use scale::*;
|
||||||
|
pub use windy::*;
|
||||||
|
pub use wrinkled::*;
|
||||||
11
src/textures/ptex.rs
Normal file
11
src/textures/ptex.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FloatPtexTexture;
|
||||||
|
impl FloatTextureTrait for FloatPtexTexture {}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct SpectrumPtexTexture;
|
||||||
|
impl SpectrumTextureTrait for SpectrumPtexTexture {
|
||||||
|
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
63
src/textures/scale.rs
Normal file
63
src/textures/scale.rs
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FloatScaledTexture {
|
||||||
|
tex: Arc<FloatTexture>,
|
||||||
|
scale: Arc<FloatTexture>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatScaledTexture {
|
||||||
|
pub fn new(tex: Arc<FloatTexture>, scale: Arc<FloatTexture>) -> Self {
|
||||||
|
Self { tex, scale }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create(
|
||||||
|
_render_from_texture: &Transform,
|
||||||
|
params: &TextureParameterDictionary,
|
||||||
|
_loc: &FileLoc,
|
||||||
|
) -> FloatTexture {
|
||||||
|
let mut tex = params.get_float_texture("tex", 1.);
|
||||||
|
let mut scale = params.get_float_texture("scale", 1.);
|
||||||
|
|
||||||
|
for _ in 0..2 {
|
||||||
|
if let FloatTexture::FloatConstant(c_tex) = &*scale {
|
||||||
|
let cs = c_tex.value;
|
||||||
|
if cs == 1.0 {
|
||||||
|
return (*tex).clone();
|
||||||
|
} else if let FloatTexture::FloatImage(img_tex) = &*tex {
|
||||||
|
let mut image_copy = img_tex.clone();
|
||||||
|
image_copy.base.multiply_scale(cs);
|
||||||
|
return FloatTexture::FloatImage(image_copy).into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::mem::swap(&mut tex, &mut scale);
|
||||||
|
}
|
||||||
|
std::mem::swap(&mut tex, &mut scale);
|
||||||
|
FloatTexture::FloatScaled(FloatScaledTexture::new(tex, scale))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatTextureTrait for FloatScaledTexture {
|
||||||
|
fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
|
||||||
|
let sc = self.scale.evaluate(ctx);
|
||||||
|
if sc == 0. {
|
||||||
|
return 0.;
|
||||||
|
}
|
||||||
|
self.tex.evaluate(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct SpectrumScaledTexture {
|
||||||
|
tex: Box<SpectrumTexture>,
|
||||||
|
scale: Box<FloatTexture>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpectrumTextureTrait for SpectrumScaledTexture {
|
||||||
|
fn evaluate(&self, ctx: &TextureEvalContext, lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
|
let sc = self.scale.evaluate(ctx);
|
||||||
|
if sc == 0. {
|
||||||
|
return SampledSpectrum::new(0.);
|
||||||
|
}
|
||||||
|
self.tex.evaluate(ctx, lambda) * sc
|
||||||
|
}
|
||||||
|
}
|
||||||
3
src/textures/windy.rs
Normal file
3
src/textures/windy.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct WindyTexture;
|
||||||
|
impl FloatTextureTrait for WindyTexture {}
|
||||||
3
src/textures/wrinkled.rs
Normal file
3
src/textures/wrinkled.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct WrinkledTexture;
|
||||||
|
impl FloatTextureTrait for WrinkledTexture {}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use pbrt::Float;
|
use shared::Float;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::{self, BufReader, Error, ErrorKind, Read};
|
use std::io::{self, BufReader, Error, ErrorKind, Read};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
use crate::spectra::color::{ColorEncoding, LINEAR, SRGB};
|
|
||||||
use crate::utils::error::ImageError;
|
use crate::utils::error::ImageError;
|
||||||
use anyhow::{Context, Result, bail};
|
use anyhow::{Context, Result, bail};
|
||||||
use exr::prelude::{read_first_rgba_layer_from_file, write_rgba_file};
|
use exr::prelude::{read_first_rgba_layer_from_file, write_rgba_file};
|
||||||
use image_rs::ImageReader;
|
use image_rs::ImageReader;
|
||||||
use image_rs::{DynamicImage, ImageBuffer, Rgb, Rgba};
|
use image_rs::{DynamicImage, ImageBuffer, Rgb, Rgba};
|
||||||
use pbrt::Float;
|
use shared::Float;
|
||||||
use pbrt::image::{
|
use shared::images::{
|
||||||
Image, ImageAndMetadata, ImageMetadata, PixelData, PixelFormat, Point2i, WrapMode,
|
Image, ImageAndMetadata, ImageMetadata, PixelData, PixelFormat, Point2i, WrapMode,
|
||||||
};
|
};
|
||||||
|
use shared::spectra::color::{ColorEncoding, LINEAR, SRGB};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufRead, BufReader, BufWriter, Read, Write};
|
use std::io::{BufRead, BufReader, BufWriter, Read, Write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::core::geometry::{Lerp, Point2f, Point2i, Vector2f, VectorLike};
|
use shared::Float;
|
||||||
use crate::core::pbrt::Float;
|
use shared::core::geometry::{Lerp, Point2f, Point2i, Vector2f, VectorLike};
|
||||||
use crate::image::{Image, ImageAndMetadata, PixelData, PixelFormat, WrapMode, WrapMode2D};
|
use shared::images::{Image, ImageAndMetadata, PixelData, PixelFormat, WrapMode, WrapMode2D};
|
||||||
use crate::spectra::{RGB, RGBColorSpace, color::ColorEncoding};
|
use shared::spectra::{RGB, RGBColorSpace, color::ColorEncoding};
|
||||||
use crate::utils::math::{lerp, safe_sqrt, square};
|
use shared::utils::math::{lerp, safe_sqrt, square};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
|
||||||
|
|
@ -4,3 +4,6 @@ pub mod io;
|
||||||
pub mod mipmap;
|
pub mod mipmap;
|
||||||
pub mod parameters;
|
pub mod parameters;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
|
||||||
|
pub use file::{read_float_file, resolve_filename};
|
||||||
|
pub use strings::*;
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
use crate::Float;
|
use crate::utils::error::FileLoc;
|
||||||
use crate::core::geometry::{Normal3f, Point2f, Point3f, Vector2f, Vector3f};
|
use shared::Float;
|
||||||
use crate::core::options::get_options;
|
use shared::core::geometry::{Normal3f, Point2f, Point3f, Vector2f, Vector3f};
|
||||||
use crate::core::texture::{
|
use shared::core::options::get_options;
|
||||||
|
use shared::core::texture::{
|
||||||
FloatConstantTexture, FloatTexture, SpectrumConstantTexture, SpectrumTexture, SpectrumType,
|
FloatConstantTexture, FloatTexture, SpectrumConstantTexture, SpectrumTexture, SpectrumType,
|
||||||
};
|
};
|
||||||
use crate::spectra::{
|
use shared::spectra::{
|
||||||
BlackbodySpectrum, ConstantSpectrum, PiecewiseLinearSpectrum, RGB, RGBAlbedoSpectrum,
|
BlackbodySpectrum, ConstantSpectrum, PiecewiseLinearSpectrum, RGB, RGBAlbedoSpectrum,
|
||||||
RGBColorSpace, RGBIlluminantSpectrum, RGBUnboundedSpectrum, SampledSpectrum, Spectrum,
|
RGBColorSpace, RGBIlluminantSpectrum, RGBUnboundedSpectrum, SampledSpectrum, Spectrum,
|
||||||
};
|
};
|
||||||
use crate::utils::error::FileLoc;
|
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
use crate::utils::parameters::{ParameterDictionary, ParsedParameter};
|
|
||||||
use flate2::read::GzDecoder;
|
use flate2::read::GzDecoder;
|
||||||
use memmap2::Mmap;
|
use memmap2::Mmap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
@ -9,8 +8,9 @@ use std::path::{Path, PathBuf};
|
||||||
use std::str::CharIndices;
|
use std::str::CharIndices;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::utils::parameters::ParsedParameterVector;
|
use crate::utils::error::FileLoc;
|
||||||
use crate::{Float, utils::error::FileLoc};
|
use crate::utils::parameters::{ParameterDictionary, ParsedParameter, ParsedParameterVector};
|
||||||
|
use shared::Float;
|
||||||
|
|
||||||
pub trait ParserTarget {
|
pub trait ParserTarget {
|
||||||
fn identity(&mut self, loc: FileLoc);
|
fn identity(&mut self, loc: FileLoc);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue