Big slog, separating GPU and CPU safe structs and constructors
This commit is contained in:
parent
2e9d3c7301
commit
cda63e42c5
52 changed files with 2253 additions and 1464 deletions
2
rust-toolchain.toml
Normal file
2
rust-toolchain.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
[toolchain]
|
||||||
|
channel = "nightly"
|
||||||
|
|
@ -14,6 +14,8 @@ num-integer = "0.1.46"
|
||||||
num-traits = "0.2.19"
|
num-traits = "0.2.19"
|
||||||
once_cell = "1.21.3"
|
once_cell = "1.21.3"
|
||||||
smallvec = "1.15.1"
|
smallvec = "1.15.1"
|
||||||
|
cuda_std = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", default-features = false, optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
cuda = []
|
use_f64 = []
|
||||||
|
cuda = ["cuda_std"]
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,9 +1,7 @@
|
||||||
use crate::core::geometry::{Bounds2f, Bounds2i, Point2f, Point2i, Vector2f};
|
use crate::core::geometry::{Bounds2f, Bounds2i, Point2f, Point2i, Vector2f};
|
||||||
use crate::core::pbrt::Float;
|
use crate::core::pbrt::Float;
|
||||||
use crate::utils::containers::Array2D;
|
use crate::utils::containers::Array2D;
|
||||||
use crate::utils::error::FileLoc;
|
|
||||||
use crate::utils::math::{gaussian, gaussian_integral, lerp, sample_tent, windowed_sinc};
|
use crate::utils::math::{gaussian, gaussian_integral, lerp, sample_tent, windowed_sinc};
|
||||||
use crate::utils::parameters::ParameterDictionary;
|
|
||||||
use crate::utils::sampling::PiecewiseConstant2D;
|
use crate::utils::sampling::PiecewiseConstant2D;
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
|
|
||||||
|
|
@ -19,6 +17,7 @@ pub struct FilterSampler {
|
||||||
f: Array2D<Float>,
|
f: Array2D<Float>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "cuda"))]
|
||||||
impl FilterSampler {
|
impl FilterSampler {
|
||||||
pub fn new<F>(radius: Vector2f, func: F) -> Self
|
pub fn new<F>(radius: Vector2f, func: F) -> Self
|
||||||
where
|
where
|
||||||
|
|
@ -45,27 +44,21 @@ impl FilterSampler {
|
||||||
let distrib = PiecewiseConstant2D::new_with_bounds(&f, domain);
|
let distrib = PiecewiseConstant2D::new_with_bounds(&f, domain);
|
||||||
Self { domain, f, distrib }
|
Self { domain, f, distrib }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Samples the filter's distribution.
|
impl FilterSampler {
|
||||||
pub fn sample(&self, u: Point2f) -> FilterSample {
|
pub fn sample(&self, u: Point2f) -> FilterSample {
|
||||||
let (p, pdf, pi) = self.distrib.sample(u);
|
let (p, pdf, pi) = self.distrib.sample(u);
|
||||||
|
|
||||||
if pdf == 0.0 {
|
if pdf == 0.0 {
|
||||||
return FilterSample { p, weight: 0.0 };
|
return FilterSample { p, weight: 0.0 };
|
||||||
}
|
}
|
||||||
let weight = self.f[pi] / pdf;
|
|
||||||
|
let weight = *self.f.get_linear(pi.x() as usize + self.f.x_size()) / pdf;
|
||||||
FilterSample { p, weight }
|
FilterSample { p, weight }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch]
|
|
||||||
pub trait FilterTrait {
|
|
||||||
fn radius(&self) -> Vector2f;
|
|
||||||
fn evaluate(&self, p: Point2f) -> Float;
|
|
||||||
fn integral(&self) -> Float;
|
|
||||||
fn sample(&self, u: Point2f) -> FilterSample;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[enum_dispatch(FilterTrait)]
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Filter {
|
pub enum Filter {
|
||||||
Box(BoxFilter),
|
Box(BoxFilter),
|
||||||
|
|
@ -76,310 +69,42 @@ pub enum Filter {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Filter {
|
impl Filter {
|
||||||
pub fn create(name: &str, params: ParameterDictionary, loc: &FileLoc) -> Result<Self, String> {
|
pub fn radius(&self) -> Vector2f {
|
||||||
match name {
|
match self {
|
||||||
"box" => {
|
Filter::Box(f) => f.radius(),
|
||||||
let filter = BoxFilter::create(¶ms, loc);
|
Filter::Gaussian(f) => f.radius(),
|
||||||
Ok(Filter::Box(filter))
|
Filter::Mitchell(f) => f.radius(),
|
||||||
}
|
Filter::LanczosSinc(f) => f.radius(),
|
||||||
"gaussian" => {
|
Filter::Triangle(f) => f.radius(),
|
||||||
let filter = GaussianFilter::create(¶ms, loc);
|
|
||||||
Ok(Filter::Gaussian(filter))
|
|
||||||
}
|
|
||||||
"mitchell" => {
|
|
||||||
let filter = MitchellFilter::create(¶ms, loc);
|
|
||||||
Ok(Filter::Mitchell(filter))
|
|
||||||
}
|
|
||||||
"sinc" => {
|
|
||||||
let filter = LanczosSincFilter::create(¶ms, loc);
|
|
||||||
Ok(Filter::LanczosSinc(filter))
|
|
||||||
}
|
|
||||||
"triangle" => {
|
|
||||||
let filter = TriangleFilter::create(¶ms, loc);
|
|
||||||
Ok(Filter::Triangle(filter))
|
|
||||||
}
|
|
||||||
_ => Err(format!("Film type '{}' unknown at {}", name, loc)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct BoxFilter {
|
|
||||||
pub radius: Vector2f,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BoxFilter {
|
|
||||||
pub fn new(radius: Vector2f) -> Self {
|
|
||||||
Self { radius }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create(params: &ParameterDictionary, _loc: &FileLoc) -> Self {
|
|
||||||
let xw = params.get_one_float("xradius", 0.5);
|
|
||||||
let yw = params.get_one_float("yradius", 0.5);
|
|
||||||
Self::new(Vector2f::new(xw, yw))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FilterTrait for BoxFilter {
|
pub fn evaluate(&self, p: Point2f) -> Float {
|
||||||
fn radius(&self) -> Vector2f {
|
match self {
|
||||||
self.radius
|
Filter::Box(f) => f.evaluate(p),
|
||||||
}
|
Filter::Gaussian(f) => f.evaluate(p),
|
||||||
|
Filter::Mitchell(f) => f.evaluate(p),
|
||||||
fn evaluate(&self, p: Point2f) -> Float {
|
Filter::LanczosSinc(f) => f.evaluate(p),
|
||||||
if p.x().abs() <= self.radius.x() && p.y().abs() <= self.radius.y() {
|
Filter::Triangle(f) => f.evaluate(p),
|
||||||
1.
|
|
||||||
} else {
|
|
||||||
0.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn integral(&self) -> Float {
|
pub fn integral(&self) -> Float {
|
||||||
(2.0 * self.radius.x()) * (2.0 * self.radius.y())
|
match self {
|
||||||
}
|
Filter::Box(f) => f.integral(),
|
||||||
|
Filter::Gaussian(f) => f.integral(),
|
||||||
fn sample(&self, u: Point2f) -> FilterSample {
|
Filter::Mitchell(f) => f.integral(),
|
||||||
let p = Point2f::new(
|
Filter::LanczosSinc(f) => f.integral(),
|
||||||
lerp(u[0], -self.radius.x(), self.radius.x()),
|
Filter::Triangle(f) => f.integral(),
|
||||||
lerp(u[1], -self.radius.y(), self.radius.y()),
|
|
||||||
);
|
|
||||||
FilterSample { p, weight: 1.0 }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn sample(&self, u: Point2f) -> FilterSample {
|
||||||
#[derive(Clone, Debug)]
|
match self {
|
||||||
pub struct GaussianFilter {
|
Filter::Box(f) => f.sample(u),
|
||||||
pub radius: Vector2f,
|
Filter::Gaussian(f) => f.sample(u),
|
||||||
pub sigma: Float,
|
Filter::Mitchell(f) => f.sample(u),
|
||||||
pub exp_x: Float,
|
Filter::LanczosSinc(f) => f.sample(u),
|
||||||
pub exp_y: Float,
|
Filter::Triangle(f) => f.sample(u),
|
||||||
pub sampler: FilterSampler,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GaussianFilter {
|
|
||||||
pub fn new(radius: Vector2f, sigma: Float) -> Self {
|
|
||||||
let exp_x = gaussian(radius.x(), 0., sigma);
|
|
||||||
let exp_y = gaussian(radius.y(), 0., sigma);
|
|
||||||
|
|
||||||
let sampler = FilterSampler::new(radius, move |p: Point2f| {
|
|
||||||
let gx = (gaussian(p.x(), 0., sigma) - exp_x).max(0.0);
|
|
||||||
let gy = (gaussian(p.y(), 0., sigma) - exp_y).max(0.0);
|
|
||||||
gx * gy
|
|
||||||
});
|
|
||||||
|
|
||||||
Self {
|
|
||||||
radius,
|
|
||||||
sigma,
|
|
||||||
exp_x: gaussian(radius.x(), 0., sigma),
|
|
||||||
exp_y: gaussian(radius.y(), 0., sigma),
|
|
||||||
sampler,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(params: &ParameterDictionary, _loc: &FileLoc) -> Self {
|
|
||||||
let xw = params.get_one_float("xradius", 1.5);
|
|
||||||
let yw = params.get_one_float("yradius", 1.5);
|
|
||||||
let sigma = params.get_one_float("sigma", 0.5);
|
|
||||||
Self::new(Vector2f::new(xw, yw), sigma)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FilterTrait for GaussianFilter {
|
|
||||||
fn radius(&self) -> Vector2f {
|
|
||||||
self.radius
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(&self, p: Point2f) -> Float {
|
|
||||||
(gaussian(p.x(), 0.0, self.sigma) - self.exp_x).max(0.0)
|
|
||||||
* (gaussian(p.y(), 0.0, self.sigma) - self.exp_y).max(0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn integral(&self) -> Float {
|
|
||||||
(gaussian_integral(-self.radius.x(), self.radius.x(), 0.0, self.sigma)
|
|
||||||
- 2.0 * self.radius.x() * self.exp_x)
|
|
||||||
* (gaussian_integral(-self.radius.y(), self.radius.y(), 0.0, self.sigma)
|
|
||||||
- 2.0 * self.radius.y() * self.exp_y)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sample(&self, u: Point2f) -> FilterSample {
|
|
||||||
self.sampler.sample(u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct MitchellFilter {
|
|
||||||
pub radius: Vector2f,
|
|
||||||
pub b: Float,
|
|
||||||
pub c: Float,
|
|
||||||
pub sampler: FilterSampler,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MitchellFilter {
|
|
||||||
pub fn new(radius: Vector2f, b: Float, c: Float) -> Self {
|
|
||||||
let sampler = FilterSampler::new(radius, move |p: Point2f| {
|
|
||||||
let nx = 2.0 * p.x() / radius.x();
|
|
||||||
let ny = 2.0 * p.y() / radius.y();
|
|
||||||
Self::mitchell_1d_eval(b, c, nx) * Self::mitchell_1d_eval(b, c, ny)
|
|
||||||
});
|
|
||||||
|
|
||||||
Self {
|
|
||||||
radius,
|
|
||||||
b,
|
|
||||||
c,
|
|
||||||
sampler,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create(params: &ParameterDictionary, _loc: &FileLoc) -> Self {
|
|
||||||
let xw = params.get_one_float("xradius", 2.);
|
|
||||||
let yw = params.get_one_float("yradius", 2.);
|
|
||||||
let b = params.get_one_float("B", 1. / 3.);
|
|
||||||
let c = params.get_one_float("C", 1. / 3.);
|
|
||||||
Self::new(Vector2f::new(xw, yw), b, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mitchell_1d_eval(b: Float, c: Float, x: Float) -> Float {
|
|
||||||
let x = x.abs();
|
|
||||||
if x <= 1.0 {
|
|
||||||
((12.0 - 9.0 * b - 6.0 * c) * x.powi(3)
|
|
||||||
+ (-18.0 + 12.0 * b + 6.0 * c) * x.powi(2)
|
|
||||||
+ (6.0 - 2.0 * b))
|
|
||||||
* (1.0 / 6.0)
|
|
||||||
} else if x <= 2.0 {
|
|
||||||
((-b - 6.0 * c) * x.powi(3)
|
|
||||||
+ (6.0 * b + 30.0 * c) * x.powi(2)
|
|
||||||
+ (-12.0 * b - 48.0 * c) * x
|
|
||||||
+ (8.0 * b + 24.0 * c))
|
|
||||||
* (1.0 / 6.0)
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mitchell_1d(&self, x: Float) -> Float {
|
|
||||||
Self::mitchell_1d_eval(self.b, self.c, x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FilterTrait for MitchellFilter {
|
|
||||||
fn radius(&self) -> Vector2f {
|
|
||||||
self.radius
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(&self, p: Point2f) -> Float {
|
|
||||||
self.mitchell_1d(2.0 * p.x() / self.radius.x())
|
|
||||||
* self.mitchell_1d(2.0 * p.y() / self.radius.y())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn integral(&self) -> Float {
|
|
||||||
self.radius.x() * self.radius.y() / 4.0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sample(&self, u: Point2f) -> FilterSample {
|
|
||||||
self.sampler.sample(u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct LanczosSincFilter {
|
|
||||||
pub radius: Vector2f,
|
|
||||||
pub tau: Float,
|
|
||||||
pub sampler: FilterSampler,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LanczosSincFilter {
|
|
||||||
pub fn new(radius: Vector2f, tau: Float) -> Self {
|
|
||||||
let sampler = FilterSampler::new(radius, move |p: Point2f| {
|
|
||||||
windowed_sinc(p.x(), radius.x(), tau) * windowed_sinc(p.y(), radius.y(), tau)
|
|
||||||
});
|
|
||||||
|
|
||||||
Self {
|
|
||||||
radius,
|
|
||||||
tau,
|
|
||||||
sampler,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create(params: &ParameterDictionary, _loc: &FileLoc) -> Self {
|
|
||||||
let xw = params.get_one_float("xradius", 4.);
|
|
||||||
let yw = params.get_one_float("yradius", 4.);
|
|
||||||
let tau = params.get_one_float("tau", 3.);
|
|
||||||
Self::new(Vector2f::new(xw, yw), tau)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FilterTrait for LanczosSincFilter {
|
|
||||||
fn radius(&self) -> Vector2f {
|
|
||||||
self.radius
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(&self, p: Point2f) -> Float {
|
|
||||||
windowed_sinc(p.x(), self.radius.x(), self.tau)
|
|
||||||
* windowed_sinc(p.y(), self.radius.y(), self.tau)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn integral(&self) -> Float {
|
|
||||||
let sqrt_samples = 64;
|
|
||||||
let n_samples = sqrt_samples * sqrt_samples;
|
|
||||||
let area = (2.0 * self.radius.x()) * (2.0 * self.radius.y());
|
|
||||||
let mut sum = 0.0;
|
|
||||||
let mut rng = rand::rng();
|
|
||||||
|
|
||||||
for y in 0..sqrt_samples {
|
|
||||||
for x in 0..sqrt_samples {
|
|
||||||
let u = Point2f::new(
|
|
||||||
(x as Float + rng.random::<Float>()) / sqrt_samples as Float,
|
|
||||||
(y as Float + rng.random::<Float>()) / sqrt_samples as Float,
|
|
||||||
);
|
|
||||||
let p = Point2f::new(
|
|
||||||
lerp(u.x(), -self.radius.x(), self.radius.x()),
|
|
||||||
lerp(u.y(), -self.radius.y(), self.radius.y()),
|
|
||||||
);
|
|
||||||
sum += self.evaluate(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sum / n_samples as Float * area
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sample(&self, u: Point2f) -> FilterSample {
|
|
||||||
self.sampler.sample(u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct TriangleFilter {
|
|
||||||
pub radius: Vector2f,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TriangleFilter {
|
|
||||||
pub fn new(radius: Vector2f) -> Self {
|
|
||||||
Self { radius }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create(params: &ParameterDictionary, _loc: &FileLoc) -> Self {
|
|
||||||
let xw = params.get_one_float("xradius", 2.);
|
|
||||||
let yw = params.get_one_float("yradius", 2.);
|
|
||||||
Self::new(Vector2f::new(xw, yw))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FilterTrait for TriangleFilter {
|
|
||||||
fn radius(&self) -> Vector2f {
|
|
||||||
self.radius
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate(&self, p: Point2f) -> Float {
|
|
||||||
(self.radius.x() - p.x().abs()).max(0.0) * (self.radius.y() - p.y().abs()).max(0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn integral(&self) -> Float {
|
|
||||||
self.radius.x().powi(2) * self.radius.y().powi(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sample(&self, u: Point2f) -> FilterSample {
|
|
||||||
let p = Point2f::new(
|
|
||||||
sample_tent(u[0], self.radius.x()),
|
|
||||||
sample_tent(u[1], self.radius.y()),
|
|
||||||
);
|
|
||||||
FilterSample { p, weight: 1.0 }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use crate::core::pbrt::{Float, PI};
|
||||||
use crate::images::Image;
|
use crate::images::Image;
|
||||||
use crate::spectra::{
|
use crate::spectra::{
|
||||||
DenselySampledSpectrum, LAMBDA_MAX, LAMBDA_MIN, RGB, RGBColorSpace, RGBIlluminantSpectrum,
|
DenselySampledSpectrum, LAMBDA_MAX, LAMBDA_MIN, RGB, RGBColorSpace, RGBIlluminantSpectrum,
|
||||||
SampledSpectrum, SampledWavelengths, Spectrum, SpectrumProvider,
|
SampledSpectrum, SampledWavelengths, Spectrum, SpectrumTrait,
|
||||||
};
|
};
|
||||||
use crate::utils::containers::InternCache;
|
use crate::utils::containers::InternCache;
|
||||||
use crate::utils::math::{equal_area_sphere_to_square, radians, safe_sqrt, smooth_step, square};
|
use crate::utils::math::{equal_area_sphere_to_square, radians, safe_sqrt, smooth_step, square};
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ use crate::core::texture::{
|
||||||
FloatTexture, FloatTextureTrait, SpectrumTexture, TextureEvalContext, TextureEvaluator,
|
FloatTexture, FloatTextureTrait, SpectrumTexture, TextureEvalContext, TextureEvaluator,
|
||||||
};
|
};
|
||||||
use crate::image::{Image, WrapMode, WrapMode2D};
|
use crate::image::{Image, WrapMode, WrapMode2D};
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths, Spectrum, SpectrumProvider};
|
use crate::spectra::{SampledSpectrum, SampledWavelengths, Spectrum, SpectrumTrait};
|
||||||
use crate::utils::hash::hash_float;
|
use crate::utils::hash::hash_float;
|
||||||
use crate::utils::math::clamp;
|
use crate::utils::math::clamp;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use crate::core::geometry::{
|
||||||
use crate::core::pbrt::{Float, INV_4_PI, PI};
|
use crate::core::pbrt::{Float, INV_4_PI, PI};
|
||||||
use crate::spectra::{
|
use crate::spectra::{
|
||||||
BlackbodySpectrum, DenselySampledSpectrum, LAMBDA_MAX, LAMBDA_MIN, RGBIlluminantSpectrum,
|
BlackbodySpectrum, DenselySampledSpectrum, LAMBDA_MAX, LAMBDA_MIN, RGBIlluminantSpectrum,
|
||||||
RGBUnboundedSpectrum, SampledSpectrum, SampledWavelengths, Spectrum, SpectrumProvider,
|
RGBUnboundedSpectrum, SampledSpectrum, SampledWavelengths, Spectrum, SpectrumTrait,
|
||||||
};
|
};
|
||||||
use crate::utils::containers::SampledGrid;
|
use crate::utils::containers::SampledGrid;
|
||||||
use crate::utils::math::{clamp, square};
|
use crate::utils::math::{clamp, square};
|
||||||
|
|
|
||||||
|
|
@ -8,29 +8,22 @@ 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, SpectrumTrait,
|
||||||
};
|
};
|
||||||
|
use crate::textures::*;
|
||||||
use crate::utils::Transform;
|
use crate::utils::Transform;
|
||||||
use crate::utils::math::square;
|
use crate::utils::math::square;
|
||||||
use crate::utils::transform::TransformGeneric;
|
|
||||||
|
|
||||||
use enum_dispatch::enum_dispatch;
|
#[repr(C)]
|
||||||
use std::path::Path;
|
pub struct TexCoord2D {
|
||||||
|
pub st: Point2f,
|
||||||
struct TexCoord2D {
|
pub dsdx: Float,
|
||||||
st: Point2f,
|
pub dsdy: Float,
|
||||||
dsdx: Float,
|
pub dtdx: Float,
|
||||||
dsdy: Float,
|
pub dtdy: Float,
|
||||||
dtdx: Float,
|
|
||||||
dtdy: Float,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch]
|
#[repr(C)]
|
||||||
trait TextureMapping2DTrait {
|
|
||||||
fn map(&self, ctx: &TextureEvalContext) -> TexCoord2D;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[enum_dispatch(TextureMapping2DTrait)]
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum TextureMapping2D {
|
pub enum TextureMapping2D {
|
||||||
UV(UVMapping),
|
UV(UVMapping),
|
||||||
|
|
@ -39,6 +32,17 @@ pub enum TextureMapping2D {
|
||||||
Planar(PlanarMapping),
|
Planar(PlanarMapping),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TextureMapping2D {
|
||||||
|
pub fn map(&self, ctx: &TextureEvalContext) -> TexCoord2D {
|
||||||
|
match self {
|
||||||
|
TextureMapping2D::UV(t) => t.map(ctx),
|
||||||
|
TextureMapping2D::Spherical(t) => t.map(ctx),
|
||||||
|
TextureMapping2D::Cylindrical(t) => t.map(ctx),
|
||||||
|
TextureMapping2D::Planar(t) => t.map(ctx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct UVMapping {
|
pub struct UVMapping {
|
||||||
su: Float,
|
su: Float,
|
||||||
|
|
@ -47,12 +51,6 @@ pub struct UVMapping {
|
||||||
dv: Float,
|
dv: Float,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UVMapping {
|
|
||||||
pub fn new(su: Float, sv: Float, du: Float, dv: Float) -> Self {
|
|
||||||
Self { su, sv, du, dv }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for UVMapping {
|
impl Default for UVMapping {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -64,8 +62,12 @@ impl Default for UVMapping {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextureMapping2DTrait for UVMapping {
|
impl UVMapping {
|
||||||
fn map(&self, ctx: &TextureEvalContext) -> TexCoord2D {
|
pub fn new(su: Float, sv: Float, du: Float, dv: Float) -> Self {
|
||||||
|
Self { su, sv, du, dv }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map(&self, ctx: &TextureEvalContext) -> TexCoord2D {
|
||||||
let dsdx = self.su * ctx.dudx;
|
let dsdx = self.su * ctx.dudx;
|
||||||
let dsdy = self.su * ctx.dudy;
|
let dsdy = self.su * ctx.dudy;
|
||||||
let dtdx = self.sv * ctx.dvdx;
|
let dtdx = self.sv * ctx.dvdx;
|
||||||
|
|
@ -83,19 +85,17 @@ impl TextureMapping2DTrait for UVMapping {
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SphericalMapping {
|
pub struct SphericalMapping {
|
||||||
texture_from_render: TransformGeneric<Float>,
|
texture_from_render: Transform,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SphericalMapping {
|
impl SphericalMapping {
|
||||||
pub fn new(texture_from_render: &TransformGeneric<Float>) -> Self {
|
pub fn new(texture_from_render: &Transform) -> Self {
|
||||||
Self {
|
Self {
|
||||||
texture_from_render: *texture_from_render,
|
texture_from_render: *texture_from_render,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl TextureMapping2DTrait for SphericalMapping {
|
pub fn map(&self, ctx: &TextureEvalContext) -> TexCoord2D {
|
||||||
fn map(&self, ctx: &TextureEvalContext) -> TexCoord2D {
|
|
||||||
let pt = self.texture_from_render.apply_to_point(ctx.p);
|
let pt = self.texture_from_render.apply_to_point(ctx.p);
|
||||||
let x2y2 = square(pt.x()) + square(pt.y());
|
let x2y2 = square(pt.x()) + square(pt.y());
|
||||||
let sqrtx2y2 = x2y2.sqrt();
|
let sqrtx2y2 = x2y2.sqrt();
|
||||||
|
|
@ -126,19 +126,17 @@ impl TextureMapping2DTrait for SphericalMapping {
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct CylindricalMapping {
|
pub struct CylindricalMapping {
|
||||||
texture_from_render: TransformGeneric<Float>,
|
texture_from_render: Transform,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CylindricalMapping {
|
impl CylindricalMapping {
|
||||||
pub fn new(texture_from_render: &TransformGeneric<Float>) -> Self {
|
pub fn new(texture_from_render: &Transform) -> Self {
|
||||||
Self {
|
Self {
|
||||||
texture_from_render: *texture_from_render,
|
texture_from_render: *texture_from_render,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl TextureMapping2DTrait for CylindricalMapping {
|
pub fn map(&self, ctx: &TextureEvalContext) -> TexCoord2D {
|
||||||
fn map(&self, ctx: &TextureEvalContext) -> TexCoord2D {
|
|
||||||
let pt = self.texture_from_render.apply_to_point(ctx.p);
|
let pt = self.texture_from_render.apply_to_point(ctx.p);
|
||||||
let x2y2 = square(pt.x()) + square(pt.y());
|
let x2y2 = square(pt.x()) + square(pt.y());
|
||||||
let dsdp = Vector3f::new(-pt.y(), pt.x(), 0.) / (2. * PI * x2y2);
|
let dsdp = Vector3f::new(-pt.y(), pt.x(), 0.) / (2. * PI * x2y2);
|
||||||
|
|
@ -162,7 +160,7 @@ impl TextureMapping2DTrait for CylindricalMapping {
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PlanarMapping {
|
pub struct PlanarMapping {
|
||||||
texture_from_render: TransformGeneric<Float>,
|
texture_from_render: Transform,
|
||||||
vs: Vector3f,
|
vs: Vector3f,
|
||||||
vt: Vector3f,
|
vt: Vector3f,
|
||||||
ds: Float,
|
ds: Float,
|
||||||
|
|
@ -171,7 +169,7 @@ pub struct PlanarMapping {
|
||||||
|
|
||||||
impl PlanarMapping {
|
impl PlanarMapping {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
texture_from_render: &TransformGeneric<Float>,
|
texture_from_render: &Transform,
|
||||||
vs: Vector3f,
|
vs: Vector3f,
|
||||||
vt: Vector3f,
|
vt: Vector3f,
|
||||||
ds: Float,
|
ds: Float,
|
||||||
|
|
@ -185,10 +183,8 @@ impl PlanarMapping {
|
||||||
dt,
|
dt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl TextureMapping2DTrait for PlanarMapping {
|
pub fn map(&self, ctx: &TextureEvalContext) -> TexCoord2D {
|
||||||
fn map(&self, ctx: &TextureEvalContext) -> TexCoord2D {
|
|
||||||
let vec: Vector3f = self.texture_from_render.apply_to_point(ctx.p).into();
|
let vec: Vector3f = self.texture_from_render.apply_to_point(ctx.p).into();
|
||||||
let dpdx = self.texture_from_render.apply_to_vector(ctx.dpdx);
|
let dpdx = self.texture_from_render.apply_to_vector(ctx.dpdx);
|
||||||
let dpdy = self.texture_from_render.apply_to_vector(ctx.dpdy);
|
let dpdy = self.texture_from_render.apply_to_vector(ctx.dpdy);
|
||||||
|
|
@ -208,9 +204,9 @@ impl TextureMapping2DTrait for PlanarMapping {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TexCoord3D {
|
pub struct TexCoord3D {
|
||||||
p: Point3f,
|
pub p: Point3f,
|
||||||
dpdx: Vector3f,
|
pub dpdx: Vector3f,
|
||||||
dpdy: Vector3f,
|
pub dpdy: Vector3f,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait TextureMapping3DTrait {
|
pub trait TextureMapping3DTrait {
|
||||||
|
|
@ -218,26 +214,31 @@ pub trait TextureMapping3DTrait {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
#[enum_dispatch(TextureMapping3DTrait)]
|
|
||||||
pub enum TextureMapping3D {
|
pub enum TextureMapping3D {
|
||||||
PointTransform(PointTransformMapping),
|
PointTransform(PointTransformMapping),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TextureMapping3D {
|
||||||
|
pub fn map(&self, ctx: &TextureEvalContext) -> TexCoord3D {
|
||||||
|
match self {
|
||||||
|
TextureMapping3D::PointTransform(t) => t.map(ctx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PointTransformMapping {
|
pub struct PointTransformMapping {
|
||||||
texture_from_render: TransformGeneric<Float>,
|
texture_from_render: Transform,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PointTransformMapping {
|
impl PointTransformMapping {
|
||||||
pub fn new(texture_from_render: &TransformGeneric<Float>) -> Self {
|
pub fn new(texture_from_render: &Transform) -> Self {
|
||||||
Self {
|
Self {
|
||||||
texture_from_render: *texture_from_render,
|
texture_from_render: *texture_from_render,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl TextureMapping3DTrait for PointTransformMapping {
|
pub fn map(&self, ctx: &TextureEvalContext) -> TexCoord3D {
|
||||||
fn map(&self, ctx: &TextureEvalContext) -> TexCoord3D {
|
|
||||||
TexCoord3D {
|
TexCoord3D {
|
||||||
p: self.texture_from_render.apply_to_point(ctx.p),
|
p: self.texture_from_render.apply_to_point(ctx.p),
|
||||||
dpdx: self.texture_from_render.apply_to_vector(ctx.dpdx),
|
dpdx: self.texture_from_render.apply_to_vector(ctx.dpdx),
|
||||||
|
|
@ -323,34 +324,39 @@ impl From<&Interaction> for TextureEvalContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch]
|
|
||||||
pub trait FloatTextureTrait: Send + Sync + std::fmt::Debug {
|
|
||||||
fn evaluate(&self, _ctx: &TextureEvalContext) -> Float {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[enum_dispatch]
|
|
||||||
pub trait SpectrumTextureTrait: Send + Sync + std::fmt::Debug {
|
|
||||||
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub enum GPUFloatTexture {
|
pub enum GPUFloatTexture {
|
||||||
Constant(Float),
|
Constant(FloatConstantTexture),
|
||||||
|
DirectionMix(FloatDirectionMixTexture),
|
||||||
|
Scaled(FloatScaledTexture),
|
||||||
|
Bilerp(FloatBilerpTexture),
|
||||||
|
Checkerboard(FloatCheckerboardTexture),
|
||||||
|
Dots(FloatDotsTexture),
|
||||||
|
FBm(FBmTexture),
|
||||||
|
Windy(WindyTexture),
|
||||||
|
Wrinkled(WrinkledTexture),
|
||||||
Ptex(GPUFloatPtexTexture),
|
Ptex(GPUFloatPtexTexture),
|
||||||
Image(GPUFloatImageTexture),
|
Image(GPUFloatImageTexture),
|
||||||
Mix(GPUFloatMixTexture),
|
Mix(GPUFloatMixTexture),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
impl GPUFloatTexture {
|
||||||
#[enum_dispatch(SpectrumTextureTrait)]
|
pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
|
||||||
pub enum SpectrumTexture {
|
match self {
|
||||||
Image(GPUSpectrumImageTexture),
|
GPUFloatTexture::Constant(t) => t.evaluate(ctx),
|
||||||
Ptex(GPUSpectrumPtexTexture),
|
GPUFloatTexture::DirectionMix(t) => t.evaluate(ctx),
|
||||||
Mix(GPUSpectrumMixTexture),
|
GPUFloatTexture::Scaled(t) => t.evaluate(ctx),
|
||||||
|
GPUFloatTexture::Bilerp(t) => t.evaluate(ctx),
|
||||||
|
GPUFloatTexture::Checkerboard(t) => t.evaluate(ctx),
|
||||||
|
GPUFloatTexture::Dots(t) => t.evaluate(ctx),
|
||||||
|
GPUFloatTexture::FBm(t) => t.evaluate(ctx),
|
||||||
|
GPUFloatTexture::Windy(t) => t.evaluate(ctx),
|
||||||
|
GPUFloatTexture::Wrinkle(t) => t.evaluate(ctx),
|
||||||
|
GPUFloatTexture::Ptex(t) => t.evaluate(ctx),
|
||||||
|
GPUFloatTexture::Image(t) => t.evaluate(ctx),
|
||||||
|
GPUFloatTexture::Mix(t) => t.evaluate(ctx),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
|
@ -359,40 +365,34 @@ pub enum SpectrumType {
|
||||||
Albedo,
|
Albedo,
|
||||||
Unbounded,
|
Unbounded,
|
||||||
}
|
}
|
||||||
pub trait TextureEvaluator: Send + Sync {
|
|
||||||
fn evaluate_float(&self, tex: &FloatTexture, ctx: &TextureEvalContext) -> Float;
|
|
||||||
fn evaluate_spectrum(
|
|
||||||
&self,
|
|
||||||
tex: &SpectrumTexture,
|
|
||||||
ctx: &TextureEvalContext,
|
|
||||||
lambda: &SampledWavelengths,
|
|
||||||
) -> SampledSpectrum;
|
|
||||||
|
|
||||||
fn can_evaluate(&self, _ftex: &[&FloatTexture], _stex: &[&SpectrumTexture]) -> bool;
|
#[repr(C)]
|
||||||
|
pub enum GPUSpectrumTexture {
|
||||||
|
Constant(SpectrumConstantTexture),
|
||||||
|
Bilerp(SpectrumBilerpTexture),
|
||||||
|
Checkerboard(SpectrumCheckerboardTexture),
|
||||||
|
Marble(MarbleTexture),
|
||||||
|
DirectionMix(SpectrumDirectionMixTexture),
|
||||||
|
Dots(SpectrumDotsTexture),
|
||||||
|
Scaled(SpectrumScaledTexture),
|
||||||
|
Image(GPUSpectrumImageTexture),
|
||||||
|
Ptex(GPUSpectrumPtexTexture),
|
||||||
|
Mix(GPUSpectrumMixTexture),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default)]
|
impl GPUSpectrumTexture {
|
||||||
pub struct UniversalTextureEvaluator;
|
pub fn evaluate(&self, ctx: &TextureEvalContext, lambda: &SampledWavelengths) -> Float {
|
||||||
|
match self {
|
||||||
impl TextureEvaluator for UniversalTextureEvaluator {
|
GPUSpectrumTexture::Constant(t) => t.evaluate(ctx, lambda),
|
||||||
fn evaluate_float(&self, tex: &FloatTexture, ctx: &TextureEvalContext) -> Float {
|
GPUSpectrumTexture::Bilerp(t) => t.evaluate(ctx, lambda),
|
||||||
tex.evaluate(ctx)
|
GPUSpectrumTexture::Checkerboard(t) => t.evaluate(ctx, lambda),
|
||||||
}
|
GPUSpectrumTexture::Marble(t) => t.evaluate(ctx, lambda),
|
||||||
|
GPUSpectrumTexture::DirectionMix(t) => t.evaluate(ctx, lambda),
|
||||||
fn evaluate_spectrum(
|
GPUSpectrumTexture::Dots(t) => t.evaluate(ctx, lambda),
|
||||||
&self,
|
GPUSpectrumTexture::Scaled(t) => t.evaluate(ctx, lambda),
|
||||||
tex: &SpectrumTexture,
|
GPUSpectrumTexture::Ptex(t) => t.evaluate(ctx, lambda),
|
||||||
ctx: &TextureEvalContext,
|
GPUSpectrumTexture::Image(t) => t.evaluate(ctx, lambda),
|
||||||
lambda: &SampledWavelengths,
|
GPUSpectrumTexture::Mix(t) => t.evaluate(ctx, lambda),
|
||||||
) -> SampledSpectrum {
|
}
|
||||||
tex.evaluate(ctx, lambda)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn can_evaluate(
|
|
||||||
&self,
|
|
||||||
_float_textures: &[&FloatTexture],
|
|
||||||
_spectrum_textures: &[&SpectrumTexture],
|
|
||||||
) -> bool {
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
38
shared/src/filters/boxf.rs
Normal file
38
shared/src/filters/boxf.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
use crate::Float;
|
||||||
|
use crate::core::filter::FilterSample;
|
||||||
|
use crate::core::geometry::{Point2f, Vector2f};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct BoxFilter {
|
||||||
|
pub radius: Vector2f,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BoxFilter {
|
||||||
|
pub fn new(radius: Vector2f) -> Self {
|
||||||
|
Self { radius }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn radius(&self) -> Vector2f {
|
||||||
|
self.radius
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn evaluate(&self, p: Point2f) -> Float {
|
||||||
|
if p.x().abs() <= self.radius.x() && p.y().abs() <= self.radius.y() {
|
||||||
|
1.
|
||||||
|
} else {
|
||||||
|
0.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn integral(&self) -> Float {
|
||||||
|
(2.0 * self.radius.x()) * (2.0 * self.radius.y())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sample(&self, u: Point2f) -> FilterSample {
|
||||||
|
let p = Point2f::new(
|
||||||
|
lerp(u[0], -self.radius.x(), self.radius.x()),
|
||||||
|
lerp(u[1], -self.radius.y(), self.radius.y()),
|
||||||
|
);
|
||||||
|
FilterSample { p, weight: 1.0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
49
shared/src/filters/gaussian.rs
Normal file
49
shared/src/filters/gaussian.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct GaussianFilter {
|
||||||
|
pub radius: Vector2f,
|
||||||
|
pub sigma: Float,
|
||||||
|
pub exp_x: Float,
|
||||||
|
pub exp_y: Float,
|
||||||
|
pub sampler: FilterSampler,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GaussianFilter {
|
||||||
|
pub fn new(radius: Vector2f, sigma: Float) -> Self {
|
||||||
|
let exp_x = gaussian(radius.x(), 0., sigma);
|
||||||
|
let exp_y = gaussian(radius.y(), 0., sigma);
|
||||||
|
|
||||||
|
let sampler = FilterSampler::new(radius, move |p: Point2f| {
|
||||||
|
let gx = (gaussian(p.x(), 0., sigma) - exp_x).max(0.0);
|
||||||
|
let gy = (gaussian(p.y(), 0., sigma) - exp_y).max(0.0);
|
||||||
|
gx * gy
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
|
radius,
|
||||||
|
sigma,
|
||||||
|
exp_x: gaussian(radius.x(), 0., sigma),
|
||||||
|
exp_y: gaussian(radius.y(), 0., sigma),
|
||||||
|
sampler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn radius(&self) -> Vector2f {
|
||||||
|
self.radius
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn evaluate(&self, p: Point2f) -> Float {
|
||||||
|
(gaussian(p.x(), 0.0, self.sigma) - self.exp_x).max(0.0)
|
||||||
|
* (gaussian(p.y(), 0.0, self.sigma) - self.exp_y).max(0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn integral(&self) -> Float {
|
||||||
|
(gaussian_integral(-self.radius.x(), self.radius.x(), 0.0, self.sigma)
|
||||||
|
- 2.0 * self.radius.x() * self.exp_x)
|
||||||
|
* (gaussian_integral(-self.radius.y(), self.radius.y(), 0.0, self.sigma)
|
||||||
|
- 2.0 * self.radius.y() * self.exp_y)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sample(&self, u: Point2f) -> FilterSample {
|
||||||
|
self.sampler.sample(u)
|
||||||
|
}
|
||||||
|
}
|
||||||
56
shared/src/filters/lanczos.rs
Normal file
56
shared/src/filters/lanczos.rs
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct LanczosSincFilter {
|
||||||
|
pub radius: Vector2f,
|
||||||
|
pub tau: Float,
|
||||||
|
pub sampler: FilterSampler,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LanczosSincFilter {
|
||||||
|
pub fn new(radius: Vector2f, tau: Float) -> Self {
|
||||||
|
let sampler = FilterSampler::new(radius, move |p: Point2f| {
|
||||||
|
windowed_sinc(p.x(), radius.x(), tau) * windowed_sinc(p.y(), radius.y(), tau)
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
|
radius,
|
||||||
|
tau,
|
||||||
|
sampler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn radius(&self) -> Vector2f {
|
||||||
|
self.radius
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn evaluate(&self, p: Point2f) -> Float {
|
||||||
|
windowed_sinc(p.x(), self.radius.x(), self.tau)
|
||||||
|
* windowed_sinc(p.y(), self.radius.y(), self.tau)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn integral(&self) -> Float {
|
||||||
|
let sqrt_samples = 64;
|
||||||
|
let n_samples = sqrt_samples * sqrt_samples;
|
||||||
|
let area = (2.0 * self.radius.x()) * (2.0 * self.radius.y());
|
||||||
|
let mut sum = 0.0;
|
||||||
|
let mut rng = rand::rng();
|
||||||
|
|
||||||
|
for y in 0..sqrt_samples {
|
||||||
|
for x in 0..sqrt_samples {
|
||||||
|
let u = Point2f::new(
|
||||||
|
(x as Float + rng.random::<Float>()) / sqrt_samples as Float,
|
||||||
|
(y as Float + rng.random::<Float>()) / sqrt_samples as Float,
|
||||||
|
);
|
||||||
|
let p = Point2f::new(
|
||||||
|
lerp(u.x(), -self.radius.x(), self.radius.x()),
|
||||||
|
lerp(u.y(), -self.radius.y(), self.radius.y()),
|
||||||
|
);
|
||||||
|
sum += self.evaluate(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sum / n_samples as Float * area
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sample(&self, u: Point2f) -> FilterSample {
|
||||||
|
self.sampler.sample(u)
|
||||||
|
}
|
||||||
|
}
|
||||||
67
shared/src/filters/mitchell.rs
Normal file
67
shared/src/filters/mitchell.rs
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
use crate::Float;
|
||||||
|
use crate::core::filter::FilterSampler;
|
||||||
|
use crate::core::geometry::{Point2f, Vector2f};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct MitchellFilter {
|
||||||
|
pub radius: Vector2f,
|
||||||
|
pub b: Float,
|
||||||
|
pub c: Float,
|
||||||
|
pub sampler: FilterSampler,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MitchellFilter {
|
||||||
|
pub fn new(radius: Vector2f, b: Float, c: Float) -> Self {
|
||||||
|
let sampler = FilterSampler::new(radius, move |p: Point2f| {
|
||||||
|
let nx = 2.0 * p.x() / radius.x();
|
||||||
|
let ny = 2.0 * p.y() / radius.y();
|
||||||
|
Self::mitchell_1d_eval(b, c, nx) * Self::mitchell_1d_eval(b, c, ny)
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
|
radius,
|
||||||
|
b,
|
||||||
|
c,
|
||||||
|
sampler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mitchell_1d_eval(b: Float, c: Float, x: Float) -> Float {
|
||||||
|
let x = x.abs();
|
||||||
|
if x <= 1.0 {
|
||||||
|
((12.0 - 9.0 * b - 6.0 * c) * x.powi(3)
|
||||||
|
+ (-18.0 + 12.0 * b + 6.0 * c) * x.powi(2)
|
||||||
|
+ (6.0 - 2.0 * b))
|
||||||
|
* (1.0 / 6.0)
|
||||||
|
} else if x <= 2.0 {
|
||||||
|
((-b - 6.0 * c) * x.powi(3)
|
||||||
|
+ (6.0 * b + 30.0 * c) * x.powi(2)
|
||||||
|
+ (-12.0 * b - 48.0 * c) * x
|
||||||
|
+ (8.0 * b + 24.0 * c))
|
||||||
|
* (1.0 / 6.0)
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mitchell_1d(&self, x: Float) -> Float {
|
||||||
|
Self::mitchell_1d_eval(self.b, self.c, x)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn radius(&self) -> Vector2f {
|
||||||
|
self.radius
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn evaluate(&self, p: Point2f) -> Float {
|
||||||
|
self.mitchell_1d(2.0 * p.x() / self.radius.x())
|
||||||
|
* self.mitchell_1d(2.0 * p.y() / self.radius.y())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn integral(&self) -> Float {
|
||||||
|
self.radius.x() * self.radius.y() / 4.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sample(&self, u: Point2f) -> FilterSample {
|
||||||
|
self.sampler.sample(u)
|
||||||
|
}
|
||||||
|
}
|
||||||
5
shared/src/filters/mod.rs
Normal file
5
shared/src/filters/mod.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
pub mod boxf;
|
||||||
|
pub mod gaussian;
|
||||||
|
pub mod lanczos;
|
||||||
|
pub mod mitchell;
|
||||||
|
pub mod triangle;
|
||||||
30
shared/src/filters/triangle.rs
Normal file
30
shared/src/filters/triangle.rs
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct TriangleFilter {
|
||||||
|
pub radius: Vector2f,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TriangleFilter {
|
||||||
|
pub fn new(radius: Vector2f) -> Self {
|
||||||
|
Self { radius }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn radius(&self) -> Vector2f {
|
||||||
|
self.radius
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn evaluate(&self, p: Point2f) -> Float {
|
||||||
|
(self.radius.x() - p.x().abs()).max(0.0) * (self.radius.y() - p.y().abs()).max(0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn integral(&self) -> Float {
|
||||||
|
self.radius.x().powi(2) * self.radius.y().powi(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sample(&self, u: Point2f) -> FilterSample {
|
||||||
|
let p = Point2f::new(
|
||||||
|
sample_tent(u[0], self.radius.x()),
|
||||||
|
sample_tent(u[1], self.radius.y()),
|
||||||
|
);
|
||||||
|
FilterSample { p, weight: 1.0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
mod cameras;
|
mod cameras;
|
||||||
mod core;
|
mod core;
|
||||||
mod data;
|
mod data;
|
||||||
|
mod filters;
|
||||||
mod images;
|
mod images;
|
||||||
mod integrators;
|
mod integrators;
|
||||||
mod lights;
|
mod lights;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use super::sampled::{LAMBDA_MAX, LAMBDA_MIN};
|
||||||
use super::simple::{DenselySampledSpectrum, PiecewiseLinearSpectrum};
|
use super::simple::{DenselySampledSpectrum, PiecewiseLinearSpectrum};
|
||||||
use crate::core::cie::{CIE_S_LAMBDA, CIE_S0, CIE_S1, CIE_S2, CIE_X, CIE_Y, CIE_Z, N_CIES};
|
use crate::core::cie::{CIE_S_LAMBDA, CIE_S0, CIE_S1, CIE_S2, CIE_X, CIE_Y, CIE_Z, N_CIES};
|
||||||
use crate::core::pbrt::Float;
|
use crate::core::pbrt::Float;
|
||||||
use crate::spectra::{BlackbodySpectrum, SpectrumProvider};
|
use crate::spectra::{BlackbodySpectrum, SpectrumTrait};
|
||||||
use crate::utils::math::square;
|
use crate::utils::math::square;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,9 @@ pub mod sampled;
|
||||||
pub mod simple;
|
pub mod simple;
|
||||||
|
|
||||||
use crate::core::pbrt::Float;
|
use crate::core::pbrt::Float;
|
||||||
use crate::utils::file::read_float_file;
|
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
|
|
||||||
pub use color::{RGB, XYZ};
|
pub use color::{ColorEncoding, RGB, RGBSigmoidPolynomial, XYZ};
|
||||||
pub use colorspace::RGBColorSpace;
|
pub use colorspace::RGBColorSpace;
|
||||||
pub use data::*;
|
pub use data::*;
|
||||||
pub use rgb::*;
|
pub use rgb::*;
|
||||||
|
|
@ -19,32 +18,33 @@ pub use sampled::{N_SPECTRUM_SAMPLES, SampledSpectrum, SampledWavelengths};
|
||||||
pub use simple::*;
|
pub use simple::*;
|
||||||
//
|
//
|
||||||
#[enum_dispatch]
|
#[enum_dispatch]
|
||||||
pub trait SpectrumProvider: Copy {
|
pub trait SpectrumTrait: Copy {
|
||||||
fn evaluate(&self, lambda: Float) -> Float;
|
fn evaluate(&self, lambda: Float) -> Float;
|
||||||
fn max_value(&self) -> Float;
|
fn max_value(&self) -> Float;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "spirv"))]
|
// #[cfg(not(target_arch = "spirv"))]
|
||||||
impl SpectrumProvider for std::sync::Arc<DenselySampledSpectrum> {
|
// impl SpectrumTrait for std::sync::Arc<DenselySampledSpectrum> {
|
||||||
fn evaluate(&self, lambda: Float) -> Float {
|
// fn evaluate(&self, lambda: Float) -> Float {
|
||||||
(**self).evaluate(lambda)
|
// (**self).evaluate(lambda)
|
||||||
}
|
// }
|
||||||
fn max_value(&self) -> Float {
|
// fn max_value(&self) -> Float {
|
||||||
(**self).max_value()
|
// (**self).max_value()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
#[cfg(target_arch = "spirv")] // or target_os = "cuda"
|
// #[cfg(target_arch = "spirv")] // or target_os = "cuda"
|
||||||
impl SpectrumProvider for u32 {
|
// impl SpectrumTrait for u32 {
|
||||||
fn evaluate(&self, lambda: Float) -> Float {
|
// fn evaluate(&self, lambda: Float) -> Float {
|
||||||
// Here you would call a global function that accesses
|
// // Here you would call a global function that accesses
|
||||||
// a static buffer of spectra data
|
// // a static buffer of spectra data
|
||||||
crate::gpu::lookup_global_spectrum(*self, lambda)
|
// crate::gpu::lookup_global_spectrum(*self, lambda)
|
||||||
}
|
// }
|
||||||
fn max_value(&self) -> Float {
|
// fn max_value(&self) -> Float {
|
||||||
crate::gpu::lookup_global_spectrum_max(*self)
|
// crate::gpu::lookup_global_spectrum_max(*self)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
|
||||||
#[enum_dispatch(SpectrumTrait)]
|
#[enum_dispatch(SpectrumTrait)]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use super::{
|
use super::{
|
||||||
DenselySampledSpectrum, LAMBDA_MAX, LAMBDA_MIN, N_SPECTRUM_SAMPLES, RGB, RGBColorSpace,
|
DenselySampledSpectrum, LAMBDA_MAX, LAMBDA_MIN, N_SPECTRUM_SAMPLES, RGB, RGBColorSpace,
|
||||||
RGBSigmoidPolynomial, SampledSpectrum, SampledWavelengths, SpectrumProvider, XYZ,
|
RGBSigmoidPolynomial, SampledSpectrum, SampledWavelengths, SpectrumTrait, XYZ,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::Float;
|
use crate::Float;
|
||||||
|
|
@ -26,7 +26,7 @@ impl RGBAlbedoSpectrum {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpectrumProvider for RGBAlbedoSpectrum {
|
impl SpectrumTrait for RGBAlbedoSpectrum {
|
||||||
fn evaluate(&self, lambda: Float) -> Float {
|
fn evaluate(&self, lambda: Float) -> Float {
|
||||||
self.rsp.evaluate(lambda)
|
self.rsp.evaluate(lambda)
|
||||||
}
|
}
|
||||||
|
|
@ -58,7 +58,7 @@ impl UnboundedRGBSpectrum {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpectrumProvider for UnboundedRGBSpectrum {
|
impl SpectrumTrait for UnboundedRGBSpectrum {
|
||||||
fn evaluate(&self, lambda: Float) -> Float {
|
fn evaluate(&self, lambda: Float) -> Float {
|
||||||
self.scale * self.rsp.evaluate(lambda)
|
self.scale * self.rsp.evaluate(lambda)
|
||||||
}
|
}
|
||||||
|
|
@ -69,10 +69,10 @@ impl SpectrumProvider for UnboundedRGBSpectrum {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct RGBIlluminantSpectrum<P: SpectrumProvider> {
|
pub struct RGBIlluminantSpectrum {
|
||||||
scale: Float,
|
scale: Float,
|
||||||
rsp: RGBSigmoidPolynomial,
|
rsp: RGBSigmoidPolynomial,
|
||||||
illuminant: P,
|
illuminant: DenselySampledSpectrum,
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl RGBIlluminantSpectrum {
|
// impl RGBIlluminantSpectrum {
|
||||||
|
|
@ -101,7 +101,7 @@ pub struct RGBIlluminantSpectrum<P: SpectrumProvider> {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
impl SpectrumProvider for RGBIlluminantSpectrum {
|
impl SpectrumTrait for RGBIlluminantSpectrum {
|
||||||
fn evaluate(&self, lambda: Float) -> Float {
|
fn evaluate(&self, lambda: Float) -> Float {
|
||||||
match &self.illuminant {
|
match &self.illuminant {
|
||||||
Some(illuminant) => {
|
Some(illuminant) => {
|
||||||
|
|
@ -161,7 +161,7 @@ impl RGBUnboundedSpectrum {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpectrumProvider for RGBUnboundedSpectrum {
|
impl SpectrumTrait for RGBUnboundedSpectrum {
|
||||||
fn evaluate(&self, lambda: Float) -> Float {
|
fn evaluate(&self, lambda: Float) -> Float {
|
||||||
self.scale * self.rsp.evaluate(lambda)
|
self.scale * self.rsp.evaluate(lambda)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use super::sampled::{LAMBDA_MAX, LAMBDA_MIN};
|
||||||
use crate::core::cie::*;
|
use crate::core::cie::*;
|
||||||
use crate::core::pbrt::Float;
|
use crate::core::pbrt::Float;
|
||||||
use crate::spectra::{
|
use crate::spectra::{
|
||||||
N_SPECTRUM_SAMPLES, SampledSpectrum, SampledWavelengths, Spectrum, SpectrumProvider,
|
N_SPECTRUM_SAMPLES, SampledSpectrum, SampledWavelengths, Spectrum, SpectrumTrait,
|
||||||
};
|
};
|
||||||
use crate::utils::file::read_float_file;
|
use crate::utils::file::read_float_file;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
@ -21,7 +21,7 @@ impl ConstantSpectrum {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpectrumProvider for ConstantSpectrum {
|
impl SpectrumTrait for ConstantSpectrum {
|
||||||
fn evaluate(&self, _lambda: Float) -> Float {
|
fn evaluate(&self, _lambda: Float) -> Float {
|
||||||
self.c
|
self.c
|
||||||
}
|
}
|
||||||
|
|
@ -165,7 +165,7 @@ impl Hash for DenselySampledSpectrum {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpectrumProvider for DenselySampledSpectrum {
|
impl SpectrumTrait for DenselySampledSpectrum {
|
||||||
fn evaluate(&self, lambda: Float) -> Float {
|
fn evaluate(&self, lambda: Float) -> Float {
|
||||||
let offset = (lambda.round() as i32) - self.lambda_min;
|
let offset = (lambda.round() as i32) - self.lambda_min;
|
||||||
if offset < 0 || offset as usize >= self.values.len() {
|
if offset < 0 || offset as usize >= self.values.len() {
|
||||||
|
|
@ -241,7 +241,7 @@ impl PiecewiseLinearSpectrum {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpectrumProvider for PiecewiseLinearSpectrum {
|
impl SpectrumTrait for PiecewiseLinearSpectrum {
|
||||||
fn evaluate(&self, lambda: Float) -> Float {
|
fn evaluate(&self, lambda: Float) -> Float {
|
||||||
if self.lambdas.is_empty() {
|
if self.lambdas.is_empty() {
|
||||||
return 0.0;
|
return 0.0;
|
||||||
|
|
@ -321,7 +321,7 @@ impl BlackbodySpectrum {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpectrumProvider for BlackbodySpectrum {
|
impl SpectrumTrait for BlackbodySpectrum {
|
||||||
fn evaluate(&self, lambda: Float) -> Float {
|
fn evaluate(&self, lambda: Float) -> Float {
|
||||||
Self::planck_law(lambda, self.temperature) * self.normalization_factor
|
Self::planck_law(lambda, self.temperature) * self.normalization_factor
|
||||||
}
|
}
|
||||||
|
|
|
||||||
85
shared/src/textures/bilerp.rs
Normal file
85
shared/src/textures/bilerp.rs
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
use crate::core::texture::{TextureEvalContext, TextureMapping2D};
|
||||||
|
use crate::spectra::{SampledSpectrum, SampledWavelengths, SpectrumTrait};
|
||||||
|
use crate::utils::Transform;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FloatBilerpTexture {
|
||||||
|
mapping: TextureMapping2D,
|
||||||
|
v00: Float,
|
||||||
|
v01: Float,
|
||||||
|
v10: Float,
|
||||||
|
v11: Float,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn bilerp(st_frac: [Float; 2], v: [SampledSpectrum; 4]) -> SampledSpectrum {
|
||||||
|
let sx = st_frac[0];
|
||||||
|
let sy = st_frac[1];
|
||||||
|
let one_minus_sx = 1.0 - sx;
|
||||||
|
let one_minus_sy = 1.0 - sy;
|
||||||
|
|
||||||
|
one_minus_sx * one_minus_sy * v[0]
|
||||||
|
+ sx * one_minus_sy * v[1]
|
||||||
|
+ one_minus_sx * sy * v[2]
|
||||||
|
+ sx * sy * v[3]
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatBilerpTexture {
|
||||||
|
pub fn new(mapping: TextureMapping2D, v00: Float, v01: Float, v10: Float, v11: Float) -> Self {
|
||||||
|
Self {
|
||||||
|
mapping,
|
||||||
|
v00,
|
||||||
|
v01,
|
||||||
|
v10,
|
||||||
|
v11,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn evaluate(&self, _ctx: &TextureEvalContext) -> Float {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct SpectrumBilerpTexture {
|
||||||
|
pub mapping: TextureMapping2D,
|
||||||
|
pub v00: Spectrum,
|
||||||
|
pub v01: Spectrum,
|
||||||
|
pub v10: Spectrum,
|
||||||
|
pub v11: Spectrum,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpectrumBilerpTexture {
|
||||||
|
pub fn new(
|
||||||
|
mapping: TextureMapping2D,
|
||||||
|
v00: Spectrum,
|
||||||
|
v01: Spectrum,
|
||||||
|
v10: Spectrum,
|
||||||
|
v11: Spectrum,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
mapping,
|
||||||
|
v00,
|
||||||
|
v01,
|
||||||
|
v10,
|
||||||
|
v11,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn evaluate(
|
||||||
|
&self,
|
||||||
|
ctx: &TextureEvalContext,
|
||||||
|
_lambda: &SampledWavelengths,
|
||||||
|
) -> SampledSpectrum {
|
||||||
|
let c = self.mapping.map(ctx);
|
||||||
|
bilerp(
|
||||||
|
[c.st[0], c.st[1], c.st[2]],
|
||||||
|
[
|
||||||
|
v00.sample(lambda),
|
||||||
|
v01.sample(lambda),
|
||||||
|
v10.sample(lambda),
|
||||||
|
v11.sample(lambda),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
29
shared/src/textures/checkerboard.rs
Normal file
29
shared/src/textures/checkerboard.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
use crate::core::texture::TextureEvalContext;
|
||||||
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
|
|
||||||
|
// TODO: I have to implement somethign like a TaggedPointer, and change the whole codebase.
|
||||||
|
// Fantastic
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FloatCheckerboardTexture {
|
||||||
|
pub map_2d: TextureMapping2D,
|
||||||
|
pub map_3d: TextureMapping3D,
|
||||||
|
pub tex: [FloatTexture; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatCheckerboardTexture {
|
||||||
|
pub fn evaluate(&self, _ctx: &TextureEvalContext) -> Float {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct SpectrumCheckerboardTexture;
|
||||||
|
impl SpectrumCheckerboardTexture {
|
||||||
|
pub fn evaluate(
|
||||||
|
&self,
|
||||||
|
_ctx: &TextureEvalContext,
|
||||||
|
_lambda: &SampledWavelengths,
|
||||||
|
) -> SampledSpectrum {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
37
shared/src/textures/constant.rs
Normal file
37
shared/src/textures/constant.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
use crate::Float;
|
||||||
|
use crate::core::texture::TextureEvalContext;
|
||||||
|
use crate::spectra::{SampledSpectrum, SampledWavelengths, Spectrum};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FloatConstantTexture {
|
||||||
|
pub value: Float,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatConstantTexture {
|
||||||
|
pub fn new(value: Float) -> Self {
|
||||||
|
Self { value }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn evaluate(&self, _ctx: &TextureEvalContext) -> Float {
|
||||||
|
self.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct SpectrumConstantTexture {
|
||||||
|
pub value: Spectrum,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpectrumConstantTexture {
|
||||||
|
pub fn new(value: Spectrum) -> Self {
|
||||||
|
Self { value }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn evaluate(
|
||||||
|
&self,
|
||||||
|
_ctx: &TextureEvalContext,
|
||||||
|
lambda: &SampledWavelengths,
|
||||||
|
) -> SampledSpectrum {
|
||||||
|
self.value.sample(lambda)
|
||||||
|
}
|
||||||
|
}
|
||||||
19
shared/src/textures/dots.rs
Normal file
19
shared/src/textures/dots.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FloatDotsTexture;
|
||||||
|
impl FloatDotsTexture {
|
||||||
|
pub fn evaluate(&self, _ctx: &TextureEvalContext) -> Float {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct SpectrumDotsTexture;
|
||||||
|
impl SpectrumDotsTexture {
|
||||||
|
pub fn evaluate(
|
||||||
|
&self,
|
||||||
|
_ctx: &TextureEvalContext,
|
||||||
|
_lambda: &SampledWavelengths,
|
||||||
|
) -> SampledSpectrum {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
15
shared/src/textures/fbm.rs
Normal file
15
shared/src/textures/fbm.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
use crate::core::texture::{TextureEvalContext, TextureMapping3D};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FBmTexture {
|
||||||
|
pub mapping: TextureMapping3D,
|
||||||
|
pub omega: Float,
|
||||||
|
pub octaves: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FBmTexture {
|
||||||
|
pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
|
||||||
|
let c = self.mapping.map(ctx);
|
||||||
|
fbm(c.p, c.dpdx, c.dpdy, self.omega, self.octaves)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
use crate::{
|
use crate::Float;
|
||||||
core::texture::{
|
use crate::core::texture::{SpectrumType, TextureEvalContext, TextureMapping2D};
|
||||||
FloatTextureTrait, ImageTextureBase, SpectrumTextureTrait, SpectrumType, TextureMapping2D,
|
use crate::spectra::RGBColorSpace;
|
||||||
},
|
|
||||||
spectra::RGBColorSpace,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* GPU heavy code, dont know if this will ever work the way Im doing things.
|
/* GPU heavy code, dont know if this will ever work the way Im doing things.
|
||||||
* Leaving it here isolated, for careful handling */
|
* Leaving it here isolated, for careful handling */
|
||||||
|
|
@ -15,13 +12,59 @@ pub struct GPUSpectrumImageTexture {
|
||||||
pub tex_obj: u64,
|
pub tex_obj: u64,
|
||||||
pub scale: Float,
|
pub scale: Float,
|
||||||
pub invert: bool,
|
pub invert: bool,
|
||||||
|
pub is_single_channel: bool,
|
||||||
pub color_space: RGBColorSpace,
|
pub color_space: RGBColorSpace,
|
||||||
pub spectrum_type: SpectrumType,
|
pub spectrum_type: SpectrumType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpectrumTextureTrait for GPUSpectrumImageTexture {
|
impl GPUSpectrumImageTexture {
|
||||||
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
pub fn evaluate(
|
||||||
todo!()
|
&self,
|
||||||
|
ctx: &TextureEvalContext,
|
||||||
|
lambda: &SampledWavelengths,
|
||||||
|
) -> SampledSpectrum {
|
||||||
|
#[cfg(not(feature = "cuda"))]
|
||||||
|
{
|
||||||
|
return SampledSpectrum::zero();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "cuda")]
|
||||||
|
{
|
||||||
|
use cuda_std::intrinsics;
|
||||||
|
let c = self.mapping.map(ctx);
|
||||||
|
let u = c.st.x;
|
||||||
|
let v = 1.0 - c.st.y;
|
||||||
|
|
||||||
|
let d_p_dx = [c.dsdx, c.dtdx];
|
||||||
|
let d_p_dy = [c.dsdy, c.dtdy];
|
||||||
|
|
||||||
|
let tex_color = if self.is_single_channel {
|
||||||
|
let val: Float =
|
||||||
|
unsafe { intrinsics::tex2d_grad(self.tex_obj, u, v, d_p_dx, d_p_dy) };
|
||||||
|
RGB::new(val, val, val)
|
||||||
|
} else {
|
||||||
|
let val: [Float; 4] =
|
||||||
|
unsafe { intrinsics::tex2d_grad(self.tex_obj, u, v, d_p_dx, d_p_dy) };
|
||||||
|
RGB::new(val[0], val[1], val[2])
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut rgb = tex_color * self.scale;
|
||||||
|
if self.invert {
|
||||||
|
rgb = (RGB::new(1.0, 1.0, 1.0) - rgb).clamp_zero();
|
||||||
|
}
|
||||||
|
|
||||||
|
let color_space = unsafe { &*self.color_space };
|
||||||
|
|
||||||
|
match self.spectrum_type {
|
||||||
|
SpectrumType::Unbounded => {
|
||||||
|
RGBUnboundedSpectrum::new(color_space, rgb).sample(lambda)
|
||||||
|
}
|
||||||
|
SpectrumType::Albedo => {
|
||||||
|
RGBAlbedoSpectrum::new(color_space, rgb.clamp(0.0, 1.0)).sample(lambda)
|
||||||
|
}
|
||||||
|
_ => RGBIlluminantSpectrum::new(color_space, rgb).sample(lambda),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,4 +76,27 @@ pub struct GPUFloatImageTexture {
|
||||||
pub invert: bool,
|
pub invert: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FloatTextureTrait for GPUFloatImageTexture {}
|
impl GPUFloatImageTexture {
|
||||||
|
pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
|
||||||
|
#[cfg(not(feature = "cuda"))]
|
||||||
|
{
|
||||||
|
return 0.;
|
||||||
|
}
|
||||||
|
#[cfg(feature = "cuda")]
|
||||||
|
{
|
||||||
|
use cuda_std::intrinsics;
|
||||||
|
let c = self.mapping.map(ctx);
|
||||||
|
let u = c.st.x;
|
||||||
|
let v = 1.0 - c.st.y;
|
||||||
|
let d_p_dx = [c.dsdx, c.dtdx];
|
||||||
|
let d_p_dy = [c.dsdy, c.dtdy];
|
||||||
|
let val: Float = unsafe { intrinsics::tex2d_grad(self.tex_obj, u, v, d_p_dx, d_p_dy) };
|
||||||
|
|
||||||
|
if invert {
|
||||||
|
return (1. - v).max(0.);
|
||||||
|
} else {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
56
shared/src/textures/marble.rs
Normal file
56
shared/src/textures/marble.rs
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
use crate::core::texture::{TextureEvalContext, TextureMapping3D};
|
||||||
|
use crate::spectra::{RGBAlbedoSpectrum, SampledSpectrum, SampledWavelengths};
|
||||||
|
use crate::utils::noise::fbm;
|
||||||
|
use crate::utils::splines::evaluate_cubic_bezier;
|
||||||
|
use crate::Float;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct MarbleTexture {
|
||||||
|
pub mapping: TextureMapping3D,
|
||||||
|
pub octaves: usize,
|
||||||
|
pub omega: Float,
|
||||||
|
pub scale: Float,
|
||||||
|
pub variation: Float,
|
||||||
|
}
|
||||||
|
impl MarbleTexture {
|
||||||
|
pub fn evaluate(
|
||||||
|
&self,
|
||||||
|
ctx: &TextureEvalContext,
|
||||||
|
_lambda: &SampledWavelengths,
|
||||||
|
) -> SampledSpectrum {
|
||||||
|
let mut c = self.mapping.map(ctx);
|
||||||
|
c.p *= self.scale;
|
||||||
|
let marble = c.p.y() + self.variation * fbm(c.p, self.scale, c.dpdy, omega, self.octaves);
|
||||||
|
const COLORS: [RGB; 9] = [
|
||||||
|
RGB::new(0.58, 0.58, 0.6),
|
||||||
|
RGB::new(0.58, 0.58, 0.6),
|
||||||
|
RGB::new(0.58, 0.58, 0.6),
|
||||||
|
RGB::new(0.5, 0.5, 0.5),
|
||||||
|
RGB::new(0.6, 0.59, 0.58),
|
||||||
|
RGB::new(0.58, 0.58, 0.6),
|
||||||
|
RGB::new(0.58, 0.58, 0.6),
|
||||||
|
RGB::new(0.2, 0.2, 0.33),
|
||||||
|
RGB::new(0.58, 0.58, 0.6),
|
||||||
|
];
|
||||||
|
|
||||||
|
const N_SEG: i32 = 6; // (9 - 3)
|
||||||
|
let t_clamped = t.clamp(0.0, 1.0);
|
||||||
|
let first = ((t_clamped * N_SEG as Float).floor() as i32).clamp(0, N_SEG - 1);
|
||||||
|
let t_segment = t_clamped * N_SEG as Float - first as Float;
|
||||||
|
let first_idx = first as usize;
|
||||||
|
let rgb = evaluate_cubic_bezier(&COLORS[first_idx..first_idx + 4], t_segment) * 1.5;
|
||||||
|
|
||||||
|
let color_space = {
|
||||||
|
#[cfg(target_os = "cuda")]
|
||||||
|
{
|
||||||
|
unsafe { &*RGBColorSpace_sRGB }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "cuda"))]
|
||||||
|
{
|
||||||
|
RGBColorSpace::srgb()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
RGBAlbedoSpectrum::new(color_space, rgb).sample(lambda)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,23 @@
|
||||||
|
pub mod bilerp;
|
||||||
|
pub mod checkerboard;
|
||||||
|
pub mod constant;
|
||||||
|
pub mod dots;
|
||||||
|
pub mod fbm;
|
||||||
pub mod image;
|
pub mod image;
|
||||||
|
pub mod marble;
|
||||||
pub mod mix;
|
pub mod mix;
|
||||||
pub mod ptex;
|
pub mod ptex;
|
||||||
|
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 windy::*;
|
||||||
|
pub use wrinkled::*;
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,55 @@
|
||||||
use crate::core::texture::{FloatTextureTrait, SpectrumTextureTrait, TextureEvalContext};
|
use crate::Float;
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
use crate::core::texture::{SpectrumType, TextureEvalContext};
|
||||||
|
use crate::spectra::{
|
||||||
|
RGBAlbedoSpectrum, RGBColorSpace, RGBIlluminantSpectrum, RGBUnboundedSpectrum, SampledSpectrum,
|
||||||
|
SampledWavelengths,
|
||||||
|
};
|
||||||
|
|
||||||
/* GPU heavy code, have to see how to best approach this
|
/* GPU heavy code, have to see how to best approach this
|
||||||
*/
|
*/
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct GPUSpectrumPtexTexture;
|
#[derive(Debug, Clone)]
|
||||||
impl SpectrumTextureTrait for GPUSpectrumPtexTexture {
|
pub struct GPUFloatPtexTexture {
|
||||||
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
pub face_values: Vec<Float>,
|
||||||
todo!()
|
}
|
||||||
|
|
||||||
|
impl GPUFloatPtexTexture {
|
||||||
|
pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
|
||||||
|
self.face_values[ctx.face_index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct GPUFloatPtexTexture;
|
pub struct GPUSpectrumPtexTexture {
|
||||||
impl FloatTextureTrait for GPUFloatPtexTexture {}
|
pub face_values: *const RGB,
|
||||||
|
pub n_faces: usize,
|
||||||
|
pub spectrum_type: SpectrumType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GPUSpectrumPtexTexture {
|
||||||
|
pub fn evaluate(
|
||||||
|
&self,
|
||||||
|
ctx: &TextureEvalContext,
|
||||||
|
lambda: &SampledWavelengths,
|
||||||
|
) -> SampledSpectrum {
|
||||||
|
let index = ctx.face_index.clamp(0, self.n_faces.saturating_sub(1));
|
||||||
|
let rgb = unsafe { *self.face_values.add(index) };
|
||||||
|
let s_rgb = {
|
||||||
|
#[cfg(feature = "cuda")]
|
||||||
|
unsafe {
|
||||||
|
&*RGBColorSpace_sRGB
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "cuda"))]
|
||||||
|
RGBColorSpace::srgb()
|
||||||
|
};
|
||||||
|
|
||||||
|
match self.spectrum_type {
|
||||||
|
SpectrumType::Unbounded => RGBUnboundedSpectrum::new(s_rgb, rgb).sample(lambda),
|
||||||
|
SpectrumType::Albedo => {
|
||||||
|
let clamped_rgb = rgb.clamp(0.0, 1.0);
|
||||||
|
RGBAlbedoSpectrum::new(s_rgb, clamped_rgb).sample(lambda)
|
||||||
|
}
|
||||||
|
SpectrumType::Illuminant => RGBIlluminantSpectrum::new(s_rgb, rgb).sample(lambda),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
16
shared/src/textures/windy.rs
Normal file
16
shared/src/textures/windy.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
use crate::core::texture::{TextureEvalContext, TextureMapping3D};
|
||||||
|
use crate::utils::noise::fbm;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct WindyTexture {
|
||||||
|
pub mapping: TextureMapping3D,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindyTexture {
|
||||||
|
pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
|
||||||
|
let c = self.mapping.map(ctx);
|
||||||
|
let wind_strength = fbm(0.1 * c.p, 0.1 * c.dpdx, 0.1 * c.dpdy, 0.5, 3);
|
||||||
|
let wave_height = fbm(c.p, c.dpdx, c.dpdy, 0.5, 6);
|
||||||
|
wind_strength.abs() * wave_height
|
||||||
|
}
|
||||||
|
}
|
||||||
16
shared/src/textures/wrinkled.rs
Normal file
16
shared/src/textures/wrinkled.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
use crate::core::texture::{TextureEvalContext, TextureMapping3D};
|
||||||
|
use crate::utils::noise::turbulence;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct WrinkledTexture {
|
||||||
|
pub mapping: TextureMapping3D,
|
||||||
|
pub octaves: usize,
|
||||||
|
pub omega: Float,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WrinkledTexture {
|
||||||
|
pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
|
||||||
|
let c = self.mapping.map(ctx);
|
||||||
|
turbulence(c.p, c.dpdx, c.dpdy, omega, octaves)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,134 +20,161 @@ use crate::core::geometry::{
|
||||||
// {
|
// {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[repr(C)]
|
||||||
pub struct Array2D<T> {
|
pub struct Array2D<T> {
|
||||||
pub values: Vec<T>,
|
pub values: *mut T,
|
||||||
pub extent: Bounds2i,
|
pub extent: Bounds2i,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Array2D<T> {
|
impl<T> Array2D<T> {
|
||||||
pub fn new(extent: Bounds2i) -> Self
|
#[inline]
|
||||||
where
|
pub fn x_size(&self) -> usize {
|
||||||
T: Default,
|
(self.extent.p_max.x() - self.extent.p_min.x()) as usize
|
||||||
{
|
|
||||||
let size = extent.area() as usize;
|
|
||||||
let mut values = Vec::with_capacity(size);
|
|
||||||
values.resize_with(size, T::default);
|
|
||||||
Self { values, extent }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_dims(nx: usize, ny: usize) -> Self
|
#[inline]
|
||||||
where
|
pub fn y_size(&self) -> usize {
|
||||||
T: Default,
|
(self.extent.p_max.y() - self.extent.p_min.y()) as usize
|
||||||
{
|
|
||||||
Self::new(Bounds2i::from_points(
|
|
||||||
Point2i::new(0, 0),
|
|
||||||
Point2i::new(nx as i32, ny as i32),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_filled(width: usize, height: usize, value: T) -> Self
|
#[inline]
|
||||||
where
|
pub fn size(&self) -> usize {
|
||||||
T: Clone,
|
self.extent.area() as usize
|
||||||
{
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn index(&self, x: i32, y: i32) -> usize {
|
||||||
|
let nx = x - self.extent.p_min.x;
|
||||||
|
let ny = y - self.extent.p_min.y;
|
||||||
|
(nx + self.x_size() * ny) as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn get(&self, x: i32, y: i32) -> &T {
|
||||||
|
unsafe { &*self.values.add(self.index(x, y)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn get_mut(&mut self, x: i32, y: i32) -> &mut T {
|
||||||
|
unsafe { &mut *self.values.add(self.index(x, y)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_linear(&self, index: usize) -> &T {
|
||||||
|
unsafe { &*self.values.add(index) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_linear_mut(&mut self, index: usize) -> &mut T {
|
||||||
|
// SAFETY: Caller must ensure index < size()
|
||||||
|
unsafe { &mut *self.values.add(index) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_slice(&self) -> &[T] {
|
||||||
|
unsafe { core::slice::from_raw_parts(self.values, self.size()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_mut_slice(&mut self) -> &mut [T] {
|
||||||
|
unsafe { core::slice::from_raw_parts_mut(self.values, self.size()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "cuda"))]
|
||||||
|
impl<T: Clone> Clone for Array2D<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
let n = self.area();
|
||||||
|
let mut v = Vec::with_capacity(n);
|
||||||
|
unsafe {
|
||||||
|
for i in 0..n {
|
||||||
|
v.push((*self.values.add(i)).clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let values = v.as_mut_ptr();
|
||||||
|
std::mem::forget(v);
|
||||||
|
Self {
|
||||||
|
extent: self.extent,
|
||||||
|
values,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "cuda")]
|
||||||
|
impl<T> Clone for Array2D<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "cuda")]
|
||||||
|
impl<T> Copy for Array2D<T> {}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "cuda"))]
|
||||||
|
impl<T: Default + Clone> Array2D<T> {
|
||||||
|
pub fn new(extent: Bounds2i) -> Self {
|
||||||
|
let n = extent.area() as usize;
|
||||||
|
let mut v = vec![T::default(); n];
|
||||||
|
let values = v.as_mut_ptr();
|
||||||
|
std::mem::forget(v);
|
||||||
|
Self { extent, values }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_dims(nx: usize, ny: usize) -> Self {
|
||||||
|
let extent = Bounds2i::new(Point2i::new(0, 0), Point2i::new(nx, ny));
|
||||||
|
let n = extent.area() as usize;
|
||||||
|
|
||||||
|
let mut v = vec![T::default(); n];
|
||||||
|
let values = v.as_mut_ptr();
|
||||||
|
|
||||||
|
std::mem::forget(v);
|
||||||
|
|
||||||
|
Self { extent, values }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_from_bounds(extent: Bounds2i, default_val: T) -> Self {
|
||||||
|
let n = extent.area() as usize;
|
||||||
|
let mut v = vec![def; n];
|
||||||
|
let values = v.as_mut_ptr();
|
||||||
|
std::mem::forget(v);
|
||||||
|
Self { extent, values }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_filled(width: usize, height: usize, value: T) -> Self {
|
||||||
let extent = Bounds2i::from_points(
|
let extent = Bounds2i::from_points(
|
||||||
Point2i::new(0, 0),
|
Point2i::new(0, 0),
|
||||||
Point2i::new(width as i32, height as i32),
|
Point2i::new(width as i32, height as i32),
|
||||||
);
|
);
|
||||||
Self::new_from_bounds(extent, value)
|
Self::new_from_bounds(extent, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_from_bounds(extent: Bounds2i, default_val: T) -> Self
|
|
||||||
where
|
|
||||||
T: Clone,
|
|
||||||
{
|
|
||||||
let size = extent.area() as usize;
|
|
||||||
let values = vec![default_val; size];
|
|
||||||
Self { values, extent }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn x_size(&self) -> usize {
|
|
||||||
(self.extent.p_max.x() - self.extent.p_min.x()) as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn y_size(&self) -> usize {
|
|
||||||
(self.extent.p_max.y() - self.extent.p_min.y()) as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn size(&self) -> usize {
|
|
||||||
self.values.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_slice(&self) -> &[T] {
|
|
||||||
&self.values
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_mut_slice(&mut self) -> &mut [T] {
|
|
||||||
&mut self.values
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_index(&self, p: Point2i) -> usize {
|
|
||||||
debug_assert!(p.x() >= self.extent.p_min.x() && p.x() < self.extent.p_max.x());
|
|
||||||
debug_assert!(p.y() >= self.extent.p_min.y() && p.y() < self.extent.p_max.y());
|
|
||||||
let width = self.x_size();
|
|
||||||
let pp = Point2i::new(p.x() - self.extent.p_min.x(), p.y() - self.extent.p_min.y());
|
|
||||||
(pp.y() * width as i32 + pp.x()) as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_linear(&self, index: usize) -> &T {
|
|
||||||
&self.values[index]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_linear_mut(&mut self, index: usize) -> &mut T {
|
|
||||||
&mut self.values[index]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "cuda"))]
|
||||||
impl<T> Index<Point2i> for Array2D<T> {
|
impl<T> Index<Point2i> for Array2D<T> {
|
||||||
type Output = T;
|
type Output = T;
|
||||||
|
|
||||||
fn index(&self, mut p: Point2i) -> &Self::Output {
|
fn index(&self, mut p: Point2i) -> &Self::Output {
|
||||||
p -= Vector2i::from(self.extent.p_min);
|
unsafe { self.get(pos.0, pos.1) }
|
||||||
let width = self.extent.p_max.x() - self.extent.p_min.x();
|
|
||||||
let idx = (p.x() + width * p.y()) as usize;
|
|
||||||
&self.values[idx]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "cuda"))]
|
||||||
impl<T> IndexMut<Point2i> for Array2D<T> {
|
impl<T> IndexMut<Point2i> for Array2D<T> {
|
||||||
fn index_mut(&mut self, mut p: Point2i) -> &mut Self::Output {
|
fn index_mut(&mut self, mut p: Point2i) -> &mut Self::Output {
|
||||||
p -= Vector2i::from(self.extent.p_min);
|
unsafe { self.get_mut(pos.0, pos.1) }
|
||||||
let width = self.extent.p_max.x() - self.extent.p_min.x();
|
|
||||||
let idx = (p.x() + width * p.y()) as usize;
|
|
||||||
&mut self.values[idx]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Index<(usize, usize)> for Array2D<T> {
|
|
||||||
type Output = T;
|
|
||||||
|
|
||||||
fn index(&self, (x, y): (usize, usize)) -> &Self::Output {
|
|
||||||
&self[(x as i32, y as i32)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> IndexMut<(usize, usize)> for Array2D<T> {
|
|
||||||
fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut Self::Output {
|
|
||||||
&mut self[(x as i32, y as i32)]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "cuda"))]
|
||||||
impl<T> Index<(i32, i32)> for Array2D<T> {
|
impl<T> Index<(i32, i32)> for Array2D<T> {
|
||||||
type Output = T;
|
type Output = T;
|
||||||
fn index(&self, index: (i32, i32)) -> &Self::Output {
|
fn index(&self, pos: (i32, i32)) -> &Self::Output {
|
||||||
self.index(Point2i::new(index.0, index.1))
|
unsafe { self.get(pos.0, pos.1) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "cuda"))]
|
||||||
impl<T> IndexMut<(i32, i32)> for Array2D<T> {
|
impl<T> IndexMut<(i32, i32)> for Array2D<T> {
|
||||||
fn index_mut(&mut self, index: (i32, i32)) -> &mut Self::Output {
|
fn index_mut(&mut self, pos: (i32, i32)) -> &mut Self::Output {
|
||||||
self.index_mut(Point2i::new(index.0, index.1))
|
unsafe { self.get_mut(pos.0, pos.1) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ pub mod hash;
|
||||||
pub mod interval;
|
pub mod interval;
|
||||||
pub mod math;
|
pub mod math;
|
||||||
pub mod mesh;
|
pub mod mesh;
|
||||||
|
pub mod noise;
|
||||||
pub mod quaternion;
|
pub mod quaternion;
|
||||||
pub mod rng;
|
pub mod rng;
|
||||||
pub mod sampling;
|
pub mod sampling;
|
||||||
|
|
@ -30,36 +31,49 @@ where
|
||||||
i
|
i
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Default, Copy, Clone)]
|
||||||
pub struct AtomicFloat {
|
pub struct AtomicFloat {
|
||||||
bits: AtomicU64,
|
value: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AtomicFloat {
|
impl AtomicFloat {
|
||||||
pub fn new(value: f64) -> Self {
|
pub fn new(value: f64) -> Self {
|
||||||
Self {
|
Self { value }
|
||||||
bits: AtomicU64::new(value.to_bits()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load(&self) -> f64 {
|
pub fn load(&self) -> f64 {
|
||||||
f64::from_bits(self.bits.load(Ordering::Relaxed))
|
#[cfg(not(target_os = "cuda"))]
|
||||||
|
{
|
||||||
|
use core::sync::atomic::{AtomicU64, Ordering};
|
||||||
|
let ptr = &self.value as *const f64 as *const AtomicU64;
|
||||||
|
f64::from_bits(unsafe { (*ptr).load(Ordering::Relaxed) })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn store(&self, value: f64) {
|
#[cfg(target_os = "cuda")]
|
||||||
self.bits.store(value.to_bits(), Ordering::Relaxed);
|
self.value
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(&self, value: f64) {
|
pub fn add(&self, v: f64) {
|
||||||
let mut current_bits = self.bits.load(Ordering::Relaxed);
|
let ptr = &self.value as *const f64 as *mut f64;
|
||||||
|
|
||||||
|
#[cfg(target_os = "cuda")]
|
||||||
|
unsafe {
|
||||||
|
cuda_std::intrinsics::atomic_add(ptr, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "cuda"))]
|
||||||
|
unsafe {
|
||||||
|
use core::sync::atomic::{AtomicU64, Ordering};
|
||||||
|
let atomic_ptr = ptr as *const AtomicU64;
|
||||||
|
let atomic = &*atomic_ptr;
|
||||||
|
let mut current_bits = atomic.load(Ordering::Relaxed);
|
||||||
loop {
|
loop {
|
||||||
let current_val = f64::from_bits(current_bits);
|
let current_val = f64::from_bits(current_bits);
|
||||||
let new_val = current_val + value;
|
let new_val = current_val + v;
|
||||||
let new_bits = new_val.to_bits();
|
match atomic.compare_exchange_weak(
|
||||||
|
|
||||||
match self.bits.compare_exchange_weak(
|
|
||||||
current_bits,
|
current_bits,
|
||||||
new_bits,
|
new_val.to_bits(),
|
||||||
Ordering::Relaxed,
|
Ordering::Relaxed,
|
||||||
Ordering::Relaxed,
|
Ordering::Relaxed,
|
||||||
) {
|
) {
|
||||||
|
|
@ -69,9 +83,4 @@ impl AtomicFloat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AtomicFloat {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new(0.0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
179
shared/src/utils/noise.rs
Normal file
179
shared/src/utils/noise.rs
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
use crate::Float;
|
||||||
|
use crate::core::geometry::{Point3f, Vector3f, VectorLike};
|
||||||
|
use crate::utils::math::{clamp, lerp, smooth_step};
|
||||||
|
|
||||||
|
static NOISE_PERM_SIZE: usize = 256;
|
||||||
|
static NOISE_PERM: [i32; 2 * NOISE_PERM_SIZE] = [
|
||||||
|
151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69,
|
||||||
|
142, // Remainder of the noise permutation table
|
||||||
|
8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203,
|
||||||
|
117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74,
|
||||||
|
165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220,
|
||||||
|
105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132,
|
||||||
|
187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3,
|
||||||
|
64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59,
|
||||||
|
227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70,
|
||||||
|
221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232,
|
||||||
|
178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162,
|
||||||
|
241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204,
|
||||||
|
176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141,
|
||||||
|
128, 195, 78, 66, 215, 61, 156, 180, 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194,
|
||||||
|
233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234,
|
||||||
|
75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174,
|
||||||
|
20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83,
|
||||||
|
111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25,
|
||||||
|
63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188,
|
||||||
|
159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147,
|
||||||
|
118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170,
|
||||||
|
213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253,
|
||||||
|
19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193,
|
||||||
|
238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31,
|
||||||
|
181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93,
|
||||||
|
222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180,
|
||||||
|
];
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn noise_weight(t: Float) -> Float {
|
||||||
|
let t2 = t * t;
|
||||||
|
let t3 = t2 * t;
|
||||||
|
let t4 = t3 * t;
|
||||||
|
let t5 = t4 * t;
|
||||||
|
6.0 * t5 - 15.0 * t4 + 10.0 * t3
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn grad(x: i32, y: i32, z: i32, dx: Float, dy: Float, dz: Float) -> Float {
|
||||||
|
let hash =
|
||||||
|
NOISE_PERM[NOISE_PERM[NOISE_PERM[x as usize] as usize + y as usize] as usize + z as usize];
|
||||||
|
let h = h & 15;
|
||||||
|
let u = if h < 8 || h == 12 || h == 13 { dx } else { dy };
|
||||||
|
let v = if h < 4 || h == 12 || h == 13 { dy } else { dz };
|
||||||
|
|
||||||
|
let mut result = 0.0;
|
||||||
|
if (h & 1) != 0 {
|
||||||
|
result -= u;
|
||||||
|
} else {
|
||||||
|
result += u;
|
||||||
|
}
|
||||||
|
if (h & 2) != 0 {
|
||||||
|
result -= v;
|
||||||
|
} else {
|
||||||
|
result += v;
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn noise_from_point(mut p: Point3f) -> Float {
|
||||||
|
noise(p.x(), p.y(), p.z())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn noise(mut x: Float, mut y: Float, mut z: Float) -> Float {
|
||||||
|
let max_coord = (1i32 << 30) as Float;
|
||||||
|
x = x % max_coord;
|
||||||
|
y = y % max_coord;
|
||||||
|
z = z % max_coord;
|
||||||
|
if x < 0.0 {
|
||||||
|
x += max_coord;
|
||||||
|
}
|
||||||
|
if y < 0.0 {
|
||||||
|
y += max_coord;
|
||||||
|
}
|
||||||
|
if z < 0.0 {
|
||||||
|
z += max_coord;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ix = x.floor() as i32;
|
||||||
|
let iy = y.floor() as i32;
|
||||||
|
let iz = z.floor() as i32;
|
||||||
|
|
||||||
|
let dx = x - ix as Float;
|
||||||
|
let dy = y - iy as Float;
|
||||||
|
let dz = z - iz as Float;
|
||||||
|
|
||||||
|
let ix = ix & (NOISE_PERM_SIZE as i32 - 1);
|
||||||
|
let iy = iy & (NOISE_PERM_SIZE as i32 - 1);
|
||||||
|
let iz = iz & (NOISE_PERM_SIZE as i32 - 1);
|
||||||
|
|
||||||
|
// Fetch gradients
|
||||||
|
let w000 = grad(ix, iy, iz, dx, dy, dz);
|
||||||
|
let w100 = grad(ix + 1, iy, iz, dx - 1, dy, dz);
|
||||||
|
let w010 = grad(ix, iy + 1, iz, dx, dy - 1, dz);
|
||||||
|
let w110 = grad(ix + 1, iy + 1, iz, dx - 1, dy - 1, dz);
|
||||||
|
let w001 = grad(ix, iy, iz + 1, dx, dy, dz - 1);
|
||||||
|
let w101 = grad(ix + 1, iy, iz + 1, dx - 1, dy, dz - 1);
|
||||||
|
let w011 = grad(ix, iy + 1, iz + 1, dx, dy - 1, dz - 1);
|
||||||
|
let w111 = grad(ix + 1, iy + 1, iz + 1, dx - 1, dy - 1, dz - 1);
|
||||||
|
|
||||||
|
let wx = noise_weight(dx);
|
||||||
|
let wy = noise_weight(dy);
|
||||||
|
let wz = noise_weight(dz);
|
||||||
|
|
||||||
|
let x00 = lerp(wx, w000, w100);
|
||||||
|
let x10 = lerp(wx, w010, w110);
|
||||||
|
let x01 = lerp(wx, w001, w101);
|
||||||
|
let x11 = lerp(wx, w011, w111);
|
||||||
|
|
||||||
|
let y0 = lerp(wy, x00, x10);
|
||||||
|
let y1 = lerp(wy, x01, x11);
|
||||||
|
|
||||||
|
lerp(wz, y0, y1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fbm(p: Point3f, dpdx: Vector3f, dpdy: Vector3f, omega: Float, max_octaves: usize) -> Float {
|
||||||
|
// Compute number of octaves for antialiased FBm
|
||||||
|
let len2 = dpdx.norm_squared().max(dpdy.norm_squared());
|
||||||
|
let n = clamp(-1. - len.log2() / 2., 0., max_octaves);
|
||||||
|
let n_int = n.floor();
|
||||||
|
|
||||||
|
let mut sum = 0.;
|
||||||
|
let mut lambda = 1.;
|
||||||
|
let mut o = 1;
|
||||||
|
for i in 0..n_int {
|
||||||
|
sum += o * Noise(lambda * p);
|
||||||
|
lambda *= 1.99;
|
||||||
|
o *= omega;
|
||||||
|
}
|
||||||
|
|
||||||
|
let n_partial = n - n_int;
|
||||||
|
sum += o * smooth_step(n_partial, 0.3, 0.7) * noise_from_point(lambda * p);
|
||||||
|
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn turbulence(
|
||||||
|
p: Point3f,
|
||||||
|
dpdx: Vector3f,
|
||||||
|
dpdy: Vector3f,
|
||||||
|
omega: Float,
|
||||||
|
max_octaves: usize,
|
||||||
|
) -> Float {
|
||||||
|
// Compute number of octaves for antialiased FBm
|
||||||
|
let len2 = dpdx.norm_squared().max(dpdy.norm_squared());
|
||||||
|
let n = clamp(-1. - len2.log2() / 2., 0, maxOctaves);
|
||||||
|
let n_int = n.floor();
|
||||||
|
|
||||||
|
// Compute sum of octaves of noise for turbulence
|
||||||
|
let mut sum = 0.;
|
||||||
|
let mut lambda = 1.;
|
||||||
|
let mut o = 1.;
|
||||||
|
for i in 0..n_int {
|
||||||
|
sum += o * noise_from_point(lambda * p).abs();
|
||||||
|
lambda *= 1.99;
|
||||||
|
o *= omega;
|
||||||
|
}
|
||||||
|
|
||||||
|
let n_partial = n - n_int;
|
||||||
|
sum += o * lerp(
|
||||||
|
smooth_step(n_partial, 0.3, 0.7),
|
||||||
|
0.2,
|
||||||
|
noise_from_point(lambda * p).abs(),
|
||||||
|
);
|
||||||
|
for i in n_int..max_octaves {
|
||||||
|
sum += o * 0.2;
|
||||||
|
o *= omega;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
@ -700,64 +700,84 @@ impl VarianceEstimator {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Default)]
|
#[derive(Debug, Copy, Clone, Default)]
|
||||||
|
#[repr(C)]
|
||||||
pub struct PLSample {
|
pub struct PLSample {
|
||||||
pub p: Point2f,
|
pub p: Point2f,
|
||||||
pub pdf: Float,
|
pub pdf: Float,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct PiecewiseConstant1D {
|
pub struct PiecewiseConstant1D {
|
||||||
pub func: Vec<Float>,
|
pub func: *mut Float,
|
||||||
pub cdf: Vec<Float>,
|
pub cdf: *mut Float,
|
||||||
pub min: Float,
|
pub min: Float,
|
||||||
pub max: Float,
|
pub max: Float,
|
||||||
|
pub n: usize,
|
||||||
pub func_integral: Float,
|
pub func_integral: Float,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "cuda"))]
|
||||||
impl PiecewiseConstant1D {
|
impl PiecewiseConstant1D {
|
||||||
pub fn new(f: &[Float]) -> Self {
|
|
||||||
Self::new_with_bounds(f, 0., 1.)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_with_bounds(f: &[Float], min: Float, max: Float) -> Self {
|
pub fn new_with_bounds(f: &[Float], min: Float, max: Float) -> Self {
|
||||||
assert!(max > min);
|
|
||||||
let n = f.len();
|
let n = f.len();
|
||||||
let mut func = Vec::with_capacity(n);
|
let mut func_vec = f.to_vec();
|
||||||
for &val in f {
|
let mut cdf_vec = vec![0.0; n + 1];
|
||||||
func.push(val.abs());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut cdf = vec![0.; n + 1];
|
cdf_vec[0] = 0.0;
|
||||||
for i in 1..=n {
|
for i in 1..=n {
|
||||||
debug_assert!(func[i - 1] >= 0.);
|
cdf_vec[i] = cdf_vec[i - 1] + func_vec[i - 1] / n as Float;
|
||||||
cdf[i] = cdf[i - 1] + func[i - 1] * (max - min) / n as Float;
|
}
|
||||||
|
let func_int = cdf_vec[n];
|
||||||
|
if func_int > 0.0 {
|
||||||
|
for i in 1..=n {
|
||||||
|
cdf_vec[i] /= func_int;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i in 1..=n {
|
||||||
|
cdf_vec[i] = i as Float / n as Float;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let func_integral = cdf[n];
|
let func = func_vec.as_mut_ptr();
|
||||||
if func_integral == 0. {
|
let cdf = cdf_vec.as_mut_ptr();
|
||||||
let n_float = n as Float;
|
std::mem::forget(func_vec);
|
||||||
cdf.iter_mut()
|
std::mem::forget(cdf_vec);
|
||||||
.enumerate()
|
|
||||||
.for_each(|(i, c)| *c = i as Float / n_float);
|
|
||||||
} else {
|
|
||||||
let inv_integral = 1.0 / func_integral;
|
|
||||||
cdf.iter_mut().for_each(|c| *c *= inv_integral);
|
|
||||||
}
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
func,
|
func,
|
||||||
cdf,
|
cdf,
|
||||||
func_integral,
|
|
||||||
min,
|
min,
|
||||||
max,
|
max,
|
||||||
|
n,
|
||||||
|
func_integral,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new(f: &[Float]) -> Self {
|
||||||
|
Self::new_with_bounds(f, 0., 1.)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "cuda"))]
|
||||||
|
impl Drop for PiecewiseConstant1D {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if !self.func.is_null() {
|
||||||
|
unsafe {
|
||||||
|
let _ = Vec::from_raw_parts(self.func, self.n, self.n);
|
||||||
|
let _ = Vec::from_raw_parts(self.cdf, self.n + 1, self.n + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PiecewiseConstant1D {
|
||||||
pub fn integral(&self) -> Float {
|
pub fn integral(&self) -> Float {
|
||||||
self.func_integral
|
self.func_integral
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn size(&self) -> usize {
|
pub fn size(&self) -> usize {
|
||||||
self.func.len()
|
self.n
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sample(&self, u: Float) -> (Float, Float, usize) {
|
pub fn sample(&self, u: Float) -> (Float, Float, usize) {
|
||||||
|
|
@ -776,37 +796,46 @@ impl PiecewiseConstant1D {
|
||||||
(value, pdf_val, o)
|
(value, pdf_val, o)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct PiecewiseConstant2D {
|
pub struct PiecewiseConstant2D {
|
||||||
pub p_conditional_v: Vec<PiecewiseConstant1D>,
|
|
||||||
pub p_marginal: PiecewiseConstant1D,
|
|
||||||
pub domain: Bounds2f,
|
pub domain: Bounds2f,
|
||||||
|
pub p_conditional_v: *mut PiecewiseConstant1D,
|
||||||
|
pub p_marginal: PiecewiseConstant1D,
|
||||||
|
pub n_conditionals: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "cuda"))]
|
||||||
impl PiecewiseConstant2D {
|
impl PiecewiseConstant2D {
|
||||||
pub fn new(data: &Array2D<Float>, nu: usize, nv: usize, domain: Bounds2f) -> Self {
|
pub fn new(data: &Array2D<Float>, nu: usize, nv: usize, domain: Bounds2f) -> Self {
|
||||||
let mut p_conditional_v = Vec::with_capacity(nv);
|
let nu = data.x_size() as usize;
|
||||||
|
let nv = data.y_size() as usize;
|
||||||
|
let mut conditionals = Vec::with_capacity(nv);
|
||||||
for v in 0..nv {
|
for v in 0..nv {
|
||||||
let start = v * nu;
|
let row = unsafe { core::slice::from_raw_parts(data.values.add(v * nu), nu) };
|
||||||
let end = start + nu;
|
conditionals.push(PiecewiseConstant1D::new_with_bounds(
|
||||||
p_conditional_v.push(PiecewiseConstant1D::new_with_bounds(
|
row,
|
||||||
&data.as_slice()[start..end],
|
|
||||||
domain.p_min.x(),
|
domain.p_min.x(),
|
||||||
domain.p_max.x(),
|
domain.p_max.x(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let marginal_func: Vec<Float> = p_conditional_v.iter().map(|p| p.integral()).collect();
|
let marginal_funcs: Vec<Float> = conditionals.iter().map(|c| c.func_integral).collect();
|
||||||
let p_marginal = PiecewiseConstant1D::new_with_bounds(
|
let p_marginal = PiecewiseConstant1D::new_with_bounds(
|
||||||
&marginal_func,
|
&marginal_funcs,
|
||||||
domain.p_min.y(),
|
domain.p_min.y(),
|
||||||
domain.p_max.y(),
|
domain.p_max.y(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let p_conditional_v = conditionals.as_mut_ptr();
|
||||||
|
std::mem::forget(conditionals);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
p_conditional_v,
|
p_conditional_v,
|
||||||
p_marginal,
|
p_marginal,
|
||||||
domain,
|
domain,
|
||||||
|
n_conditionals: nv,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -817,14 +846,28 @@ impl PiecewiseConstant2D {
|
||||||
pub fn new_with_data(data: &Array2D<Float>) -> Self {
|
pub fn new_with_data(data: &Array2D<Float>) -> Self {
|
||||||
let nx = data.x_size();
|
let nx = data.x_size();
|
||||||
let ny = data.y_size();
|
let ny = data.y_size();
|
||||||
Self::new(
|
let domain = Bounds2f::new(Point2f::new(0.0, 0.0), Point2f::new(1.0, 1.0));
|
||||||
data,
|
|
||||||
nx,
|
Self::new(data, nx, ny, domain)
|
||||||
ny,
|
}
|
||||||
Bounds2f::from_points(Point2f::new(0., 0.), Point2f::new(1., 1.)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "cuda"))]
|
||||||
|
impl Drop for PiecewiseConstant2D {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if !self.p_conditional_v.is_null() {
|
||||||
|
unsafe {
|
||||||
|
let _ = Vec::from_raw_parts(
|
||||||
|
self.p_conditional_v,
|
||||||
|
self.n_conditionals,
|
||||||
|
self.n_conditionals,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PiecewiseConstant2D {
|
||||||
pub fn resolution(&self) -> Point2i {
|
pub fn resolution(&self) -> Point2i {
|
||||||
Point2i::new(
|
Point2i::new(
|
||||||
self.p_conditional_v[0].size() as i32,
|
self.p_conditional_v[0].size() as i32,
|
||||||
|
|
|
||||||
349
src/core/film.rs
Normal file
349
src/core/film.rs
Normal file
|
|
@ -0,0 +1,349 @@
|
||||||
|
use shared::film::{Film, FilmBase, GBufferFilm, PixelSensor, RGBFilm, SpectralFilm};
|
||||||
|
|
||||||
|
const N_SWATCH_REFLECTANCES: usize = 24;
|
||||||
|
const SWATCH_REFLECTANCES: Lazy<[Spectrum; N_SWATCH_REFLECTANCES]> = Lazy::new(|| {
|
||||||
|
std::array::from_fn(|i| {
|
||||||
|
let raw_data = crate::core::cie::SWATCHES_RAW[i];
|
||||||
|
let pls = PiecewiseLinearSpectrum::from_interleaved(raw_data, false);
|
||||||
|
Spectrum::PiecewiseLinear(pls)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
pub trait PixelSensorHost {
|
||||||
|
pub fn get_swatches() -> &[Spectrum; N_SWATCH_REFLECTANCES] {
|
||||||
|
&*SWATCH_REFLECTANCES
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create(
|
||||||
|
params: &ParameterDictionary,
|
||||||
|
output_colorspace: Arc<RGBColorSpace>,
|
||||||
|
exposure_time: Float,
|
||||||
|
loc: &FileLoc,
|
||||||
|
) -> Result<Self, String> {
|
||||||
|
let iso = params.get_one_float("iso", 100.);
|
||||||
|
let mut white_balance_temp = params.get_one_float("whitebalance", 0.);
|
||||||
|
let sensor_name = params.get_one_string("sensor", "cie1931");
|
||||||
|
if sensor_name != "cie1931" && white_balance_temp == 0. {
|
||||||
|
white_balance_temp = 6500.;
|
||||||
|
}
|
||||||
|
let imaging_ratio = exposure_time * iso / 100.;
|
||||||
|
|
||||||
|
let d_illum = if white_balance_temp == 0. {
|
||||||
|
generate_cie_d(6500.)
|
||||||
|
} else {
|
||||||
|
generate_cie_d(white_balance_temp)
|
||||||
|
};
|
||||||
|
|
||||||
|
let sensor_illum: Option<Arc<Spectrum>> = if white_balance_temp != 0. {
|
||||||
|
Some(Arc::new(Spectrum::DenselySampled(d_illum)))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
if sensor_name == "cie1931" {
|
||||||
|
return Ok(PixelSensor::new_with_white_balance(
|
||||||
|
output_colorspace,
|
||||||
|
sensor_illum,
|
||||||
|
imaging_ratio,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
let r_opt = get_named_spectrum(&format!("{}_r", sensor_name));
|
||||||
|
let g_opt = get_named_spectrum(&format!("{}_g", sensor_name));
|
||||||
|
let b_opt = get_named_spectrum(&format!("{}_b", sensor_name));
|
||||||
|
if r_opt.is_none() || g_opt.is_none() || b_opt.is_none() {
|
||||||
|
return Err(format!(
|
||||||
|
"{}: unknown sensor type '{}' (missing RGB spectral data)",
|
||||||
|
loc, sensor_name
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let r = Arc::new(r_opt.unwrap());
|
||||||
|
let g = Arc::new(g_opt.unwrap());
|
||||||
|
let b = Arc::new(b_opt.unwrap());
|
||||||
|
|
||||||
|
return PixelSensor::new(
|
||||||
|
r,
|
||||||
|
g,
|
||||||
|
b,
|
||||||
|
output_colorspace.clone(),
|
||||||
|
sensor_illum,
|
||||||
|
imaging_ratio,
|
||||||
|
)
|
||||||
|
.map_err(|e| e.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FilmBaseHost {
|
||||||
|
fn create(
|
||||||
|
params: &ParameterDictionary,
|
||||||
|
filter: Filter,
|
||||||
|
sensor: Option<&PixelSensor>,
|
||||||
|
loc: &FileLoc,
|
||||||
|
) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FilmBaseHost for FilmBase {
|
||||||
|
fn create(
|
||||||
|
params: &ParameterDictionary,
|
||||||
|
filter: Filter,
|
||||||
|
sensor: Option<PixelSensor>,
|
||||||
|
loc: &FileLoc,
|
||||||
|
) -> Self {
|
||||||
|
let x_res = params.get_one_int("xresolution", 1280);
|
||||||
|
let y_res = params.get_one_int("yresolution", 720);
|
||||||
|
|
||||||
|
if x_res <= 0 || y_res <= 0 {
|
||||||
|
eprintln!(
|
||||||
|
"{}: Film resolution must be > 0. Defaulting to 1280x720.",
|
||||||
|
loc
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let full_resolution = Point2i::new(x_res.max(1), y_res.max(1));
|
||||||
|
|
||||||
|
let crop_data = params.get_float_array("cropwindow");
|
||||||
|
let crop = if crop_data.len() == 4 {
|
||||||
|
Bounds2f::from_points(
|
||||||
|
Point2f::new(crop_data[0], crop_data[2]),
|
||||||
|
Point2f::new(crop_data[1], crop_data[3]),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Bounds2f::from_points(Point2f::zero(), Point2f::new(1.0, 1.0))
|
||||||
|
};
|
||||||
|
|
||||||
|
let p_min = Point2i::new(
|
||||||
|
(full_resolution.x() as Float * crop.p_min.x()).ceil() as i32,
|
||||||
|
(full_resolution.y() as Float * crop.p_min.y()).ceil() as i32,
|
||||||
|
);
|
||||||
|
let p_max = Point2i::new(
|
||||||
|
(full_resolution.x() as Float * crop.p_max.x()).ceil() as i32,
|
||||||
|
(full_resolution.y() as Float * crop.p_max.y()).ceil() as i32,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut pixel_bounds = Bounds2i::from_points(p_min, p_max);
|
||||||
|
|
||||||
|
if pixel_bounds.is_empty() {
|
||||||
|
eprintln!("{}: Film crop window results in empty pixel bounds.", loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
let rad = filter.radius();
|
||||||
|
let expansion = Point2i::new(rad.x().ceil() as i32, rad.y().ceil() as i32);
|
||||||
|
pixel_bounds = pixel_bounds.expand(expansion);
|
||||||
|
|
||||||
|
let diagonal_mm = params.get_one_float("diagonal", 35.0);
|
||||||
|
let filename = params.get_one_string("filename", "pbrt.exr");
|
||||||
|
|
||||||
|
Self {
|
||||||
|
full_resolution,
|
||||||
|
pixel_bounds,
|
||||||
|
filter,
|
||||||
|
diagonal: diagonal_mm * 0.001,
|
||||||
|
sensor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FilmHost {
|
||||||
|
fn write_image(&self, metadata: &ImageMetadata, splat_scale: Float) {
|
||||||
|
let image = self.get_image(metadata, splat_scale);
|
||||||
|
image
|
||||||
|
.write(self.get_filename(), metadata)
|
||||||
|
.expect("Something")
|
||||||
|
}
|
||||||
|
fn get_image(&self, _metadata: &ImageMetadata, splat_scale: Float) -> Image {
|
||||||
|
let write_fp16 = true;
|
||||||
|
let format = if write_fp16 {
|
||||||
|
PixelFormat::F16
|
||||||
|
} else {
|
||||||
|
PixelFormat::F32
|
||||||
|
};
|
||||||
|
|
||||||
|
let channel_names = &["R", "G", "B"];
|
||||||
|
|
||||||
|
let pixel_bounds = self.base().pixel_bounds;
|
||||||
|
let resolution = Point2i::from(pixel_bounds.diagonal());
|
||||||
|
|
||||||
|
let n_clamped = Arc::new(AtomicUsize::new(0));
|
||||||
|
let processed_rows: Vec<Vec<Float>> = (pixel_bounds.p_min.y()..pixel_bounds.p_max.y())
|
||||||
|
.into_par_iter()
|
||||||
|
.map(|y| {
|
||||||
|
let n_clamped = Arc::clone(&n_clamped);
|
||||||
|
let mut row_data = Vec::with_capacity(resolution.x() as usize * 3);
|
||||||
|
for x in pixel_bounds.p_min.x()..pixel_bounds.p_max.x() {
|
||||||
|
let p = Point2i::new(x, y);
|
||||||
|
let mut rgb = self.get_pixel_rgb(p, Some(splat_scale));
|
||||||
|
let mut was_clamped = false;
|
||||||
|
if write_fp16 {
|
||||||
|
if rgb.r > 65504.0 {
|
||||||
|
rgb.r = 65504.0;
|
||||||
|
was_clamped = true;
|
||||||
|
}
|
||||||
|
if rgb.g > 65504.0 {
|
||||||
|
rgb.g = 65504.0;
|
||||||
|
was_clamped = true;
|
||||||
|
}
|
||||||
|
if rgb.b > 65504.0 {
|
||||||
|
rgb.b = 65504.0;
|
||||||
|
was_clamped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if was_clamped {
|
||||||
|
n_clamped.fetch_add(1, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
row_data.push(rgb.r);
|
||||||
|
row_data.push(rgb.g);
|
||||||
|
row_data.push(rgb.b);
|
||||||
|
}
|
||||||
|
row_data
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut image = Image::new(format, resolution, channel_names, SRGB);
|
||||||
|
let rgb_desc = ImageChannelDesc::new(&[0, 1, 2]);
|
||||||
|
|
||||||
|
for (iy, row_data) in processed_rows.into_iter().enumerate() {
|
||||||
|
for (ix, rgb_chunk) in row_data.chunks_exact(3).enumerate() {
|
||||||
|
let p_offset = Point2i::new(ix as i32, iy as i32);
|
||||||
|
let values = ImageChannelValues::from(rgb_chunk);
|
||||||
|
image.set_channels(p_offset, &rgb_desc, &values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let clamped_count = n_clamped.load(Ordering::SeqCst);
|
||||||
|
if clamped_count > 0 {
|
||||||
|
println!(
|
||||||
|
"{} pixel values clamped to maximum fp16 value.",
|
||||||
|
clamped_count
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// self.base().pixel_bounds = pixel_bounds;
|
||||||
|
// self.base().full_resolution = resolution;
|
||||||
|
// self.colorspace = colorspace;
|
||||||
|
|
||||||
|
image
|
||||||
|
}
|
||||||
|
fn get_filename(&self) -> &str;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FilmFactory {
|
||||||
|
fn create(
|
||||||
|
name: &str,
|
||||||
|
params: &ParameterDictionary,
|
||||||
|
exposure_time: Float,
|
||||||
|
filter: Filter,
|
||||||
|
_camera_transform: Option<CameraTransform>,
|
||||||
|
loc: &FileLoc,
|
||||||
|
) -> Result<Self, String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FilmFactory for Film {
|
||||||
|
fn create(
|
||||||
|
name: &str,
|
||||||
|
params: &ParameterDictionary,
|
||||||
|
exposure_time: Float,
|
||||||
|
filter: Filter,
|
||||||
|
camera_transform: Option<CameraTransform>,
|
||||||
|
loc: &FileLoc,
|
||||||
|
) -> Result<Self, String> {
|
||||||
|
match name {
|
||||||
|
"rgb" => {
|
||||||
|
let colorspace = params.color_space.as_ref().unwrap();
|
||||||
|
let max_component_value =
|
||||||
|
params.get_one_float("maxcomponentvalue", Float::INFINITY);
|
||||||
|
let write_fp16 = params.get_one_bool("savefp16", true);
|
||||||
|
let sensor = PixelSensor::create(params, colorspace.clone(), exposure_time, loc)?;
|
||||||
|
let film_base = FilmBase::create(params, filter, Some(sensor), loc);
|
||||||
|
Ok(RGBFilm::new(
|
||||||
|
film_base,
|
||||||
|
&colorspace,
|
||||||
|
max_component_value,
|
||||||
|
write_fp16,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
"gbuffer" => {
|
||||||
|
let colorspace = params.color_space.as_ref().unwrap();
|
||||||
|
let max_component_value =
|
||||||
|
params.get_one_float("maxcomponentvalue", Float::INFINITY);
|
||||||
|
let write_fp16 = params.get_one_bool("savefp16", true);
|
||||||
|
let sensor = PixelSensor::create(params, colorspace.clone(), exposure_time, loc)?;
|
||||||
|
let film_base = FilmBase::create(params, filter, Some(sensor), loc);
|
||||||
|
|
||||||
|
let filename = params.get_one_string("filename", "pbrt.exr");
|
||||||
|
if Path::new(&ilename).extension() != Some("exr".as_ref()) {
|
||||||
|
return Err(format!(
|
||||||
|
"{}: EXR is the only format supported by GBufferFilm",
|
||||||
|
loc
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let coords_system = params.get_one_string("coordinatesystem", "camera");
|
||||||
|
let mut apply_inverse = false;
|
||||||
|
let camera_transform = camera_transform
|
||||||
|
.ok_or_else(|| "GBufferFilm requires a camera_transform".to_string())?;
|
||||||
|
let output_from_render = if coords_system == "camera" {
|
||||||
|
apply_inverse = true;
|
||||||
|
camera_transform.render_from_camera
|
||||||
|
} else if coords_system == "world" {
|
||||||
|
AnimatedTransform::from_transform(&camera_transform.world_from_render)
|
||||||
|
} else {
|
||||||
|
return Err(format!(
|
||||||
|
"{}: unknown coordinate system for GBufferFilm. (Expecting camera
|
||||||
|
or world",
|
||||||
|
loc
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(GBufferFilm::new(
|
||||||
|
&film_base,
|
||||||
|
&output_from_render,
|
||||||
|
apply_inverse,
|
||||||
|
colorspace,
|
||||||
|
max_component_value,
|
||||||
|
write_fp16,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
"spectral" => {
|
||||||
|
let colorspace = params.color_space.as_ref().unwrap();
|
||||||
|
let max_component_value =
|
||||||
|
params.get_one_float("maxcomponentvalue", Float::INFINITY);
|
||||||
|
let write_fp16 = params.get_one_bool("savefp16", true);
|
||||||
|
let sensor = PixelSensor::create(params, colorspace.clone(), exposure_time, loc)?;
|
||||||
|
let film_base = FilmBase::create(params, filter, Some(sensor), loc);
|
||||||
|
|
||||||
|
let filename = params.get_one_string("filename", "pbrt.exr");
|
||||||
|
if Path::new(&filename).extension() != Some("exr".as_ref()) {
|
||||||
|
return Err(format!(
|
||||||
|
"{}: EXR is the only format supported by GBufferFilm",
|
||||||
|
loc
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let n_buckets = params.get_one_int("nbuckets", 16) as usize;
|
||||||
|
let lambda_min = params.get_one_float("lambdamin", LAMBDA_MIN as Float);
|
||||||
|
let lambda_max = params.get_one_float("lambdamin", LAMBDA_MAX as Float);
|
||||||
|
if lambda_min < LAMBDA_MIN as Float && lambda_max > LAMBDA_MAX as Float {
|
||||||
|
return Err(format!(
|
||||||
|
"{}: PBRT must be recompiled with different values of LAMBDA_MIN and LAMBDA_MAX",
|
||||||
|
loc
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(SpectralFilm::new(
|
||||||
|
&film_base,
|
||||||
|
lambda_min,
|
||||||
|
lambda_max,
|
||||||
|
n_buckets,
|
||||||
|
colorspace,
|
||||||
|
max_component_value,
|
||||||
|
write_fp16,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
_ => Err(format!("Film type '{}' unknown at {}", name, loc)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
48
src/core/filter.rs
Normal file
48
src/core/filter.rs
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
use shared::filter::Filter;
|
||||||
|
use shared::filters::*;
|
||||||
|
|
||||||
|
pub trait FilterFactory {
|
||||||
|
fn create(name: &str, params: &ParameterDictionary, loc: &FileLoc) -> Result<Filter, String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FilterFactory for Filter {
|
||||||
|
fn create(name: &str, params: ParameterDictionary, loc: &FileLoc) -> Result<Self, String> {
|
||||||
|
match name {
|
||||||
|
"box" => {
|
||||||
|
let xw = params.get_one_float("xradius", 0.5);
|
||||||
|
let yw = params.get_one_float("yradius", 0.5);
|
||||||
|
let filter = BoxFilter::new(Vector2f::new(xw, yw));
|
||||||
|
Ok(Filter::Box(filter))
|
||||||
|
}
|
||||||
|
"gaussian" => {
|
||||||
|
let xw = params.get_one_float("xradius", 1.5);
|
||||||
|
let yw = params.get_one_float("yradius", 1.5);
|
||||||
|
let sigma = params.get_one_float("sigma", 0.5);
|
||||||
|
let filter = GaussianFilter::new(Vector2f::new(xw, yw), sigma);
|
||||||
|
Ok(Filter::Gaussian(filter))
|
||||||
|
}
|
||||||
|
"mitchell" => {
|
||||||
|
let xw = params.get_one_float("xradius", 2.);
|
||||||
|
let yw = params.get_one_float("yradius", 2.);
|
||||||
|
let b = params.get_one_float("B", 1. / 3.);
|
||||||
|
let c = params.get_one_float("C", 1. / 3.);
|
||||||
|
let filter = MitchellFilter::new(Vector2f::new(xw, yw), b, c);
|
||||||
|
Ok(Filter::Mitchell(filter))
|
||||||
|
}
|
||||||
|
"sinc" => {
|
||||||
|
let xw = params.get_one_float("xradius", 4.);
|
||||||
|
let yw = params.get_one_float("yradius", 4.);
|
||||||
|
let tau = params.get_one_float("tau", 3.);
|
||||||
|
let filter = LanczosSincFilter::new(Vector2f::new(xw, yw), tau);
|
||||||
|
Ok(Filter::LanczosSinc(filter))
|
||||||
|
}
|
||||||
|
"triangle" => {
|
||||||
|
let xw = params.get_one_float("xradius", 2.);
|
||||||
|
let yw = params.get_one_float("yradius", 2.);
|
||||||
|
let filter = TriangleFilter::new(Vector2f::new(xw, yw));
|
||||||
|
Ok(Filter::Triangle(filter))
|
||||||
|
}
|
||||||
|
_ => Err(format!("Film type '{}' unknown at {}", name, loc)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,2 +1,4 @@
|
||||||
|
pub mod film;
|
||||||
|
pub mod filter;
|
||||||
pub mod scene;
|
pub mod scene;
|
||||||
pub mod texture;
|
pub mod texture;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::core::filter::FilterFactory;
|
||||||
use crate::utils::parameters::{ParameterDictionary, ParsedParameterVector};
|
use crate::utils::parameters::{ParameterDictionary, ParsedParameterVector};
|
||||||
use crate::utils::{normalize_utf8, resolve_filename};
|
use crate::utils::{normalize_utf8, resolve_filename};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::textures::*;
|
use crate::textures::*;
|
||||||
use crate::utils::error::FileLoc;
|
use crate::utils::FileLoc;
|
||||||
use crate::utils::mipmap::MIPMapFilterOptions;
|
use crate::utils::mipmap::MIPMapFilterOptions;
|
||||||
use crate::utils::parameters::TextureParameterDictionary;
|
use crate::utils::{FileLoc, TextureParameterDictionary};
|
||||||
|
use shared::textures::*;
|
||||||
use shared::utils::Transform;
|
use shared::utils::Transform;
|
||||||
use shared::images::
|
|
||||||
use std::sync::{Arc, Mutex, OnceLock};
|
use std::sync::{Arc, Mutex, OnceLock};
|
||||||
|
|
||||||
#[enum_dispatch(FloatTextureTrait)]
|
#[enum_dispatch(FloatTextureTrait)]
|
||||||
|
|
@ -23,6 +23,33 @@ pub enum FloatTexture {
|
||||||
Wrinkled(WrinkledTexture),
|
Wrinkled(WrinkledTexture),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FloatConstantTexture {
|
||||||
|
pub fn create(
|
||||||
|
name: &str,
|
||||||
|
render_from_texture: &Transform,
|
||||||
|
params: &TextureParameterDictionary,
|
||||||
|
loc: &FileLoc,
|
||||||
|
) -> Result<Self, String> {
|
||||||
|
Self::new(params.get_one_float("value", 1.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FloatBilerpTexture {
|
||||||
|
pub fn create(
|
||||||
|
name: &str,
|
||||||
|
render_from_texture: &Transform,
|
||||||
|
params: &TextureParameterDictionary,
|
||||||
|
loc: &FileLoc,
|
||||||
|
) -> Result<Self, String> {
|
||||||
|
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 FloatTexture {
|
impl FloatTexture {
|
||||||
pub fn create(
|
pub fn create(
|
||||||
name: &str,
|
name: &str,
|
||||||
|
|
@ -223,3 +250,41 @@ struct TexInfo {
|
||||||
wrap_mode: WrapMode,
|
wrap_mode: WrapMode,
|
||||||
encoding: ColorEncoding,
|
encoding: ColorEncoding,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait TextureEvaluator: Send + Sync {
|
||||||
|
fn evaluate_float(&self, tex: &FloatTexture, ctx: &TextureEvalContext) -> Float;
|
||||||
|
fn evaluate_spectrum(
|
||||||
|
&self,
|
||||||
|
tex: &SpectrumTexture,
|
||||||
|
ctx: &TextureEvalContext,
|
||||||
|
lambda: &SampledWavelengths,
|
||||||
|
) -> SampledSpectrum;
|
||||||
|
|
||||||
|
fn can_evaluate(&self, _ftex: &[&FloatTexture], _stex: &[&SpectrumTexture]) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Default)]
|
||||||
|
pub struct UniversalTextureEvaluator;
|
||||||
|
|
||||||
|
impl TextureEvaluator for UniversalTextureEvaluator {
|
||||||
|
fn evaluate_float(&self, tex: &FloatTexture, ctx: &TextureEvalContext) -> Float {
|
||||||
|
tex.evaluate(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn evaluate_spectrum(
|
||||||
|
&self,
|
||||||
|
tex: &SpectrumTexture,
|
||||||
|
ctx: &TextureEvalContext,
|
||||||
|
lambda: &SampledWavelengths,
|
||||||
|
) -> SampledSpectrum {
|
||||||
|
tex.evaluate(ctx, lambda)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_evaluate(
|
||||||
|
&self,
|
||||||
|
_float_textures: &[&FloatTexture],
|
||||||
|
_spectrum_textures: &[&SpectrumTexture],
|
||||||
|
) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
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!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
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!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
#[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 {}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct FBmTexture;
|
|
||||||
impl FloatTextureTrait for FBmTexture {}
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::texture::ImageTextureBase;
|
use crate::core::texture::ImageTextureBase;
|
||||||
|
use crate::utils::{FileLoc, TextureParameterDictionary};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SpectrumImageTexture {
|
pub struct SpectrumImageTexture {
|
||||||
base: ImageTextureBase,
|
pub base: ImageTextureBase,
|
||||||
spectrum_type: SpectrumType,
|
pub spectrum_type: SpectrumType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpectrumImageTexture {
|
impl SpectrumImageTexture {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct MarbleTexture;
|
|
||||||
impl SpectrumTextureTrait for MarbleTexture {
|
|
||||||
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +1,11 @@
|
||||||
|
use crate::core::texture::{
|
||||||
|
FloatTexture, FloatTextureTrait, SpectrumTexture, SpectrumTextureTrait,
|
||||||
|
};
|
||||||
|
use crate::utils::{FileLoc, TextureParameterDictionary};
|
||||||
|
use shared::core::geometry::Vector3f;
|
||||||
|
use shared::utils::Transform;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FloatMixTexture {
|
pub struct FloatMixTexture {
|
||||||
tex1: Arc<FloatTexture>,
|
tex1: Arc<FloatTexture>,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,155 @@
|
||||||
|
use crate::utils::{FileLoc, TextureParameterDictionary};
|
||||||
|
use shared::core::texture::{SpectrumType, TextureEvalContext};
|
||||||
|
use shared::spectra::ColorEncoding;
|
||||||
|
use shared::spectra::color::RGB;
|
||||||
|
|
||||||
|
use ptex::Cache;
|
||||||
|
use ptex_sys::ffi;
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
static PTEX_CACHE: OnceLock<Cache> = OnceLock::new();
|
||||||
|
|
||||||
|
fn get_ptex_cache() -> &'static Cache {
|
||||||
|
PTEX_CACHE.get_or_init(|| {
|
||||||
|
let max_files = 100;
|
||||||
|
let max_mem = 1 << 32;
|
||||||
|
let premultiply = true;
|
||||||
|
Cache::new(max_files, max_mem, premultiply)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FloatPtexTexture;
|
pub struct PtexTextureBase {
|
||||||
impl FloatTextureTrait for FloatPtexTexture {}
|
pub valid: bool,
|
||||||
|
pub filename: String,
|
||||||
|
pub encoding: ColorEncoding,
|
||||||
|
pub scale: Float,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PtexTextureBase {
|
||||||
|
pub fn new(filename: String, encoding: ColorEncoding, scale: Float) -> Self {
|
||||||
|
let cache = get_ptex_cache();
|
||||||
|
|
||||||
|
// Attempt to get the texture to verify it exists and is valid
|
||||||
|
let (valid, num_channels) = match cache.get(&filename) {
|
||||||
|
Ok(tex) => {
|
||||||
|
let nc = tex.num_channels();
|
||||||
|
(nc == 1 || nc == 3, nc)
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Ptex Error for {}: {}", filename, e);
|
||||||
|
(false, 0)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !valid && num_channels != 0 {
|
||||||
|
log::error!(
|
||||||
|
"{}: only 1 and 3 channel ptex textures are supported",
|
||||||
|
filename
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
filename,
|
||||||
|
encoding,
|
||||||
|
scale,
|
||||||
|
valid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sample_texture(ctx: &TextureEvalContext) -> Option<RGB> {
|
||||||
|
if !self.valid {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let mut result = [0.0; 3];
|
||||||
|
let nc = self.ptex_handle.eval(
|
||||||
|
&mut result,
|
||||||
|
ctx.face_index,
|
||||||
|
ctx.uv[0],
|
||||||
|
ctx.uv[1],
|
||||||
|
ctx.dudx,
|
||||||
|
ctx.dvdx,
|
||||||
|
ctx.dudy,
|
||||||
|
ctx.dvdy,
|
||||||
|
);
|
||||||
|
let cache = get_ptex_cache();
|
||||||
|
let texture = match cache.get(&self.filename) {
|
||||||
|
Ok(t) => t,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Ptex cache lookup failed for {}: {}", self.filename, e);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let nc = texture.num_channels();
|
||||||
|
let mut result = [0.0f32; 4];
|
||||||
|
unsafe {
|
||||||
|
let opts = ffi::PtexFilter_Options {
|
||||||
|
filter: ffi::FilterType::f_bspline,
|
||||||
|
lerp: 1,
|
||||||
|
sharpness: 0.0,
|
||||||
|
noedgeblend: 0,
|
||||||
|
__structSize: std::mem::size_of::<ffi::PtexFilter_Options>() as i32,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the raw filter from the low-level FFI
|
||||||
|
// Assuming your 'texture' can provide the raw C++ pointer
|
||||||
|
let filter_ptr = ffi::PtexFilter_getFilter(texture.as_ptr(), &opts);
|
||||||
|
if filter_ptr.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate
|
||||||
|
(*filter_ptr).eval(
|
||||||
|
result.as_mut_ptr(),
|
||||||
|
0, // first channel
|
||||||
|
texture.num_channels(),
|
||||||
|
ctx.face_index as i32,
|
||||||
|
ctx.uv[0],
|
||||||
|
ctx.uv[1],
|
||||||
|
ctx.dudx,
|
||||||
|
ctx.dvdx,
|
||||||
|
ctx.dudy,
|
||||||
|
ctx.dvdy,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Crucial: Manually release the C++ object
|
||||||
|
(*filter_ptr).release();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut rgb = RGB::new(result[0], result[1], result[2]);
|
||||||
|
|
||||||
|
if self.encoding != ColorEncoding::Linear {
|
||||||
|
// Convert to 8-bit, process, and convert back
|
||||||
|
let r8 = (rgb.r * 255.0 + 0.5).clamp(0.0, 255.0) as u8;
|
||||||
|
let g8 = (rgb.g * 255.0 + 0.5).clamp(0.0, 255.0) as u8;
|
||||||
|
let b8 = (rgb.b * 255.0 + 0.5).clamp(0.0, 255.0) as u8;
|
||||||
|
|
||||||
|
rgb = self.encoding.to_linear_rgb([r8, g8, b8]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(rgb * self.scale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FloatPtexTexture {
|
||||||
|
pub base: PtexTextureBase,
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl FloatPtexTexture {
|
||||||
|
// pub fn evaluate(&self, _ctx: &TextureEvalContext) -> Float {
|
||||||
|
// let
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SpectrumPtexTexture;
|
pub struct SpectrumPtexTexture {
|
||||||
|
pub base: PtexTextureBase,
|
||||||
|
pub spectrum_type: SpectrumType,
|
||||||
|
}
|
||||||
|
|
||||||
impl SpectrumTextureTrait for SpectrumPtexTexture {
|
impl SpectrumTextureTrait for SpectrumPtexTexture {
|
||||||
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
todo!()
|
todo!()
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,11 @@
|
||||||
|
use crate::core::texture::{
|
||||||
|
FloatTexture, FloatTextureTrait, SpectrumTexture, SpectrumTextureTrait,
|
||||||
|
};
|
||||||
|
use crate::utils::{FileLoc, TextureParameterDictionary};
|
||||||
|
use shared::core::texture::TextureEvalContext;
|
||||||
|
use shared::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
|
use shared::utils::Transform;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FloatScaledTexture {
|
pub struct FloatScaledTexture {
|
||||||
tex: Arc<FloatTexture>,
|
tex: Arc<FloatTexture>,
|
||||||
|
|
@ -18,14 +26,14 @@ impl FloatScaledTexture {
|
||||||
let mut scale = params.get_float_texture("scale", 1.);
|
let mut scale = params.get_float_texture("scale", 1.);
|
||||||
|
|
||||||
for _ in 0..2 {
|
for _ in 0..2 {
|
||||||
if let FloatTexture::FloatConstant(c_tex) = &*scale {
|
if let FloatTexture::Constant(c_tex) = &*scale {
|
||||||
let cs = c_tex.value;
|
let cs = c_tex.value;
|
||||||
if cs == 1.0 {
|
if cs == 1.0 {
|
||||||
return (*tex).clone();
|
return (*tex).clone();
|
||||||
} else if let FloatTexture::FloatImage(img_tex) = &*tex {
|
} else if let FloatTexture::Image(img_tex) = &*tex {
|
||||||
let mut image_copy = img_tex.clone();
|
let mut image_copy = img_tex.clone();
|
||||||
image_copy.base.multiply_scale(cs);
|
image_copy.base.multiply_scale(cs);
|
||||||
return FloatTexture::FloatImage(image_copy).into();
|
return FloatTexture::Image(image_copy).into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -48,8 +56,8 @@ impl FloatTextureTrait for FloatScaledTexture {
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SpectrumScaledTexture {
|
pub struct SpectrumScaledTexture {
|
||||||
tex: Box<SpectrumTexture>,
|
tex: Arc<SpectrumTexture>,
|
||||||
scale: Box<FloatTexture>,
|
scale: Arc<FloatTexture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpectrumTextureTrait for SpectrumScaledTexture {
|
impl SpectrumTextureTrait for SpectrumScaledTexture {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct WindyTexture;
|
|
||||||
impl FloatTextureTrait for WindyTexture {}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct WrinkledTexture;
|
|
||||||
impl FloatTextureTrait for WrinkledTexture {}
|
|
||||||
|
|
@ -5,5 +5,9 @@ pub mod mipmap;
|
||||||
pub mod parameters;
|
pub mod parameters;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
|
||||||
|
pub use error::FileLoc;
|
||||||
pub use file::{read_float_file, resolve_filename};
|
pub use file::{read_float_file, resolve_filename};
|
||||||
|
pub use parameters::{
|
||||||
|
ParameterDictionary, ParsedParameter, ParsedParameterVector, TextureParameterDictionary,
|
||||||
|
};
|
||||||
pub use strings::*;
|
pub use strings::*;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue