Continuing cleanup
This commit is contained in:
parent
384a0019d8
commit
72acb8ccdf
40 changed files with 369 additions and 293 deletions
|
|
@ -167,6 +167,17 @@ impl Pixels {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_u8(&self) -> &[u8] {
|
||||||
|
&self.data
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_f16(&mut self) -> &[u16] {
|
||||||
|
assert_eq!(self.format, PixelFormat::F16);
|
||||||
|
unsafe {
|
||||||
|
core::slice::from_raw_parts(self.data.as_ptr() as *const u16, self.data.len() / 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_u8_mut(&mut self) -> &mut [u8] {
|
pub fn as_u8_mut(&mut self) -> &mut [u8] {
|
||||||
&mut self.data
|
&mut self.data
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,4 +12,4 @@ pub use cylinder::*;
|
||||||
pub use disk::*;
|
pub use disk::*;
|
||||||
pub use sphere::*;
|
pub use sphere::*;
|
||||||
pub use triangle::*;
|
pub use triangle::*;
|
||||||
pub use mesh::*;
|
pub use mesh::{TriangleMesh, BilinearPatchMesh};
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ use super::sampled::{LAMBDA_MAX, LAMBDA_MIN};
|
||||||
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths, N_SPECTRUM_SAMPLES};
|
use crate::spectra::{SampledSpectrum, SampledWavelengths, N_SPECTRUM_SAMPLES};
|
||||||
use crate::utils::find_interval;
|
use crate::utils::find_interval;
|
||||||
use crate::{gvec, gvec_with_capacity, Float, GVec, Ptr};
|
use crate::utils::math::square;
|
||||||
|
use crate::{gvec, gvec_from_slice, gvec_with_capacity, Float, GVec, Ptr};
|
||||||
use core::hash::{Hash, Hasher};
|
use core::hash::{Hash, Hasher};
|
||||||
use num_traits::Float as NumFloat;
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
|
|
@ -88,6 +89,41 @@ impl DenselySampledSpectrum {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn generate_cie_d(temperature: Float) -> Self {
|
||||||
|
let cct = temperature * 1.4388 / 1.4380;
|
||||||
|
|
||||||
|
if cct < 4000.0 {
|
||||||
|
return Self::from_function(
|
||||||
|
|lambda| BlackbodySpectrum::new(cct).evaluate(lambda),
|
||||||
|
LAMBDA_MIN,
|
||||||
|
LAMBDA_MAX,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let x = if cct < 7000. {
|
||||||
|
-4.607 * 1e9 / cct.powi(3) + 2.9678 * 1e6 / square(cct) + 0.09911 * 1e3 / cct + 0.244063
|
||||||
|
} else {
|
||||||
|
-2.0064 * 1e9 / cct.powi(3) + 1.9018 * 1e6 / square(cct) + 0.24748 * 1e3 / cct + 0.23704
|
||||||
|
};
|
||||||
|
let y = -3. * x + 2.87 * x - 0.275;
|
||||||
|
let m = 0.0241 + 0.2562 * x - 0.7341 * y;
|
||||||
|
let m1 = (-1.3515 - 1.7703 * x + 5.9114 * y) / m;
|
||||||
|
let m2 = (0.0300 - 31.4424 * x + 30.0717 * y) / m;
|
||||||
|
|
||||||
|
let mut coarse_values = gvec_with_capacity(N_CIES);
|
||||||
|
for i in 0..N_CIES {
|
||||||
|
coarse_values.push((CIE_S0[i] + CIE_S1[i] * m1 + CIE_S2[i] * m2) * 0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
let temp_pls = PiecewiseLinearSpectrum {
|
||||||
|
lambdas: gvec_from_slice(&CIE_S_LAMBDA),
|
||||||
|
values: gvec_from_slice(&coarse_values),
|
||||||
|
count: N_CIES as u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
Self::from_function(|lambda| temp_pls.evaluate(lambda), LAMBDA_MIN, LAMBDA_MAX)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn scale(&mut self, s: Float) {
|
pub fn scale(&mut self, s: Float) {
|
||||||
for v in &mut self.values {
|
for v in &mut self.values {
|
||||||
*v *= s;
|
*v *= s;
|
||||||
|
|
@ -183,7 +219,6 @@ impl PiecewiseLinearSpectrum {
|
||||||
self.count.try_into().unwrap()
|
self.count.try_into().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn lambda(&self, idx: u32) -> Float {
|
pub fn lambda(&self, idx: u32) -> Float {
|
||||||
unsafe { *self.lambdas.as_ptr().add(idx as usize) }
|
unsafe { *self.lambdas.as_ptr().add(idx as usize) }
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use shared::core::aggregates::{BVHAggregate, LinearBVHNode};
|
use shared::core::aggregates::{BVHAggregate as DeviceBVHAggregate, LinearBVHNode};
|
||||||
use shared::core::geometry::{Bounds3f, Point3f, Ray, Vector3f};
|
use shared::core::geometry::{Bounds3f, Point3f, Ray, Vector3f};
|
||||||
use shared::core::primitive::{Primitive, PrimitiveTrait};
|
use shared::core::primitive::{Primitive, PrimitiveTrait};
|
||||||
use shared::core::shape::ShapeIntersection;
|
use shared::core::shape::ShapeIntersection;
|
||||||
|
|
@ -890,8 +890,6 @@ impl<P: PrimitiveTrait + Clone + Send + Sync> BVHAggregate<P> {
|
||||||
|
|
||||||
impl BVHAggregate<Primitive> {
|
impl BVHAggregate<Primitive> {
|
||||||
pub fn to_device(&self, arena: &mut Arena) -> DeviceBVHAggregate {
|
pub fn to_device(&self, arena: &mut Arena) -> DeviceBVHAggregate {
|
||||||
let (prims_ptr, _) = arena.alloc_slice(&self.primitives);
|
|
||||||
|
|
||||||
let shared_nodes: Vec<shared::core::aggregates::LinearBVHNode> = self.nodes
|
let shared_nodes: Vec<shared::core::aggregates::LinearBVHNode> = self.nodes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|n| shared::core::aggregates::LinearBVHNode {
|
.map(|n| shared::core::aggregates::LinearBVHNode {
|
||||||
|
|
@ -902,13 +900,12 @@ impl BVHAggregate<Primitive> {
|
||||||
pad: 0,
|
pad: 0,
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let (nodes_ptr, _) = arena.alloc_slice(&shared_nodes);
|
|
||||||
|
|
||||||
DeviceBVHAggregate {
|
DeviceBVHAggregate {
|
||||||
max_prims_in_node: self.max_prims_in_node as u32,
|
max_prims_in_node: self.max_prims_in_node as u32,
|
||||||
primitives: prims_ptr,
|
primitives: gvec_from_slice(&self.primitives),
|
||||||
primitive_count: self.primitives.len() as u32,
|
primitive_count: self.primitives.len() as u32,
|
||||||
nodes: nodes_ptr,
|
nodes: gvec_from_slice(&self.shared_nodes),
|
||||||
node_count: self.nodes.len() as u32,
|
node_count: self.nodes.len() as u32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
use crate::core::image::ImageMetadata;
|
use crate::core::image::{HostImage, ImageIO, ImageMetadata};
|
||||||
use crate::core::image::{Image, ImageIO};
|
|
||||||
use crate::globals::get_options;
|
use crate::globals::get_options;
|
||||||
use crate::utils::read_float_file;
|
use crate::utils::read_float_file;
|
||||||
use crate::{Arena, FileLoc, ParameterDictionary};
|
use crate::{Arena, FileLoc, ParameterDictionary, ArenaUpload};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use shared::cameras::*;
|
use shared::cameras::*;
|
||||||
use shared::core::camera::{Camera, CameraBase, CameraTrait, CameraTransform};
|
use shared::core::camera::{Camera, CameraBase, CameraTrait, CameraTransform};
|
||||||
|
|
@ -232,8 +231,8 @@ impl CameraFactory for Camera {
|
||||||
}
|
}
|
||||||
|
|
||||||
let builtin_res = 256;
|
let builtin_res = 256;
|
||||||
let rasterize = |vert: &[Point2f]| -> Image {
|
let rasterize = |vert: &[Point2f]| -> HostImage {
|
||||||
let mut image = Image::new(
|
let mut image = HostImage::new(
|
||||||
PixelFormat::F32,
|
PixelFormat::F32,
|
||||||
Point2i::new(builtin_res, builtin_res),
|
Point2i::new(builtin_res, builtin_res),
|
||||||
&["Y"],
|
&["Y"],
|
||||||
|
|
@ -279,12 +278,12 @@ impl CameraFactory for Camera {
|
||||||
};
|
};
|
||||||
|
|
||||||
let aperture_name = params.get_one_string("aperture", "")?;
|
let aperture_name = params.get_one_string("aperture", "")?;
|
||||||
let mut aperture_image: Option<Image> = None;
|
let mut aperture_image: Option<HostImage> = None;
|
||||||
|
|
||||||
if !aperture_name.is_empty() {
|
if !aperture_name.is_empty() {
|
||||||
match aperture_name.as_str() {
|
match aperture_name.as_str() {
|
||||||
"gaussian" => {
|
"gaussian" => {
|
||||||
let mut img = Image::new(
|
let mut img = HostImage::new(
|
||||||
PixelFormat::F32,
|
PixelFormat::F32,
|
||||||
Point2i::new(builtin_res, builtin_res),
|
Point2i::new(builtin_res, builtin_res),
|
||||||
&["Y"],
|
&["Y"],
|
||||||
|
|
@ -306,7 +305,7 @@ impl CameraFactory for Camera {
|
||||||
aperture_image = Some(img);
|
aperture_image = Some(img);
|
||||||
}
|
}
|
||||||
"square" => {
|
"square" => {
|
||||||
let mut img = Image::new(
|
let mut img = HostImage::new(
|
||||||
PixelFormat::F32,
|
PixelFormat::F32,
|
||||||
Point2i::new(builtin_res, builtin_res),
|
Point2i::new(builtin_res, builtin_res),
|
||||||
&["Y"],
|
&["Y"],
|
||||||
|
|
@ -316,7 +315,7 @@ impl CameraFactory for Camera {
|
||||||
let high = (0.75 * builtin_res as Float) as i32;
|
let high = (0.75 * builtin_res as Float) as i32;
|
||||||
for y in low..high {
|
for y in low..high {
|
||||||
for x in low..high {
|
for x in low..high {
|
||||||
img.set_channel(Point2i::new(x, y), 0, 4.0);
|
img.inner.set_channel(Point2i::new(x, y), 0, 4.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
aperture_image = Some(img);
|
aperture_image = Some(img);
|
||||||
|
|
@ -353,9 +352,9 @@ impl CameraFactory for Camera {
|
||||||
aperture_image = Some(rasterize(&vert));
|
aperture_image = Some(rasterize(&vert));
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if let Ok(im) = Image::read(Path::new(&aperture_name), None) {
|
if let Ok(im) = HostImage::read(Path::new(&aperture_name), None) {
|
||||||
if im.image.n_channels() > 1 {
|
if im.image.n_channels() > 1 {
|
||||||
let mut mono = Image::new(
|
let mut mono = HostImage::new(
|
||||||
PixelFormat::F32,
|
PixelFormat::F32,
|
||||||
im.image.resolution(),
|
im.image.resolution(),
|
||||||
&["Y"],
|
&["Y"],
|
||||||
|
|
@ -366,7 +365,7 @@ impl CameraFactory for Camera {
|
||||||
for x in 0..res.x() {
|
for x in 0..res.x() {
|
||||||
let avg =
|
let avg =
|
||||||
im.image.get_channels(Point2i::new(x, y)).average();
|
im.image.get_channels(Point2i::new(x, y)).average();
|
||||||
mono.set_channel(Point2i::new(x, y), 0, avg);
|
mono.inner.set_channel(Point2i::new(x, y), 0, avg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
aperture_image = Some(mono);
|
aperture_image = Some(mono);
|
||||||
|
|
@ -383,7 +382,8 @@ impl CameraFactory for Camera {
|
||||||
&lens_params,
|
&lens_params,
|
||||||
focal_distance,
|
focal_distance,
|
||||||
aperture_diameter,
|
aperture_diameter,
|
||||||
Ptr::from(&*aperture_image.unwrap()),
|
arena.upload(aperture_image)
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
arena.alloc(camera);
|
arena.alloc(camera);
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ impl CreateRGBToSpectrumTable for RGBToSpectrumTable {
|
||||||
assert_eq!(z_nodes.len(), RES as usize);
|
assert_eq!(z_nodes.len(), RES as usize);
|
||||||
assert_eq!(coeffs.len(), (RES * RES * RES) as usize * 3 * 3);
|
assert_eq!(coeffs.len(), (RES * RES * RES) as usize * 3 * 3);
|
||||||
Self {
|
Self {
|
||||||
|
n_nodes: z_nodes.len().try_into().unwrap(),
|
||||||
z_nodes: gvec_from_slice(z_nodes),
|
z_nodes: gvec_from_slice(z_nodes),
|
||||||
coeffs: gvec_from_slice(unsafe {
|
coeffs: gvec_from_slice(unsafe {
|
||||||
core::slice::from_raw_parts(
|
core::slice::from_raw_parts(
|
||||||
|
|
|
||||||
|
|
@ -87,8 +87,10 @@ impl CreatePixelSensor for PixelSensor {
|
||||||
DenselySampledSpectrum::generate_cie_d(white_balance_temp)
|
DenselySampledSpectrum::generate_cie_d(white_balance_temp)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let d_ptr = arena.alloc(d_illum);
|
||||||
|
|
||||||
let sensor_illum: Option<Arc<Spectrum>> = if white_balance_temp != 0. {
|
let sensor_illum: Option<Arc<Spectrum>> = if white_balance_temp != 0. {
|
||||||
Some(Spectrum::Dense(d_illum.device()).into())
|
Some(Spectrum::Dense(d_ptr).into())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
@ -150,8 +152,8 @@ impl CreatePixelSensor for PixelSensor {
|
||||||
let g_bar = DenselySampledSpectrum::from_spectrum(g);
|
let g_bar = DenselySampledSpectrum::from_spectrum(g);
|
||||||
let b_bar = DenselySampledSpectrum::from_spectrum(b);
|
let b_bar = DenselySampledSpectrum::from_spectrum(b);
|
||||||
let r_ptr = arena.alloc(r_bar);
|
let r_ptr = arena.alloc(r_bar);
|
||||||
let g_ptr = arena.alloc(r_bar);
|
let g_ptr = arena.alloc(g_bar);
|
||||||
let v_ptr = arena.alloc(r_bar);
|
let b_ptr = arena.alloc(b_bar);
|
||||||
let mut rgb_camera = [[0.; 3]; N_SWATCH_REFLECTANCES];
|
let mut rgb_camera = [[0.; 3]; N_SWATCH_REFLECTANCES];
|
||||||
|
|
||||||
let swatches = get_swatches();
|
let swatches = get_swatches();
|
||||||
|
|
@ -203,6 +205,7 @@ impl CreatePixelSensor for PixelSensor {
|
||||||
output_colorspace: &RGBColorSpace,
|
output_colorspace: &RGBColorSpace,
|
||||||
sensor_illum: Option<&Spectrum>,
|
sensor_illum: Option<&Spectrum>,
|
||||||
imaging_ratio: Float,
|
imaging_ratio: Float,
|
||||||
|
arena: &Arena
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let spectra = get_spectra_context();
|
let spectra = get_spectra_context();
|
||||||
let r_bar = CIE_X_DATA.clone();
|
let r_bar = CIE_X_DATA.clone();
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::utils::sampling::PiecewiseConstant2D;
|
use crate::utils::sampling::PiecewiseConstant2D;
|
||||||
use crate::utils::{FileLoc, ParameterDictionary};
|
use crate::utils::{FileLoc, ParameterDictionary};
|
||||||
use crate::Arena;
|
use crate::Arena;
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{bail, Result};
|
||||||
use shared::core::filter::Filter;
|
use shared::core::filter::Filter;
|
||||||
use shared::core::geometry::{Bounds2f, Point2f, Vector2f};
|
use shared::core::geometry::{Bounds2f, Point2f, Vector2f};
|
||||||
use shared::filters::*;
|
use shared::filters::*;
|
||||||
|
|
@ -28,13 +28,14 @@ impl FilterFactory for Filter {
|
||||||
let xw = params.get_one_float("xradius", 0.5)?;
|
let xw = params.get_one_float("xradius", 0.5)?;
|
||||||
let yw = params.get_one_float("yradius", 0.5)?;
|
let yw = params.get_one_float("yradius", 0.5)?;
|
||||||
let filter = BoxFilter::new(Vector2f::new(xw, yw));
|
let filter = BoxFilter::new(Vector2f::new(xw, yw));
|
||||||
Ok(Filter::Box(filter))
|
Ok(Filter::Box(arena.alloc(filter)))
|
||||||
}
|
}
|
||||||
"gaussian" => {
|
"gaussian" => {
|
||||||
let xw = params.get_one_float("xradius", 1.5)?;
|
let xw = params.get_one_float("xradius", 1.5)?;
|
||||||
let yw = params.get_one_float("yradius", 1.5)?;
|
let yw = params.get_one_float("yradius", 1.5)?;
|
||||||
let sigma = params.get_one_float("sigma", 0.5)?;
|
let sigma = params.get_one_float("sigma", 0.5)?;
|
||||||
let filter = GaussianFilter::new(Vector2f::new(xw, yw), sigma);
|
let filter = GaussianFilter::new(Vector2f::new(xw, yw), sigma);
|
||||||
|
Ok(Filter::Gaussian(arena.alloc(filter)))
|
||||||
}
|
}
|
||||||
"mitchell" => {
|
"mitchell" => {
|
||||||
let xw = params.get_one_float("xradius", 2.)?;
|
let xw = params.get_one_float("xradius", 2.)?;
|
||||||
|
|
@ -42,22 +43,22 @@ impl FilterFactory for Filter {
|
||||||
let b = params.get_one_float("B", 1. / 3.)?;
|
let b = params.get_one_float("B", 1. / 3.)?;
|
||||||
let c = params.get_one_float("C", 1. / 3.)?;
|
let c = params.get_one_float("C", 1. / 3.)?;
|
||||||
let filter = MitchellFilter::new(Vector2f::new(xw, yw), b, c);
|
let filter = MitchellFilter::new(Vector2f::new(xw, yw), b, c);
|
||||||
Ok(Filter::Mitchell(filter))
|
Ok(Filter::Mitchell(arena.alloc(filter)))
|
||||||
}
|
}
|
||||||
"sinc" => {
|
"sinc" => {
|
||||||
let xw = params.get_one_float("xradius", 4.)?;
|
let xw = params.get_one_float("xradius", 4.)?;
|
||||||
let yw = params.get_one_float("yradius", 4.)?;
|
let yw = params.get_one_float("yradius", 4.)?;
|
||||||
let tau = params.get_one_float("tau", 3.)?;
|
let tau = params.get_one_float("tau", 3.)?;
|
||||||
let filter = LanczosSincFilter::new(Vector2f::new(xw, yw), tau);
|
let filter = LanczosSincFilter::new(Vector2f::new(xw, yw), tau);
|
||||||
Ok(Filter::LanczosSinc(filter))
|
Ok(Filter::LanczosSinc(arena.alloc(filter)))
|
||||||
}
|
}
|
||||||
"triangle" => {
|
"triangle" => {
|
||||||
let xw = params.get_one_float("xradius", 2.)?;
|
let xw = params.get_one_float("xradius", 2.)?;
|
||||||
let yw = params.get_one_float("yradius", 2.)?;
|
let yw = params.get_one_float("yradius", 2.)?;
|
||||||
let filter = TriangleFilter::new(Vector2f::new(xw, yw));
|
let filter = TriangleFilter::new(Vector2f::new(xw, yw));
|
||||||
Ok(Filter::Triangle(filter))
|
Ok(Filter::Triangle(arena.alloc(filter)))
|
||||||
}
|
}
|
||||||
_ => Err(anyhow!("Film type '{}' unknown at {}", name, loc)),
|
_ => bail!("Film type '{}' unknown at {}", name, loc),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
use super::{HostImage, ImageAndMetadata, ImageMetadata};
|
use super::{HostImage, ImageAndMetadata, ImageMetadata};
|
||||||
use crate::core::image::{PixelStorage, WrapMode};
|
use crate::core::image::WrapMode;
|
||||||
use crate::utils::error::ImageError;
|
|
||||||
use anyhow::{Context, Result, bail};
|
use anyhow::{Context, Result, bail};
|
||||||
use exr::prelude::{read_first_rgba_layer_from_file, write_rgba_file};
|
use exr::prelude::{read_first_rgba_layer_from_file, write_rgba_file};
|
||||||
use image_rs::{DynamicImage, ImageReader};
|
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
use shared::core::color::{ColorEncoding, LINEAR, SRGB};
|
use shared::core::color::{ColorEncoding, LINEAR, SRGB};
|
||||||
use shared::core::geometry::Point2i;
|
use shared::core::geometry::Point2i;
|
||||||
use shared::core::image::PixelFormat;
|
use shared::core::image::PixelFormat;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufRead, BufReader, BufWriter, Read, Write};
|
use std::io::{BufRead, BufReader, BufWriter, Read, Write};
|
||||||
|
use image_rs::{DynamicImage, ImageReader};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
pub trait ImageIO {
|
pub trait ImageIO {
|
||||||
|
|
@ -40,55 +39,39 @@ impl ImageIO for HostImage {
|
||||||
fn write(&self, filename: &str, metadata: &ImageMetadata) -> Result<()> {
|
fn write(&self, filename: &str, metadata: &ImageMetadata) -> Result<()> {
|
||||||
let path = Path::new(filename);
|
let path = Path::new(filename);
|
||||||
let ext = path.extension().and_then(|s| s.to_str()).unwrap_or("");
|
let ext = path.extension().and_then(|s| s.to_str()).unwrap_or("");
|
||||||
let res = match ext.to_lowercase().as_str() {
|
match ext.to_lowercase().as_str() {
|
||||||
"exr" => self.write_exr(path, metadata),
|
"exr" => self.write_exr(path, metadata),
|
||||||
"png" => self.write_png(path),
|
"png" => self.write_png(path),
|
||||||
"pfm" => self.write_pfm(path),
|
"pfm" => self.write_pfm(path),
|
||||||
"qoi" => self.write_qoi(path),
|
"qoi" => self.write_qoi(path),
|
||||||
_ => Err(anyhow::anyhow!("Unsupported write format: {}", ext)),
|
_ => Err(anyhow::anyhow!("Unsupported write format: {}", ext)),
|
||||||
};
|
}
|
||||||
res.map_err(|e| ImageError::Io(std::io::Error::other(e)))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_png(&self, path: &Path) -> Result<()> {
|
fn write_png(&self, path: &Path) -> Result<()> {
|
||||||
let w = self.resolution().x() as u32;
|
let w = self.inner.resolution().x() as u32;
|
||||||
let h = self.resolution().y() as u32;
|
let h = self.inner.resolution().y() as u32;
|
||||||
|
|
||||||
// Convert whatever we have to u8 [0..255]
|
|
||||||
let data = self.to_u8_buffer();
|
let data = self.to_u8_buffer();
|
||||||
let channels = self.n_channels();
|
let channels = self.inner.n_channels();
|
||||||
|
|
||||||
match channels {
|
match channels {
|
||||||
1 => {
|
1 => {
|
||||||
// Luma
|
|
||||||
image_rs::save_buffer_with_format(
|
image_rs::save_buffer_with_format(
|
||||||
path,
|
path, &data, w, h,
|
||||||
&data,
|
|
||||||
w,
|
|
||||||
h,
|
|
||||||
image_rs::ColorType::L8,
|
image_rs::ColorType::L8,
|
||||||
image_rs::ImageFormat::Png,
|
image_rs::ImageFormat::Png,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
3 => {
|
3 => {
|
||||||
// RGB
|
|
||||||
image_rs::save_buffer_with_format(
|
image_rs::save_buffer_with_format(
|
||||||
path,
|
path, &data, w, h,
|
||||||
&data,
|
|
||||||
w,
|
|
||||||
h,
|
|
||||||
image_rs::ColorType::Rgb8,
|
image_rs::ColorType::Rgb8,
|
||||||
image_rs::ImageFormat::Png,
|
image_rs::ImageFormat::Png,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
4 => {
|
4 => {
|
||||||
// RGBA
|
|
||||||
image_rs::save_buffer_with_format(
|
image_rs::save_buffer_with_format(
|
||||||
path,
|
path, &data, w, h,
|
||||||
&data,
|
|
||||||
w,
|
|
||||||
h,
|
|
||||||
image_rs::ColorType::Rgba8,
|
image_rs::ColorType::Rgba8,
|
||||||
image_rs::ImageFormat::Png,
|
image_rs::ImageFormat::Png,
|
||||||
)?;
|
)?;
|
||||||
|
|
@ -99,37 +82,30 @@ impl ImageIO for HostImage {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_qoi(&self, path: &Path) -> Result<()> {
|
fn write_qoi(&self, path: &Path) -> Result<()> {
|
||||||
let w = self.resolution().x() as u32;
|
let w = self.inner.resolution().x() as u32;
|
||||||
let h = self.resolution().y() as u32;
|
let h = self.inner.resolution().y() as u32;
|
||||||
let data = self.to_u8_buffer();
|
let data = self.to_u8_buffer();
|
||||||
|
|
||||||
let color_type = match self.n_channels() {
|
let color_type = match self.inner.n_channels() {
|
||||||
3 => image_rs::ColorType::Rgb8,
|
3 => image_rs::ColorType::Rgb8,
|
||||||
4 => image_rs::ColorType::Rgba8,
|
4 => image_rs::ColorType::Rgba8,
|
||||||
_ => bail!("QOI only supports 3 or 4 channels"),
|
_ => bail!("QOI only supports 3 or 4 channels"),
|
||||||
};
|
};
|
||||||
|
|
||||||
image_rs::save_buffer_with_format(
|
image_rs::save_buffer_with_format(
|
||||||
path,
|
path, &data, w, h, color_type, image_rs::ImageFormat::Qoi,
|
||||||
&data,
|
|
||||||
w,
|
|
||||||
h,
|
|
||||||
color_type,
|
|
||||||
image_rs::ImageFormat::Qoi,
|
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_exr(&self, path: &Path, _metadata: &ImageMetadata) -> Result<()> {
|
fn write_exr(&self, path: &Path, _metadata: &ImageMetadata) -> Result<()> {
|
||||||
// EXR requires F32
|
let w = self.inner.resolution().x() as usize;
|
||||||
let w = self.resolution().x() as usize;
|
let h = self.inner.resolution().y() as usize;
|
||||||
let h = self.resolution().y() as usize;
|
let c = self.inner.n_channels();
|
||||||
let c = self.n_channels();
|
|
||||||
|
|
||||||
write_rgba_file(path, w, h, |x, y| {
|
write_rgba_file(path, w, h, |x, y| {
|
||||||
// Helper to get float value regardless of internal storage
|
|
||||||
let get = |ch| {
|
let get = |ch| {
|
||||||
self.get_channel_with_wrap(
|
self.inner.get_channel_with_wrap(
|
||||||
Point2i::new(x as i32, y as i32),
|
Point2i::new(x as i32, y as i32),
|
||||||
ch,
|
ch,
|
||||||
WrapMode::Clamp.into(),
|
WrapMode::Clamp.into(),
|
||||||
|
|
@ -154,27 +130,22 @@ impl ImageIO for HostImage {
|
||||||
let file = File::create(path)?;
|
let file = File::create(path)?;
|
||||||
let mut writer = BufWriter::new(file);
|
let mut writer = BufWriter::new(file);
|
||||||
|
|
||||||
if self.n_channels() != 3 {
|
if self.inner.n_channels() != 3 {
|
||||||
bail!("PFM writing currently only supports 3 channels (RGB)");
|
bail!("PFM writing currently only supports 3 channels (RGB)");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header
|
let res = self.inner.resolution();
|
||||||
let res = self.resolution();
|
|
||||||
writeln!(writer, "PF")?;
|
writeln!(writer, "PF")?;
|
||||||
writeln!(writer, "{} {}", res.x(), res.y())?;
|
writeln!(writer, "{} {}", res.x(), res.y())?;
|
||||||
let scale = if cfg!(target_endian = "little") {
|
let scale = if cfg!(target_endian = "little") { -1.0 } else { 1.0 };
|
||||||
-1.0
|
|
||||||
} else {
|
|
||||||
1.0
|
|
||||||
};
|
|
||||||
writeln!(writer, "{}", scale)?;
|
writeln!(writer, "{}", scale)?;
|
||||||
|
|
||||||
// PBRT stores top-to-bottom.
|
|
||||||
for y in (0..res.y()).rev() {
|
for y in (0..res.y()).rev() {
|
||||||
for x in 0..res.x() {
|
for x in 0..res.x() {
|
||||||
for c in 0..3 {
|
for c in 0..3 {
|
||||||
let val =
|
let val = self.inner.get_channel_with_wrap(
|
||||||
self.get_channel_with_wrap(Point2i::new(x, y), c, WrapMode::Clamp.into());
|
Point2i::new(x, y), c, WrapMode::Clamp.into(),
|
||||||
|
);
|
||||||
writer.write_all(&val.to_le_bytes())?;
|
writer.write_all(&val.to_le_bytes())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -183,20 +154,18 @@ impl ImageIO for HostImage {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Change Image to use Vec for data, always. Only convert to Device types on
|
|
||||||
// constructors/creation
|
|
||||||
fn to_u8_buffer(&self) -> Vec<u8> {
|
fn to_u8_buffer(&self) -> Vec<u8> {
|
||||||
match &self.pixels {
|
let res = self.inner.resolution();
|
||||||
PixelStorage::U8(data) => data.to_vec(),
|
let n_pixels = (res.x() * res.y()) as usize;
|
||||||
PixelStorage::F16(data) => data
|
let nc = self.inner.n_channels() as usize;
|
||||||
.iter()
|
let total = n_pixels * nc;
|
||||||
.map(|v| (v.to_f32().clamp(0.0, 1.0) * 255.0 + 0.5) as u8)
|
|
||||||
.collect(),
|
let mut buf = Vec::with_capacity(total);
|
||||||
PixelStorage::F32(data) => data
|
for i in 0..total {
|
||||||
.iter()
|
let val = unsafe { self.inner.pixels.read(i, &self.inner.encoding) };
|
||||||
.map(|v| (v.clamp(0.0, 1.0) * 255.0 + 0.5) as u8)
|
buf.push((val.clamp(0.0, 1.0) * 255.0 + 0.5) as u8);
|
||||||
.collect(),
|
|
||||||
}
|
}
|
||||||
|
buf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -209,21 +178,24 @@ fn read_generic(path: &Path, encoding: Option<ColorEncoding>) -> Result<ImageAnd
|
||||||
let h = dyn_img.height() as i32;
|
let h = dyn_img.height() as i32;
|
||||||
let res = Point2i::new(w, h);
|
let res = Point2i::new(w, h);
|
||||||
|
|
||||||
// Check if it was loaded as high precision or standard
|
let rgb_names: &[&str] = &["R", "G", "B"];
|
||||||
let rgb_names = vec!["R", "G", "B"];
|
let rgba_names: &[&str] = &["R", "G", "B", "A"];
|
||||||
let rgba_names = vec!["R", "G", "B", "A"];
|
|
||||||
let image = match dyn_img {
|
let image = match dyn_img {
|
||||||
DynamicImage::ImageRgb32F(buf) => Image::from_f32(buf.into_raw(), res, &rgb_names),
|
DynamicImage::ImageRgb32F(buf) => {
|
||||||
DynamicImage::ImageRgba32F(buf) => Image::from_f32(buf.into_raw(), res, &rgba_names),
|
HostImage::from_f32(&buf.into_raw(), res, rgb_names)
|
||||||
|
}
|
||||||
|
DynamicImage::ImageRgba32F(buf) => {
|
||||||
|
HostImage::from_f32(&buf.into_raw(), res, rgba_names)
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Default to RGB8 for everything else
|
|
||||||
let enc = encoding.unwrap_or(SRGB);
|
let enc = encoding.unwrap_or(SRGB);
|
||||||
if dyn_img.color().has_alpha() {
|
if dyn_img.color().has_alpha() {
|
||||||
let buf = dyn_img.to_rgba8();
|
let buf = dyn_img.to_rgba8();
|
||||||
Image::from_u8(buf.into_raw(), res, &rgba_names, enc)
|
HostImage::from_u8(&buf.into_raw(), res, rgba_names, enc)
|
||||||
} else {
|
} else {
|
||||||
let buf = dyn_img.to_rgb8();
|
let buf = dyn_img.to_rgb8();
|
||||||
Image::from_u8(buf.into_raw(), res, &rgb_names, enc)
|
HostImage::from_u8(&buf.into_raw(), res, rgb_names, enc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -242,7 +214,6 @@ fn read_exr(path: &Path) -> Result<ImageAndMetadata> {
|
||||||
|buffer, position, pixel| {
|
|buffer, position, pixel| {
|
||||||
let width = position.width();
|
let width = position.width();
|
||||||
let idx = (position.y() * width + position.x()) * 4;
|
let idx = (position.y() * width + position.x()) * 4;
|
||||||
// Map exr pixel struct to our buffer
|
|
||||||
buffer[idx] = pixel.0;
|
buffer[idx] = pixel.0;
|
||||||
buffer[idx + 1] = pixel.1;
|
buffer[idx + 1] = pixel.1;
|
||||||
buffer[idx + 2] = pixel.2;
|
buffer[idx + 2] = pixel.2;
|
||||||
|
|
@ -254,10 +225,11 @@ fn read_exr(path: &Path) -> Result<ImageAndMetadata> {
|
||||||
let w = image.layer_data.size.width() as i32;
|
let w = image.layer_data.size.width() as i32;
|
||||||
let h = image.layer_data.size.height() as i32;
|
let h = image.layer_data.size.height() as i32;
|
||||||
|
|
||||||
let image = Image::from_f32(
|
let rgba_names: &[&str] = &["R", "G", "B", "A"];
|
||||||
image.layer_data.channel_data.pixels,
|
let image = HostImage::from_f32(
|
||||||
|
&image.layer_data.channel_data.pixels,
|
||||||
Point2i::new(w, h),
|
Point2i::new(w, h),
|
||||||
&vec!["R", "G", "B", "A"],
|
rgba_names,
|
||||||
);
|
);
|
||||||
|
|
||||||
let metadata = ImageMetadata::default();
|
let metadata = ImageMetadata::default();
|
||||||
|
|
@ -268,7 +240,6 @@ fn read_pfm(path: &Path) -> Result<ImageAndMetadata> {
|
||||||
let file = File::open(path)?;
|
let file = File::open(path)?;
|
||||||
let mut reader = BufReader::new(file);
|
let mut reader = BufReader::new(file);
|
||||||
|
|
||||||
// PFM Headers are: "PF\nwidth height\nscale\n" (or Pf for grayscale)
|
|
||||||
let mut header_word = String::new();
|
let mut header_word = String::new();
|
||||||
reader.read_line(&mut header_word)?;
|
reader.read_line(&mut header_word)?;
|
||||||
let header_word = header_word.trim();
|
let header_word = header_word.trim();
|
||||||
|
|
@ -309,9 +280,7 @@ fn read_pfm(path: &Path) -> Result<ImageAndMetadata> {
|
||||||
|
|
||||||
let mut pixels = vec![0.0 as Float; (w * h * channels) as usize];
|
let mut pixels = vec![0.0 as Float; (w * h * channels) as usize];
|
||||||
|
|
||||||
// PFM is Bottom-to-Top
|
|
||||||
for y in 0..h {
|
for y in 0..h {
|
||||||
// Flippety-do
|
|
||||||
let src_y = h - 1 - y;
|
let src_y = h - 1 - y;
|
||||||
for x in 0..w {
|
for x in 0..w {
|
||||||
for c in 0..channels {
|
for c in 0..channels {
|
||||||
|
|
@ -331,13 +300,9 @@ fn read_pfm(path: &Path) -> Result<ImageAndMetadata> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let names = if channels == 1 {
|
let names: &[&str] = if channels == 1 { &["Y"] } else { &["R", "G", "B"] };
|
||||||
vec!["Y"]
|
|
||||||
} else {
|
|
||||||
vec!["R", "G", "B"]
|
|
||||||
};
|
|
||||||
|
|
||||||
let image = Image::new(PixelFormat::F32, Point2i::new(w, h), &names, LINEAR.into());
|
let image = HostImage::new(PixelFormat::F32, Point2i::new(w, h), names, LINEAR.into());
|
||||||
let metadata = ImageMetadata::default();
|
let metadata = ImageMetadata::default();
|
||||||
|
|
||||||
Ok(ImageAndMetadata { image, metadata })
|
Ok(ImageAndMetadata { image, metadata })
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
use crate::utils::containers::Array2D;
|
use anyhow::{anyhow, Result};
|
||||||
use anyhow::{Result, anyhow};
|
|
||||||
use half::f16;
|
use half::f16;
|
||||||
use rayon::prelude::{IndexedParallelIterator, ParallelIterator, ParallelSliceMut};
|
use rayon::prelude::{IndexedParallelIterator, ParallelIterator, ParallelSliceMut};
|
||||||
use shared::Float;
|
|
||||||
use shared::Ptr;
|
|
||||||
use shared::core::color::{ColorEncoding, ColorEncodingTrait, LINEAR};
|
use shared::core::color::{ColorEncoding, ColorEncodingTrait, LINEAR};
|
||||||
use shared::core::geometry::{Bounds2f, Point2f, Point2i};
|
use shared::core::geometry::{Bounds2f, Point2f, Point2i};
|
||||||
use shared::core::image::{Image, ImageBase, PixelFormat, Pixels, WrapMode, WrapMode2D};
|
use shared::core::image::{Image, ImageBase, PixelFormat, Pixels, WrapMode, WrapMode2D};
|
||||||
use shared::utils::math::square;
|
use shared::utils::math::square;
|
||||||
use smallvec::{SmallVec, smallvec};
|
use shared::{Array2D, Float, Ptr};
|
||||||
|
use smallvec::{smallvec, SmallVec};
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|
@ -90,31 +88,32 @@ impl HostImage {
|
||||||
let n_channels = channel_names.len() as i32;
|
let n_channels = channel_names.len() as i32;
|
||||||
Self {
|
Self {
|
||||||
inner: Image::from_u8(data, resolution, n_channels, encoding),
|
inner: Image::from_u8(data, resolution, n_channels, encoding),
|
||||||
channel_names: channel_names.iter().map(|s| s.as_ref().to_string()).collect(),
|
channel_names: channel_names
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.as_ref().to_string())
|
||||||
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_f32(
|
pub fn from_f32(data: &[f32], resolution: Point2i, channel_names: &[impl AsRef<str>]) -> Self {
|
||||||
data: &[f32],
|
|
||||||
resolution: Point2i,
|
|
||||||
channel_names: &[impl AsRef<str>],
|
|
||||||
) -> Self {
|
|
||||||
let n_channels = channel_names.len() as i32;
|
let n_channels = channel_names.len() as i32;
|
||||||
Self {
|
Self {
|
||||||
inner: Image::from_f32(data, resolution, n_channels),
|
inner: Image::from_f32(data, resolution, n_channels),
|
||||||
channel_names: channel_names.iter().map(|s| s.as_ref().to_string()).collect(),
|
channel_names: channel_names
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.as_ref().to_string())
|
||||||
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_f16(
|
pub fn from_f16(data: &[u16], resolution: Point2i, channel_names: &[impl AsRef<str>]) -> Self {
|
||||||
data: &[u16],
|
|
||||||
resolution: Point2i,
|
|
||||||
channel_names: &[impl AsRef<str>],
|
|
||||||
) -> Self {
|
|
||||||
let n_channels = channel_names.len() as i32;
|
let n_channels = channel_names.len() as i32;
|
||||||
Self {
|
Self {
|
||||||
inner: Image::from_f16(data, resolution, n_channels),
|
inner: Image::from_f16(data, resolution, n_channels),
|
||||||
channel_names: channel_names.iter().map(|s| s.as_ref().to_string()).collect(),
|
channel_names: channel_names
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.as_ref().to_string())
|
||||||
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,7 +126,10 @@ impl HostImage {
|
||||||
let n_channels = channel_names.len() as i32;
|
let n_channels = channel_names.len() as i32;
|
||||||
Self {
|
Self {
|
||||||
inner: Image::new(format, resolution, n_channels, encoding),
|
inner: Image::new(format, resolution, n_channels, encoding),
|
||||||
channel_names: channel_names.iter().map(|s| s.as_ref().to_string()).collect(),
|
channel_names: channel_names
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.as_ref().to_string())
|
||||||
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,7 +139,11 @@ impl HostImage {
|
||||||
values: &[f32],
|
values: &[f32],
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let n_channels = channel_names.len();
|
let n_channels = channel_names.len();
|
||||||
assert_eq!(values.len(), n_channels, "values length must match channel count");
|
assert_eq!(
|
||||||
|
values.len(),
|
||||||
|
n_channels,
|
||||||
|
"values length must match channel count"
|
||||||
|
);
|
||||||
|
|
||||||
let n_pixels = (resolution.x() * resolution.y()) as usize;
|
let n_pixels = (resolution.x() * resolution.y()) as usize;
|
||||||
let mut data = Vec::with_capacity(n_pixels * n_channels);
|
let mut data = Vec::with_capacity(n_pixels * n_channels);
|
||||||
|
|
@ -179,14 +185,37 @@ impl HostImage {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn channel_names_from_desc(&self, desc: &ImageChannelDesc) -> Vec<&str> {
|
pub fn channel_names_from_desc(&self, desc: &ImageChannelDesc) -> Vec<&str> {
|
||||||
desc.offset.iter().map(|&i| self.channel_names[i].as_str()).collect()
|
desc.offset
|
||||||
|
.iter()
|
||||||
|
.map(|&i| self.channel_names[i].as_str())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bilerp_channel_with_wrap(&self, p: Point2f, c: i32, wrap: WrapMode2D) -> Float {
|
||||||
|
self.inner.bilerp_channel_with_wrap(p, c, wrap)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bilerp_channel(&self, p: Point2f, c: i32) -> Float {
|
||||||
|
self.bilerp_channel_with_wrap(p, c, WrapMode::Clamp.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_channel(&self, p: Point2i, c: i32) -> Float {
|
||||||
|
self.get_channel_with_wrap(p, c, WrapMode::Clamp.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_channel_with_wrap(&self, p: Point2i, c: i32, wrap_mode: WrapMode2D) -> Float {
|
||||||
|
self.inner.get_channel_with_wrap(p, c, wrap_mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_channels(&self, p: Point2i) -> ImageChannelValues {
|
pub fn get_channels(&self, p: Point2i) -> ImageChannelValues {
|
||||||
self.get_channels_with_wrap(p, WrapMode::Clamp.into())
|
self.get_channels_with_wrap(p, WrapMode::Clamp.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_channels_with_wrap(&self, mut p: Point2i, wrap_mode: WrapMode2D) -> ImageChannelValues {
|
pub fn get_channels_with_wrap(
|
||||||
|
&self,
|
||||||
|
mut p: Point2i,
|
||||||
|
wrap_mode: WrapMode2D,
|
||||||
|
) -> ImageChannelValues {
|
||||||
if !self.inner.remap_pixel_coords(&mut p, wrap_mode) {
|
if !self.inner.remap_pixel_coords(&mut p, wrap_mode) {
|
||||||
return ImageChannelValues(SmallVec::from_elem(0.0, self.inner.n_channels() as usize));
|
return ImageChannelValues(SmallVec::from_elem(0.0, self.inner.n_channels() as usize));
|
||||||
}
|
}
|
||||||
|
|
@ -212,7 +241,11 @@ impl HostImage {
|
||||||
let pixel_offset = self.inner.pixel_offset(pp);
|
let pixel_offset = self.inner.pixel_offset(pp);
|
||||||
let mut values = SmallVec::with_capacity(desc.offset.len());
|
let mut values = SmallVec::with_capacity(desc.offset.len());
|
||||||
for &c in &desc.offset {
|
for &c in &desc.offset {
|
||||||
values.push(unsafe { self.inner.pixels.read(pixel_offset + c, &self.inner.encoding) });
|
values.push(unsafe {
|
||||||
|
self.inner
|
||||||
|
.pixels
|
||||||
|
.read(pixel_offset + c, &self.inner.encoding)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
ImageChannelValues(values)
|
ImageChannelValues(values)
|
||||||
}
|
}
|
||||||
|
|
@ -223,20 +256,29 @@ impl HostImage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_channel(&mut self, p: Point2i, values: &ImageChannelValues) {
|
||||||
|
self.inner.set_channel(p, values[i]);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn select_channels(&self, desc: &ImageChannelDesc) -> Self {
|
pub fn select_channels(&self, desc: &ImageChannelDesc) -> Self {
|
||||||
let new_names: Vec<String> = desc.offset.iter().map(|&i| self.channel_names[i].clone()).collect();
|
let new_names: Vec<String> = desc
|
||||||
|
.offset
|
||||||
|
.iter()
|
||||||
|
.map(|&i| self.channel_names[i].clone())
|
||||||
|
.collect();
|
||||||
let res = self.inner.resolution();
|
let res = self.inner.resolution();
|
||||||
let pixel_count = (res.x() * res.y()) as usize;
|
let pixel_count = (res.x() * res.y()) as usize;
|
||||||
let src_nc = self.inner.n_channels() as usize;
|
let src_nc = self.inner.n_channels() as usize;
|
||||||
let dst_nc = desc.offset.len();
|
let dst_nc = desc.offset.len();
|
||||||
|
|
||||||
// Always produce f32 output for simplicity
|
|
||||||
let mut dst = vec![0.0f32; pixel_count * dst_nc];
|
let mut dst = vec![0.0f32; pixel_count * dst_nc];
|
||||||
for i in 0..pixel_count {
|
for i in 0..pixel_count {
|
||||||
let src_offset = i * src_nc;
|
let src_offset = i * src_nc;
|
||||||
for (out_idx, &in_c) in desc.offset.iter().enumerate() {
|
for (out_idx, &in_c) in desc.offset.iter().enumerate() {
|
||||||
dst[i * dst_nc + out_idx] = unsafe {
|
dst[i * dst_nc + out_idx] = unsafe {
|
||||||
self.inner.pixels.read(src_offset + in_c, &self.inner.encoding)
|
self.inner
|
||||||
|
.pixels
|
||||||
|
.read(src_offset + in_c, &self.inner.encoding)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -256,7 +298,7 @@ impl HostImage {
|
||||||
dist.as_mut_slice()
|
dist.as_mut_slice()
|
||||||
.par_chunks_mut(width as usize)
|
.par_chunks_mut(width as usize)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.for_each(|(y, row)| {
|
.for_each(|(y, row): (usize, &mut [Float])| {
|
||||||
let y = y as i32;
|
let y = y as i32;
|
||||||
for (x, out_val) in row.iter_mut().enumerate() {
|
for (x, out_val) in row.iter_mut().enumerate() {
|
||||||
let x = x as i32;
|
let x = x as i32;
|
||||||
|
|
@ -310,15 +352,18 @@ impl HostImage {
|
||||||
|
|
||||||
for y in 0..res.y() {
|
for y in 0..res.y() {
|
||||||
for x in 0..res.x() {
|
for x in 0..res.x() {
|
||||||
let v = self.get_channels_with_desc(
|
let v =
|
||||||
Point2i::new(x, y), desc, WrapMode::Clamp.into(),
|
self.get_channels_with_desc(Point2i::new(x, y), desc, WrapMode::Clamp.into());
|
||||||
);
|
|
||||||
let v_ref = ref_img.get_channels_with_desc(
|
let v_ref = ref_img.get_channels_with_desc(
|
||||||
Point2i::new(x, y), &ref_desc, WrapMode::Clamp.into(),
|
Point2i::new(x, y),
|
||||||
|
&ref_desc,
|
||||||
|
WrapMode::Clamp.into(),
|
||||||
);
|
);
|
||||||
for c in 0..n_channels {
|
for c in 0..n_channels {
|
||||||
let se = square(v[c] as f64 - v_ref[c] as f64);
|
let se = square(v[c] as f64 - v_ref[c] as f64);
|
||||||
if se.is_infinite() { continue; }
|
if se.is_infinite() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
sum_se[c] += se;
|
sum_se[c] += se;
|
||||||
if generate_mse_image {
|
if generate_mse_image {
|
||||||
let idx = (y as usize * width + x as usize) * n_channels + c;
|
let idx = (y as usize * width + x as usize) * n_channels + c;
|
||||||
|
|
|
||||||
|
|
@ -191,19 +191,19 @@ impl HostImage {
|
||||||
PixelFormat::U8 => downsample_kernel(
|
PixelFormat::U8 => downsample_kernel(
|
||||||
next.inner.pixels.as_u8_mut(),
|
next.inner.pixels.as_u8_mut(),
|
||||||
new_res,
|
new_res,
|
||||||
&prev.inner,
|
&prev,
|
||||||
internal_wrap,
|
internal_wrap,
|
||||||
),
|
),
|
||||||
PixelFormat::F16 => downsample_kernel(
|
PixelFormat::F16 => downsample_kernel(
|
||||||
next.inner.pixels.as_f16_mut(),
|
next.inner.pixels.as_f16_mut(),
|
||||||
new_res,
|
new_res,
|
||||||
&prev.inner,
|
&prev,
|
||||||
internal_wrap,
|
internal_wrap,
|
||||||
),
|
),
|
||||||
PixelFormat::F32 => downsample_kernel(
|
PixelFormat::F32 => downsample_kernel(
|
||||||
next.inner.pixels.as_f32_slice_mut(),
|
next.inner.pixels.as_f32_slice_mut(),
|
||||||
new_res,
|
new_res,
|
||||||
&prev.inner,
|
&prev,
|
||||||
internal_wrap,
|
internal_wrap,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
@ -322,7 +322,7 @@ fn copy_rect_in_kernel<T: PixelStorageTrait>(
|
||||||
fn downsample_kernel<T: PixelStorageTrait>(
|
fn downsample_kernel<T: PixelStorageTrait>(
|
||||||
dst: &mut [T],
|
dst: &mut [T],
|
||||||
dst_res: Point2i,
|
dst_res: Point2i,
|
||||||
prev: &Image,
|
prev: &HostImage,
|
||||||
wrap: WrapMode2D,
|
wrap: WrapMode2D,
|
||||||
) {
|
) {
|
||||||
let w = dst_res.x() as usize;
|
let w = dst_res.x() as usize;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use shared::core::shape::Shape;
|
||||||
use shared::spectra::DenselySampledSpectrum;
|
use shared::spectra::DenselySampledSpectrum;
|
||||||
use shared::core::spectrum::Spectrum;
|
use shared::core::spectrum::Spectrum;
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::RGBColorSpace;
|
||||||
use shared::Transform;
|
use shared::{Ptr, Transform};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub fn lookup_spectrum(s: &Spectrum) -> Arc<DenselySampledSpectrum> {
|
pub fn lookup_spectrum(s: &Spectrum) -> Arc<DenselySampledSpectrum> {
|
||||||
|
|
@ -117,7 +117,7 @@ pub fn create_area_light(
|
||||||
shape: &Shape,
|
shape: &Shape,
|
||||||
alpha_tex: &FloatTexture,
|
alpha_tex: &FloatTexture,
|
||||||
colorspace: Option<&RGBColorSpace>,
|
colorspace: Option<&RGBColorSpace>,
|
||||||
arena: &mut Arena,
|
arena: &Arena,
|
||||||
) -> Result<Ptr<Light>> {
|
) -> Result<Ptr<Light>> {
|
||||||
let light = crate::lights::diffuse::create(
|
let light = crate::lights::diffuse::create(
|
||||||
render_from_light, medium, parameters, loc,
|
render_from_light, medium, parameters, loc,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::Arena;
|
use crate::Arena;
|
||||||
use crate::core::image::Image;
|
use crate::core::image::HostImage;
|
||||||
use crate::utils::TextureParameterDictionary;
|
use crate::utils::TextureParameterDictionary;
|
||||||
use crate::utils::error::FileLoc;
|
use crate::utils::error::FileLoc;
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{Result, anyhow};
|
||||||
|
|
@ -11,7 +11,7 @@ use std::sync::Arc;
|
||||||
pub trait CreateMaterial: Sized {
|
pub trait CreateMaterial: Sized {
|
||||||
fn create(
|
fn create(
|
||||||
parameters: &TextureParameterDictionary,
|
parameters: &TextureParameterDictionary,
|
||||||
normal_map: Option<Arc<Image>>,
|
normal_map: Option<Arc<HostImage>>,
|
||||||
named_materials: &HashMap<String, Material>,
|
named_materials: &HashMap<String, Material>,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
arena: &Arena,
|
arena: &Arena,
|
||||||
|
|
@ -22,7 +22,7 @@ pub trait MaterialFactory {
|
||||||
fn create(
|
fn create(
|
||||||
name: &str,
|
name: &str,
|
||||||
params: &TextureParameterDictionary,
|
params: &TextureParameterDictionary,
|
||||||
normal_map: Option<Arc<Image>>,
|
normal_map: Option<Arc<HostImage>>,
|
||||||
named_materials: &HashMap<String, Material>,
|
named_materials: &HashMap<String, Material>,
|
||||||
loc: FileLoc,
|
loc: FileLoc,
|
||||||
arena: &Arena,
|
arena: &Arena,
|
||||||
|
|
@ -35,7 +35,7 @@ impl MaterialFactory for Material {
|
||||||
fn create(
|
fn create(
|
||||||
name: &str,
|
name: &str,
|
||||||
parameters: &TextureParameterDictionary,
|
parameters: &TextureParameterDictionary,
|
||||||
normal_map: Option<Arc<Image>>,
|
normal_map: Option<Arc<HostImage>>,
|
||||||
named_materials: &HashMap<String, Material>,
|
named_materials: &HashMap<String, Material>,
|
||||||
loc: FileLoc,
|
loc: FileLoc,
|
||||||
arena: &Arena,
|
arena: &Arena,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use super::state::*;
|
||||||
use crate::core::camera::CameraFactory;
|
use crate::core::camera::CameraFactory;
|
||||||
use crate::core::film::FilmFactory;
|
use crate::core::film::FilmFactory;
|
||||||
use crate::core::filter::FilterFactory;
|
use crate::core::filter::FilterFactory;
|
||||||
use crate::core::image::{io::ImageIO, Image};
|
use crate::core::image::{HostImage, ImageIO};
|
||||||
use crate::core::material::MaterialFactory;
|
use crate::core::material::MaterialFactory;
|
||||||
use crate::core::primitive::{CreateGeometricPrimitive, CreateSimplePrimitive};
|
use crate::core::primitive::{CreateGeometricPrimitive, CreateSimplePrimitive};
|
||||||
use crate::core::sampler::SamplerFactory;
|
use crate::core::sampler::SamplerFactory;
|
||||||
|
|
@ -12,7 +12,7 @@ use crate::core::texture::{FloatTexture, SpectrumTexture};
|
||||||
use crate::utils::parallel::{run_async, AsyncJob};
|
use crate::utils::parallel::{run_async, AsyncJob};
|
||||||
use crate::utils::parameters::{NamedTextures, ParameterDictionary, TextureParameterDictionary};
|
use crate::utils::parameters::{NamedTextures, ParameterDictionary, TextureParameterDictionary};
|
||||||
use crate::utils::resolve_filename;
|
use crate::utils::resolve_filename;
|
||||||
use crate::{Arena, ArenaUpload, FileLoc, Upload};
|
use crate::{Arena, ArenaUpload, FileLoc};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use shared::core::camera::{Camera, CameraTransform};
|
use shared::core::camera::{Camera, CameraTransform};
|
||||||
|
|
@ -25,9 +25,9 @@ use shared::core::medium::{Medium, MediumInterface};
|
||||||
use shared::core::primitive::{AnimatedPrimitive, GeometricPrimitive, Primitive, SimplePrimitive};
|
use shared::core::primitive::{AnimatedPrimitive, GeometricPrimitive, Primitive, SimplePrimitive};
|
||||||
use shared::core::sampler::Sampler;
|
use shared::core::sampler::Sampler;
|
||||||
use shared::core::shape::Shape;
|
use shared::core::shape::Shape;
|
||||||
use shared::core::texture::SpectrumType;
|
use shared::core::texture::{GPUFloatTexture, SpectrumType};
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::RGBColorSpace;
|
||||||
use shared::utils::Ptr;
|
use shared::{Ptr, Transform};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|
@ -169,7 +169,6 @@ impl BasicScene {
|
||||||
});
|
});
|
||||||
self.sampler_state.lock().job = Some(sampler_job);
|
self.sampler_state.lock().job = Some(sampler_job);
|
||||||
|
|
||||||
let arena_camera = Arc::clone(&arena);
|
|
||||||
let camera_film = Arc::clone(&film_instance);
|
let camera_film = Arc::clone(&film_instance);
|
||||||
let scene_ptr = Arc::clone(self);
|
let scene_ptr = Arc::clone(self);
|
||||||
let camera_job = run_async(move || {
|
let camera_job = run_async(move || {
|
||||||
|
|
@ -181,7 +180,7 @@ impl BasicScene {
|
||||||
medium,
|
medium,
|
||||||
camera_film,
|
camera_film,
|
||||||
&camera.base.loc,
|
&camera.base.loc,
|
||||||
&arena_camera,
|
&arena,
|
||||||
)
|
)
|
||||||
.map_err(|e| anyhow!("Failed to create camera: {}", e))
|
.map_err(|e| anyhow!("Failed to create camera: {}", e))
|
||||||
});
|
});
|
||||||
|
|
@ -1027,7 +1026,7 @@ impl BasicScene {
|
||||||
let filename_clone = filename.clone();
|
let filename_clone = filename.clone();
|
||||||
let job = run_async(move || {
|
let job = run_async(move || {
|
||||||
let path = std::path::Path::new(&filename_clone);
|
let path = std::path::Path::new(&filename_clone);
|
||||||
let immeta = Image::read(path, Some(LINEAR)).expect(&format!(
|
let immeta = HostImage::read(path, Some(LINEAR)).expect(&format!(
|
||||||
"{}: normal map must contain R, G, B channels",
|
"{}: normal map must contain R, G, B channels",
|
||||||
filename_clone
|
filename_clone
|
||||||
));
|
));
|
||||||
|
|
@ -1051,7 +1050,7 @@ impl BasicScene {
|
||||||
&self,
|
&self,
|
||||||
state: &MaterialState,
|
state: &MaterialState,
|
||||||
params: &ParameterDictionary,
|
params: &ParameterDictionary,
|
||||||
) -> Result<Option<Arc<Image>>> {
|
) -> Result<Option<Arc<HostImage>>> {
|
||||||
let filename = resolve_filename(¶ms.get_one_string("normalmap", "")?);
|
let filename = resolve_filename(¶ms.get_one_string("normalmap", "")?);
|
||||||
if filename.is_empty() {
|
if filename.is_empty() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use super::{LightSceneEntity, SceneEntity, TextureSceneEntity};
|
use super::{LightSceneEntity, SceneEntity, TextureSceneEntity};
|
||||||
use crate::core::image::Image;
|
use crate::core::image::HostImage;
|
||||||
use crate::core::texture::{FloatTexture, SpectrumTexture};
|
use crate::core::texture::{FloatTexture, SpectrumTexture};
|
||||||
use crate::utils::parallel::AsyncJob;
|
use crate::utils::parallel::AsyncJob;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
@ -22,8 +22,8 @@ pub struct TextureState {
|
||||||
pub struct MaterialState {
|
pub struct MaterialState {
|
||||||
pub named_materials: Vec<(String, SceneEntity)>,
|
pub named_materials: Vec<(String, SceneEntity)>,
|
||||||
pub materials: Vec<SceneEntity>,
|
pub materials: Vec<SceneEntity>,
|
||||||
pub normal_map_jobs: HashMap<String, AsyncJob<Arc<Image>>>,
|
pub normal_map_jobs: HashMap<String, AsyncJob<Arc<HostImage>>>,
|
||||||
pub normal_maps: HashMap<String, Arc<Image>>,
|
pub normal_maps: HashMap<String, Arc<HostImage>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
|
|
|
||||||
|
|
@ -116,8 +116,8 @@ impl ShapeFactory for Shape {
|
||||||
global_store.push(host_arc.clone());
|
global_store.push(host_arc.clone());
|
||||||
drop(global_store);
|
drop(global_store);
|
||||||
|
|
||||||
let n_tris = host_arc..n_triangles;
|
let n_tris = host_arc.n_triangles;
|
||||||
let mesh_ptr = Ptr::from(&host_arc);
|
let mesh_ptr = arena.alloc_arc(host_arc);
|
||||||
let shapes: Vec<Ptr<Shape>> = (0..n_tris)
|
let shapes: Vec<Ptr<Shape>> = (0..n_tris)
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
let tri_shape = Shape::Triangle(TriangleShape {
|
let tri_shape = Shape::Triangle(TriangleShape {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::textures::*;
|
use crate::textures::*;
|
||||||
use crate::utils::TextureParameterDictionary;
|
use crate::utils::{MIPMap, MIPMapFilterOptions, TextureParameterDictionary};
|
||||||
use crate::{Arena, FileLoc};
|
use crate::{Arena, FileLoc};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
|
|
@ -8,8 +8,8 @@ use shared::core::geometry::Vector3f;
|
||||||
use shared::core::image::WrapMode;
|
use shared::core::image::WrapMode;
|
||||||
use shared::core::texture::SpectrumType;
|
use shared::core::texture::SpectrumType;
|
||||||
use shared::core::texture::{
|
use shared::core::texture::{
|
||||||
CylindricalMapping, GPUFloatTexture, GPUSpectrumTexture, PlanarMapping, SphericalMapping,
|
CylindricalMapping, PlanarMapping, SphericalMapping, TextureEvalContext, TextureMapping2D,
|
||||||
TextureEvalContext, TextureMapping2D, UVMapping,
|
UVMapping,
|
||||||
};
|
};
|
||||||
use shared::spectra::{SampledSpectrum, SampledWavelengths};
|
use shared::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use shared::textures::*;
|
use shared::textures::*;
|
||||||
|
|
@ -133,7 +133,10 @@ impl SpectrumTexture {
|
||||||
invert: inner.base.invert,
|
invert: inner.base.invert,
|
||||||
is_single_channel: inner.base.mipmap.is_single_channel(),
|
is_single_channel: inner.base.mipmap.is_single_channel(),
|
||||||
color_space: arena.alloc(
|
color_space: arena.alloc(
|
||||||
inner.base.mipmap.color_space
|
inner
|
||||||
|
.base
|
||||||
|
.mipmap
|
||||||
|
.color_space
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(crate::spectra::default_colorspace),
|
.unwrap_or_else(crate::spectra::default_colorspace),
|
||||||
),
|
),
|
||||||
|
|
@ -262,4 +265,3 @@ pub struct TexInfo {
|
||||||
pub wrap_mode: WrapMode,
|
pub wrap_mode: WrapMode,
|
||||||
pub encoding: ColorEncoding,
|
pub encoding: ColorEncoding,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::core::film::{CreateFilmBase, PixelSensor};
|
use crate::core::film::{CreateFilmBase, CreatePixelSensor};
|
||||||
use crate::utils::containers::Array2D;
|
use shared::core::film::PixelSensor;
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{Result, anyhow};
|
||||||
use shared::core::film::{FilmBase, GBufferFilm};
|
use shared::core::film::{FilmBase, GBufferFilm};
|
||||||
use shared::core::filter::FilterTrait;
|
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::RGBColorSpace;
|
||||||
use shared::utils::AnimatedTransform;
|
use shared::utils::AnimatedTransform;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
@ -16,13 +15,13 @@ impl CreateFilm for GBufferFilm {
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
camera_transform: Option<CameraTransform>,
|
camera_transform: Option<CameraTransform>,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
_arena: &Arena,
|
arena: &Arena,
|
||||||
) -> Result<Film> {
|
) -> Result<Film> {
|
||||||
let colorspace = params.color_space.as_ref().unwrap();
|
let colorspace = params.color_space.as_ref().unwrap();
|
||||||
let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY)?;
|
let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY)?;
|
||||||
let write_fp16 = params.get_one_bool("savefp16", true)?;
|
let write_fp16 = params.get_one_bool("savefp16", true)?;
|
||||||
let sensor = PixelSensor::create(params, colorspace.clone(), exposure_time, loc)?;
|
let sensor = PixelSensor::create(params, colorspace.clone(), exposure_time, loc, arena)?;
|
||||||
let film_base = FilmBase::create(params, filter, Some(&sensor.device()), loc)?;
|
let film_base = FilmBase::create(params, filter, Some(&sensor), loc)?;
|
||||||
|
|
||||||
let filename = params.get_one_string("filename", "pbrt.exr")?;
|
let filename = params.get_one_string("filename", "pbrt.exr")?;
|
||||||
if Path::new(&filename).extension() != Some("exr".as_ref()) {
|
if Path::new(&filename).extension() != Some("exr".as_ref()) {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::core::film::{CreateFilmBase, PixelSensor};
|
use crate::core::film::{CreateFilmBase, PixelSensor};
|
||||||
use crate::utils::containers::Array2D;
|
|
||||||
use crate::Arena;
|
use crate::Arena;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use shared::core::camera::CameraTransform;
|
use shared::core::camera::CameraTransform;
|
||||||
use shared::core::film::{Film, FilmBase, RGBFilm, RGBPixel};
|
use shared::core::film::{Film, FilmBase, RGBFilm, RGBPixel};
|
||||||
use shared::core::filter::FilterTrait;
|
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::RGBColorSpace;
|
||||||
|
|
||||||
impl CreateFilm for RGBFilm {
|
impl CreateFilm for RGBFilm {
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,14 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::core::film::{CreateFilmBase, PixelSensor};
|
use crate::core::film::{CreateFilmBase, CreatePixelSensor};
|
||||||
use crate::{Arena, FileLoc, ParameterDictionary};
|
use crate::{Arena, FileLoc, ParameterDictionary};
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{anyhow, Result};
|
||||||
use shared::Float;
|
|
||||||
use shared::core::camera::CameraTransform;
|
use shared::core::camera::CameraTransform;
|
||||||
use shared::core::film::{FilmBase, SpectralFilm};
|
use shared::core::film::{FilmBase, PixelSensor, SpectralFilm};
|
||||||
use shared::core::filter::FilterTrait;
|
|
||||||
use shared::spectra::{LAMBDA_MAX, LAMBDA_MIN};
|
use shared::spectra::{LAMBDA_MAX, LAMBDA_MIN};
|
||||||
use shared::utils::math::SquareMatrix;
|
use shared::utils::math::SquareMatrix;
|
||||||
|
use shared::Float;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
|
||||||
impl CreateFilm for SpectralFilm {
|
impl CreateFilm for SpectralFilm {
|
||||||
fn create(
|
fn create(
|
||||||
params: &ParameterDictionary,
|
params: &ParameterDictionary,
|
||||||
|
|
@ -18,14 +16,14 @@ impl CreateFilm for SpectralFilm {
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
_camera_transform: Option<CameraTransform>,
|
_camera_transform: Option<CameraTransform>,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
_arena: &Arena,
|
arena: &Arena,
|
||||||
) -> Result<Film> {
|
) -> Result<Film> {
|
||||||
// Missing default illuminant, use srgb
|
// Missing default illuminant, use srgb
|
||||||
let colorspace = params.color_space.as_ref().unwrap();
|
let colorspace = params.color_space.as_ref().unwrap();
|
||||||
let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY)?;
|
let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY)?;
|
||||||
let write_fp16 = params.get_one_bool("savefp16", true)?;
|
let write_fp16 = params.get_one_bool("savefp16", true)?;
|
||||||
let sensor = PixelSensor::create(params, colorspace.clone(), exposure_time, loc)?;
|
let sensor = PixelSensor::create(params, colorspace.clone(), exposure_time, loc, arena)?;
|
||||||
let film_base = FilmBase::create(params, filter, Some(&sensor.device()), loc)?;
|
let film_base = FilmBase::create(params, filter, Some(&sensor), loc)?;
|
||||||
|
|
||||||
let filename = params.get_one_string("filename", "pbrt.exr")?;
|
let filename = params.get_one_string("filename", "pbrt.exr")?;
|
||||||
if Path::new(&filename).extension() != Some("exr".as_ref()) {
|
if Path::new(&filename).extension() != Some("exr".as_ref()) {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use super::base::IntegratorBase;
|
||||||
use super::RayIntegratorTrait;
|
use super::RayIntegratorTrait;
|
||||||
use crate::core::camera::InitMetadata;
|
use crate::core::camera::InitMetadata;
|
||||||
use crate::core::film::FilmTrait;
|
use crate::core::film::FilmTrait;
|
||||||
use crate::core::image::{Image, ImageIO, ImageMetadata};
|
use crate::core::image::{HostImage, ImageIO, ImageMetadata};
|
||||||
use crate::globals::get_options;
|
use crate::globals::get_options;
|
||||||
use crate::spectra::get_spectra_context;
|
use crate::spectra::get_spectra_context;
|
||||||
use crate::Arena;
|
use crate::Arena;
|
||||||
|
|
@ -108,12 +108,12 @@ pub fn render<T>(
|
||||||
let mut wave_end = 1;
|
let mut wave_end = 1;
|
||||||
let mut next_wave_size = 1;
|
let mut next_wave_size = 1;
|
||||||
|
|
||||||
let mut reference_image: Option<Image> = None;
|
let mut reference_image: Option<HostImage> = None;
|
||||||
let mut mse_out_file: Option<std::fs::File> = None;
|
let mut mse_out_file: Option<std::fs::File> = None;
|
||||||
|
|
||||||
if let Some(ref_path) = &options.mse_reference_image {
|
if let Some(ref_path) = &options.mse_reference_image {
|
||||||
let image_and_metadata =
|
let image_and_metadata =
|
||||||
Image::read(Path::new(&ref_path), None).expect("Could not load image");
|
HostImage::read(Path::new(&ref_path), None).expect("Could not load image");
|
||||||
let image = image_and_metadata.image;
|
let image = image_and_metadata.image;
|
||||||
let metadata = image_and_metadata.metadata;
|
let metadata = image_and_metadata.metadata;
|
||||||
let resolution = image.resolution();
|
let resolution = image.resolution();
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub mod core;
|
pub mod core;
|
||||||
pub mod films;
|
pub mod films;
|
||||||
pub mod filters;
|
|
||||||
pub mod globals;
|
pub mod globals;
|
||||||
pub mod integrators;
|
pub mod integrators;
|
||||||
pub mod lights;
|
pub mod lights;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
use std::path::Path;
|
use crate::core::image::{HostImage, ImageIO};
|
||||||
|
|
||||||
use crate::core::image::{Image, ImageIO};
|
|
||||||
use crate::core::light::lookup_spectrum;
|
use crate::core::light::lookup_spectrum;
|
||||||
use crate::core::spectrum::spectrum_to_photometric;
|
use crate::core::spectrum::spectrum_to_photometric;
|
||||||
use crate::core::texture::FloatTexture;
|
use crate::core::texture::FloatTexture;
|
||||||
|
|
@ -18,6 +16,7 @@ use shared::lights::DiffuseAreaLight;
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::RGBColorSpace;
|
||||||
use shared::utils::Transform;
|
use shared::utils::Transform;
|
||||||
use shared::{Float, PI};
|
use shared::{Float, PI};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
pub fn create(
|
pub fn create(
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
|
|
@ -37,13 +36,13 @@ pub fn create(
|
||||||
let two_sided = params.get_one_bool("twosided", false)?;
|
let two_sided = params.get_one_bool("twosided", false)?;
|
||||||
|
|
||||||
let filename = resolve_filename(¶ms.get_one_string("filename", "")?);
|
let filename = resolve_filename(¶ms.get_one_string("filename", "")?);
|
||||||
let (image, image_color_space): (Option<Image>, Option<RGBColorSpace>) =
|
let (image, image_color_space): (Option<HostImage>, Option<RGBColorSpace>) =
|
||||||
if !filename.is_empty() {
|
if !filename.is_empty() {
|
||||||
if l.is_some() {
|
if l.is_some() {
|
||||||
return Err(anyhow!("{}: both \"L\" and \"filename\" specified", loc));
|
return Err(anyhow!("{}: both \"L\" and \"filename\" specified", loc));
|
||||||
}
|
}
|
||||||
|
|
||||||
let im = Image::read(Path::new(&filename), None)?;
|
let im = HostImage::read(Path::new(&filename), None)?;
|
||||||
|
|
||||||
if im.image.has_any_infinite_pixels() {
|
if im.image.has_any_infinite_pixels() {
|
||||||
return Err(anyhow!("{}: image has infinite pixel values", loc));
|
return Err(anyhow!("{}: image has infinite pixel values", loc));
|
||||||
|
|
@ -100,12 +99,13 @@ pub fn create(
|
||||||
scale *= phi_v / k_e;
|
scale *= phi_v / k_e;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upload alpha texture to GPU and check for constant-zero
|
// Upload alpha texture to GPU and check for null texture
|
||||||
let alpha_ptr = arena.upload(alpha);
|
let alpha_ptr = arena.upload(alpha);
|
||||||
let light_type = if alpha_ptr.is_constant_zero() {
|
let light_type = match alpha_ptr.as_ref() {
|
||||||
LightType::DeltaPosition
|
GPUFloatTexture::Constant(t) if t.evaluate(&TextureEvalContext::default()) == 0.0 => {
|
||||||
} else {
|
LightType::DeltaPosition
|
||||||
LightType::Area
|
}
|
||||||
|
_ => LightType::Area,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mi = match medium {
|
let mi = match medium {
|
||||||
|
|
@ -146,7 +146,7 @@ pub fn create(
|
||||||
area: shape.area(),
|
area: shape.area(),
|
||||||
shape: arena.alloc(*shape),
|
shape: arena.alloc(*shape),
|
||||||
alpha: alpha_ptr,
|
alpha: alpha_ptr,
|
||||||
image: arena.alloc(image),
|
image: arena.upload(image),
|
||||||
colorspace: arena.alloc_opt(image_color_space),
|
colorspace: arena.alloc_opt(image_color_space),
|
||||||
lemit: arena.alloc((*lookup_spectrum(l_for_scale)).clone()),
|
lemit: arena.alloc((*lookup_spectrum(l_for_scale)).clone()),
|
||||||
two_sided,
|
two_sided,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::core::image::{Image, ImageIO};
|
use crate::core::image::{HostImage, ImageIO};
|
||||||
use crate::core::light::lookup_spectrum;
|
use crate::core::light::lookup_spectrum;
|
||||||
use crate::core::spectrum::spectrum_to_photometric;
|
use crate::core::spectrum::spectrum_to_photometric;
|
||||||
use crate::core::texture::FloatTexture;
|
use crate::core::texture::FloatTexture;
|
||||||
|
|
@ -41,10 +41,10 @@ pub fn create(
|
||||||
.expect("Could not retrieve spectrum");
|
.expect("Could not retrieve spectrum");
|
||||||
let mut scale = params.get_one_float("scale", 1.)?;
|
let mut scale = params.get_one_float("scale", 1.)?;
|
||||||
let filename = resolve_filename(¶ms.get_one_string("filename", "")?);
|
let filename = resolve_filename(¶ms.get_one_string("filename", "")?);
|
||||||
let image: Ptr<Image> = if filename.is_empty() {
|
let image: Ptr<HostImage> = if filename.is_empty() {
|
||||||
Ptr::null()
|
Ptr::null()
|
||||||
} else {
|
} else {
|
||||||
let im = Image::read(Path::new(&filename), None)
|
let im = HostImage::read(Path::new(&filename), None)
|
||||||
.map_err(|e| anyhow!("could not load image '{}': {}", filename, e))?;
|
.map_err(|e| anyhow!("could not load image '{}': {}", filename, e))?;
|
||||||
|
|
||||||
let loaded = im.image;
|
let loaded = im.image;
|
||||||
|
|
@ -117,7 +117,7 @@ pub fn create(
|
||||||
Ok(Light::Goniometric(specific))
|
Ok(Light::Goniometric(specific))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_to_luminance_image(image: &Image, filename: &str, loc: &FileLoc) -> Result<Image> {
|
fn convert_to_luminance_image(image: &HostImage, filename: &str, loc: &FileLoc) -> Result<HostImage> {
|
||||||
let res = image.resolution();
|
let res = image.resolution();
|
||||||
let rgb_desc = image.get_channel_desc(&["R", "G", "B"]);
|
let rgb_desc = image.get_channel_desc(&["R", "G", "B"]);
|
||||||
let y_desc = image.get_channel_desc(&["Y"]);
|
let y_desc = image.get_channel_desc(&["Y"]);
|
||||||
|
|
@ -142,7 +142,7 @@ fn convert_to_luminance_image(image: &Image, filename: &str, loc: &FileLoc) -> R
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Image::from_f32(y_pixels, res, &["Y"].to_vec()))
|
Ok(HostImage::from_f32(&y_pixels, res, &["Y"].to_vec()))
|
||||||
}
|
}
|
||||||
|
|
||||||
(Err(_), Ok(_)) => {
|
(Err(_), Ok(_)) => {
|
||||||
|
|
@ -158,7 +158,7 @@ fn convert_to_luminance_image(image: &Image, filename: &str, loc: &FileLoc) -> R
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_emissive_power(image: &Image) -> Float {
|
fn compute_emissive_power(image: &HostImage) -> Float {
|
||||||
let res = image.resolution();
|
let res = image.resolution();
|
||||||
let mut sum_y = 0.0;
|
let mut sum_y = 0.0;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,11 @@ use shared::core::camera::CameraTransform;
|
||||||
use shared::core::geometry::{cos_theta, Bounds2f, Frame, Point2f, Point2i, Point3f, VectorLike};
|
use shared::core::geometry::{cos_theta, Bounds2f, Frame, Point2f, Point2i, Point3f, VectorLike};
|
||||||
use shared::core::image::WrapMode;
|
use shared::core::image::WrapMode;
|
||||||
use shared::core::light::{Light, LightBase, LightType};
|
use shared::core::light::{Light, LightBase, LightType};
|
||||||
use shared::core::medium::{Medium, MediumInterface};
|
use shared::core::medium::Medium;
|
||||||
use shared::core::spectrum::Spectrum;
|
use shared::core::spectrum::Spectrum;
|
||||||
use shared::core::texture::SpectrumType;
|
use shared::core::texture::SpectrumType;
|
||||||
use shared::lights::{ImageInfiniteLight, PortalInfiniteLight, UniformInfiniteLight};
|
use shared::lights::{ImageInfiniteLight, PortalInfiniteLight, UniformInfiniteLight};
|
||||||
use shared::spectra::{DenselySampledSpectrum, RGBColorSpace};
|
use shared::spectra::RGBColorSpace;
|
||||||
use shared::utils::math::{equal_area_sphere_to_square, equal_area_square_to_sphere};
|
use shared::utils::math::{equal_area_sphere_to_square, equal_area_square_to_sphere};
|
||||||
use shared::utils::sampling::{PiecewiseConstant2D, WindowedPiecewiseConstant2D};
|
use shared::utils::sampling::{PiecewiseConstant2D, WindowedPiecewiseConstant2D};
|
||||||
use shared::{Float, Ptr, Transform, PI};
|
use shared::{Float, Ptr, Transform, PI};
|
||||||
|
|
@ -63,7 +63,7 @@ pub fn create(
|
||||||
}
|
}
|
||||||
|
|
||||||
let lemit = lookup_spectrum(&spectrum);
|
let lemit = lookup_spectrum(&spectrum);
|
||||||
let light = UniformInfiniteLight::new(render_from_light, scale, arena.alloc(*lemit));
|
let light = UniformInfiniteLight::new(render_from_light, scale, arena.alloc_arc(lemit));
|
||||||
return Ok(Light::InfiniteUniform(light));
|
return Ok(Light::InfiniteUniform(light));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,7 +96,7 @@ pub fn create(
|
||||||
fn create_image_light(
|
fn create_image_light(
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
scale: Float,
|
scale: Float,
|
||||||
image: Image,
|
image: HostImage,
|
||||||
image_cs: RGBColorSpace,
|
image_cs: RGBColorSpace,
|
||||||
arena: &Arena,
|
arena: &Arena,
|
||||||
) -> Result<Light> {
|
) -> Result<Light> {
|
||||||
|
|
@ -110,7 +110,6 @@ fn create_image_light(
|
||||||
let (n_u, n_v) = (res.x() as usize, res.y() as usize);
|
let (n_u, n_v) = (res.x() as usize, res.y() as usize);
|
||||||
|
|
||||||
// Extract luminance data
|
// Extract luminance data
|
||||||
let image_ptr = image.upload(arena);
|
|
||||||
let value = ℑ
|
let value = ℑ
|
||||||
let mut data: Vec<Float> = (0..n_v)
|
let mut data: Vec<Float> = (0..n_v)
|
||||||
.flat_map(|v| {
|
.flat_map(|v| {
|
||||||
|
|
@ -139,7 +138,7 @@ fn create_image_light(
|
||||||
let light = ImageInfiniteLight::new(
|
let light = ImageInfiniteLight::new(
|
||||||
render_from_light,
|
render_from_light,
|
||||||
scale,
|
scale,
|
||||||
image_ptr,
|
arena.upload(image),
|
||||||
arena.alloc(image_cs),
|
arena.alloc(image_cs),
|
||||||
arena.alloc(distrib),
|
arena.alloc(distrib),
|
||||||
arena.alloc(compensated_distrib),
|
arena.alloc(compensated_distrib),
|
||||||
|
|
@ -151,7 +150,7 @@ fn create_image_light(
|
||||||
fn create_portal_light(
|
fn create_portal_light(
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
scale: Float,
|
scale: Float,
|
||||||
image: Image,
|
image: HostImage,
|
||||||
image_cs: RGBColorSpace,
|
image_cs: RGBColorSpace,
|
||||||
portal_points: &[Point3f],
|
portal_points: &[Point3f],
|
||||||
camera_transform: CameraTransform,
|
camera_transform: CameraTransform,
|
||||||
|
|
@ -198,7 +197,7 @@ fn create_portal_light(
|
||||||
let light = PortalInfiniteLight::new(
|
let light = PortalInfiniteLight::new(
|
||||||
render_from_light,
|
render_from_light,
|
||||||
scale,
|
scale,
|
||||||
arena.alloc(remapped),
|
arena.upload(remapped),
|
||||||
arena.alloc(image_cs),
|
arena.alloc(image_cs),
|
||||||
portal,
|
portal,
|
||||||
portal_frame,
|
portal_frame,
|
||||||
|
|
@ -230,10 +229,10 @@ fn validate_and_build_portal_frame(portal: &[Point3f; 4], loc: &FileLoc) -> Resu
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remap_image_through_portal(
|
fn remap_image_through_portal(
|
||||||
image: &Image,
|
image: &HostImage,
|
||||||
render_from_light: &Transform,
|
render_from_light: &Transform,
|
||||||
portal_frame: &Frame,
|
portal_frame: &Frame,
|
||||||
) -> Image {
|
) -> HostImage {
|
||||||
let res = image.resolution();
|
let res = image.resolution();
|
||||||
let (width, height) = (res.x() as usize, res.y() as usize);
|
let (width, height) = (res.x() as usize, res.y() as usize);
|
||||||
|
|
||||||
|
|
@ -263,7 +262,7 @@ fn remap_image_through_portal(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Image::from_f32(pixels, res, &["R", "G", "B"])
|
HostImage::from_f32(&pixels, res, &["R", "G", "B"])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_image(
|
fn load_image(
|
||||||
|
|
@ -271,16 +270,16 @@ fn load_image(
|
||||||
l: &[Spectrum],
|
l: &[Spectrum],
|
||||||
colorspace: &RGBColorSpace,
|
colorspace: &RGBColorSpace,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
) -> Result<(Image, RGBColorSpace)> {
|
) -> Result<(HostImage, RGBColorSpace)> {
|
||||||
if filename.is_empty() {
|
if filename.is_empty() {
|
||||||
let stdspec = get_spectra_context();
|
let stdspec = get_spectra_context();
|
||||||
let rgb = l[0].to_rgb(colorspace, &stdspec);
|
let rgb = l[0].to_rgb(colorspace, &stdspec);
|
||||||
let image =
|
let image =
|
||||||
Image::new_constant(Point2i::new(1, 1), &["R", "G", "B"], &[rgb.r, rgb.g, rgb.b]);
|
HostImage::new_constant(Point2i::new(1, 1), &["R", "G", "B"], &[rgb.r, rgb.g, rgb.b]);
|
||||||
return Ok((image, colorspace.clone()));
|
return Ok((image, colorspace.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let im = Image::read(Path::new(filename), None)
|
let im = HostImage::read(Path::new(filename), None)
|
||||||
.map_err(|e| anyhow!("failed to load '{}': {}", filename, e))?;
|
.map_err(|e| anyhow!("failed to load '{}': {}", filename, e))?;
|
||||||
|
|
||||||
if im.image.has_any_infinite_pixels() || im.image.has_any_nan_pixels() {
|
if im.image.has_any_infinite_pixels() || im.image.has_any_nan_pixels() {
|
||||||
|
|
@ -296,7 +295,7 @@ fn load_image(
|
||||||
Ok((im.image.select_channels(&desc), cs))
|
Ok((im.image.select_channels(&desc), cs))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_hemisphere_illuminance(image: &Image, cs: &RGBColorSpace) -> Float {
|
fn compute_hemisphere_illuminance(image: &HostImage, cs: &RGBColorSpace) -> Float {
|
||||||
let lum = cs.luminance_vector();
|
let lum = cs.luminance_vector();
|
||||||
let res = image.resolution();
|
let res = image.resolution();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::core::image::{Image, ImageIO};
|
use crate::core::image::{HostImage, ImageIO};
|
||||||
use crate::core::spectrum::spectrum_to_photometric;
|
use crate::core::spectrum::spectrum_to_photometric;
|
||||||
use crate::core::texture::FloatTexture;
|
use crate::core::texture::FloatTexture;
|
||||||
use crate::utils::sampling::PiecewiseConstant2D;
|
use crate::utils::sampling::PiecewiseConstant2D;
|
||||||
use crate::utils::resolve_filename;
|
use crate::utils::resolve_filename;
|
||||||
use crate::{Arena, FileLoc, ParameterDictionary};
|
use crate::{Arena, FileLoc, ParameterDictionary, ArenaUpload};
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{Result, anyhow};
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
use shared::core::geometry::{
|
use shared::core::geometry::{
|
||||||
|
|
@ -41,7 +41,7 @@ pub fn create(
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let im = Image::read(Path::new(&filename), None)
|
let im = HostImage::read(Path::new(&filename), None)
|
||||||
.map_err(|e| anyhow!("{}: could not load image '{}': {}", loc, filename, e))?;
|
.map_err(|e| anyhow!("{}: could not load image '{}': {}", loc, filename, e))?;
|
||||||
|
|
||||||
if im.image.has_any_infinite_pixels() {
|
if im.image.has_any_infinite_pixels() {
|
||||||
|
|
@ -125,9 +125,9 @@ pub fn create(
|
||||||
|
|
||||||
let specific = ProjectionLight {
|
let specific = ProjectionLight {
|
||||||
base,
|
base,
|
||||||
image: image.upload(arena),
|
image: arena.upload(image),
|
||||||
image_color_space: colorspace.upload(arena),
|
image_color_space: arena.alloc(colorspace),
|
||||||
distrib: distrib.upload(arena),
|
distrib: arena.alloc(distrib),
|
||||||
screen_bounds,
|
screen_bounds,
|
||||||
screen_from_light,
|
screen_from_light,
|
||||||
light_from_screen,
|
light_from_screen,
|
||||||
|
|
@ -150,7 +150,7 @@ fn compute_screen_bounds(aspect: Float) -> Bounds2f {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_emissive_power(image: &Image, colorspace: &RGBColorSpace, fov: Float) -> Float {
|
fn compute_emissive_power(image: &HostImage, colorspace: &RGBColorSpace, fov: Float) -> Float {
|
||||||
let res = image.resolution();
|
let res = image.resolution();
|
||||||
let aspect = res.x() as Float / res.y() as Float;
|
let aspect = res.x() as Float / res.y() as Float;
|
||||||
let screen_bounds = compute_screen_bounds(aspect);
|
let screen_bounds = compute_screen_bounds(aspect);
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ use shared::core::texture::SpectrumType;
|
||||||
use shared::lights::SpotLight;
|
use shared::lights::SpotLight;
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::RGBColorSpace;
|
||||||
use shared::utils::math::radians;
|
use shared::utils::math::radians;
|
||||||
use shared::utils::{Ptr, Transform};
|
|
||||||
use shared::{Float, Ptr, Transform, PI};
|
use shared::{Float, Ptr, Transform, PI};
|
||||||
|
|
||||||
trait CreateSpotLight {
|
trait CreateSpotLight {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use crate::core::texture::SpectrumTexture;
|
||||||
use crate::globals::get_options;
|
use crate::globals::get_options;
|
||||||
use crate::spectra::data::get_named_spectrum;
|
use crate::spectra::data::get_named_spectrum;
|
||||||
use crate::utils::TextureParameterDictionary;
|
use crate::utils::TextureParameterDictionary;
|
||||||
use crate::{Arena, FileLoc, Upload, ArenaUpload};
|
use crate::{Arena, FileLoc, ArenaUpload};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use shared::core::material::Material;
|
use shared::core::material::Material;
|
||||||
use shared::core::spectrum::Spectrum;
|
use shared::core::spectrum::Spectrum;
|
||||||
|
|
@ -64,7 +64,7 @@ impl CreateMaterial for CoatedDiffuseMaterial {
|
||||||
arena.upload(g),
|
arena.upload(g),
|
||||||
arena.upload(displacement),
|
arena.upload(displacement),
|
||||||
arena.alloc(eta),
|
arena.alloc(eta),
|
||||||
arena.alloc(normal_map),
|
arena.upload(normal_map),
|
||||||
remap_roughness,
|
remap_roughness,
|
||||||
max_depth as u32,
|
max_depth as u32,
|
||||||
n_samples as u32,
|
n_samples as u32,
|
||||||
|
|
@ -154,7 +154,7 @@ impl CreateMaterial for CoatedConductorMaterial {
|
||||||
let remap_roughness = parameters.get_one_bool("remaproughness", true)?;
|
let remap_roughness = parameters.get_one_bool("remaproughness", true)?;
|
||||||
|
|
||||||
let material = Self::new(
|
let material = Self::new(
|
||||||
arena.upload(displacement)
|
arena.upload(displacement),
|
||||||
arena.upload(interface_u_roughness),
|
arena.upload(interface_u_roughness),
|
||||||
arena.upload(interface_v_roughness),
|
arena.upload(interface_v_roughness),
|
||||||
arena.upload(thickness),
|
arena.upload(thickness),
|
||||||
|
|
@ -165,7 +165,7 @@ impl CreateMaterial for CoatedConductorMaterial {
|
||||||
arena.upload(conductor_eta),
|
arena.upload(conductor_eta),
|
||||||
arena.upload(k),
|
arena.upload(k),
|
||||||
arena.upload(reflectance),
|
arena.upload(reflectance),
|
||||||
arena.alloc(normal_map),
|
arena.upload(normal_map),
|
||||||
arena.alloc(interface_eta),
|
arena.alloc(interface_eta),
|
||||||
max_depth as u32,
|
max_depth as u32,
|
||||||
n_samples as u32,
|
n_samples as u32,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use crate::core::material::CreateMaterial;
|
||||||
use crate::core::texture::SpectrumTexture;
|
use crate::core::texture::SpectrumTexture;
|
||||||
use crate::spectra::get_colorspace_device;
|
use crate::spectra::get_colorspace_device;
|
||||||
use crate::utils::TextureParameterDictionary;
|
use crate::utils::TextureParameterDictionary;
|
||||||
use crate::{Arena, FileLoc, Upload, ArenaUpload};
|
use crate::{Arena, ArenaUpload, FileLoc};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use shared::bxdfs::HairBxDF;
|
use shared::bxdfs::HairBxDF;
|
||||||
use shared::core::material::Material;
|
use shared::core::material::Material;
|
||||||
|
|
@ -60,7 +60,6 @@ impl CreateMaterial for HairMaterial {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl CreateMaterial for SubsurfaceMaterial {
|
impl CreateMaterial for SubsurfaceMaterial {
|
||||||
fn create(
|
fn create(
|
||||||
_parameters: &TextureParameterDictionary,
|
_parameters: &TextureParameterDictionary,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::core::image::{Image, ImageIO};
|
use crate::core::image::{HostImage, ImageIO};
|
||||||
use crate::core::shape::{CreateShape, ALL_BILINEAR_MESHES};
|
use crate::core::shape::{CreateShape, ALL_BILINEAR_MESHES};
|
||||||
use crate::core::texture::FloatTexture;
|
use crate::core::texture::FloatTexture;
|
||||||
use crate::utils::sampling::PiecewiseConstant2D;
|
use crate::utils::sampling::PiecewiseConstant2D;
|
||||||
|
|
@ -6,7 +6,7 @@ use crate::{Arena, FileLoc, ParameterDictionary};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use shared::core::shape::Shape;
|
use shared::core::shape::Shape;
|
||||||
use shared::shapes::BilinearPatchShape;
|
use shared::shapes::{BilinearPatchMesh, BilinearPatchShape};
|
||||||
use shared::{Ptr, Transform};
|
use shared::{Ptr, Transform};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
@ -92,7 +92,7 @@ impl CreateShape for BilinearPatchShape {
|
||||||
);
|
);
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let im = Image::read(Path::new(&filename), None)?;
|
let im = HostImage::read(Path::new(&filename), None)?;
|
||||||
let mut img = im.image;
|
let mut img = im.image;
|
||||||
img.flip_y();
|
img.flip_y();
|
||||||
Some(PiecewiseConstant2D::from_image(&img))
|
Some(PiecewiseConstant2D::from_image(&img))
|
||||||
|
|
@ -104,10 +104,10 @@ impl CreateShape for BilinearPatchShape {
|
||||||
let host = BilinearPatchMesh::new(
|
let host = BilinearPatchMesh::new(
|
||||||
&render_from_object,
|
&render_from_object,
|
||||||
reverse_orientation,
|
reverse_orientation,
|
||||||
vertex_indices,
|
&vertex_indices,
|
||||||
p,
|
&p,
|
||||||
n,
|
&n,
|
||||||
uv,
|
&uv,
|
||||||
image_dist,
|
image_dist,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -116,8 +116,8 @@ impl CreateShape for BilinearPatchShape {
|
||||||
// let mesh_index = global_store.len() as u32;
|
// let mesh_index = global_store.len() as u32;
|
||||||
global_store.push(host_arc.clone());
|
global_store.push(host_arc.clone());
|
||||||
drop(global_store);
|
drop(global_store);
|
||||||
let n_patches = host_arc.device.n_patches;
|
let n_patches = host_arc.n_patches;
|
||||||
let mesh_ptr = Ptr::from(&host_arc.device);
|
let mesh_ptr = arena.alloc_arc(host_arc);
|
||||||
let mut shapes = Vec::with_capacity(n_patches as usize);
|
let mut shapes = Vec::with_capacity(n_patches as usize);
|
||||||
for i in 0..n_patches as i32 {
|
for i in 0..n_patches as i32 {
|
||||||
shapes.push(arena.alloc(Shape::BilinearPatch(BilinearPatchShape {
|
shapes.push(arena.alloc(Shape::BilinearPatch(BilinearPatchShape {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
use crate::utils::sampling::PiecewiseConstant2D;
|
use crate::utils::sampling::PiecewiseConstant2D;
|
||||||
use crate::Arena;
|
|
||||||
use anyhow::{bail, Context, Result as AnyResult};
|
use anyhow::{bail, Context, Result as AnyResult};
|
||||||
use ply_rs::parser::Parser;
|
use ply_rs::parser::Parser;
|
||||||
use ply_rs::ply::{DefaultElement, Property};
|
use ply_rs::ply::{DefaultElement, Property};
|
||||||
use shared::core::geometry::{Normal3f, Point2f, Point3f, Vector3f, VectorLike};
|
use shared::core::geometry::{Normal3f, Point2f, Point3f, VectorLike};
|
||||||
use shared::shapes::mesh::{BilinearPatchMesh, TriangleMesh};
|
use shared::shapes::mesh::TriangleMesh;
|
||||||
use shared::{Ptr, Transform};
|
use shared::Transform;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
|
@ -269,11 +268,13 @@ impl TriQuadMesh {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ReadTriangleMesh {
|
pub trait ReadTriangleMesh {
|
||||||
pub fn from_ply<P: AsRef<Path>>(
|
fn from_ply<P: AsRef<Path>>(
|
||||||
filename: P,
|
filename: P,
|
||||||
render_from_object: &Transform,
|
render_from_object: &Transform,
|
||||||
reverse_orientation: bool,
|
reverse_orientation: bool,
|
||||||
) -> AnyResult<Self>;
|
) -> AnyResult<Self>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReadTriangleMesh for TriangleMesh {
|
impl ReadTriangleMesh for TriangleMesh {
|
||||||
|
|
|
||||||
|
|
@ -99,8 +99,8 @@ impl CreateShape for TriangleShape {
|
||||||
// let mesh_index = global_store.len() as u32;
|
// let mesh_index = global_store.len() as u32;
|
||||||
global_store.push(host_arc.clone());
|
global_store.push(host_arc.clone());
|
||||||
drop(global_store);
|
drop(global_store);
|
||||||
let n_patches = host_arc.device.n_triangles;
|
let n_patches = host_arc.n_triangles;
|
||||||
let mesh_ptr = Ptr::from(&host_arc.device);
|
let mesh_ptr = arena.alloc_arc(host_arc);
|
||||||
let mut shapes = Vec::with_capacity(n_patches as usize);
|
let mut shapes = Vec::with_capacity(n_patches as usize);
|
||||||
for i in 0..n_patches {
|
for i in 0..n_patches {
|
||||||
shapes.push(arena.alloc(Shape::Triangle(TriangleShape {
|
shapes.push(arena.alloc(Shape::Triangle(TriangleShape {
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,7 @@ use shared::core::spectrum::{Spectrum, StandardSpectra};
|
||||||
use shared::spectra::cie::{CIE_D65, CIE_X, CIE_Y, CIE_Z};
|
use shared::spectra::cie::{CIE_D65, CIE_X, CIE_Y, CIE_Z};
|
||||||
use shared::spectra::{DenselySampledSpectrum, DeviceStandardColorSpaces, RGBColorSpace};
|
use shared::spectra::{DenselySampledSpectrum, DeviceStandardColorSpaces, RGBColorSpace};
|
||||||
use shared::Ptr;
|
use shared::Ptr;
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, OnceLock, LazyLock};
|
||||||
use std::sync::LazyLock;
|
|
||||||
|
|
||||||
pub mod colorspace;
|
pub mod colorspace;
|
||||||
pub mod data;
|
pub mod data;
|
||||||
|
|
@ -68,7 +67,7 @@ pub static DCI_P3: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| {
|
||||||
Arc::new(RGBColorSpace::new(r, g, b, &illum, &table_ptr))
|
Arc::new(RGBColorSpace::new(r, g, b, &illum, &table_ptr))
|
||||||
});
|
});
|
||||||
|
|
||||||
pub static REC2020: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
|
pub static REC2020: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| {
|
||||||
let illum = get_d65_illuminant_buffer();
|
let illum = get_d65_illuminant_buffer();
|
||||||
let r = Point2f::new(0.708, 0.292);
|
let r = Point2f::new(0.708, 0.292);
|
||||||
let g = Point2f::new(0.170, 0.797);
|
let g = Point2f::new(0.170, 0.797);
|
||||||
|
|
@ -77,7 +76,7 @@ pub static REC2020: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
|
||||||
Arc::new(RGBColorSpace::new(r, g, b, &illum, &table_ptr))
|
Arc::new(RGBColorSpace::new(r, g, b, &illum, &table_ptr))
|
||||||
});
|
});
|
||||||
|
|
||||||
pub static ACES: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
|
pub static ACES: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| {
|
||||||
let illum = get_d65_illuminant_buffer();
|
let illum = get_d65_illuminant_buffer();
|
||||||
let r = Point2f::new(0.7347, 0.2653);
|
let r = Point2f::new(0.7347, 0.2653);
|
||||||
let g = Point2f::new(0.0000, 1.0000);
|
let g = Point2f::new(0.0000, 1.0000);
|
||||||
|
|
@ -88,10 +87,10 @@ pub static ACES: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct StandardColorSpaces {
|
pub struct StandardColorSpaces {
|
||||||
pub srgb: Arc<RGBColorSpaceData>,
|
pub srgb: Arc<RGBColorSpace>,
|
||||||
pub dci_p3: Arc<RGBColorSpaceData>,
|
pub dci_p3: Arc<RGBColorSpace>,
|
||||||
pub rec2020: Arc<RGBColorSpaceData>,
|
pub rec2020: Arc<RGBColorSpace>,
|
||||||
pub aces2065_1: Arc<RGBColorSpaceData>,
|
pub aces2065_1: Arc<RGBColorSpace>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StandardColorSpaces {
|
impl StandardColorSpaces {
|
||||||
|
|
@ -141,3 +140,4 @@ pub fn default_colorspace_ref() -> &'static RGBColorSpace {
|
||||||
pub fn default_illuminant() -> Spectrum {
|
pub fn default_illuminant() -> Spectrum {
|
||||||
Spectrum::Dense(default_colorspace().illuminant)
|
Spectrum::Dense(default_colorspace().illuminant)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,6 @@ use anyhow::Result;
|
||||||
use shared::core::geometry::{Vector3f, VectorLike};
|
use shared::core::geometry::{Vector3f, VectorLike};
|
||||||
use shared::core::texture::{SpectrumType, TextureEvalContext};
|
use shared::core::texture::{SpectrumType, TextureEvalContext};
|
||||||
use shared::spectra::{SampledSpectrum, SampledWavelengths};
|
use shared::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use shared::textures::{
|
|
||||||
GPUFloatDirectionMixTexture, GPUFloatMixTexture, GPUSpectrumDirectionMixTexture,
|
|
||||||
GPUSpectrumMixTexture,
|
|
||||||
};
|
|
||||||
use shared::utils::Transform;
|
use shared::utils::Transform;
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ use crate::Arena;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use shared::core::texture::{SpectrumType, TextureEvalContext};
|
use shared::core::texture::{SpectrumType, TextureEvalContext};
|
||||||
use shared::spectra::{SampledSpectrum, SampledWavelengths};
|
use shared::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use shared::textures::{GPUFloatScaledTexture, GPUSpectrumScaledTexture};
|
|
||||||
use shared::utils::Transform;
|
use shared::utils::Transform;
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,13 @@ impl<A: GpuAllocator> Arena<A> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn alloc_arc<T: Clone>(&self, value: Arc<T>) -> Ptr<T> {
|
||||||
|
match Arc::try_unwrap(value) {
|
||||||
|
Ok(inner) => self.alloc(inner),
|
||||||
|
Err(arc) => self.alloc((*arc).clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn alloc_slice<T: Copy>(&self, values: &[T]) -> (Ptr<T>, usize) {
|
pub fn alloc_slice<T: Copy>(&self, values: &[T]) -> (Ptr<T>, usize) {
|
||||||
let mut bump = self.bump.lock();
|
let mut bump = self.bump.lock();
|
||||||
let (ptr, len) = bump.alloc_slice(values);
|
let (ptr, len) = bump.alloc_slice(values);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use crate::core::image::{Image, ImageIO};
|
use crate::core::image::{HostImage, ImageIO};
|
||||||
use shared::Float;
|
|
||||||
use shared::core::color::{ColorEncoding, RGB};
|
use shared::core::color::{ColorEncoding, RGB};
|
||||||
use shared::core::geometry::{Point2f, Point2i, Vector2f, VectorLike};
|
use shared::core::geometry::{Point2f, Point2i, Vector2f, VectorLike};
|
||||||
use shared::core::image::{WrapMode, WrapMode2D};
|
use shared::core::image::{WrapMode, WrapMode2D};
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::RGBColorSpace;
|
||||||
use shared::utils::math::{lerp, safe_sqrt, square};
|
use shared::utils::math::{lerp, safe_sqrt, square};
|
||||||
|
use shared::Float;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::ops::{Add, Mul, Sub};
|
use std::ops::{Add, Mul, Sub};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
@ -69,18 +69,18 @@ pub trait MIPMapSample:
|
||||||
Copy + Add<Output = Self> + Sub<Output = Self> + Mul<Float, Output = Self> + std::fmt::Debug
|
Copy + Add<Output = Self> + Sub<Output = Self> + Mul<Float, Output = Self> + std::fmt::Debug
|
||||||
{
|
{
|
||||||
fn zero() -> Self;
|
fn zero() -> Self;
|
||||||
fn sample_bilerp(image: &Image, st: Point2f, wrap: WrapMode2D) -> Self;
|
fn sample_bilerp(image: &HostImage, st: Point2f, wrap: WrapMode2D) -> Self;
|
||||||
fn sample_texel(image: &Image, st: Point2i, wrap: WrapMode2D) -> Self;
|
fn sample_texel(image: &HostImage, st: Point2i, wrap: WrapMode2D) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MIPMapSample for Float {
|
impl MIPMapSample for Float {
|
||||||
fn zero() -> Self {
|
fn zero() -> Self {
|
||||||
0.
|
0.
|
||||||
}
|
}
|
||||||
fn sample_bilerp(image: &Image, st: Point2f, wrap: WrapMode2D) -> Self {
|
fn sample_bilerp(image: &HostImage, st: Point2f, wrap: WrapMode2D) -> Self {
|
||||||
image.bilerp_channel_with_wrap(st, 0, wrap)
|
image.bilerp_channel_with_wrap(st, 0, wrap)
|
||||||
}
|
}
|
||||||
fn sample_texel(image: &Image, st: Point2i, wrap: WrapMode2D) -> Self {
|
fn sample_texel(image: &HostImage, st: Point2i, wrap: WrapMode2D) -> Self {
|
||||||
image.get_channel_with_wrap(st, 0, wrap)
|
image.get_channel_with_wrap(st, 0, wrap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -89,7 +89,7 @@ impl MIPMapSample for RGB {
|
||||||
fn zero() -> Self {
|
fn zero() -> Self {
|
||||||
RGB::new(0., 0., 0.)
|
RGB::new(0., 0., 0.)
|
||||||
}
|
}
|
||||||
fn sample_bilerp(image: &Image, st: Point2f, wrap: WrapMode2D) -> Self {
|
fn sample_bilerp(image: &HostImage, st: Point2f, wrap: WrapMode2D) -> Self {
|
||||||
let nc = image.n_channels();
|
let nc = image.n_channels();
|
||||||
if nc >= 3 {
|
if nc >= 3 {
|
||||||
let r = image.bilerp_channel_with_wrap(st, 0, wrap);
|
let r = image.bilerp_channel_with_wrap(st, 0, wrap);
|
||||||
|
|
@ -101,7 +101,7 @@ impl MIPMapSample for RGB {
|
||||||
RGB::new(v, v, v)
|
RGB::new(v, v, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn sample_texel(image: &Image, st: Point2i, wrap: WrapMode2D) -> Self {
|
fn sample_texel(image: &HostImage, st: Point2i, wrap: WrapMode2D) -> Self {
|
||||||
let nc = image.n_channels();
|
let nc = image.n_channels();
|
||||||
if nc >= 3 {
|
if nc >= 3 {
|
||||||
let r = image.get_channel_with_wrap(st, 0, wrap);
|
let r = image.get_channel_with_wrap(st, 0, wrap);
|
||||||
|
|
@ -117,7 +117,7 @@ impl MIPMapSample for RGB {
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct MIPMap {
|
pub struct MIPMap {
|
||||||
pub pyramid: Vec<Image>,
|
pub pyramid: Vec<HostImage>,
|
||||||
pub color_space: Option<RGBColorSpace>,
|
pub color_space: Option<RGBColorSpace>,
|
||||||
pub wrap_mode: WrapMode,
|
pub wrap_mode: WrapMode,
|
||||||
pub options: MIPMapFilterOptions,
|
pub options: MIPMapFilterOptions,
|
||||||
|
|
@ -127,12 +127,12 @@ pub struct MIPMap {
|
||||||
|
|
||||||
impl MIPMap {
|
impl MIPMap {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
image: Image,
|
image: HostImage,
|
||||||
color_space: Option<RGBColorSpace>,
|
color_space: Option<RGBColorSpace>,
|
||||||
wrap_mode: WrapMode,
|
wrap_mode: WrapMode,
|
||||||
options: MIPMapFilterOptions,
|
options: MIPMapFilterOptions,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let pyramid = Image::generate_pyramid(image, wrap_mode);
|
let pyramid = HostImage::generate_pyramid(image, wrap_mode);
|
||||||
Self {
|
Self {
|
||||||
pyramid,
|
pyramid,
|
||||||
color_space,
|
color_space,
|
||||||
|
|
@ -160,11 +160,11 @@ impl MIPMap {
|
||||||
self.color_space.clone()
|
self.color_space.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_level(&self, level: usize) -> &Image {
|
pub fn get_level(&self, level: usize) -> &HostImage {
|
||||||
&self.pyramid[level]
|
&self.pyramid[level]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn base_image(&self) -> &Image {
|
pub fn base_image(&self) -> &HostImage {
|
||||||
&self.pyramid[0]
|
&self.pyramid[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -321,7 +321,7 @@ impl MIPMap {
|
||||||
wrap_mode: WrapMode,
|
wrap_mode: WrapMode,
|
||||||
encoding: ColorEncoding,
|
encoding: ColorEncoding,
|
||||||
) -> Result<MIPMap, ()> {
|
) -> Result<MIPMap, ()> {
|
||||||
let image_and_metadata = Image::read(filename, Some(encoding)).unwrap();
|
let image_and_metadata = HostImage::read(filename, Some(encoding)).unwrap();
|
||||||
let image = image_and_metadata.image;
|
let image = image_and_metadata.image;
|
||||||
Ok(MIPMap::new(
|
Ok(MIPMap::new(
|
||||||
image,
|
image,
|
||||||
|
|
@ -345,7 +345,7 @@ impl MIPMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "cuda")]
|
#[cfg(feature = "cuda")]
|
||||||
fn create_cuda_texture(pyramid: &[Image], wrap_mode: WrapMode) -> u64 {
|
fn create_cuda_texture(pyramid: &[HostImage], wrap_mode: WrapMode) -> u64 {
|
||||||
use cuda_runtime_sys::*;
|
use cuda_runtime_sys::*;
|
||||||
|
|
||||||
let base = &pyramid[0];
|
let base = &pyramid[0];
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ pub mod mipmap;
|
||||||
pub mod parallel;
|
pub mod parallel;
|
||||||
pub mod parameters;
|
pub mod parameters;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod sampling;
|
|
||||||
pub mod strings;
|
pub mod strings;
|
||||||
pub mod upload;
|
pub mod upload;
|
||||||
|
|
||||||
|
|
@ -18,6 +17,7 @@ pub use parameters::{
|
||||||
ParameterDictionary, ParsedParameter, ParsedParameterVector, TextureParameterDictionary,
|
ParameterDictionary, ParsedParameter, ParsedParameterVector, TextureParameterDictionary,
|
||||||
};
|
};
|
||||||
pub use strings::*;
|
pub use strings::*;
|
||||||
|
pub use mipmap::{MIPMap, MIPMapFilterOptions};
|
||||||
pub use upload::{Upload, ArenaUpload};
|
pub use upload::{Upload, ArenaUpload};
|
||||||
|
|
||||||
#[cfg(feature = "vulkan")]
|
#[cfg(feature = "vulkan")]
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use shared::spectra::{
|
||||||
PiecewiseLinearSpectrum, RGBAlbedoSpectrum, RGBColorSpace, RGBIlluminantSpectrum,
|
PiecewiseLinearSpectrum, RGBAlbedoSpectrum, RGBColorSpace, RGBIlluminantSpectrum,
|
||||||
RGBUnboundedSpectrum,
|
RGBUnboundedSpectrum,
|
||||||
};
|
};
|
||||||
use shared::Float;
|
use shared::{gvec_from_slice, leak, Float};
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{
|
use std::sync::{
|
||||||
|
|
@ -682,8 +682,8 @@ impl ParameterDictionary {
|
||||||
.unzip();
|
.unzip();
|
||||||
|
|
||||||
vec![Spectrum::Piecewise(leak(PiecewiseLinearSpectrum {
|
vec![Spectrum::Piecewise(leak(PiecewiseLinearSpectrum {
|
||||||
lambdas: gvec_from_slice(lambdas),
|
lambdas: gvec_from_slice(&lambdas),
|
||||||
values: gvec_from_slice(values),
|
values: gvec_from_slice(&values),
|
||||||
count: lambdas.len() as u32,
|
count: lambdas.len() as u32,
|
||||||
}))]
|
}))]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
use crate::core::texture::{FloatTexture, SpectrumTexture};
|
use crate::core::texture::{FloatTexture, SpectrumTexture};
|
||||||
|
use crate::core::image::HostImage;
|
||||||
|
use shared::core::image::Image;
|
||||||
use crate::Arena;
|
use crate::Arena;
|
||||||
use shared::core::texture::{GPUFloatTexture, GPUSpectrumTexture};
|
use shared::core::texture::{GPUFloatTexture, GPUSpectrumTexture};
|
||||||
use shared::textures::*;
|
use shared::textures::*;
|
||||||
|
|
@ -149,6 +151,27 @@ impl Upload for Option<Arc<SpectrumTexture>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Upload for HostImage {
|
||||||
|
type Target = Ptr<Image>;
|
||||||
|
fn upload(self, arena: &Arena) -> Ptr<Image> {
|
||||||
|
arena.alloc(self.inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Upload for Option<HostImage> {
|
||||||
|
type Target = Ptr<Image>;
|
||||||
|
fn upload(self, arena: &Arena) -> Ptr<Image> {
|
||||||
|
arena.alloc_opt(self.map(|h| h.inner))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Upload for Option<Arc<HostImage>> {
|
||||||
|
type Target = Ptr<Image>;
|
||||||
|
fn upload(self, arena: &Arena) -> Ptr<Image> {
|
||||||
|
arena.alloc_opt(self.map(|h| h.as_ref().inner.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait ArenaUpload {
|
pub trait ArenaUpload {
|
||||||
fn upload<T: Upload>(&self, value: T) -> T::Target;
|
fn upload<T: Upload>(&self, value: T) -> T::Target;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue