Continuing cleanup of scene creation, texture ownership, and memory management
This commit is contained in:
parent
64a139d108
commit
640e17110a
44 changed files with 464 additions and 467 deletions
|
|
@ -17,6 +17,7 @@ smallvec = "1.15.1"
|
||||||
cuda_std = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", default-features = false, optional = true }
|
cuda_std = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", default-features = false, optional = true }
|
||||||
half = "2.7.1"
|
half = "2.7.1"
|
||||||
rand = "0.9.2"
|
rand = "0.9.2"
|
||||||
|
anyhow = "1.0.100"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
use_f64 = []
|
use_f64 = []
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ impl RGBFilm {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unsafe { &*self.base.sensor }
|
&*self.base.sensor
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_sample(
|
pub fn add_sample(
|
||||||
|
|
@ -241,7 +241,7 @@ impl GBufferFilm {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unsafe { &*self.base.sensor }
|
&*self.base.sensor
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_sample(
|
pub fn add_sample(
|
||||||
|
|
|
||||||
|
|
@ -501,7 +501,7 @@ pub struct GridMedium {
|
||||||
pub sigma_s_spec: DenselySampledSpectrum,
|
pub sigma_s_spec: DenselySampledSpectrum,
|
||||||
pub density_grid: SampledGrid<Float>,
|
pub density_grid: SampledGrid<Float>,
|
||||||
pub phase: HGPhaseFunction,
|
pub phase: HGPhaseFunction,
|
||||||
pub temperature_grid: SampledGrid<Float>,
|
pub temperature_grid: Option<SampledGrid<Float>>,
|
||||||
pub le_spec: DenselySampledSpectrum,
|
pub le_spec: DenselySampledSpectrum,
|
||||||
pub le_scale: SampledGrid<Float>,
|
pub le_scale: SampledGrid<Float>,
|
||||||
pub is_emissive: bool,
|
pub is_emissive: bool,
|
||||||
|
|
@ -528,8 +528,8 @@ impl MediumTrait for GridMedium {
|
||||||
};
|
};
|
||||||
|
|
||||||
let le = if scale > 0.0 {
|
let le = if scale > 0.0 {
|
||||||
let raw_emission = if self.temperature_grid.is_valid() {
|
let raw_emission = if let Some(temp_grid) = &self.temperature_grid {
|
||||||
let temp = self.temperature_grid.lookup(p);
|
let temp = temp_grid.lookup(p);
|
||||||
BlackbodySpectrum::new(temp).sample(lambda)
|
BlackbodySpectrum::new(temp).sample(lambda)
|
||||||
} else {
|
} else {
|
||||||
self.le_spec.sample(lambda)
|
self.le_spec.sample(lambda)
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ use crate::{Float, PI};
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct GoniometricLight {
|
pub struct GoniometricLight {
|
||||||
pub base: LightBase,
|
pub base: LightBase,
|
||||||
pub iemit: DenselySampledSpectrum,
|
pub iemit: Ptr<DenselySampledSpectrum>,
|
||||||
pub scale: Float,
|
pub scale: Float,
|
||||||
pub image: Ptr<DeviceImage>,
|
pub image: Ptr<DeviceImage>,
|
||||||
pub distrib: Ptr<DevicePiecewiseConstant2D>,
|
pub distrib: Ptr<DevicePiecewiseConstant2D>,
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use crate::core::pbrt::Float;
|
||||||
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum};
|
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum};
|
||||||
use crate::utils::math::SquareMatrix3f;
|
use crate::utils::math::SquareMatrix3f;
|
||||||
use crate::utils::ptr::Ptr;
|
use crate::utils::ptr::Ptr;
|
||||||
|
use anyhow::{Result, anyhow};
|
||||||
|
|
||||||
use std::cmp::{Eq, PartialEq};
|
use std::cmp::{Eq, PartialEq};
|
||||||
|
|
||||||
|
|
@ -18,13 +19,13 @@ pub struct DeviceStandardColorSpaces {
|
||||||
|
|
||||||
impl DeviceStandardColorSpaces {
|
impl DeviceStandardColorSpaces {
|
||||||
#[cfg(not(target_arch = "nvptx64"))]
|
#[cfg(not(target_arch = "nvptx64"))]
|
||||||
pub fn get_named(&self, name: &str) -> Option<Ptr<RGBColorSpace>> {
|
pub fn get_named(&self, name: &str) -> Result<Ptr<RGBColorSpace>> {
|
||||||
match name.to_lowercase().as_str() {
|
match name.to_lowercase().as_str() {
|
||||||
"srgb" => Some(self.srgb),
|
"srgb" => Ok(self.srgb),
|
||||||
"dci-p3" => Some(self.dci_p3),
|
"dci-p3" => Ok(self.dci_p3),
|
||||||
"rec2020" => Some(self.rec2020),
|
"rec2020" => Ok(self.rec2020),
|
||||||
"aces2065-1" => Some(self.aces2065_1),
|
"aces2065-1" => Ok(self.aces2065_1),
|
||||||
_ => None,
|
_ => Err(anyhow!("No such spectrum")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -392,7 +392,6 @@ impl FilmBaseHost for FilmBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch]
|
|
||||||
pub trait FilmTrait: Sync {
|
pub trait FilmTrait: Sync {
|
||||||
fn base(&self) -> &FilmBase;
|
fn base(&self) -> &FilmBase;
|
||||||
fn get_pixel_rgb(&self, p: Point2i, splat_scale: Option<Float>) -> RGB;
|
fn get_pixel_rgb(&self, p: Point2i, splat_scale: Option<Float>) -> RGB;
|
||||||
|
|
@ -403,6 +402,7 @@ pub trait FilmTrait: Sync {
|
||||||
.write(self.get_filename(), metadata)
|
.write(self.get_filename(), metadata)
|
||||||
.expect("Something")
|
.expect("Something")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_image(&self, _metadata: &ImageMetadata, splat_scale: Float) -> Image {
|
fn get_image(&self, _metadata: &ImageMetadata, splat_scale: Float) -> Image {
|
||||||
let write_fp16 = true;
|
let write_fp16 = true;
|
||||||
let format = if write_fp16 {
|
let format = if write_fp16 {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use image_rs::{DynamicImage, ImageReader};
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
use shared::core::color::{ColorEncoding, LINEAR};
|
use shared::core::color::{ColorEncoding, LINEAR};
|
||||||
use shared::core::geometry::Point2i;
|
use shared::core::geometry::Point2i;
|
||||||
use shared::core::image::PixelFormat;
|
use shared::core::image::{DeviceImage, ImageBase, 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 std::path::Path;
|
use std::path::Path;
|
||||||
|
|
@ -343,14 +343,8 @@ fn read_pfm(path: &Path) -> Result<ImageAndMetadata> {
|
||||||
vec!["R".into(), "G".into(), "B".into()]
|
vec!["R".into(), "G".into(), "B".into()]
|
||||||
};
|
};
|
||||||
|
|
||||||
let image = Image {
|
let image = Image::new(PixelFormat::F32, Point2i::new(w, h), names, LINEAR);
|
||||||
format: PixelFormat::F32,
|
|
||||||
resolution: Point2i::new(w, h),
|
|
||||||
channel_names: names,
|
|
||||||
encoding: LINEAR,
|
|
||||||
pixels: PixelStorage::F32(pixels),
|
|
||||||
};
|
|
||||||
|
|
||||||
let metadata = ImageMetadata::default();
|
let metadata = ImageMetadata::default();
|
||||||
|
|
||||||
Ok(ImageAndMetadata { image, metadata })
|
Ok(ImageAndMetadata { image, metadata })
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ impl ImageChannelDesc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct ImageMetadata {
|
pub struct ImageMetadata {
|
||||||
pub render_time_seconds: Option<Float>,
|
pub render_time_seconds: Option<Float>,
|
||||||
pub camera_from_world: Option<SquareMatrix<Float, 4>>,
|
pub camera_from_world: Option<SquareMatrix<Float, 4>>,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::utils::containers::Array2D;
|
use crate::utils::containers::Array2D;
|
||||||
use anyhow::Result;
|
use anyhow::{Result, anyhow};
|
||||||
use half::f16;
|
use half::f16;
|
||||||
|
use rayon::prelude::ParallelIterator;
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
use shared::core::color::ColorEncoding;
|
use shared::core::color::{ColorEncoding, ColorEncodingTrait, LINEAR};
|
||||||
use shared::core::color::LINEAR;
|
|
||||||
use shared::core::geometry::{Bounds2f, Point2f, Point2i};
|
use shared::core::geometry::{Bounds2f, Point2f, Point2i};
|
||||||
use shared::core::image::{DeviceImage, ImageBase, PixelFormat, Pixels, WrapMode, WrapMode2D};
|
use shared::core::image::{DeviceImage, ImageBase, PixelFormat, Pixels, WrapMode, WrapMode2D};
|
||||||
use shared::utils::math::square;
|
use shared::utils::math::square;
|
||||||
|
|
@ -77,9 +77,9 @@ pub enum PixelStorage {
|
||||||
impl PixelStorage {
|
impl PixelStorage {
|
||||||
pub fn as_pixels(&self) -> Pixels {
|
pub fn as_pixels(&self) -> Pixels {
|
||||||
match self {
|
match self {
|
||||||
PixelStorage::U8(data) => Pixels::U8(data.as_ptr()),
|
PixelStorage::U8(data) => Pixels::U8(data.as_ptr().into()),
|
||||||
PixelStorage::F16(data) => Pixels::F16(data.as_ptr() as *const u16),
|
PixelStorage::F16(data) => Pixels::F16((data.as_ptr() as *const u16).into()),
|
||||||
PixelStorage::F32(data) => Pixels::F32(data.as_ptr()),
|
PixelStorage::F32(data) => Pixels::F32(data.as_ptr().into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,7 +126,7 @@ impl Image {
|
||||||
fn from_storage(
|
fn from_storage(
|
||||||
storage: PixelStorage,
|
storage: PixelStorage,
|
||||||
resolution: Point2i,
|
resolution: Point2i,
|
||||||
channel_names: &[&str],
|
channel_names: &[impl AsRef<str>],
|
||||||
encoding: ColorEncoding,
|
encoding: ColorEncoding,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let n_channels = channel_names.len() as i32;
|
let n_channels = channel_names.len() as i32;
|
||||||
|
|
@ -145,7 +145,7 @@ impl Image {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
storage,
|
storage,
|
||||||
channel_names,
|
channel_names: String::from(channel_names),
|
||||||
device,
|
device,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -153,7 +153,7 @@ impl Image {
|
||||||
pub fn from_u8(
|
pub fn from_u8(
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
resolution: Point2i,
|
resolution: Point2i,
|
||||||
channel_names: &[&str],
|
channel_names: &[impl AsRef<str>],
|
||||||
encoding: ColorEncoding,
|
encoding: ColorEncoding,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::from_storage(
|
Self::from_storage(
|
||||||
|
|
@ -167,7 +167,7 @@ impl Image {
|
||||||
pub fn from_u8(
|
pub fn from_u8(
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
resolution: Point2i,
|
resolution: Point2i,
|
||||||
channel_names: &[&str],
|
channel_names: &[impl AsRef<str>],
|
||||||
encoding: ColorEncoding,
|
encoding: ColorEncoding,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::from_storage(
|
Self::from_storage(
|
||||||
|
|
@ -178,44 +178,56 @@ impl Image {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_f16(data: Vec<half::f16>, resolution: Point2i, channel_names: &[&str]) -> Self {
|
pub fn from_f16(
|
||||||
|
data: Vec<half::f16>,
|
||||||
|
resolution: Point2i,
|
||||||
|
channel_names: &[impl AsRef<str>],
|
||||||
|
) -> Self {
|
||||||
Self::from_storage(
|
Self::from_storage(
|
||||||
PixelStorage::F16(data.into_boxed_slice()),
|
PixelStorage::F16(data.into_boxed_slice()),
|
||||||
resolution,
|
resolution,
|
||||||
channel_names,
|
channel_names,
|
||||||
ColorEncoding::Linear,
|
LINEAR,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_f32(data: Vec<f32>, resolution: Point2i, channel_names: &[&str]) -> Self {
|
pub fn from_f32(
|
||||||
|
data: Vec<f32>,
|
||||||
|
resolution: Point2i,
|
||||||
|
channel_names: &[impl AsRef<str>],
|
||||||
|
) -> Self {
|
||||||
Self::from_storage(
|
Self::from_storage(
|
||||||
PixelStorage::F32(data.into_boxed_slice()),
|
PixelStorage::F32(data.into_boxed_slice()),
|
||||||
resolution,
|
resolution,
|
||||||
channel_names,
|
channel_names,
|
||||||
ColorEncoding::Linear,
|
LINEAR,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
format: PixelFormat,
|
format: PixelFormat,
|
||||||
resolution: Point2i,
|
resolution: Point2i,
|
||||||
channel_names: &[&str],
|
channel_names: &[impl AsRef<str>],
|
||||||
encoding: Arc<ColorEncoding>,
|
encoding: Arc<ColorEncoding>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let n_channels = channel_names.len();
|
let n_channels = channel_names.len();
|
||||||
let pixel_count = (resolution.x * resolution.y) as usize * n_channels;
|
let pixel_count = (resolution.x() * resolution.y()) as usize * n_channels;
|
||||||
let owned_names: Vec<String> = channel_names.iter().map(|s| s.to_string()).collect();
|
let owned_names: Vec<String> = channel_names.iter().map(|s| s.to_string()).collect();
|
||||||
|
|
||||||
let storage = match format {
|
let storage = match format {
|
||||||
PixelFormat::U8 => PixelStorage::U8(vec![0; pixel_count]),
|
PixelFormat::U8 => PixelStorage::U8(vec![0; pixel_count].into()),
|
||||||
PixelFormat::F16 => PixelStorage::F16(vec![0; pixel_count]),
|
PixelFormat::F16 => PixelStorage::F16(vec![0; pixel_count].into()),
|
||||||
PixelFormat::F32 => PixelStorage::F32(vec![0.0; pixel_count]),
|
PixelFormat::F32 => PixelStorage::F32(vec![0.0; pixel_count].into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
Self::from_storage(storage, resolution, owned_names, encoding)
|
Self::from_storage(storage, resolution, owned_names, encoding)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_constant(resolution: Point2i, channel_names: &[&str], values: &[f32]) -> Self {
|
pub fn new_constant(
|
||||||
|
resolution: Point2i,
|
||||||
|
channel_names: &[impl AsRef<str>],
|
||||||
|
values: &[f32],
|
||||||
|
) -> Self {
|
||||||
let n_channels = channel_names.len();
|
let n_channels = channel_names.len();
|
||||||
if values.len() != n_channels {
|
if values.len() != n_channels {
|
||||||
panic!(
|
panic!(
|
||||||
|
|
@ -225,7 +237,7 @@ impl Image {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
|
@ -248,11 +260,11 @@ impl Image {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn n_channels(&self) -> i32 {
|
fn n_channels(&self) -> i32 {
|
||||||
self.base.n_channels
|
self.base().n_channels
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format(&self) -> PixelFormat {
|
pub fn format(&self) -> PixelFormat {
|
||||||
self.device.base.format
|
self.base().format
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn channel_names(&self) -> Vec<&str> {
|
pub fn channel_names(&self) -> Vec<&str> {
|
||||||
|
|
@ -260,7 +272,7 @@ impl Image {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encoding(&self) -> ColorEncoding {
|
pub fn encoding(&self) -> ColorEncoding {
|
||||||
self.view.encoding
|
self.base().encoding
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pixel_offset(&self, p: Point2i) -> usize {
|
fn pixel_offset(&self, p: Point2i) -> usize {
|
||||||
|
|
@ -399,7 +411,10 @@ impl Image {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_channel_desc(&self, requested_channels: &[&str]) -> Result<ImageChannelDesc> {
|
pub fn get_channel_desc(
|
||||||
|
&self,
|
||||||
|
requested_channels: &[impl AsRef<str>],
|
||||||
|
) -> Result<ImageChannelDesc> {
|
||||||
let mut offset = Vec::with_capacity(requested_channels.len());
|
let mut offset = Vec::with_capacity(requested_channels.len());
|
||||||
|
|
||||||
for &req in requested_channels.iter() {
|
for &req in requested_channels.iter() {
|
||||||
|
|
@ -408,9 +423,10 @@ impl Image {
|
||||||
offset.push(idx);
|
offset.push(idx);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
return Err(format!(
|
return Err(anyhow!(
|
||||||
"Missing channel '{}'. Available: {:?}",
|
"Missing channel '{}'. Available: {:?}",
|
||||||
req, self.channel_names
|
req,
|
||||||
|
self.channel_names
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -467,7 +483,7 @@ impl Image {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Self::from_storage(new_storage, res, new_names, self.encoding())
|
Self::from_storage(new_storage, res, &new_names, self.encoding())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_sampling_distribution<F>(&self, dxd_a: F, domain: Bounds2f) -> Array2D<Float>
|
pub fn get_sampling_distribution<F>(&self, dxd_a: F, domain: Bounds2f) -> Array2D<Float>
|
||||||
|
|
@ -477,7 +493,7 @@ impl Image {
|
||||||
let width = self.resolution().x();
|
let width = self.resolution().x();
|
||||||
let height = self.resolution().y();
|
let height = self.resolution().y();
|
||||||
|
|
||||||
let mut dist = Array2D::new_dims(width, height);
|
let mut dist: Array2D<Float> = Array2D::new_dims(width, height);
|
||||||
|
|
||||||
dist.values
|
dist.values
|
||||||
.par_chunks_mut(width as usize)
|
.par_chunks_mut(width as usize)
|
||||||
|
|
@ -488,7 +504,7 @@ impl Image {
|
||||||
for (x, out_val) in row.iter_mut().enumerate() {
|
for (x, out_val) in row.iter_mut().enumerate() {
|
||||||
let x = x as i32;
|
let x = x as i32;
|
||||||
|
|
||||||
let value = self.get_channels_default(Point2i::new(x, y)).average();
|
let value = self.get_channels(Point2i::new(x, y)).average();
|
||||||
|
|
||||||
let u = (x as Float + 0.5) / width as Float;
|
let u = (x as Float + 0.5) / width as Float;
|
||||||
let v = (y as Float + 0.5) / height as Float;
|
let v = (y as Float + 0.5) / height as Float;
|
||||||
|
|
@ -512,15 +528,17 @@ impl Image {
|
||||||
ref_img: &Image,
|
ref_img: &Image,
|
||||||
generate_mse_image: bool,
|
generate_mse_image: bool,
|
||||||
) -> (ImageChannelValues, Option<Image>) {
|
) -> (ImageChannelValues, Option<Image>) {
|
||||||
|
let res = self.resolution();
|
||||||
|
|
||||||
let mut sum_se: Vec<f64> = vec![0.; desc.size()];
|
let mut sum_se: Vec<f64> = vec![0.; desc.size()];
|
||||||
let names_ref = self.channel_names_from_desc(&desc);
|
let names_ref = self.channel_names_from_desc(&desc);
|
||||||
let ref_desc = ref_img
|
let ref_desc = ref_img
|
||||||
.get_channel_desc(&self.channel_names_from_desc(&desc))
|
.get_channel_desc(&self.channel_names_from_desc(&desc))
|
||||||
.expect("Channels not found in image");
|
.expect("Channels not found in image");
|
||||||
assert_eq!(self.resolution(), ref_img.resolution());
|
assert_eq!(res, ref_img.resolution());
|
||||||
|
|
||||||
let width = self.resolution.x() as usize;
|
let width = res.x() as usize;
|
||||||
let height = self.resolution.y() as usize;
|
let height = res.y() as usize;
|
||||||
let n_channels = desc.offset.len();
|
let n_channels = desc.offset.len();
|
||||||
let mut mse_pixels = if generate_mse_image {
|
let mut mse_pixels = if generate_mse_image {
|
||||||
vec![0.0f32; width * height * n_channels]
|
vec![0.0f32; width * height * n_channels]
|
||||||
|
|
@ -528,11 +546,11 @@ impl Image {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
for y in 0..self.resolution().y() {
|
for y in 0..res.y() {
|
||||||
for x in 0..self.resolution().x() {
|
for x in 0..res.x() {
|
||||||
let v =
|
let v =
|
||||||
self.get_channel_with_desc(Point2i::new(x, y), &desc, WrapMode::Clamp.into());
|
self.get_channels_with_desc(Point2i::new(x, y), &desc, WrapMode::Clamp.into());
|
||||||
let v_ref = self.get_channel_with_desc(
|
let v_ref = self.get_channels_with_desc(
|
||||||
Point2i::new(x, y),
|
Point2i::new(x, y),
|
||||||
&ref_desc,
|
&ref_desc,
|
||||||
WrapMode::Clamp.into(),
|
WrapMode::Clamp.into(),
|
||||||
|
|
@ -551,17 +569,12 @@ impl Image {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let pixel_count = (self.resolution().x() * self.resolution.y()) as f64;
|
let pixel_count = (res.x() * res.y()) as f64;
|
||||||
let mse_values: SmallVec<[Float; 4]> =
|
let mse_values: SmallVec<[Float; 4]> =
|
||||||
sum_se.iter().map(|&s| (s / pixel_count) as Float).collect();
|
sum_se.iter().map(|&s| (s / pixel_count) as Float).collect();
|
||||||
|
|
||||||
let mse_image = if generate_mse_image {
|
let mse_image = if generate_mse_image {
|
||||||
Some(Image::new(
|
Some(Image::new(PixelFormat::F32, res, &names_ref, LINEAR.into()))
|
||||||
PixelFormat::F32,
|
|
||||||
self.resolution,
|
|
||||||
&names_ref,
|
|
||||||
LINEAR,
|
|
||||||
))
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
@ -570,15 +583,15 @@ impl Image {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_view_pointers(&mut self) {
|
pub fn update_view_pointers(&mut self) {
|
||||||
self.view.pixels = match &self._storage {
|
self.device.pixels = match &self.storage {
|
||||||
PixelStorage::U8(vec) => Pixels::U8(vec.as_ptr()),
|
PixelStorage::U8(vec) => Pixels::U8(vec.as_ptr().into()),
|
||||||
PixelStorage::F16(vec) => Pixels::F16(vec.as_ptr() as *const u16),
|
PixelStorage::F16(vec) => Pixels::F16((vec.as_ptr() as *const u16).into()),
|
||||||
PixelStorage::F32(vec) => Pixels::F32(vec.as_ptr()),
|
PixelStorage::F32(vec) => Pixels::F32(vec.as_ptr().into()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_any_infinite_pixels(&self) -> bool {
|
pub fn has_any_infinite_pixels(&self) -> bool {
|
||||||
if self.format() == PixelFormat::Float {
|
if self.format() == PixelFormat::F32 {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -595,7 +608,7 @@ impl Image {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_any_nan_pixels(&self) -> bool {
|
pub fn has_any_nan_pixels(&self) -> bool {
|
||||||
if self.format() == PixelFormat::Float {
|
if self.format() == PixelFormat::F32 {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@ use rayon::prelude::*;
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
use shared::core::color::ColorEncoding;
|
use shared::core::color::ColorEncoding;
|
||||||
use shared::core::geometry::{Bounds2i, Point2i};
|
use shared::core::geometry::{Bounds2i, Point2i};
|
||||||
use shared::core::image::{PixelFormat, WrapMode, WrapMode2D};
|
use shared::core::image::{PixelFormat, Pixels, WrapMode, WrapMode2D};
|
||||||
|
use shared::utils::Ptr;
|
||||||
use shared::utils::math::windowed_sinc;
|
use shared::utils::math::windowed_sinc;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
|
@ -16,17 +17,20 @@ pub struct ResampleWeight {
|
||||||
|
|
||||||
impl Image {
|
impl Image {
|
||||||
pub fn flip_y(&mut self) {
|
pub fn flip_y(&mut self) {
|
||||||
let res = self.resolution;
|
let res = self.resolution();
|
||||||
let nc = self.n_channels();
|
let nc = self.n_channels();
|
||||||
|
|
||||||
match &mut self.pixels {
|
match &mut self.pixels {
|
||||||
PixelStorage::U8(d) => flip_y_kernel(d, res, nc),
|
Pixels::U8(d) => flip_y_kernel(d, res, nc),
|
||||||
PixelStorage::F16(d) => flip_y_kernel(d, res, nc),
|
Pixels::F16(d) => flip_y_kernel(d, res, nc),
|
||||||
PixelStorage::F32(d) => flip_y_kernel(d, res, nc),
|
Pixels::F32(d) => flip_y_kernel(d, res, nc),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn crop(&self, bounds: Bounds2i) -> Image {
|
pub fn crop(&self, bounds: Bounds2i) -> Image {
|
||||||
|
let res = self.resolution();
|
||||||
|
let n_channels = self.n_channels();
|
||||||
|
|
||||||
let new_res = Point2i::new(
|
let new_res = Point2i::new(
|
||||||
bounds.p_max.x() - bounds.p_min.x(),
|
bounds.p_max.x() - bounds.p_min.x(),
|
||||||
bounds.p_max.y() - bounds.p_min.y(),
|
bounds.p_max.y() - bounds.p_min.y(),
|
||||||
|
|
@ -36,19 +40,13 @@ impl Image {
|
||||||
self.format,
|
self.format,
|
||||||
new_res,
|
new_res,
|
||||||
self.channel_names.clone(),
|
self.channel_names.clone(),
|
||||||
self.encoding,
|
self.encoding(),
|
||||||
);
|
);
|
||||||
|
|
||||||
match (&self.pixels, &mut new_image.pixels) {
|
match (&self.pixels, &mut new_image.pixels) {
|
||||||
(PixelStorage::U8(src), PixelStorage::U8(dst)) => {
|
(Pixels::U8(src), Pixels::U8(dst)) => crop_kernel(src, dst, res, bounds, n_channels),
|
||||||
crop_kernel(src, dst, self.resolution, bounds, self.n_channels())
|
(Pixels::F16(src), Pixels::F16(dst)) => crop_kernel(src, dst, res, bounds, n_channels),
|
||||||
}
|
(Pixels::F32(src), Pixels::F32(dst)) => crop_kernel(src, dst, res, bounds, n_channels),
|
||||||
(PixelStorage::F16(src), PixelStorage::F16(dst)) => {
|
|
||||||
crop_kernel(src, dst, self.resolution, bounds, self.n_channels())
|
|
||||||
}
|
|
||||||
(PixelStorage::F32(src), PixelStorage::F32(dst)) => {
|
|
||||||
crop_kernel(src, dst, self.resolution, bounds, self.n_channels())
|
|
||||||
}
|
|
||||||
_ => panic!("Format mismatch in crop"),
|
_ => panic!("Format mismatch in crop"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,34 +55,29 @@ impl Image {
|
||||||
|
|
||||||
pub fn copy_rect_out(&self, extent: Bounds2i, buf: &mut [Float], wrap: WrapMode2D) {
|
pub fn copy_rect_out(&self, extent: Bounds2i, buf: &mut [Float], wrap: WrapMode2D) {
|
||||||
match &self.pixels {
|
match &self.pixels {
|
||||||
PixelStorage::U8(d) => copy_rect_out_kernel(d, self, extent, buf, wrap),
|
Pixels::U8(d) => copy_rect_out_kernel(d, self, extent, buf, wrap),
|
||||||
PixelStorage::F16(d) => copy_rect_out_kernel(d, self, extent, buf, wrap),
|
Pixels::F16(d) => copy_rect_out_kernel(d, self, extent, buf, wrap),
|
||||||
PixelStorage::F32(d) => copy_rect_out_kernel(d, self, extent, buf, wrap),
|
Pixels::F32(d) => copy_rect_out_kernel(d, self, extent, buf, wrap),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_rect_in(&mut self, extent: Bounds2i, buf: &[Float]) {
|
pub fn copy_rect_in(&mut self, extent: Bounds2i, buf: &[Float]) {
|
||||||
let resolution = self.resolution;
|
let res = self.resolution();
|
||||||
let n_channels = self.n_channels();
|
let n_channels = self.n_channels() as usize;
|
||||||
let encoding = self.encoding;
|
let encoding = self.encoding();
|
||||||
|
|
||||||
match &mut self.pixels {
|
match &mut self.pixels {
|
||||||
PixelStorage::U8(d) => {
|
Pixels::U8(d) => copy_rect_in_kernel(d, res, n_channels, encoding, extent, buf),
|
||||||
copy_rect_in_kernel(d, resolution, n_channels, encoding, extent, buf)
|
Pixels::F16(d) => copy_rect_in_kernel(d, res, n_channels, encoding, extent, buf),
|
||||||
}
|
Pixels::F32(d) => copy_rect_in_kernel(d, res, n_channels, encoding, extent, buf),
|
||||||
PixelStorage::F16(d) => {
|
|
||||||
copy_rect_in_kernel(d, resolution, n_channels, encoding, extent, buf)
|
|
||||||
}
|
|
||||||
PixelStorage::F32(d) => {
|
|
||||||
copy_rect_in_kernel(d, resolution, n_channels, encoding, extent, buf)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn float_resize_up(&self, new_res: Point2i, wrap_mode: WrapMode2D) -> Image {
|
pub fn float_resize_up(&self, new_res: Point2i, wrap_mode: WrapMode2D) -> Image {
|
||||||
assert!(new_res.x() >= self.resolution.x() && new_res.y() >= self.resolution.y());
|
let res = self.resolution();
|
||||||
|
assert!(new_res.x() >= res.x() && new_res.y() >= res.y());
|
||||||
assert!(
|
assert!(
|
||||||
matches!(self.format, PixelFormat::F32),
|
matches!(self.format(), PixelFormat::F32),
|
||||||
"ResizeUp requires Float format"
|
"ResizeUp requires Float format"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -92,11 +85,11 @@ impl Image {
|
||||||
PixelFormat::F32, // Force float output
|
PixelFormat::F32, // Force float output
|
||||||
new_res,
|
new_res,
|
||||||
self.channel_names.clone(),
|
self.channel_names.clone(),
|
||||||
self.encoding,
|
self.encoding(),
|
||||||
)));
|
)));
|
||||||
|
|
||||||
let x_weights = resample_weights(self.resolution.x() as usize, new_res.x() as usize);
|
let x_weights = resample_weights(res.x() as usize, new_res.x() as usize);
|
||||||
let y_weights = resample_weights(self.resolution.y() as usize, new_res.y() as usize);
|
let y_weights = resample_weights(res.y() as usize, new_res.y() as usize);
|
||||||
let n_channels = self.n_channels();
|
let n_channels = self.n_channels();
|
||||||
|
|
||||||
let tile_size = 16;
|
let tile_size = 16;
|
||||||
|
|
@ -118,7 +111,7 @@ impl Image {
|
||||||
&in_buf,
|
&in_buf,
|
||||||
in_extent,
|
in_extent,
|
||||||
*out_extent,
|
*out_extent,
|
||||||
n_channels,
|
n_channels.try_into().unwrap(),
|
||||||
&x_weights,
|
&x_weights,
|
||||||
&y_weights,
|
&y_weights,
|
||||||
);
|
);
|
||||||
|
|
@ -141,23 +134,23 @@ impl Image {
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let prev = levels.last().unwrap();
|
let prev = levels.last().unwrap();
|
||||||
let old = prev.resolution;
|
let old = prev.resolution();
|
||||||
if old.x() == 1 && old.y() == 1 {
|
if old.x() == 1 && old.y() == 1 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_res = Point2i::new((old.x() / 2).max(1), (old.y() / 2).max(1));
|
let new_res = Point2i::new((old.x() / 2).max(1), (old.y() / 2).max(1));
|
||||||
let mut next = Image::from_vector(
|
let mut next = Image::new(
|
||||||
prev.format,
|
prev.format(),
|
||||||
new_res,
|
new_res,
|
||||||
prev.channel_names.clone(),
|
prev.channel_names.clone(),
|
||||||
prev.encoding,
|
prev.encoding(),
|
||||||
);
|
);
|
||||||
|
|
||||||
match &mut next.pixels {
|
match &mut next.pixels {
|
||||||
PixelStorage::U8(d) => downsample_kernel(d, new_res, prev, internal_wrap),
|
Pixels::U8(d) => downsample_kernel(d, new_res, prev, internal_wrap),
|
||||||
PixelStorage::F16(d) => downsample_kernel(d, new_res, prev, internal_wrap),
|
Pixels::F16(d) => downsample_kernel(d, new_res, prev, internal_wrap),
|
||||||
PixelStorage::F32(d) => downsample_kernel(d, new_res, prev, internal_wrap),
|
Pixels::F32(d) => downsample_kernel(d, new_res, prev, internal_wrap),
|
||||||
}
|
}
|
||||||
levels.push(next);
|
levels.push(next);
|
||||||
}
|
}
|
||||||
|
|
@ -209,9 +202,9 @@ fn copy_rect_out_kernel<T: PixelStorage>(
|
||||||
wrap: WrapMode2D,
|
wrap: WrapMode2D,
|
||||||
) {
|
) {
|
||||||
let w = (extent.p_max.x() - extent.p_min.x()) as usize;
|
let w = (extent.p_max.x() - extent.p_min.x()) as usize;
|
||||||
let channels = image.n_channels();
|
let channels = image.n_channels() as usize;
|
||||||
let enc = image.encoding;
|
let enc = image.encoding();
|
||||||
let res = image.resolution;
|
let res = image.resolution();
|
||||||
|
|
||||||
buf.par_chunks_mut(w * channels)
|
buf.par_chunks_mut(w * channels)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
|
|
@ -220,7 +213,6 @@ fn copy_rect_out_kernel<T: PixelStorage>(
|
||||||
for x_rel in 0..w {
|
for x_rel in 0..w {
|
||||||
let x = extent.p_min.x() + x_rel as i32;
|
let x = extent.p_min.x() + x_rel as i32;
|
||||||
|
|
||||||
// This allows us to use 'src' directly (Fast Path).
|
|
||||||
if x >= 0 && x < res.x() && y >= 0 && y < res.y() {
|
if x >= 0 && x < res.x() && y >= 0 && y < res.y() {
|
||||||
let offset = (y as usize * res.x() as usize + x as usize) * channels;
|
let offset = (y as usize * res.x() as usize + x as usize) * channels;
|
||||||
|
|
||||||
|
|
@ -228,7 +220,6 @@ fn copy_rect_out_kernel<T: PixelStorage>(
|
||||||
row_buf[x_rel * channels + c] = T::to_linear(src[offset + c], enc);
|
row_buf[x_rel * channels + c] = T::to_linear(src[offset + c], enc);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Slow path: Out of bounds, requires Wrap Mode logic.
|
|
||||||
// We fall back to get_channel which handles the wrapping math.
|
// We fall back to get_channel which handles the wrapping math.
|
||||||
let p = Point2i::new(x, y);
|
let p = Point2i::new(x, y);
|
||||||
for c in 0..channels {
|
for c in 0..channels {
|
||||||
|
|
@ -273,17 +264,17 @@ fn copy_rect_in_kernel<T: PixelStorage>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn downsample_kernel<T: PixelStorage>(
|
fn downsample_kernel<T: PixelStorage>(
|
||||||
dst: &mut [T],
|
dst: &mut Ptr<T>,
|
||||||
dst_res: Point2i,
|
dst_res: Point2i,
|
||||||
prev: &Image,
|
prev: &Image,
|
||||||
wrap: WrapMode2D,
|
wrap: WrapMode2D,
|
||||||
) {
|
) {
|
||||||
let w = dst_res.x() as usize;
|
let w = dst_res.x() as usize;
|
||||||
let channels = prev.n_channels();
|
let channels = prev.n_channels();
|
||||||
let enc = prev.encoding;
|
let enc = prev.encoding();
|
||||||
let old_res = prev.resolution;
|
let old_res = prev.resolution();
|
||||||
|
|
||||||
dst.par_chunks_mut(w * channels)
|
dst.par_chunks_mut(w * channels as usize)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.for_each(|(y, row)| {
|
.for_each(|(y, row)| {
|
||||||
let src_y = y * 2;
|
let src_y = y * 2;
|
||||||
|
|
@ -305,7 +296,7 @@ fn downsample_kernel<T: PixelStorage>(
|
||||||
}
|
}
|
||||||
|
|
||||||
let avg = if count > 0.0 { sum / count } else { 0.0 };
|
let avg = if count > 0.0 { sum / count } else { 0.0 };
|
||||||
row[x * channels + c] = T::from_linear(avg, enc);
|
row[x * channels as usize + c as usize] = T::from_linear(avg, enc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use shared::Float;
|
|
||||||
use shared::core::color::ColorEncoding;
|
|
||||||
use half::f16;
|
use half::f16;
|
||||||
|
use shared::Float;
|
||||||
|
use shared::core::color::{ColorEncoding, ColorEncodingTrait};
|
||||||
|
|
||||||
// Allows writing generic algorithms that work on any image format.
|
// Allows writing generic algorithms that work on any image format.
|
||||||
pub trait PixelStorage: Copy + Send + Sync + 'static + PartialEq {
|
pub trait PixelStorage: Copy + Send + Sync + 'static + PartialEq {
|
||||||
|
|
@ -34,14 +34,14 @@ impl PixelStorage for u8 {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from_linear(val: Float, enc: ColorEncoding) -> Self {
|
fn from_linear(val: Float, enc: ColorEncoding) -> Self {
|
||||||
let mut out = [0u8];
|
let mut out = [0u8];
|
||||||
enc.from_linear_slice(&[val], &mut out);
|
enc.from_linear(&[val], &mut out);
|
||||||
out[0]
|
out[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn to_linear(self, enc: ColorEncoding) -> Float {
|
fn to_linear(self, enc: ColorEncoding) -> Float {
|
||||||
let mut out = [0.0];
|
let mut out = [0.0];
|
||||||
enc.to_linear_slice(&[self], &mut out);
|
enc.to_linear(&[self], &mut out);
|
||||||
out[0]
|
out[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
use crate::core::spectrum::SPECTRUM_CACHE;
|
use crate::core::spectrum::SPECTRUM_CACHE;
|
||||||
use crate::core::texture::FloatTexture;
|
use crate::core::texture::FloatTexture;
|
||||||
use crate::spectra::DenselySampledSpectrumBuffer;
|
use crate::spectra::DenselySampledSpectrumBuffer;
|
||||||
use crate::utils::containers::InternCache;
|
|
||||||
use crate::utils::{Arena, FileLoc, ParameterDictionary};
|
use crate::utils::{Arena, FileLoc, ParameterDictionary};
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{Result, anyhow};
|
||||||
use shared::core::camera::CameraTransform;
|
use shared::core::camera::CameraTransform;
|
||||||
|
|
@ -12,11 +11,12 @@ use shared::core::spectrum::Spectrum;
|
||||||
use shared::lights::*;
|
use shared::lights::*;
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::RGBColorSpace;
|
||||||
use shared::utils::Transform;
|
use shared::utils::Transform;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub fn lookup_spectrum(s: &Spectrum) -> DenselySampledSpectrumBuffer {
|
pub fn lookup_spectrum(s: &Spectrum) -> Arc<DenselySampledSpectrumBuffer> {
|
||||||
let cache = SPECTRUM_CACHE.get_or_init(InternCache::new);
|
let cache = &SPECTRUM_CACHE;
|
||||||
let dense_spectrum = DenselySampledSpectrumBuffer::from_spectrum(s);
|
let dense_spectrum = DenselySampledSpectrumBuffer::from_spectrum(s);
|
||||||
cache.lookup(dense_spectrum).as_ref()
|
cache.lookup(dense_spectrum).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait CreateLight {
|
pub trait CreateLight {
|
||||||
|
|
@ -62,7 +62,6 @@ impl LightFactory for Light {
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
match name {
|
match name {
|
||||||
"diffuse" => DiffuseAreaLight::create(
|
"diffuse" => DiffuseAreaLight::create(
|
||||||
name,
|
|
||||||
arena,
|
arena,
|
||||||
render_from_light,
|
render_from_light,
|
||||||
medium,
|
medium,
|
||||||
|
|
@ -70,7 +69,8 @@ impl LightFactory for Light {
|
||||||
loc,
|
loc,
|
||||||
shape,
|
shape,
|
||||||
alpha_tex,
|
alpha_tex,
|
||||||
)?,
|
colorspace,
|
||||||
|
),
|
||||||
"point" => PointLight::create(
|
"point" => PointLight::create(
|
||||||
arena,
|
arena,
|
||||||
render_from_light,
|
render_from_light,
|
||||||
|
|
@ -80,7 +80,7 @@ impl LightFactory for Light {
|
||||||
shape,
|
shape,
|
||||||
alpha_tex,
|
alpha_tex,
|
||||||
colorspace,
|
colorspace,
|
||||||
)?,
|
),
|
||||||
"spot" => SpotLight::create(
|
"spot" => SpotLight::create(
|
||||||
arena,
|
arena,
|
||||||
render_from_light,
|
render_from_light,
|
||||||
|
|
@ -90,7 +90,7 @@ impl LightFactory for Light {
|
||||||
shape,
|
shape,
|
||||||
alpha_tex,
|
alpha_tex,
|
||||||
colorspace,
|
colorspace,
|
||||||
)?,
|
),
|
||||||
"goniometric" => GoniometricLight::create(
|
"goniometric" => GoniometricLight::create(
|
||||||
arena,
|
arena,
|
||||||
render_from_light,
|
render_from_light,
|
||||||
|
|
@ -100,7 +100,7 @@ impl LightFactory for Light {
|
||||||
shape,
|
shape,
|
||||||
alpha_tex,
|
alpha_tex,
|
||||||
colorspace,
|
colorspace,
|
||||||
)?,
|
),
|
||||||
"projection" => ProjectionLight::create(
|
"projection" => ProjectionLight::create(
|
||||||
arena,
|
arena,
|
||||||
render_from_light,
|
render_from_light,
|
||||||
|
|
@ -110,7 +110,7 @@ impl LightFactory for Light {
|
||||||
shape,
|
shape,
|
||||||
alpha_tex,
|
alpha_tex,
|
||||||
colorspace,
|
colorspace,
|
||||||
)?,
|
),
|
||||||
"distant" => DistantLight::create(
|
"distant" => DistantLight::create(
|
||||||
arena,
|
arena,
|
||||||
render_from_light,
|
render_from_light,
|
||||||
|
|
@ -120,17 +120,17 @@ impl LightFactory for Light {
|
||||||
shape,
|
shape,
|
||||||
alpha_tex,
|
alpha_tex,
|
||||||
colorspace,
|
colorspace,
|
||||||
)?,
|
),
|
||||||
"infinite" => crate::lights::infinite::create(
|
"infinite" => crate::lights::infinite::create(
|
||||||
arena,
|
arena,
|
||||||
render_from_light,
|
render_from_light,
|
||||||
medium,
|
medium.into(),
|
||||||
camera_transform,
|
camera_transform,
|
||||||
parameters,
|
parameters,
|
||||||
colorspace,
|
colorspace,
|
||||||
loc,
|
loc,
|
||||||
)?,
|
),
|
||||||
_ => Err(anyhow!(loc, "unknown light type: \"{}\"", name)),
|
_ => Err(anyhow!("{}: unknown light type: \"{}\"", loc, name)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ pub trait MaterialFactory {
|
||||||
name: &str,
|
name: &str,
|
||||||
params: &TextureParameterDictionary,
|
params: &TextureParameterDictionary,
|
||||||
normal_map: Option<Arc<Image>>,
|
normal_map: Option<Arc<Image>>,
|
||||||
named_materials: HashMap<String, Material>,
|
named_materials: Arc<HashMap<String, Material>>,
|
||||||
loc: FileLoc,
|
loc: FileLoc,
|
||||||
arena: &mut Arena,
|
arena: &mut Arena,
|
||||||
) -> Result<Self>;
|
) -> Result<Self>;
|
||||||
|
|
@ -34,7 +34,7 @@ impl MaterialFactory for Material {
|
||||||
name: &str,
|
name: &str,
|
||||||
parameters: &TextureParameterDictionary,
|
parameters: &TextureParameterDictionary,
|
||||||
normal_map: Option<Arc<Image>>,
|
normal_map: Option<Arc<Image>>,
|
||||||
named_materials: HashMap<String, Material>,
|
named_materials: Arc<HashMap<String, Material>>,
|
||||||
loc: FileLoc,
|
loc: FileLoc,
|
||||||
arena: &mut Arena,
|
arena: &mut Arena,
|
||||||
) -> Result<Material> {
|
) -> Result<Material> {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use crate::spectra::dense::DenselySampledSpectrumBuffer;
|
||||||
use shared::core::geometry::{Bounds3f, Point3i};
|
use shared::core::geometry::{Bounds3f, Point3i};
|
||||||
use shared::core::medium::{GridMedium, HGPhaseFunction, HomogeneousMedium, RGBGridMedium};
|
use shared::core::medium::{GridMedium, HGPhaseFunction, HomogeneousMedium, RGBGridMedium};
|
||||||
use shared::core::spectrum::{Spectrum, SpectrumTrait};
|
use shared::core::spectrum::{Spectrum, SpectrumTrait};
|
||||||
use shared::spectra::{DenselySampledSpectrum, RGBIlluminantSpectrum, RGBUnboundedSpectrum};
|
use shared::spectra::{RGBIlluminantSpectrum, RGBUnboundedSpectrum};
|
||||||
use shared::utils::Transform;
|
use shared::utils::Transform;
|
||||||
use shared::utils::containers::SampledGrid;
|
use shared::utils::containers::SampledGrid;
|
||||||
use shared::{Float, core::medium::MajorantGrid};
|
use shared::{Float, core::medium::MajorantGrid};
|
||||||
|
|
@ -89,7 +89,7 @@ pub trait GridMediumCreator {
|
||||||
sigma_scale: Float,
|
sigma_scale: Float,
|
||||||
g: Float,
|
g: Float,
|
||||||
density_grid: SampledGrid<Float>,
|
density_grid: SampledGrid<Float>,
|
||||||
temperature_grid: SampledGrid<Float>,
|
temperature_grid: Option<SampledGrid<Float>>,
|
||||||
le: &Spectrum,
|
le: &Spectrum,
|
||||||
le_scale: SampledGrid<Float>,
|
le_scale: SampledGrid<Float>,
|
||||||
) -> Self;
|
) -> Self;
|
||||||
|
|
@ -105,21 +105,23 @@ impl GridMediumCreator for GridMedium {
|
||||||
sigma_scale: Float,
|
sigma_scale: Float,
|
||||||
g: Float,
|
g: Float,
|
||||||
density_grid: SampledGrid<Float>,
|
density_grid: SampledGrid<Float>,
|
||||||
temperature_grid: SampledGrid<Float>,
|
temperature_grid: Option<SampledGrid<Float>>,
|
||||||
le: &Spectrum,
|
le: &Spectrum,
|
||||||
le_scale: SampledGrid<Float>,
|
le_scale: SampledGrid<Float>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut sigma_a_spec = DenselySampledSpectrum::from_spectrum(sigma_a);
|
let mut sigma_a_spec = DenselySampledSpectrumBuffer::from_spectrum(sigma_a);
|
||||||
let mut sigma_s_spec = DenselySampledSpectrum::from_spectrum(sigma_s);
|
let mut sigma_s_spec = DenselySampledSpectrumBuffer::from_spectrum(sigma_s);
|
||||||
let le_spec = DenselySampledSpectrum::from_spectrum(le);
|
|
||||||
sigma_a_spec.scale(sigma_scale);
|
sigma_a_spec.scale(sigma_scale);
|
||||||
sigma_s_spec.scale(sigma_scale);
|
sigma_s_spec.scale(sigma_scale);
|
||||||
|
|
||||||
|
let le_spec = DenselySampledSpectrumBuffer::from_spectrum(le);
|
||||||
|
|
||||||
let mut majorant_grid = MajorantGrid::new(*bounds, Point3i::new(16, 16, 16));
|
let mut majorant_grid = MajorantGrid::new(*bounds, Point3i::new(16, 16, 16));
|
||||||
let is_emissive = if temperature_grid.is_some() {
|
let is_emissive = if temperature_grid.is_some() {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
le_spec.max_value() > 0.
|
Spectrum::Dense(le_spec.device()).max_value() > 0.
|
||||||
};
|
};
|
||||||
|
|
||||||
for z in 0..majorant_grid.res.z() {
|
for z in 0..majorant_grid.res.z() {
|
||||||
|
|
@ -134,12 +136,12 @@ impl GridMediumCreator for GridMedium {
|
||||||
Self {
|
Self {
|
||||||
bounds: *bounds,
|
bounds: *bounds,
|
||||||
render_from_medium: *render_from_medium,
|
render_from_medium: *render_from_medium,
|
||||||
sigma_a_spec,
|
sigma_a_spec: sigma_a_spec.device(),
|
||||||
sigma_s_spec,
|
sigma_s_spec: sigma_s_spec.device(),
|
||||||
density_grid,
|
density_grid,
|
||||||
phase: HGPhaseFunction::new(g),
|
phase: HGPhaseFunction::new(g),
|
||||||
temperature_grid,
|
temperature_grid,
|
||||||
le_spec,
|
le_spec: le_spec.device(),
|
||||||
le_scale,
|
le_scale,
|
||||||
is_emissive,
|
is_emissive,
|
||||||
majorant_grid,
|
majorant_grid,
|
||||||
|
|
@ -169,16 +171,16 @@ impl HomogeneousMediumCreator for HomogeneousMedium {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut sigma_a_spec = DenselySampledSpectrumBuffer::from_spectrum(&sigma_a);
|
let mut sigma_a_spec = DenselySampledSpectrumBuffer::from_spectrum(&sigma_a);
|
||||||
let mut sigma_s_spec = DenselySampledSpectrumBuffer::from_spectrum(&sigma_s);
|
let mut sigma_s_spec = DenselySampledSpectrumBuffer::from_spectrum(&sigma_s);
|
||||||
let mut le_spec = DenselySampledSpectrum::from_spectrum(&le);
|
let mut le_spec = DenselySampledSpectrumBuffer::from_spectrum(&le);
|
||||||
|
|
||||||
sigma_a_spec.scale(sigma_scale);
|
sigma_a_spec.scale(sigma_scale);
|
||||||
sigma_s_spec.scale(sigma_scale);
|
sigma_s_spec.scale(sigma_scale);
|
||||||
le_spec.scale(le_scale);
|
le_spec.scale(le_scale);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
sigma_a_spec,
|
sigma_a_spec: sigma_a_spec.device(),
|
||||||
sigma_s_spec,
|
sigma_s_spec: sigma_s_spec.device(),
|
||||||
le_spec,
|
le_spec: le_spec.device(),
|
||||||
phase: HGPhaseFunction::new(g),
|
phase: HGPhaseFunction::new(g),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ pub mod material;
|
||||||
pub mod medium;
|
pub mod medium;
|
||||||
pub mod primitive;
|
pub mod primitive;
|
||||||
pub mod sampler;
|
pub mod sampler;
|
||||||
pub mod sampler;
|
|
||||||
pub mod scene;
|
pub mod scene;
|
||||||
pub mod shape;
|
pub mod shape;
|
||||||
pub mod spectrum;
|
pub mod spectrum;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use shared::core::{
|
||||||
light::Light,
|
light::Light,
|
||||||
material::Material,
|
material::Material,
|
||||||
medium::MediumInterface,
|
medium::MediumInterface,
|
||||||
|
texture::GPUFloatTexture
|
||||||
primitive::{GeometricPrimitive, SimplePrimitive},
|
primitive::{GeometricPrimitive, SimplePrimitive},
|
||||||
shape::Shape,
|
shape::Shape,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::Arena;
|
use crate::Arena;
|
||||||
|
use crate::samplers::CreateSampler;
|
||||||
use crate::utils::{FileLoc, ParameterDictionary};
|
use crate::utils::{FileLoc, ParameterDictionary};
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{Result, anyhow};
|
||||||
use shared::core::geometry::Point2i;
|
use shared::core::geometry::Point2i;
|
||||||
|
|
@ -7,15 +8,6 @@ use shared::core::sampler::{
|
||||||
StratifiedSampler, ZSobolSampler,
|
StratifiedSampler, ZSobolSampler,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait CreateSampler {
|
|
||||||
fn create(
|
|
||||||
params: &ParameterDictionary,
|
|
||||||
full_res: Point2i,
|
|
||||||
loc: &FileLoc,
|
|
||||||
arena: &mut Arena,
|
|
||||||
) -> Result<Self>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait SamplerFactory {
|
pub trait SamplerFactory {
|
||||||
fn create(
|
fn create(
|
||||||
name: &str,
|
name: &str,
|
||||||
|
|
@ -35,30 +27,12 @@ impl SamplerFactory for Sampler {
|
||||||
arena: &mut Arena,
|
arena: &mut Arena,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
match name {
|
match name {
|
||||||
"zsobol" => {
|
"zsobol" => ZSobolSampler::create(params, full_res, loc, arena),
|
||||||
let sampler = ZSobolSampler::create(params, full_res, loc, arena)?;
|
"paddedsobol" => PaddedSobolSampler::create(params, full_res, loc, arena),
|
||||||
Ok(Sampler::ZSobol(sampler))
|
"halton" => HaltonSampler::create(params, full_res, loc, arena),
|
||||||
}
|
"sobol" => SobolSampler::create(params, full_res, loc, arena),
|
||||||
"paddedsobol" => {
|
"Independent" => IndependentSampler::create(params, full_res, loc, arena),
|
||||||
let sampler = PaddedSobolSampler::create(params, full_res, loc, arena)?;
|
"stratified" => StratifiedSampler::create(params, full_res, loc, arena),
|
||||||
Ok(Sampler::PaddedSobol(sampler))
|
|
||||||
}
|
|
||||||
"halton" => {
|
|
||||||
let sampler = HaltonSampler::create(params, full_res, loc, arena)?;
|
|
||||||
Ok(Sampler::Halton(sampler))
|
|
||||||
}
|
|
||||||
"sobol" => {
|
|
||||||
let sampler = SobolSampler::create(params, full_res, loc, arena)?;
|
|
||||||
Ok(Sampler::Sobol(sampler))
|
|
||||||
}
|
|
||||||
"Independent" => {
|
|
||||||
let sampler = IndependentSampler::create(params, full_res, loc, arena)?;
|
|
||||||
Ok(Sampler::Independent(sampler))
|
|
||||||
}
|
|
||||||
"stratified" => {
|
|
||||||
let sampler = StratifiedSampler::create(params, full_res, loc, arena)?;
|
|
||||||
Ok(Sampler::Stratified(sampler))
|
|
||||||
}
|
|
||||||
_ => Err(anyhow!("Film type '{}' unknown at {}", name, loc)),
|
_ => Err(anyhow!("Film type '{}' unknown at {}", name, loc)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
use super::BasicScene;
|
use super::BasicScene;
|
||||||
use super::entities::*;
|
use super::entities::*;
|
||||||
|
use crate::spectra::get_colorspace_context;
|
||||||
use crate::utils::error::FileLoc;
|
use crate::utils::error::FileLoc;
|
||||||
use crate::utils::normalize_utf8;
|
use crate::utils::normalize_utf8;
|
||||||
|
use crate::utils::parameters::error_exit;
|
||||||
use crate::utils::parameters::{ParameterDictionary, ParsedParameterVector};
|
use crate::utils::parameters::{ParameterDictionary, ParsedParameterVector};
|
||||||
use crate::utils::parser::ParserTarget;
|
use crate::utils::parser::ParserTarget;
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
|
|
@ -186,7 +188,8 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn color_space(&mut self, name: &str, loc: FileLoc) {
|
fn color_space(&mut self, name: &str, loc: FileLoc) {
|
||||||
let _ = match RGBColorSpace::get_named(name) {
|
let stdcs = get_colorspace_context();
|
||||||
|
let _ = match stdcs.get_named(name) {
|
||||||
Ok(cs) => {
|
Ok(cs) => {
|
||||||
self.graphics_state.color_space = Some(cs);
|
self.graphics_state.color_space = Some(cs);
|
||||||
}
|
}
|
||||||
|
|
@ -536,14 +539,13 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
);
|
);
|
||||||
|
|
||||||
if type_name != "float" && type_name != "spectrum" {
|
if type_name != "float" && type_name != "spectrum" {
|
||||||
self.error_exit_deferred(
|
error_exit(
|
||||||
&loc,
|
Some(&loc),
|
||||||
&format!(
|
&format!(
|
||||||
"{}: texture type unknown. Must be \"float\" or \"spectrum\".",
|
"{}: texture type unknown. Must be \"float\" or \"spectrum\".",
|
||||||
tex_name
|
tex_name
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -554,8 +556,7 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
};
|
};
|
||||||
|
|
||||||
if names.contains(&name) {
|
if names.contains(&name) {
|
||||||
self.error_exit_deferred(&loc, &format!("Redefining texture \"{}\".", name));
|
error_exit(Some(&loc), &format!("Redefining texture \"{}\".", name));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
names.insert(name.to_string());
|
names.insert(name.to_string());
|
||||||
}
|
}
|
||||||
|
|
@ -582,9 +583,9 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
let entity = SceneEntity {
|
let entity = SceneEntity {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
loc,
|
loc,
|
||||||
parameters: params,
|
parameters: ParameterDictionary::new(*params, None),
|
||||||
};
|
};
|
||||||
self.graphics_state.current_material_name = self.scene.add_material(name);
|
self.graphics_state.current_material_name = self.scene.add_material(entity);
|
||||||
}
|
}
|
||||||
fn make_named_material(&mut self, _name: &str, _params: &ParsedParameterVector, _loc: FileLoc) {
|
fn make_named_material(&mut self, _name: &str, _params: &ParsedParameterVector, _loc: FileLoc) {
|
||||||
todo!()
|
todo!()
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use crate::core::texture::{FloatTexture, SpectrumTexture};
|
||||||
use crate::utils::arena;
|
use crate::utils::arena;
|
||||||
use crate::utils::arena::Arena;
|
use crate::utils::arena::Arena;
|
||||||
use crate::utils::error::FileLoc;
|
use crate::utils::error::FileLoc;
|
||||||
use crate::utils::parallel::run_async;
|
use crate::utils::parallel::{AsyncJob, run_async};
|
||||||
use crate::utils::parameters::{NamedTextures, ParameterDictionary, TextureParameterDictionary};
|
use crate::utils::parameters::{NamedTextures, ParameterDictionary, TextureParameterDictionary};
|
||||||
use crate::utils::{Upload, resolve_filename};
|
use crate::utils::{Upload, resolve_filename};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
@ -28,6 +28,7 @@ use shared::core::sampler::Sampler;
|
||||||
use shared::core::shape::Shape;
|
use shared::core::shape::Shape;
|
||||||
use shared::core::texture::SpectrumType;
|
use shared::core::texture::SpectrumType;
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::RGBColorSpace;
|
||||||
|
use shared::utils::Ptr;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|
@ -101,10 +102,6 @@ impl BasicScene {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================================================
|
|
||||||
// Options
|
|
||||||
// ========================================================================
|
|
||||||
|
|
||||||
pub fn set_options(
|
pub fn set_options(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
filter: SceneEntity,
|
filter: SceneEntity,
|
||||||
|
|
@ -113,6 +110,7 @@ impl BasicScene {
|
||||||
sampler: SceneEntity,
|
sampler: SceneEntity,
|
||||||
integ: SceneEntity,
|
integ: SceneEntity,
|
||||||
accel: SceneEntity,
|
accel: SceneEntity,
|
||||||
|
arena: &mut Arena,
|
||||||
) {
|
) {
|
||||||
*self.integrator.lock() = Some(integ);
|
*self.integrator.lock() = Some(integ);
|
||||||
*self.accelerator.lock() = Some(accel);
|
*self.accelerator.lock() = Some(accel);
|
||||||
|
|
@ -159,7 +157,7 @@ impl BasicScene {
|
||||||
&camera.base.name,
|
&camera.base.name,
|
||||||
&camera.base.parameters,
|
&camera.base.parameters,
|
||||||
&camera.camera_transform,
|
&camera.camera_transform,
|
||||||
medium.unwrap(),
|
*medium.unwrap(),
|
||||||
camera_film,
|
camera_film,
|
||||||
&camera.base.loc,
|
&camera.base.loc,
|
||||||
arena,
|
arena,
|
||||||
|
|
@ -169,10 +167,6 @@ impl BasicScene {
|
||||||
self.camera_state.lock().job = Some(camera_job);
|
self.camera_state.lock().job = Some(camera_job);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================================================
|
|
||||||
// Add methods
|
|
||||||
// ========================================================================
|
|
||||||
|
|
||||||
pub fn add_named_material(&self, name: &str, material: SceneEntity) {
|
pub fn add_named_material(&self, name: &str, material: SceneEntity) {
|
||||||
let mut state = self.material_state.lock();
|
let mut state = self.material_state.lock();
|
||||||
self.start_loading_normal_maps(&mut state, &material.parameters);
|
self.start_loading_normal_maps(&mut state, &material.parameters);
|
||||||
|
|
@ -186,7 +180,18 @@ impl BasicScene {
|
||||||
state.materials.len() - 1
|
state.materials.len() - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_float_texture(&self, name: String, texture: TextureSceneEntity) {
|
fn add_texture_generic<T, F>(
|
||||||
|
&self,
|
||||||
|
name: String,
|
||||||
|
texture: TextureSceneEntity,
|
||||||
|
state: &mut TextureState,
|
||||||
|
get_serial: impl FnOnce(&mut TextureState) -> &mut Vec<(String, TextureSceneEntity)>,
|
||||||
|
get_jobs: impl FnOnce(&mut TextureState) -> &mut HashMap<String, AsyncJob<Arc<T>>>,
|
||||||
|
create_fn: F,
|
||||||
|
) where
|
||||||
|
T: Send + 'static,
|
||||||
|
F: FnOnce(TextureSceneEntity) -> T + Send + 'static,
|
||||||
|
{
|
||||||
if texture.render_from_object.is_animated() {
|
if texture.render_from_object.is_animated() {
|
||||||
log::info!(
|
log::info!(
|
||||||
"{}: Animated world to texture not supported, using start",
|
"{}: Animated world to texture not supported, using start",
|
||||||
|
|
@ -194,113 +199,94 @@ impl BasicScene {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut state = self.texture_state.lock();
|
|
||||||
|
|
||||||
if texture.base.name != "imagemap" && texture.base.name != "ptex" {
|
if texture.base.name != "imagemap" && texture.base.name != "ptex" {
|
||||||
state.serial_float_textures.push((name, texture));
|
get_serial(state).push((name, texture));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let filename = resolve_filename(&texture.base.parameters.get_one_string("filename", ""));
|
let filename = resolve_filename(&texture.base.parameters.get_one_string("filename", ""));
|
||||||
if filename.is_empty() {
|
if !self.validate_texture_file(&filename, &texture.base.loc, &mut state.n_missing_textures)
|
||||||
log::error!(
|
{
|
||||||
"[{:?}] \"string filename\" not provided for image texture.",
|
|
||||||
texture.base.loc
|
|
||||||
);
|
|
||||||
state.n_missing_textures += 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !std::path::Path::new(&filename).exists() {
|
|
||||||
log::error!("[{:?}] {}: file not found.", texture.base.loc, filename);
|
|
||||||
state.n_missing_textures += 1;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.loading_texture_filenames.contains(&filename) {
|
if state.loading_texture_filenames.contains(&filename) {
|
||||||
state.serial_float_textures.push((name, texture));
|
get_serial(state).push((name, texture));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.loading_texture_filenames.insert(filename.clone());
|
state.loading_texture_filenames.insert(filename);
|
||||||
|
let job = run_async(move || Arc::new(create_fn(texture)));
|
||||||
|
get_jobs(state).insert(name, job);
|
||||||
|
}
|
||||||
|
|
||||||
let texture_clone = texture.clone();
|
fn validate_texture_file(&self, filename: &str, loc: &FileLoc, n_missing: &mut usize) -> bool {
|
||||||
let job = run_async(move || {
|
if filename.is_empty() {
|
||||||
let render_from_texture = texture_clone.render_from_object.start_transform;
|
log::error!(
|
||||||
let tex_dict =
|
"[{:?}] \"string filename\" not provided for image texture.",
|
||||||
TextureParameterDictionary::new(texture_clone.base.parameters.into(), None);
|
loc
|
||||||
let texture = FloatTexture::create(
|
);
|
||||||
&texture_clone.base.name,
|
*n_missing += 1;
|
||||||
&render_from_texture,
|
return false;
|
||||||
|
}
|
||||||
|
if !std::path::Path::new(filename).exists() {
|
||||||
|
log::error!("[{:?}] {}: file not found.", loc, filename);
|
||||||
|
*n_missing += 1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_float_texture(&self, name: String, texture: TextureSceneEntity, arena: &mut Arena) {
|
||||||
|
let mut state = self.texture_state.lock();
|
||||||
|
self.add_texture_generic(
|
||||||
|
name,
|
||||||
|
texture,
|
||||||
|
&mut state,
|
||||||
|
|s| &mut s.serial_float_textures,
|
||||||
|
|s| &mut s.float_texture_jobs,
|
||||||
|
|tex| {
|
||||||
|
let render_from_texture = tex.render_from_object.start_transform;
|
||||||
|
let tex_dict = TextureParameterDictionary::new(tex.base.parameters.into(), None);
|
||||||
|
FloatTexture::create(
|
||||||
|
&tex.base.name,
|
||||||
|
render_from_texture,
|
||||||
tex_dict,
|
tex_dict,
|
||||||
texture_clone.base.loc,
|
tex.base.loc,
|
||||||
arena,
|
arena,
|
||||||
)
|
)
|
||||||
.expect("Could not create Float texture");
|
.expect("Could not create Float texture")
|
||||||
|
},
|
||||||
Arc::new(texture)
|
|
||||||
});
|
|
||||||
|
|
||||||
state.float_texture_jobs.insert(name, job);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_spectrum_texture(&self, name: String, texture: TextureSceneEntity) {
|
|
||||||
if texture.render_from_object.is_animated() {
|
|
||||||
log::info!(
|
|
||||||
"{}: Animated world to texture not supported, using start",
|
|
||||||
texture.base.loc
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_spectrum_texture(
|
||||||
|
&self,
|
||||||
|
name: String,
|
||||||
|
texture: TextureSceneEntity,
|
||||||
|
arena: &mut Arena,
|
||||||
|
) {
|
||||||
let mut state = self.texture_state.lock();
|
let mut state = self.texture_state.lock();
|
||||||
|
self.add_texture_generic(
|
||||||
if texture.base.name != "imagemap" && texture.base.name != "ptex" {
|
name,
|
||||||
state.serial_spectrum_textures.push((name, texture));
|
texture,
|
||||||
return;
|
&mut state,
|
||||||
}
|
|s| &mut s.serial_spectrum_textures,
|
||||||
|
|s| &mut s.spectrum_texture_jobs,
|
||||||
let filename = resolve_filename(&texture.base.parameters.get_one_string("filename", ""));
|
|tex| {
|
||||||
if filename.is_empty() {
|
let render_from_texture = tex.render_from_object.start_transform;
|
||||||
log::error!(
|
let tex_dict = TextureParameterDictionary::new(tex.base.parameters.into(), None);
|
||||||
"[{:?}] \"string filename\" not provided for image texture.",
|
SpectrumTexture::create(
|
||||||
texture.base.loc
|
&tex.base.name,
|
||||||
);
|
|
||||||
state.n_missing_textures += 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !std::path::Path::new(&filename).exists() {
|
|
||||||
log::error!("[{:?}] {}: file not found.", texture.base.loc, filename);
|
|
||||||
state.n_missing_textures += 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if state.loading_texture_filenames.contains(&filename) {
|
|
||||||
state.serial_spectrum_textures.push((name, texture));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
state.loading_texture_filenames.insert(filename.clone());
|
|
||||||
|
|
||||||
let texture_clone = texture.clone();
|
|
||||||
let job = run_async(move || {
|
|
||||||
let render_from_texture = texture_clone.render_from_object.start_transform;
|
|
||||||
let tex_dict =
|
|
||||||
TextureParameterDictionary::new(texture_clone.base.parameters.into(), None);
|
|
||||||
let texture = SpectrumTexture::create(
|
|
||||||
&texture_clone.base.name,
|
|
||||||
render_from_texture,
|
render_from_texture,
|
||||||
tex_dict,
|
tex_dict,
|
||||||
SpectrumType::Albedo,
|
SpectrumType::Albedo,
|
||||||
texture_clone.base.loc,
|
tex.base.loc,
|
||||||
arena,
|
arena,
|
||||||
)
|
)
|
||||||
.expect("Could not crate spectrum texture.");
|
.expect("Could not create spectrum texture")
|
||||||
|
},
|
||||||
Arc::new(texture)
|
);
|
||||||
});
|
|
||||||
|
|
||||||
state.spectrum_texture_jobs.insert(name, job);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_area_light(&self, light: SceneEntity) -> usize {
|
pub fn add_area_light(&self, light: SceneEntity) -> usize {
|
||||||
|
|
@ -328,11 +314,7 @@ impl BasicScene {
|
||||||
self.instances.lock().extend(uses);
|
self.instances.lock().extend(uses);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================================================
|
pub fn create_textures(&self, arena: &mut Arena) -> NamedTextures {
|
||||||
// Finalization: Textures
|
|
||||||
// ========================================================================
|
|
||||||
|
|
||||||
pub fn create_textures(&self) -> NamedTextures {
|
|
||||||
let mut state = self.texture_state.lock();
|
let mut state = self.texture_state.lock();
|
||||||
|
|
||||||
let mut float_textures: HashMap<String, Arc<FloatTexture>> = HashMap::new();
|
let mut float_textures: HashMap<String, Arc<FloatTexture>> = HashMap::new();
|
||||||
|
|
@ -347,18 +329,18 @@ impl BasicScene {
|
||||||
spectrum_textures.insert(name, job.wait());
|
spectrum_textures.insert(name, job.wait());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create serial textures (need access to already-loaded textures)
|
// Create serial textures (need access to loaded textures, using shared memory)
|
||||||
let named = NamedTextures {
|
let mut named = NamedTextures {
|
||||||
float_textures: float_textures.clone(),
|
float_textures: Arc::new(float_textures.clone()),
|
||||||
albedo_spectrum_textures: spectrum_textures.clone(),
|
albedo_spectrum_textures: Arc::new(spectrum_textures.clone()),
|
||||||
illuminant_spectrum_textures: spectrum_textures.clone(),
|
illuminant_spectrum_textures: Arc::new(spectrum_textures.clone()),
|
||||||
unbounded_spectrum_textures: spectrum_textures.clone(),
|
unbounded_spectrum_textures: Arc::new(spectrum_textures.clone()),
|
||||||
};
|
};
|
||||||
|
|
||||||
for (name, entity) in state.serial_float_textures.drain(..) {
|
for (name, entity) in state.serial_float_textures.drain(..) {
|
||||||
let render_from_texture = entity.render_from_object.start_transform;
|
let render_from_texture = entity.render_from_object.start_transform;
|
||||||
let tex_dict =
|
let tex_dict =
|
||||||
TextureParameterDictionary::new(entity.base.parameters.into(), Some(named));
|
TextureParameterDictionary::new(entity.base.parameters.into(), Some(&named));
|
||||||
let tex = FloatTexture::create(
|
let tex = FloatTexture::create(
|
||||||
&entity.base.name,
|
&entity.base.name,
|
||||||
render_from_texture,
|
render_from_texture,
|
||||||
|
|
@ -367,13 +349,13 @@ impl BasicScene {
|
||||||
arena,
|
arena,
|
||||||
)
|
)
|
||||||
.expect("Could not create float texture");
|
.expect("Could not create float texture");
|
||||||
float_textures.insert(name, Arc::new(tex));
|
Arc::make_mut(&mut named.float_textures).insert(name, Arc::new(tex));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (name, entity) in state.serial_spectrum_textures.drain(..) {
|
for (name, entity) in state.serial_spectrum_textures.drain(..) {
|
||||||
let render_from_texture = entity.render_from_object.start_transform;
|
let render_from_texture = entity.render_from_object.start_transform;
|
||||||
let tex_dict =
|
let tex_dict =
|
||||||
TextureParameterDictionary::new(entity.base.parameters.into(), Some(named));
|
TextureParameterDictionary::new(entity.base.parameters.into(), Some(&named));
|
||||||
let tex = SpectrumTexture::create(
|
let tex = SpectrumTexture::create(
|
||||||
&entity.base.name,
|
&entity.base.name,
|
||||||
render_from_texture,
|
render_from_texture,
|
||||||
|
|
@ -383,32 +365,21 @@ impl BasicScene {
|
||||||
arena,
|
arena,
|
||||||
)
|
)
|
||||||
.expect("Could not create spectrum texture");
|
.expect("Could not create spectrum texture");
|
||||||
spectrum_textures.insert(name, Arc::new(tex));
|
Arc::make_mut(&mut named.albedo_spectrum_textures).insert(name, Arc::new(tex));
|
||||||
}
|
}
|
||||||
|
named
|
||||||
NamedTextures {
|
|
||||||
float_textures,
|
|
||||||
albedo_spectrum_textures: spectrum_textures,
|
|
||||||
unbounded_spectrum_textures: spectrum_textures,
|
|
||||||
illuminant_spectrum_textures: spectrum_textures,
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// ========================================================================
|
|
||||||
// Finalization: Materials
|
|
||||||
// ========================================================================
|
|
||||||
|
|
||||||
pub fn create_materials(
|
pub fn create_materials(
|
||||||
&self,
|
&self,
|
||||||
textures: &NamedTextures,
|
textures: &NamedTextures,
|
||||||
|
arena: &mut Arena,
|
||||||
) -> (HashMap<String, Material>, Vec<Material>) {
|
) -> (HashMap<String, Material>, Vec<Material>) {
|
||||||
let mut state = self.material_state.lock();
|
let mut state = self.material_state.lock();
|
||||||
|
|
||||||
// Resolve normal map jobs
|
// Resolve normal map jobs
|
||||||
let jobs: Vec<_> = state.normal_map_jobs.drain().collect();
|
for (filename, job) in state.normal_map_jobs.drain() {
|
||||||
for (filename, job) in jobs {
|
state.normal_maps.insert(filename, job.wait());
|
||||||
let image = job.wait();
|
|
||||||
state.normal_maps.insert(filename, image);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut named_materials: HashMap<String, Material> = HashMap::new();
|
let mut named_materials: HashMap<String, Material> = HashMap::new();
|
||||||
|
|
@ -440,7 +411,7 @@ impl BasicScene {
|
||||||
&mat_type,
|
&mat_type,
|
||||||
&tex_dict,
|
&tex_dict,
|
||||||
normal_map,
|
normal_map,
|
||||||
named_materials,
|
named_materials.into(),
|
||||||
entity.loc,
|
entity.loc,
|
||||||
arena,
|
arena,
|
||||||
)
|
)
|
||||||
|
|
@ -449,24 +420,27 @@ impl BasicScene {
|
||||||
named_materials.insert(name.clone(), mat);
|
named_materials.insert(name.clone(), mat);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut materials: Vec<Material> = Vec::with_capacity(state.materials.len());
|
let materials: Vec<Material> = state
|
||||||
|
.materials
|
||||||
for entity in &state.materials {
|
.iter()
|
||||||
|
.map(|entity| {
|
||||||
let normal_map = self.get_normal_map(&state, &entity.parameters);
|
let normal_map = self.get_normal_map(&state, &entity.parameters);
|
||||||
let tex_dict =
|
let tex_dict = TextureParameterDictionary::new(
|
||||||
TextureParameterDictionary::new(entity.parameters.into(), Some(*textures));
|
entity.parameters.clone().into(),
|
||||||
|
Some(textures),
|
||||||
|
);
|
||||||
|
|
||||||
let mat = Material::create(
|
Material::create(
|
||||||
&entity.name,
|
&entity.name,
|
||||||
&tex_dict,
|
&tex_dict,
|
||||||
normal_map,
|
normal_map,
|
||||||
named_materials,
|
&named_materials, // Reference
|
||||||
entity.loc,
|
entity.loc.clone(),
|
||||||
arena,
|
arena,
|
||||||
)
|
)
|
||||||
.expect("Could not create material");
|
.expect("Could not create material")
|
||||||
materials.push(mat);
|
})
|
||||||
}
|
.collect();
|
||||||
|
|
||||||
(named_materials, materials)
|
(named_materials, materials)
|
||||||
}
|
}
|
||||||
|
|
@ -478,6 +452,7 @@ impl BasicScene {
|
||||||
named_materials: &HashMap<String, Material>,
|
named_materials: &HashMap<String, Material>,
|
||||||
materials: &Vec<Material>,
|
materials: &Vec<Material>,
|
||||||
shape_lights: &HashMap<usize, Vec<Light>>,
|
shape_lights: &HashMap<usize, Vec<Light>>,
|
||||||
|
arena: &mut Arena,
|
||||||
) -> Vec<Primitive> {
|
) -> Vec<Primitive> {
|
||||||
let shapes = self.shapes.lock();
|
let shapes = self.shapes.lock();
|
||||||
let animated_shapes = self.animated_shapes.lock();
|
let animated_shapes = self.animated_shapes.lock();
|
||||||
|
|
@ -493,12 +468,10 @@ impl BasicScene {
|
||||||
|
|
||||||
let mut primitives = Vec::new();
|
let mut primitives = Vec::new();
|
||||||
|
|
||||||
// Static shapes: parallel load, serial upload
|
let loaded = self.load_shapes_parallel(&shapes, &lookup, arena);
|
||||||
let loaded = self.load_shapes_parallel(&shapes, &lookup);
|
|
||||||
primitives.extend(self.upload_shapes(arena, &shapes, loaded, &lookup));
|
primitives.extend(self.upload_shapes(arena, &shapes, loaded, &lookup));
|
||||||
|
|
||||||
// Animated shapes
|
let loaded_anim = self.load_animated_shapes_parallel(&animated_shapes, &lookup, arena);
|
||||||
let loaded_anim = self.load_animated_shapes_parallel(&animated_shapes, &lookup);
|
|
||||||
primitives.extend(self.upload_animated_shapes(
|
primitives.extend(self.upload_animated_shapes(
|
||||||
arena,
|
arena,
|
||||||
&animated_shapes,
|
&animated_shapes,
|
||||||
|
|
@ -513,6 +486,7 @@ impl BasicScene {
|
||||||
&self,
|
&self,
|
||||||
entities: &[ShapeSceneEntity],
|
entities: &[ShapeSceneEntity],
|
||||||
lookup: &SceneLookup,
|
lookup: &SceneLookup,
|
||||||
|
arena: &mut Arena,
|
||||||
) -> Vec<Vec<Shape>> {
|
) -> Vec<Vec<Shape>> {
|
||||||
entities
|
entities
|
||||||
.par_iter()
|
.par_iter()
|
||||||
|
|
@ -535,6 +509,7 @@ impl BasicScene {
|
||||||
&self,
|
&self,
|
||||||
entities: &[AnimatedShapeSceneEntity],
|
entities: &[AnimatedShapeSceneEntity],
|
||||||
lookup: &SceneLookup,
|
lookup: &SceneLookup,
|
||||||
|
arena: &mut Arena,
|
||||||
) -> Vec<Vec<Shape>> {
|
) -> Vec<Vec<Shape>> {
|
||||||
entities
|
entities
|
||||||
.par_iter()
|
.par_iter()
|
||||||
|
|
@ -549,6 +524,11 @@ impl BasicScene {
|
||||||
sh.transformed_base.base.loc,
|
sh.transformed_base.base.loc,
|
||||||
arena,
|
arena,
|
||||||
)
|
)
|
||||||
|
.map_err(|e| {
|
||||||
|
log::error!("{}: Failed to create shape: {}", sh.base.loc, e);
|
||||||
|
e
|
||||||
|
})
|
||||||
|
.ok()
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
@ -609,10 +589,10 @@ impl BasicScene {
|
||||||
} else {
|
} else {
|
||||||
let p = GeometricPrimitive::new(
|
let p = GeometricPrimitive::new(
|
||||||
shape_ptr,
|
shape_ptr,
|
||||||
Ptr::from(&mtl),
|
mtl.upload(arena),
|
||||||
area_light,
|
area_light.upload(arena),
|
||||||
mi.clone(),
|
mi.clone(),
|
||||||
alpha_tex.clone(),
|
alpha_tex.upload(arena),
|
||||||
);
|
);
|
||||||
Primitive::Geometric(p)
|
Primitive::Geometric(p)
|
||||||
};
|
};
|
||||||
|
|
@ -752,11 +732,13 @@ impl BasicScene {
|
||||||
let mut state = self.media_state.lock();
|
let mut state = self.media_state.lock();
|
||||||
|
|
||||||
if let Some(medium) = state.map.get(name) {
|
if let Some(medium) = state.map.get(name) {
|
||||||
return Some(medium.clone());
|
return Some(Arc::clone(medium));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(job) = state.jobs.remove(name) {
|
if let Some(job) = state.jobs.remove(name) {
|
||||||
let medium = Arc::new(job.wait());
|
let job: AsyncJob<Medium> = job;
|
||||||
|
let result: Medium = job.wait();
|
||||||
|
let medium: Arc<Medium> = Arc::new(job.wait());
|
||||||
state.map.insert(name.to_string(), medium.clone());
|
state.map.insert(name.to_string(), medium.clone());
|
||||||
return Some(medium);
|
return Some(medium);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ pub struct TextureState {
|
||||||
pub loading_texture_filenames: HashSet<String>,
|
pub loading_texture_filenames: HashSet<String>,
|
||||||
pub float_texture_jobs: HashMap<String, AsyncJob<Arc<FloatTexture>>>,
|
pub float_texture_jobs: HashMap<String, AsyncJob<Arc<FloatTexture>>>,
|
||||||
pub spectrum_texture_jobs: HashMap<String, AsyncJob<Arc<SpectrumTexture>>>,
|
pub spectrum_texture_jobs: HashMap<String, AsyncJob<Arc<SpectrumTexture>>>,
|
||||||
pub n_missing_textures: i32,
|
pub n_missing_textures: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -43,3 +43,12 @@ pub struct SingletonState<T> {
|
||||||
pub result: Option<Arc<T>>,
|
pub result: Option<Arc<T>>,
|
||||||
pub job: Option<AsyncJob<T>>,
|
pub job: Option<AsyncJob<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Default for SingletonState<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
result: None,
|
||||||
|
job: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,19 +69,21 @@ impl FloatTexture {
|
||||||
arena: &mut Arena,
|
arena: &mut Arena,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
match name {
|
match name {
|
||||||
"constant" => FloatConstantTexture::create(render_from_texture, params, loc),
|
"constant" => FloatConstantTexture::create(render_from_texture, params, loc, arena),
|
||||||
"scale" => FloatScaledTexture::create(&render_from_texture, ¶ms, &loc, arena),
|
"scale" => FloatScaledTexture::create(&render_from_texture, ¶ms, &loc, arena),
|
||||||
"mix" => FloatMixTexture::create(&render_from_texture, ¶ms, &loc, arena),
|
"mix" => FloatMixTexture::create(&render_from_texture, ¶ms, &loc, arena),
|
||||||
"directionmix" => {
|
"directionmix" => {
|
||||||
FloatDirectionMixTexture::create(&render_from_texture, ¶ms, &loc, arena)
|
FloatDirectionMixTexture::create(&render_from_texture, ¶ms, &loc, arena)
|
||||||
}
|
}
|
||||||
"bilerp" => FloatBilerpTexture::create(render_from_texture, params, loc),
|
"bilerp" => FloatBilerpTexture::create(render_from_texture, params, loc, arena),
|
||||||
"imagemap" => FloatImageTexture::create(render_from_texture, params, loc),
|
"imagemap" => FloatImageTexture::create(render_from_texture, params, loc, arena),
|
||||||
"checkerboard" => FloatCheckerboardTexture::create(render_from_texture, params, loc),
|
"checkerboard" => {
|
||||||
"dots" => FloatDotsTexture::create(render_from_texture, params, loc),
|
FloatCheckerboardTexture::create(render_from_texture, params, loc, arena)
|
||||||
"fbm" => FBmTexture::create(render_from_texture, params, loc),
|
}
|
||||||
"wrinkled" => WrinkledTexture::create(render_from_texture, params, loc),
|
"dots" => FloatDotsTexture::create(render_from_texture, params, loc, arena),
|
||||||
"windy" => WindyTexture::create(render_from_texture, params, loc),
|
"fbm" => FBmTexture::create(render_from_texture, params, loc, arena),
|
||||||
|
"wrinkled" => WrinkledTexture::create(render_from_texture, params, loc, arena),
|
||||||
|
"windy" => WindyTexture::create(render_from_texture, params, loc, arena),
|
||||||
_ => Err(anyhow!("Float texture type '{}' unknown at {}", name, loc)),
|
_ => Err(anyhow!("Float texture type '{}' unknown at {}", name, loc)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use super::state::PathState;
|
use super::state::PathState;
|
||||||
use shared::core::geometry::Ray;
|
use shared::core::geometry::Ray;
|
||||||
use shared::core::interaction::{Interaction, InteractionTrait};
|
use shared::core::interaction::{Interaction, InteractionTrait};
|
||||||
use shared::core::light::Light;
|
use shared::core::light::{Light, LightTrait};
|
||||||
use shared::core::primitive::{Primitive, PrimitiveTrait};
|
use shared::core::primitive::{Primitive, PrimitiveTrait};
|
||||||
use shared::core::shape::ShapeIntersection;
|
use shared::core::shape::ShapeIntersection;
|
||||||
use shared::lights::LightSampler;
|
use shared::lights::LightSampler;
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ impl PathConfig {
|
||||||
pub struct PathIntegrator {
|
pub struct PathIntegrator {
|
||||||
base: IntegratorBase,
|
base: IntegratorBase,
|
||||||
camera: Arc<Camera>,
|
camera: Arc<Camera>,
|
||||||
light_sampler: LightSampler,
|
sampler: LightSampler,
|
||||||
config: PathConfig,
|
config: PathConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,14 +77,14 @@ impl PathIntegrator {
|
||||||
aggregate: Arc<Primitive>,
|
aggregate: Arc<Primitive>,
|
||||||
lights: Vec<Arc<Light>>,
|
lights: Vec<Arc<Light>>,
|
||||||
camera: Arc<Camera>,
|
camera: Arc<Camera>,
|
||||||
|
sampler: LightSampler,
|
||||||
config: PathConfig,
|
config: PathConfig,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let base = IntegratorBase::new(aggregate, lights.clone());
|
let base = IntegratorBase::new(aggregate, lights.clone());
|
||||||
let light_sampler = LightSampler::new(&lights);
|
|
||||||
Self {
|
Self {
|
||||||
base,
|
base,
|
||||||
camera,
|
camera,
|
||||||
light_sampler,
|
sampler,
|
||||||
config,
|
config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -99,10 +99,7 @@ impl PathIntegrator {
|
||||||
) -> SampledSpectrum {
|
) -> SampledSpectrum {
|
||||||
let ctx = LightSampleContext::from(intr);
|
let ctx = LightSampleContext::from(intr);
|
||||||
|
|
||||||
let Some(sampled) = self
|
let Some(sampled) = self.sampler.sample_with_context(&ctx, sampler.get1d()) else {
|
||||||
.light_sampler
|
|
||||||
.sample_with_context(&ctx, sampler.get1d())
|
|
||||||
else {
|
|
||||||
return SampledSpectrum::zero();
|
return SampledSpectrum::zero();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -220,7 +217,7 @@ impl RayIntegratorTrait for PathIntegrator {
|
||||||
&mut state,
|
&mut state,
|
||||||
&ray,
|
&ray,
|
||||||
lambda,
|
lambda,
|
||||||
Some(&self.light_sampler),
|
Some(&self.sampler),
|
||||||
self.config.use_mis,
|
self.config.use_mis,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
@ -236,7 +233,7 @@ impl RayIntegratorTrait for PathIntegrator {
|
||||||
} else if self.config.use_mis {
|
} else if self.config.use_mis {
|
||||||
if !isect.area_light.is_null() {
|
if !isect.area_light.is_null() {
|
||||||
let light = &isect.area_light;
|
let light = &isect.area_light;
|
||||||
let p_l = self.light_sampler.pmf_with_context(&state.prev_ctx, light)
|
let p_l = self.sampler.pmf_with_context(&state.prev_ctx, light)
|
||||||
* light.pdf_li(&state.prev_ctx, ray.d, true);
|
* light.pdf_li(&state.prev_ctx, ray.d, true);
|
||||||
let w_b = power_heuristic(1, state.prev_pdf, 1, p_l);
|
let w_b = power_heuristic(1, state.prev_pdf, 1, p_l);
|
||||||
state.l += state.beta * w_b * le;
|
state.l += state.beta * w_b * le;
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ use indicatif::{ProgressBar, ProgressStyle};
|
||||||
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
use shared::core::camera::{Camera, CameraTrait};
|
use shared::core::camera::{Camera, CameraTrait};
|
||||||
|
use shared::core::film::Film;
|
||||||
use shared::core::geometry::{Bounds2i, Point2i, VectorLike};
|
use shared::core::geometry::{Bounds2i, Point2i, VectorLike};
|
||||||
use shared::core::options::get_options;
|
use shared::core::options::get_options;
|
||||||
use shared::core::sampler::get_camera_sample;
|
use shared::core::sampler::get_camera_sample;
|
||||||
|
|
@ -235,8 +236,9 @@ pub fn evaluate_pixel_sample<T: RayIntegratorTrait>(
|
||||||
if get_options().disable_wavelength_jitter {
|
if get_options().disable_wavelength_jitter {
|
||||||
lu = 0.5;
|
lu = 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
let lambda = camera.get_film().sample_wavelengths(lu);
|
let lambda = camera.get_film().sample_wavelengths(lu);
|
||||||
let film = camera.get_film();
|
let mut film: &mut Film = camera.get_film();
|
||||||
let filter = film.get_filter();
|
let filter = film.get_filter();
|
||||||
let camera_sample = get_camera_sample(sampler, pixel, filter);
|
let camera_sample = get_camera_sample(sampler, pixel, filter);
|
||||||
if let Some(mut camera_ray) = camera.generate_ray_differential(camera_sample, &lambda) {
|
if let Some(mut camera_ray) = camera.generate_ray_differential(camera_sample, &lambda) {
|
||||||
|
|
@ -258,7 +260,7 @@ pub fn evaluate_pixel_sample<T: RayIntegratorTrait>(
|
||||||
l *= camera_ray.weight;
|
l *= camera_ray.weight;
|
||||||
|
|
||||||
let std_spectra = get_spectra_context();
|
let std_spectra = get_spectra_context();
|
||||||
if l.has_nans() || l.y(&lambda, std_spectra).is_infinite() {
|
if l.has_nans() || l.y(&lambda, &std_spectra).is_infinite() {
|
||||||
l = SampledSpectrum::new(0.);
|
l = SampledSpectrum::new(0.);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
#[allow(dead_code)]
|
||||||
pub mod core;
|
pub mod core;
|
||||||
pub mod filters;
|
pub mod filters;
|
||||||
pub mod globals;
|
pub mod globals;
|
||||||
|
|
@ -14,3 +15,5 @@ pub mod utils;
|
||||||
pub mod gpu;
|
pub mod gpu;
|
||||||
|
|
||||||
pub use utils::arena::Arena;
|
pub use utils::arena::Arena;
|
||||||
|
pub use utils::error::FileLoc;
|
||||||
|
pub use utils::parameters::ParameterDictionary;
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ impl CreateDiffuseLight for DiffuseAreaLight {
|
||||||
colorspace,
|
colorspace,
|
||||||
shape,
|
shape,
|
||||||
alpha: stored_alpha.expect("Could not retrieve texture"),
|
alpha: stored_alpha.expect("Could not retrieve texture"),
|
||||||
lemit: Ptr::from(lemit.device()),
|
lemit: Ptr::from(&lemit.device()),
|
||||||
two_sided,
|
two_sided,
|
||||||
scale,
|
scale,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ impl CreateDistantLight for DistantLight {
|
||||||
let lemit = lookup_spectrum(&le);
|
let lemit = lookup_spectrum(&le);
|
||||||
Self {
|
Self {
|
||||||
base,
|
base,
|
||||||
lemit: Ptr::from(lemit.device()),
|
lemit: Ptr::from(&lemit.device()),
|
||||||
scale,
|
scale,
|
||||||
scene_center: Point3f::default(),
|
scene_center: Point3f::default(),
|
||||||
scene_radius: 0.,
|
scene_radius: 0.,
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ impl CreateGoniometricLight for GoniometricLight {
|
||||||
let distrib = PiecewiseConstant2D::from_image(&image);
|
let distrib = PiecewiseConstant2D::from_image(&image);
|
||||||
Self {
|
Self {
|
||||||
base,
|
base,
|
||||||
iemit: Ptr::from(iemit.device()),
|
iemit: Ptr::from(&iemit.device()),
|
||||||
scale,
|
scale,
|
||||||
image: Ptr::from(image.device_image()),
|
image: Ptr::from(image.device_image()),
|
||||||
distrib: Ptr::from(&distrib.device),
|
distrib: Ptr::from(&distrib.device),
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ pub fn create(
|
||||||
_medium: MediumInterface,
|
_medium: MediumInterface,
|
||||||
camera_transform: CameraTransform,
|
camera_transform: CameraTransform,
|
||||||
parameters: &ParameterDictionary,
|
parameters: &ParameterDictionary,
|
||||||
colorspace: &RGBColorSpace,
|
colorspace: Option<&RGBColorSpace>,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
) -> Result<Light> {
|
) -> Result<Light> {
|
||||||
let l = parameters.get_spectrum_array("L", SpectrumType::Illuminant);
|
let l = parameters.get_spectrum_array("L", SpectrumType::Illuminant);
|
||||||
|
|
@ -152,7 +152,7 @@ pub fn create(
|
||||||
scale /= spectrum_to_photometric(l[0]);
|
scale /= spectrum_to_photometric(l[0]);
|
||||||
l[0]
|
l[0]
|
||||||
} else {
|
} else {
|
||||||
Spectrum::Dense(colorspace.illuminant)
|
Spectrum::Dense(colorspace.unwrap().illuminant)
|
||||||
};
|
};
|
||||||
|
|
||||||
if e_v > 0.0 {
|
if e_v > 0.0 {
|
||||||
|
|
@ -165,7 +165,7 @@ pub fn create(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Image-based lights
|
// Image-based lights
|
||||||
let (image, image_cs) = load_image(&filename, &l, colorspace, loc)?;
|
let (image, image_cs) = load_image(&filename, &l, colorspace.unwrap(), loc)?;
|
||||||
|
|
||||||
scale /= spectrum_to_photometric(Spectrum::Dense(image_cs.illuminant));
|
scale /= spectrum_to_photometric(Spectrum::Dense(image_cs.illuminant));
|
||||||
|
|
||||||
|
|
@ -208,11 +208,11 @@ fn create_image_light(
|
||||||
|
|
||||||
// Extract luminance data
|
// Extract luminance data
|
||||||
let image_ptr = image.upload(arena);
|
let image_ptr = image.upload(arena);
|
||||||
let value = image.clone();
|
let value = ℑ
|
||||||
let mut data: Vec<Float> = (0..n_v)
|
let mut data: Vec<Float> = (0..n_v)
|
||||||
.flat_map(|v| {
|
.flat_map(|v| {
|
||||||
(0..n_u).map(move |u| {
|
(0..n_u).map(move |u| {
|
||||||
&value
|
value
|
||||||
.get_channels(Point2i::new(u as i32, v as i32))
|
.get_channels(Point2i::new(u as i32, v as i32))
|
||||||
.average()
|
.average()
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ impl CreatePointLight for PointLight {
|
||||||
medium_interface,
|
medium_interface,
|
||||||
);
|
);
|
||||||
let iemit = lookup_spectrum(&le);
|
let iemit = lookup_spectrum(&le);
|
||||||
let i = Ptr::from(iemit.device());
|
let i = Ptr::from(&iemit.device());
|
||||||
|
|
||||||
Self { base, scale, i }
|
Self { base, scale, i }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ use shared::core::spectrum::Spectrum;
|
||||||
use shared::lights::ProjectionLight;
|
use shared::lights::ProjectionLight;
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::RGBColorSpace;
|
||||||
use shared::utils::math::{radians, square};
|
use shared::utils::math::{radians, square};
|
||||||
use shared::utils::sampling::DeviceWindowedPiecewiseConstant2D;
|
use shared::utils::sampling::DeviceiecewiseConstant2D;
|
||||||
use shared::utils::{Ptr, Transform};
|
use shared::utils::{Ptr, Transform};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
|
@ -28,7 +28,6 @@ pub trait CreateProjectionLight {
|
||||||
scale: Float,
|
scale: Float,
|
||||||
image: Ptr<DeviceImage>,
|
image: Ptr<DeviceImage>,
|
||||||
image_color_space: Ptr<RGBColorSpace>,
|
image_color_space: Ptr<RGBColorSpace>,
|
||||||
distrib: Ptr<DeviceWindowedPiecewiseConstant2D>,
|
|
||||||
fov: Float,
|
fov: Float,
|
||||||
) -> Self;
|
) -> Self;
|
||||||
}
|
}
|
||||||
|
|
@ -40,7 +39,6 @@ impl CreateProjectionLight for ProjectionLight {
|
||||||
scale: Float,
|
scale: Float,
|
||||||
image: Ptr<DeviceImage>,
|
image: Ptr<DeviceImage>,
|
||||||
image_color_space: Ptr<RGBColorSpace>,
|
image_color_space: Ptr<RGBColorSpace>,
|
||||||
distrib: Ptr<DeviceWindowedPiecewiseConstant2D>,
|
|
||||||
fov: Float,
|
fov: Float,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let base = LightBase::new(
|
let base = LightBase::new(
|
||||||
|
|
@ -49,20 +47,39 @@ impl CreateProjectionLight for ProjectionLight {
|
||||||
medium_interface,
|
medium_interface,
|
||||||
);
|
);
|
||||||
|
|
||||||
let screen_from_light = Transform::perspective(fov, hither, 1e30).unwrap();
|
let opposite = (radians(fov) / 2.0).tan();
|
||||||
let light_from_screen = screen_from_light.inverse();
|
|
||||||
let hither = 1e-3;
|
|
||||||
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 opposite = (radians(fov) / 2.0).tan();
|
|
||||||
let aspect_ratio = if aspect > 1.0 { aspect } else { 1.0 / aspect };
|
let aspect_ratio = if aspect > 1.0 { aspect } else { 1.0 / aspect };
|
||||||
let a = 4.0 * square(opposite) * aspect_ratio;
|
let a = 4.0 * square(opposite) * aspect_ratio;
|
||||||
|
let screen_bounds = if aspect > 1.0 {
|
||||||
|
Bounds2f::from_points(Point2f::new(-aspect, -1.0), Point2f::new(aspect, 1.0))
|
||||||
|
} else {
|
||||||
|
Bounds2f::from_points(
|
||||||
|
Point2f::new(-1.0, -1.0 / aspect),
|
||||||
|
Point2f::new(1.0, 1.0 / aspect),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let hither = 1e-3;
|
||||||
|
let screen_from_light = Transform::perspective(fov, hither, 1e30).unwrap();
|
||||||
|
let light_from_screen = screen_from_light.inverse();
|
||||||
|
|
||||||
|
let dwda = |p: Point2f| {
|
||||||
|
let w =
|
||||||
|
Vector3f::from(light_from_screen.apply_to_point(Point3f::new(p.x(), p.y(), 0.0)));
|
||||||
|
cos_theta(w.normalize()).powi(3)
|
||||||
|
};
|
||||||
|
|
||||||
|
let d = image.get_sampling_distribution(dwda, screen_bounds);
|
||||||
|
let distrib =
|
||||||
|
PiecewiseConstant2D::new(d.as_slice(), d.x_size() as usize, d.y_size() as usize);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
base,
|
base,
|
||||||
image,
|
image,
|
||||||
image_color_space,
|
image_color_space,
|
||||||
distrib,
|
distrib: Ptr::from(&distrib.device),
|
||||||
screen_bounds,
|
screen_bounds,
|
||||||
screen_from_light,
|
screen_from_light,
|
||||||
light_from_screen,
|
light_from_screen,
|
||||||
|
|
@ -131,38 +148,12 @@ impl CreateLight for ProjectionLight {
|
||||||
let flip = Transform::scale(1., -1., 1.);
|
let flip = Transform::scale(1., -1., 1.);
|
||||||
let render_from_light_flip = render_from_light * flip;
|
let render_from_light_flip = render_from_light * flip;
|
||||||
|
|
||||||
let hither = 1e-3;
|
|
||||||
let screen_from_light = Transform::perspective(fov, hither, 1e30).unwrap();
|
|
||||||
let light_from_screen = screen_from_light.inverse();
|
|
||||||
|
|
||||||
let dwda = |p: Point2f| {
|
|
||||||
let w =
|
|
||||||
Vector3f::from(light_from_screen.apply_to_point(Point3f::new(p.x(), p.y(), 0.0)));
|
|
||||||
cos_theta(w.normalize()).powi(3)
|
|
||||||
};
|
|
||||||
|
|
||||||
let res = image.resolution();
|
|
||||||
let aspect = res.x() as Float / res.y() as Float;
|
|
||||||
let screen_bounds = if aspect > 1.0 {
|
|
||||||
Bounds2f::from_points(Point2f::new(-aspect, -1.0), Point2f::new(aspect, 1.0))
|
|
||||||
} else {
|
|
||||||
Bounds2f::from_points(
|
|
||||||
Point2f::new(-1.0, -1.0 / aspect),
|
|
||||||
Point2f::new(1.0, 1.0 / aspect),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let d = image.get_sampling_distribution(dwda, screen_bounds);
|
|
||||||
let distrib =
|
|
||||||
PiecewiseConstant2D::new(d.as_slice(), d.x_size() as usize, d.y_size() as usize);
|
|
||||||
|
|
||||||
let specific = ProjectionLight::new(
|
let specific = ProjectionLight::new(
|
||||||
render_from_light_flip,
|
render_from_light_flip,
|
||||||
medium.into(),
|
medium.into(),
|
||||||
scale,
|
scale,
|
||||||
image.upload(arena),
|
image.upload(arena),
|
||||||
colorspace.upload(arena),
|
colorspace.upload(arena),
|
||||||
distrib.upload(arena),
|
|
||||||
fov,
|
fov,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
0
src/materials/measured.rs
Normal file
0
src/materials/measured.rs
Normal file
|
|
@ -1,12 +1,9 @@
|
||||||
use crate::Arena;
|
use super::*;
|
||||||
use crate::core::sampler::CreateSampler;
|
|
||||||
use crate::utils::math::compute_radical_inverse_permutations;
|
use crate::utils::math::compute_radical_inverse_permutations;
|
||||||
use crate::utils::{FileLoc, ParameterDictionary};
|
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{Result, anyhow};
|
||||||
use shared::core::geometry::Point2i;
|
use shared::core::geometry::Point2i;
|
||||||
use shared::core::options::get_options;
|
use shared::core::options::get_options;
|
||||||
use shared::core::sampler::{HaltonSampler, MAX_HALTON_RESOLUTION, RandomizeStrategy};
|
use shared::core::sampler::{HaltonSampler, MAX_HALTON_RESOLUTION, RandomizeStrategy};
|
||||||
use shared::utils::Ptr;
|
|
||||||
|
|
||||||
pub trait CreateHaltonSampler {
|
pub trait CreateHaltonSampler {
|
||||||
fn new(
|
fn new(
|
||||||
|
|
@ -72,7 +69,7 @@ impl CreateSampler for HaltonSampler {
|
||||||
full_res: Point2i,
|
full_res: Point2i,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
_arena: &mut Arena,
|
_arena: &mut Arena,
|
||||||
) -> Result<Self> {
|
) -> Result<Sampler> {
|
||||||
let options = get_options();
|
let options = get_options();
|
||||||
let nsamp = options
|
let nsamp = options
|
||||||
.quick_render
|
.quick_render
|
||||||
|
|
@ -98,6 +95,6 @@ impl CreateSampler for HaltonSampler {
|
||||||
|
|
||||||
let sampler = HaltonSampler::new(nsamp, full_res, s, seed as u64);
|
let sampler = HaltonSampler::new(nsamp, full_res, s, seed as u64);
|
||||||
// arena.alloc(sampler);
|
// arena.alloc(sampler);
|
||||||
Ok(sampler)
|
Ok(Sampler::Halton(sampler))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,4 @@
|
||||||
use crate::Arena;
|
use super::*;
|
||||||
use crate::core::sampler::CreateSampler;
|
|
||||||
use crate::utils::{FileLoc, ParameterDictionary};
|
|
||||||
use anyhow::Result;
|
|
||||||
use shared::core::geometry::Point2i;
|
|
||||||
use shared::core::options::get_options;
|
use shared::core::options::get_options;
|
||||||
use shared::core::sampler::IndependentSampler;
|
use shared::core::sampler::IndependentSampler;
|
||||||
|
|
||||||
|
|
@ -12,7 +8,7 @@ impl CreateSampler for IndependentSampler {
|
||||||
_full_res: Point2i,
|
_full_res: Point2i,
|
||||||
_loc: &FileLoc,
|
_loc: &FileLoc,
|
||||||
arena: &mut Arena,
|
arena: &mut Arena,
|
||||||
) -> Result<Self> {
|
) -> Result<Sampler> {
|
||||||
let options = get_options();
|
let options = get_options();
|
||||||
let nsamp = options
|
let nsamp = options
|
||||||
.quick_render
|
.quick_render
|
||||||
|
|
@ -22,6 +18,6 @@ impl CreateSampler for IndependentSampler {
|
||||||
let seed = params.get_one_int("seed", options.seed);
|
let seed = params.get_one_int("seed", options.seed);
|
||||||
let sampler = Self::new(nsamp, seed as u64);
|
let sampler = Self::new(nsamp, seed as u64);
|
||||||
arena.alloc(sampler);
|
arena.alloc(sampler);
|
||||||
Ok(sampler)
|
Ok(Sampler::Independent(sampler))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,3 +2,17 @@ pub mod halton;
|
||||||
pub mod independent;
|
pub mod independent;
|
||||||
pub mod sobol;
|
pub mod sobol;
|
||||||
pub mod stratified;
|
pub mod stratified;
|
||||||
|
|
||||||
|
use crate::{Arena, FileLoc, ParameterDictionary};
|
||||||
|
use anyhow::Result;
|
||||||
|
use shared::core::geometry::Point2i;
|
||||||
|
use shared::core::sampler::Sampler;
|
||||||
|
|
||||||
|
pub trait CreateSampler {
|
||||||
|
fn create(
|
||||||
|
params: &ParameterDictionary,
|
||||||
|
full_res: Point2i,
|
||||||
|
loc: &FileLoc,
|
||||||
|
arena: &mut Arena,
|
||||||
|
) -> Result<Sampler>;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,4 @@
|
||||||
use crate::Arena;
|
use super::*;
|
||||||
use crate::core::sampler::CreateSampler;
|
|
||||||
use crate::utils::{FileLoc, ParameterDictionary};
|
|
||||||
use anyhow::{Result, anyhow};
|
|
||||||
use shared::core::geometry::Point2i;
|
use shared::core::geometry::Point2i;
|
||||||
use shared::core::options::get_options;
|
use shared::core::options::get_options;
|
||||||
use shared::core::sampler::{PaddedSobolSampler, RandomizeStrategy, SobolSampler, ZSobolSampler};
|
use shared::core::sampler::{PaddedSobolSampler, RandomizeStrategy, SobolSampler, ZSobolSampler};
|
||||||
|
|
@ -12,7 +9,7 @@ impl CreateSampler for SobolSampler {
|
||||||
full_res: Point2i,
|
full_res: Point2i,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
_arena: &mut Arena,
|
_arena: &mut Arena,
|
||||||
) -> Result<Self> {
|
) -> Result<Sampler> {
|
||||||
let options = get_options();
|
let options = get_options();
|
||||||
let nsamp = options
|
let nsamp = options
|
||||||
.quick_render
|
.quick_render
|
||||||
|
|
@ -32,7 +29,7 @@ impl CreateSampler for SobolSampler {
|
||||||
|
|
||||||
let sampler = Self::new(nsamp, full_res, s, Some(seed as u64));
|
let sampler = Self::new(nsamp, full_res, s, Some(seed as u64));
|
||||||
// arena.alloc(sampler);
|
// arena.alloc(sampler);
|
||||||
Ok(sampler)
|
Ok(Sampler::Sobol(sampler))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,7 +39,7 @@ impl CreateSampler for PaddedSobolSampler {
|
||||||
_full_res: Point2i,
|
_full_res: Point2i,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
arena: &mut Arena,
|
arena: &mut Arena,
|
||||||
) -> Result<Self> {
|
) -> Result<Sampler> {
|
||||||
let options = get_options();
|
let options = get_options();
|
||||||
let nsamp = options
|
let nsamp = options
|
||||||
.quick_render
|
.quick_render
|
||||||
|
|
@ -64,8 +61,8 @@ impl CreateSampler for PaddedSobolSampler {
|
||||||
};
|
};
|
||||||
|
|
||||||
let sampler = Self::new(nsamp, s, Some(seed as u64));
|
let sampler = Self::new(nsamp, s, Some(seed as u64));
|
||||||
arena.alloc(sampler);
|
// arena.alloc(sampler);
|
||||||
Ok(sampler)
|
Ok(Sampler::PaddedSobol(sampler))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,7 +72,7 @@ impl CreateSampler for ZSobolSampler {
|
||||||
full_res: Point2i,
|
full_res: Point2i,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
arena: &mut Arena,
|
arena: &mut Arena,
|
||||||
) -> Result<Self> {
|
) -> Result<Sampler> {
|
||||||
let options = get_options();
|
let options = get_options();
|
||||||
let nsamp = options
|
let nsamp = options
|
||||||
.quick_render
|
.quick_render
|
||||||
|
|
@ -97,7 +94,7 @@ impl CreateSampler for ZSobolSampler {
|
||||||
};
|
};
|
||||||
|
|
||||||
let sampler = ZSobolSampler::new(nsamp, full_res, s, Some(seed as u64));
|
let sampler = ZSobolSampler::new(nsamp, full_res, s, Some(seed as u64));
|
||||||
arena.alloc(sampler);
|
// arena.alloc(sampler);
|
||||||
Ok(sampler)
|
Ok(Sampler::ZSobol(sampler))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,4 @@
|
||||||
use crate::Arena;
|
use super::*;
|
||||||
use crate::core::sampler::CreateSampler;
|
|
||||||
use crate::utils::{FileLoc, ParameterDictionary};
|
|
||||||
use anyhow::Result;
|
|
||||||
use shared::core::geometry::Point2i;
|
|
||||||
use shared::core::options::get_options;
|
use shared::core::options::get_options;
|
||||||
use shared::core::sampler::StratifiedSampler;
|
use shared::core::sampler::StratifiedSampler;
|
||||||
|
|
||||||
|
|
@ -12,7 +8,7 @@ impl CreateSampler for StratifiedSampler {
|
||||||
_full_res: Point2i,
|
_full_res: Point2i,
|
||||||
_loc: &FileLoc,
|
_loc: &FileLoc,
|
||||||
_arena: &mut Arena,
|
_arena: &mut Arena,
|
||||||
) -> Result<Self> {
|
) -> Result<Sampler> {
|
||||||
let options = get_options();
|
let options = get_options();
|
||||||
let jitter = params.get_one_bool("jitter", true);
|
let jitter = params.get_one_bool("jitter", true);
|
||||||
let (x_samples, y_samples) = if options.quick_render {
|
let (x_samples, y_samples) = if options.quick_render {
|
||||||
|
|
@ -32,6 +28,6 @@ impl CreateSampler for StratifiedSampler {
|
||||||
let sampler = Self::new(x_samples, y_samples, Some(seed as u64), jitter);
|
let sampler = Self::new(x_samples, y_samples, Some(seed as u64), jitter);
|
||||||
// arena.aloc(sampler);
|
// arena.aloc(sampler);
|
||||||
|
|
||||||
Ok(sampler)
|
Ok(Sampler::Stratified(sampler))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,15 @@ use shared::spectra::{
|
||||||
use shared::utils::math::square;
|
use shared::utils::math::square;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct DenselySampledSpectrumBuffer {
|
pub struct DenselySampledSpectrumBuffer {
|
||||||
pub lambda_min: i32,
|
pub lambda_min: i32,
|
||||||
pub lambda_max: i32,
|
pub lambda_max: i32,
|
||||||
pub values: Vec<Float>,
|
pub values: Vec<Float>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Eq for DenselySampledSpectrumBuffer {}
|
||||||
|
|
||||||
impl Hash for DenselySampledSpectrumBuffer {
|
impl Hash for DenselySampledSpectrumBuffer {
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
self.lambda_min.hash(state);
|
self.lambda_min.hash(state);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::globals::{ACES_TABLE, DCI_P3_TABLE, REC2020_TABLE, SRGB_TABLE};
|
use crate::globals::{ACES_TABLE, DCI_P3_TABLE, REC2020_TABLE, SRGB_TABLE};
|
||||||
use crate::spectra::colorspace::RGBColorSpaceData;
|
use crate::spectra::colorspace::RGBColorSpaceData;
|
||||||
|
use anyhow::{Result, anyhow};
|
||||||
use shared::core::geometry::Point2f;
|
use shared::core::geometry::Point2f;
|
||||||
use shared::core::spectrum::Spectrum;
|
use shared::core::spectrum::Spectrum;
|
||||||
use shared::core::spectrum::StandardSpectra;
|
use shared::core::spectrum::StandardSpectra;
|
||||||
|
|
@ -105,6 +106,18 @@ pub struct StandardColorSpaces {
|
||||||
pub aces2065_1: Arc<RGBColorSpaceData>,
|
pub aces2065_1: Arc<RGBColorSpaceData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl StandardColorSpaces {
|
||||||
|
pub fn get_named(&self, name: &str) -> Result<Arc<RGBColorSpaceData>> {
|
||||||
|
match name.to_lowercase().as_str() {
|
||||||
|
"srgb" => Ok(self.srgb.clone()),
|
||||||
|
"dci-p3" => Ok(self.dci_p3.clone()),
|
||||||
|
"rec2020" => Ok(self.rec2020.clone()),
|
||||||
|
"aces2065-1" => Ok(self.aces2065_1.clone()),
|
||||||
|
_ => Err(anyhow!("No such spectrum")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_colorspace_context() -> StandardColorSpaces {
|
pub fn get_colorspace_context() -> StandardColorSpaces {
|
||||||
StandardColorSpaces {
|
StandardColorSpaces {
|
||||||
srgb: SRGB.clone().into(),
|
srgb: SRGB.clone().into(),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use anyhow::Result;
|
|
||||||
use crate::Arena;
|
use crate::Arena;
|
||||||
|
use anyhow::Result;
|
||||||
use shared::{
|
use shared::{
|
||||||
core::texture::{SpectrumType, TextureEvalContext},
|
core::texture::{SpectrumType, TextureEvalContext},
|
||||||
textures::{FloatConstantTexture, SpectrumConstantTexture},
|
textures::{FloatConstantTexture, SpectrumConstantTexture},
|
||||||
|
|
@ -9,7 +9,7 @@ use shared::{
|
||||||
use crate::{
|
use crate::{
|
||||||
core::texture::{
|
core::texture::{
|
||||||
CreateFloatTexture, CreateSpectrumTexture, FloatTexture, FloatTextureTrait,
|
CreateFloatTexture, CreateSpectrumTexture, FloatTexture, FloatTextureTrait,
|
||||||
SpectrumTextureTrait, SpectrumTextureTrait, SpectrumTexture
|
SpectrumTexture, SpectrumTextureTrait,
|
||||||
},
|
},
|
||||||
utils::{FileLoc, TextureParameterDictionary},
|
utils::{FileLoc, TextureParameterDictionary},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ use crate::utils::mipmap::MIPMap;
|
||||||
use crate::utils::sampling::{PiecewiseConstant2D, WindowedPiecewiseConstant2D};
|
use crate::utils::sampling::{PiecewiseConstant2D, WindowedPiecewiseConstant2D};
|
||||||
use shared::core::color::RGBToSpectrumTable;
|
use shared::core::color::RGBToSpectrumTable;
|
||||||
use shared::core::image::DeviceImage;
|
use shared::core::image::DeviceImage;
|
||||||
|
use shared::core::light::Light;
|
||||||
|
use shared::core::material::Material;
|
||||||
use shared::core::shape::Shape;
|
use shared::core::shape::Shape;
|
||||||
use shared::core::spectrum::Spectrum;
|
use shared::core::spectrum::Spectrum;
|
||||||
use shared::core::texture::{GPUFloatTexture, GPUSpectrumTexture};
|
use shared::core::texture::{GPUFloatTexture, GPUSpectrumTexture};
|
||||||
|
|
@ -155,6 +157,13 @@ impl Upload for Shape {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Upload for Light {
|
||||||
|
type Target = Light;
|
||||||
|
fn upload(&self, arena: &mut Arena) -> Ptr<Self::Target> {
|
||||||
|
arena.alloc(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Upload for Image {
|
impl Upload for Image {
|
||||||
type Target = DeviceImage;
|
type Target = DeviceImage;
|
||||||
fn upload(&self, arena: &mut Arena) -> Ptr<Self::Target> {
|
fn upload(&self, arena: &mut Arena) -> Ptr<Self::Target> {
|
||||||
|
|
@ -169,6 +178,13 @@ impl Upload for Spectrum {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Upload for Material {
|
||||||
|
type Target = Material;
|
||||||
|
fn upload(&self, arena: &mut Arena) -> Ptr<Self::Target> {
|
||||||
|
arena.alloc(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Upload for DenselySampledSpectrumBuffer {
|
impl Upload for DenselySampledSpectrumBuffer {
|
||||||
type Target = DenselySampledSpectrum;
|
type Target = DenselySampledSpectrum;
|
||||||
fn upload(&self, arena: &mut Arena) -> Ptr<Self::Target> {
|
fn upload(&self, arena: &mut Arena) -> Ptr<Self::Target> {
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ impl DigitPermutation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compute_radical_inverse_permutations(seed: u64) -> (Vec<u16>, Vec<DigitPermutation>) {
|
pub fn compute_radical_inverse_permutations(seed: u64) -> (Vec<u16>, Vec<DeviceDigitPermutation>) {
|
||||||
let temp_data: Vec<Vec<u16>> = PRIMES
|
let temp_data: Vec<Vec<u16>> = PRIMES
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&base| DigitPermutation::new(base as i32, seed).permutations)
|
.map(|&base| DigitPermutation::new(base as i32, seed).permutations)
|
||||||
|
|
@ -68,11 +68,13 @@ pub fn compute_radical_inverse_permutations(seed: u64) -> (Vec<u16>, Vec<DigitPe
|
||||||
|
|
||||||
// let ptr_to_data = storage_base_ptr.add(current_offset);
|
// let ptr_to_data = storage_base_ptr.add(current_offset);
|
||||||
|
|
||||||
views.push(DigitPermutation::new(
|
views.push(
|
||||||
|
DigitPermutation::new(
|
||||||
base as i32,
|
base as i32,
|
||||||
n_digits as u64,
|
n_digits as u64,
|
||||||
// Ptr::from(ptr_to_data),
|
)
|
||||||
));
|
.device,
|
||||||
|
);
|
||||||
|
|
||||||
// current_offset += len;
|
// current_offset += len;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ where
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct AsyncJob<T> {
|
pub struct AsyncJob<T> {
|
||||||
receiver: Receiver<T>,
|
receiver: Receiver<T>,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -234,12 +234,12 @@ impl PBRTParameter for String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default, Clone)]
|
||||||
pub struct NamedTextures {
|
pub struct NamedTextures {
|
||||||
pub float_textures: HashMap<String, Arc<FloatTexture>>,
|
pub float_textures: Arc<HashMap<String, Arc<FloatTexture>>>,
|
||||||
pub albedo_spectrum_textures: HashMap<String, Arc<SpectrumTexture>>,
|
pub albedo_spectrum_textures: Arc<HashMap<String, Arc<SpectrumTexture>>>,
|
||||||
pub unbounded_spectrum_textures: HashMap<String, Arc<SpectrumTexture>>,
|
pub unbounded_spectrum_textures: Arc<HashMap<String, Arc<SpectrumTexture>>>,
|
||||||
pub illuminant_spectrum_textures: HashMap<String, Arc<SpectrumTexture>>,
|
pub illuminant_spectrum_textures: Arc<HashMap<String, Arc<SpectrumTexture>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
|
|
@ -704,11 +704,11 @@ fn read_spectrum_from_file(filename: &str) -> Result<Spectrum, String> {
|
||||||
|
|
||||||
pub struct TextureParameterDictionary {
|
pub struct TextureParameterDictionary {
|
||||||
dict: Arc<ParameterDictionary>,
|
dict: Arc<ParameterDictionary>,
|
||||||
textures: Option<NamedTextures>,
|
textures: Option<&NamedTextures>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextureParameterDictionary {
|
impl TextureParameterDictionary {
|
||||||
pub fn new(dict: Arc<ParameterDictionary>, textures: Option<NamedTextures>) -> Self {
|
pub fn new(dict: Arc<ParameterDictionary>, textures: Option<&NamedTextures>) -> Self {
|
||||||
Self { dict, textures }
|
Self { dict, textures }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue