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:
pingu 2025-12-20 18:37:52 +00:00
parent 8bcc2fb0c8
commit 2e9d3c7301
61 changed files with 1734 additions and 1716 deletions

2
.gitignore vendored
View file

@ -3,3 +3,5 @@
*.log *.log
*.bak *.bak
flip.rs flip.rs
.vscode
rust-analyzer.json

View file

@ -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 }

View file

@ -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"]

View file

@ -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 = []

View 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;

View file

@ -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,

View file

@ -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;

View file

@ -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,
} }
} }
} }

View file

@ -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};

View file

@ -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,

View file

@ -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
View 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,
))
}
}

View file

@ -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 {

View file

@ -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;

View file

@ -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

View file

View 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(

View file

@ -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::*;

View file

@ -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)]

View file

@ -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,

View file

@ -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,
))
}
}

View 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 {}

View 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,
}

View file

@ -0,0 +1,3 @@
pub mod image;
pub mod mix;
pub mod ptex;

View 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 {}

View file

@ -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> {

View file

@ -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 {

View file

@ -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
View file

@ -0,0 +1,2 @@
pub mod scene;
pub mod texture;

View file

@ -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
View 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,
}

View file

@ -1,2 +1,4 @@
pub mod scene; pub mod core;
pub mod lights;
pub mod textures;
pub mod utils; pub mod utils;

View file

@ -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
View file

@ -0,0 +1 @@
pub mod diffuse;

48
src/textures/bilerp.rs Normal file
View 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!()
}
}

View 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
View 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
View 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
View file

@ -0,0 +1,3 @@
#[derive(Debug, Clone)]
pub struct FBmTexture;
impl FloatTextureTrait for FBmTexture {}

91
src/textures/image.rs Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,3 @@
#[derive(Debug, Clone)]
pub struct WindyTexture;
impl FloatTextureTrait for WindyTexture {}

3
src/textures/wrinkled.rs Normal file
View file

@ -0,0 +1,3 @@
#[derive(Debug, Clone)]
pub struct WrinkledTexture;
impl FloatTextureTrait for WrinkledTexture {}

View file

@ -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};

View file

@ -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;

View file

@ -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};

View file

@ -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::*;

View file

@ -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;

View file

@ -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);