ALmost done with changes

This commit is contained in:
Wito Wiala 2026-05-20 20:52:34 +01:00
parent 72acb8ccdf
commit 82255e5046
18 changed files with 168 additions and 321 deletions

View file

@ -1,5 +1,5 @@
use crate::core::geometry::{Bounds3f, Ray};
use crate::core::aggregates::BVHAggregate; use crate::core::aggregates::BVHAggregate;
use crate::core::geometry::{Bounds3f, Ray};
use crate::core::interaction::{Interaction, InteractionTrait, SurfaceInteraction}; use crate::core::interaction::{Interaction, InteractionTrait, SurfaceInteraction};
use crate::core::light::Light; use crate::core::light::Light;
use crate::core::material::Material; use crate::core::material::Material;
@ -104,7 +104,12 @@ impl PrimitiveTrait for SimplePrimitive {
fn intersect(&self, r: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection> { fn intersect(&self, r: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection> {
let mut si = self.shape.intersect(r, t_max)?; let mut si = self.shape.intersect(r, t_max)?;
si.set_intersection_properties(self.material, Ptr::null(), MediumInterface::default(), r.medium); si.set_intersection_properties(
self.material,
Ptr::null(),
MediumInterface::default(),
r.medium,
);
Some(si) Some(si)
} }
@ -183,7 +188,7 @@ impl PrimitiveTrait for AnimatedPrimitive {
} }
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Copy)] #[derive(Default, Debug, Clone, Copy)]
pub struct LinearBVHNode { pub struct LinearBVHNode {
bounds: Bounds3f, bounds: Bounds3f,
} }
@ -217,7 +222,6 @@ pub enum Primitive {
KdTree(KdTreeAggregate), KdTree(KdTreeAggregate),
} }
impl<T: PrimitiveTrait> PrimitiveTrait for Ptr<T> { impl<T: PrimitiveTrait> PrimitiveTrait for Ptr<T> {
fn bounds(&self) -> Bounds3f { fn bounds(&self) -> Bounds3f {
unsafe { self.as_ref().bounds() } unsafe { self.as_ref().bounds() }
@ -231,5 +235,3 @@ impl<T: PrimitiveTrait> PrimitiveTrait for Ptr<T> {
unsafe { self.as_ref().intersect_p(r, t_max) } unsafe { self.as_ref().intersect_p(r, t_max) }
} }
} }

View file

@ -1,3 +1,4 @@
use crate::Arena;
use rayon::prelude::*; use rayon::prelude::*;
use shared::core::aggregates::{BVHAggregate as DeviceBVHAggregate, LinearBVHNode}; use shared::core::aggregates::{BVHAggregate as DeviceBVHAggregate, LinearBVHNode};
use shared::core::geometry::{Bounds3f, Point3f, Ray, Vector3f}; use shared::core::geometry::{Bounds3f, Point3f, Ray, Vector3f};
@ -5,8 +6,7 @@ use shared::core::primitive::{Primitive, PrimitiveTrait};
use shared::core::shape::ShapeIntersection; use shared::core::shape::ShapeIntersection;
use shared::utils::math::encode_morton_3; use shared::utils::math::encode_morton_3;
use shared::utils::{find_interval, partition_slice}; use shared::utils::{find_interval, partition_slice};
use crate::Arena; use shared::{gvec_from_slice, Float};
use shared::Float;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
@ -26,7 +26,6 @@ struct BVHSplitBucket {
pub bounds: Bounds3f, pub bounds: Bounds3f,
} }
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]
struct MortonPrimitive { struct MortonPrimitive {
primitive_index: usize, primitive_index: usize,
@ -890,7 +889,8 @@ impl<P: PrimitiveTrait + Clone + Send + Sync> BVHAggregate<P> {
impl BVHAggregate<Primitive> { impl BVHAggregate<Primitive> {
pub fn to_device(&self, arena: &mut Arena) -> DeviceBVHAggregate { pub fn to_device(&self, arena: &mut Arena) -> DeviceBVHAggregate {
let shared_nodes: Vec<shared::core::aggregates::LinearBVHNode> = self.nodes let shared_nodes: Vec<shared::core::aggregates::LinearBVHNode> = self
.nodes
.iter() .iter()
.map(|n| shared::core::aggregates::LinearBVHNode { .map(|n| shared::core::aggregates::LinearBVHNode {
bounds: n.bounds, bounds: n.bounds,
@ -905,7 +905,7 @@ impl BVHAggregate<Primitive> {
max_prims_in_node: self.max_prims_in_node as u32, max_prims_in_node: self.max_prims_in_node as u32,
primitives: gvec_from_slice(&self.primitives), primitives: gvec_from_slice(&self.primitives),
primitive_count: self.primitives.len() as u32, primitive_count: self.primitives.len() as u32,
nodes: gvec_from_slice(&self.shared_nodes), nodes: gvec_from_slice(&shared_nodes),
node_count: self.nodes.len() as u32, node_count: self.nodes.len() as u32,
} }
} }

View file

@ -386,7 +386,7 @@ impl CameraFactory for Camera {
); );
arena.alloc(camera); arena.alloc(camera.clone());
Ok(Camera::Realistic(camera)) Ok(Camera::Realistic(camera))
} }
"spherical" => { "spherical" => {

View file

@ -15,7 +15,7 @@ use shared::spectra::{
cie::SWATCHES_RAW, DenselySampledSpectrum, PiecewiseLinearSpectrum, RGBColorSpace, cie::SWATCHES_RAW, DenselySampledSpectrum, PiecewiseLinearSpectrum, RGBColorSpace,
}; };
use shared::utils::math::{linear_least_squares, SquareMatrix}; use shared::utils::math::{linear_least_squares, SquareMatrix};
use shared::{Float, Ptr}; use shared::{Float, Ptr, leak};
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, LazyLock}; use std::sync::{Arc, LazyLock};
@ -27,7 +27,7 @@ const SWATCH_REFLECTANCES: LazyLock<[Spectrum; N_SWATCH_REFLECTANCES]> = LazyLoc
std::array::from_fn(|i| { std::array::from_fn(|i| {
let raw_data = SWATCHES_RAW[i]; let raw_data = SWATCHES_RAW[i];
let pls = PiecewiseLinearSpectrum::from_interleaved(raw_data, false); let pls = PiecewiseLinearSpectrum::from_interleaved(raw_data, false);
Spectrum::Piecewise(pls) Spectrum::Piecewise(leak(pls))
}) })
}); });

View file

@ -1,10 +1,10 @@
use crate::utils::sampling::PiecewiseConstant2D;
use crate::utils::{FileLoc, ParameterDictionary};
use crate::Arena; use crate::Arena;
use crate::{FileLoc, ParameterDictionary};
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use shared::core::filter::Filter; use shared::core::filter::Filter;
use shared::core::geometry::{Bounds2f, Point2f, Vector2f}; use shared::core::geometry::{Bounds2f, Point2f, Vector2f};
use shared::filters::*; use shared::filters::*;
use shared::utils::sampling::PiecewiseConstant2D;
use shared::{Array2D, Float}; use shared::{Array2D, Float};
pub trait FilterFactory { pub trait FilterFactory {

View file

@ -256,8 +256,8 @@ impl HostImage {
} }
} }
pub fn set_channel(&mut self, p: Point2i, values: &ImageChannelValues) { pub fn set_channel(&mut self, p: Point2i, c: i32, val: Float) {
self.inner.set_channel(p, values[i]); self.inner.set_channel(p, c, val);
} }
pub fn select_channels(&self, desc: &ImageChannelDesc) -> Self { pub fn select_channels(&self, desc: &ImageChannelDesc) -> Self {

View file

@ -1,6 +1,4 @@
use super::HostImage; use super::HostImage;
use crate::core::image::pixel::PixelStorageTrait;
use crate::core::image::PixelStorage;
use rayon::prelude::*; use rayon::prelude::*;
use shared::core::color::ColorEncoding; use shared::core::color::ColorEncoding;
use shared::core::geometry::{Bounds2i, Point2i}; use shared::core::geometry::{Bounds2i, Point2i};
@ -18,98 +16,84 @@ pub struct ResampleWeight {
impl HostImage { impl HostImage {
pub fn flip_y(&mut self) { pub fn flip_y(&mut self) {
let res = self.inner.resolution; let res = self.inner.resolution;
let nc = self.inner.n_channels as usize; let nc = self.inner.n_channels;
match self.inner.format { for y in 0..res.y() / 2 {
PixelFormat::U8 => flip_y_kernel(self.inner.pixels.as_u8_mut(), res, nc), let y2 = res.y() - 1 - y;
PixelFormat::F16 => flip_y_kernel(self.inner.pixels.as_f16_mut(), res, nc), for x in 0..res.x() {
PixelFormat::F32 => flip_y_kernel(self.inner.pixels.as_f32_slice_mut(), res, nc), for c in 0..nc {
let a = self.inner.get_channel(Point2i::new(x, y), c);
let b = self.inner.get_channel(Point2i::new(x, y2), c);
self.inner.set_channel(Point2i::new(x, y), c, b);
self.inner.set_channel(Point2i::new(x, y2), c, a);
}
}
} }
} }
pub fn crop(&self, bounds: Bounds2i) -> HostImage { pub fn crop(&self, bounds: Bounds2i) -> HostImage {
let n_channels = self.inner.n_channels as usize; let nc = self.inner.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(),
); );
let new_names = self.channel_names.clone();
let mut new_image = let mut new_image =
HostImage::new(self.inner.format, new_res, &new_names, self.inner.encoding); HostImage::new(self.inner.format, new_res, &self.channel_names, self.inner.encoding);
match self.inner.format { for y in 0..new_res.y() {
PixelFormat::U8 => crop_kernel( for x in 0..new_res.x() {
self.inner.pixels.as_u8(), let src = Point2i::new(bounds.p_min.x() + x, bounds.p_min.y() + y);
new_image.inner.pixels.as_u8_mut(), let dst = Point2i::new(x, y);
self.inner.resolution, for c in 0..nc {
bounds, let val = self.inner.get_channel(src, c);
n_channels, new_image.inner.set_channel(dst, c, val);
), }
PixelFormat::F16 => crop_kernel( }
self.inner.pixels.as_f16(),
new_image.inner.pixels.as_f16_mut(),
self.inner.resolution,
bounds,
n_channels,
),
PixelFormat::F32 => crop_kernel(
self.inner.pixels.as_f32_slice(),
new_image.inner.pixels.as_f32_slice_mut(),
self.inner.resolution,
bounds,
n_channels,
),
} }
new_image new_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.inner.format { let w = (extent.p_max.x() - extent.p_min.x()) as usize;
PixelFormat::U8 => { let channels = self.inner.n_channels as usize;
copy_rect_out_kernel(self.inner.pixels.as_u8(), self, extent, buf, wrap)
} buf.par_chunks_mut(w * channels)
PixelFormat::F16 => { .enumerate()
copy_rect_out_kernel(self.inner.pixels.as_f16(), self, extent, buf, wrap) .for_each(|(y_rel, row_buf)| {
} let y = extent.p_min.y() + y_rel as i32;
PixelFormat::F32 => { for x_rel in 0..w {
copy_rect_out_kernel(self.inner.pixels.as_f32_slice(), self, extent, buf, wrap) let x = extent.p_min.x() + x_rel as i32;
} let p = Point2i::new(x, y);
} for c in 0..channels {
row_buf[x_rel * channels + c] =
self.inner.get_channel_with_wrap(p, c as i32, wrap);
}
}
});
} }
pub fn copy_rect_in(&mut self, extent: Bounds2i, buf: &[Float]) { pub fn copy_rect_in(&mut self, extent: Bounds2i, buf: &[Float]) {
let res = self.inner.resolution; let w = (extent.p_max.x() - extent.p_min.x()) as usize;
let n_channels = self.inner.n_channels as usize; let channels = self.inner.n_channels as usize;
let encoding = self.inner.encoding;
let format = self.inner.format;
match format { for (y_rel, row) in buf.chunks(w * channels).enumerate() {
PixelFormat::U8 => copy_rect_in_kernel( let y = extent.p_min.y() + y_rel as i32;
self.inner.pixels.as_u8_mut(), if y < 0 || y >= self.inner.resolution.y() {
res, continue;
n_channels, }
encoding,
extent, for x_rel in 0..w {
buf, let x = extent.p_min.x() + x_rel as i32;
), if x < 0 || x >= self.inner.resolution.x() {
PixelFormat::F16 => copy_rect_in_kernel( continue;
self.inner.pixels.as_f16_mut(), }
res, for c in 0..channels {
n_channels, let val = row[x_rel * channels + c];
encoding, self.inner.set_channel(Point2i::new(x, y), c as i32, val);
extent, }
buf, }
),
PixelFormat::F32 => copy_rect_in_kernel(
self.inner.pixels.as_f32_slice_mut(),
res,
n_channels,
encoding,
extent,
buf,
),
} }
} }
@ -180,6 +164,7 @@ impl HostImage {
} }
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 nc = prev.n_channels();
let mut next = HostImage::new( let mut next = HostImage::new(
prev.inner.format, prev.inner.format,
new_res, new_res,
@ -187,177 +172,37 @@ impl HostImage {
prev.inner.encoding, prev.inner.encoding,
); );
match next.inner.format { for y in 0..new_res.y() {
PixelFormat::U8 => downsample_kernel( for x in 0..new_res.x() {
next.inner.pixels.as_u8_mut(), let src_x = x * 2;
new_res, let src_y = y * 2;
&prev, for c in 0..nc {
internal_wrap, let mut sum = 0.0;
), let mut count = 0.0;
PixelFormat::F16 => downsample_kernel( for dy in 0..2i32 {
next.inner.pixels.as_f16_mut(), for dx in 0..2i32 {
new_res, let sx = src_x + dx;
&prev, let sy = src_y + dy;
internal_wrap, if sx < old.x() && sy < old.y() {
), sum += prev.inner.get_channel_with_wrap(
PixelFormat::F32 => downsample_kernel( Point2i::new(sx, sy), c, internal_wrap,
next.inner.pixels.as_f32_slice_mut(), );
new_res, count += 1.0;
&prev, }
internal_wrap, }
), }
let avg = if count > 0.0 { sum / count } else { 0.0 };
next.inner.set_channel(Point2i::new(x, y), c, avg);
}
}
} }
levels.push(next); levels.push(next);
} }
levels levels
} }
} }
fn flip_y_kernel<T: PixelStorageTrait>(pixels: &mut [T], res: Point2i, channels: usize) {
let w = res.x() as usize;
let h = res.y() as usize;
let stride = w * channels;
for y in 0..(h / 2) {
let bot = h - 1 - y;
for i in 0..stride {
pixels.swap(y * stride + i, bot * stride + i);
}
}
}
fn crop_kernel<T: PixelStorageTrait>(
src: &[T],
dst: &mut [T],
src_res: Point2i,
bounds: Bounds2i,
channels: usize,
) {
let dst_w = (bounds.p_max.x() - bounds.p_min.x()) as usize;
// let dst_h = (bounds.p_max.y() - bounds.p_min.y()) as usize;
dst.par_chunks_mut(dst_w * channels)
.enumerate()
.for_each(|(dy, dst_row)| {
let sy = bounds.p_min.y() as usize + dy;
let sx_start = bounds.p_min.x() as usize;
let src_offset = (sy * src_res.x() as usize + sx_start) * channels;
let count = dst_w * channels;
dst_row.copy_from_slice(&src[src_offset..src_offset + count]);
});
}
fn copy_rect_out_kernel<T: PixelStorageTrait>(
src: &[T],
image: &HostImage,
extent: Bounds2i,
buf: &mut [Float],
wrap: WrapMode2D,
) {
let w = (extent.p_max.x() - extent.p_min.x()) as usize;
let channels = image.n_channels() as usize;
let enc = image.encoding();
let res = image.resolution();
buf.par_chunks_mut(w * channels)
.enumerate()
.for_each(|(y_rel, row_buf)| {
let y = extent.p_min.y() + y_rel as i32;
for x_rel in 0..w {
let x = extent.p_min.x() + x_rel as i32;
if x >= 0 && x < res.x() && y >= 0 && y < res.y() {
let offset = (y as usize * res.x() as usize + x as usize) * channels;
for c in 0..channels {
row_buf[x_rel * channels + c] = T::to_linear(src[offset + c], enc);
}
} else {
// We fall back to get_channel which handles the wrapping math.
let p = Point2i::new(x, y);
for c in 0..channels {
row_buf[x_rel * channels + c] =
image.get_channel_with_wrap(p, c.try_into().unwrap(), wrap);
}
}
}
});
}
fn copy_rect_in_kernel<T: PixelStorageTrait>(
dst: &mut [T],
res: Point2i,
channels: usize,
enc: ColorEncoding,
extent: Bounds2i,
buf: &[Float],
) {
let w = (extent.p_max.x() - extent.p_min.x()) as usize;
let res_x = res.x() as usize;
let rows = buf.chunks(w * channels);
for (y_rel, row) in rows.enumerate() {
let y = extent.p_min.y() + y_rel as i32;
if y < 0 || y >= res.y() {
continue;
}
let dst_row_start = (y as usize * res_x) * channels;
for (x_rel, &val) in row.iter().enumerate() {
let c = x_rel % channels;
let x_pixel = x_rel / channels;
let x = extent.p_min.x() + x_pixel as i32;
if x >= 0 && x < res.x() {
let idx = dst_row_start + (x as usize * channels) + c;
dst[idx] = T::from_linear(val, enc);
}
}
}
}
fn downsample_kernel<T: PixelStorageTrait>(
dst: &mut [T],
dst_res: Point2i,
prev: &HostImage,
wrap: WrapMode2D,
) {
let w = dst_res.x() as usize;
let channels = prev.n_channels();
let enc = prev.encoding();
let old_res = prev.resolution();
dst.par_chunks_mut(w * channels as usize)
.enumerate()
.for_each(|(y, row)| {
let src_y = y * 2;
for x in 0..w {
let src_x = x * 2;
for c in 0..channels {
let mut sum = 0.0;
let mut count = 0.0;
for dy in 0..2 {
for dx in 0..2 {
let sx = src_x as i32 + dx;
let sy = src_y as i32 + dy;
if sx < old_res.x() && sy < old_res.y() {
sum += prev.get_channel_with_wrap(Point2i::new(sx, sy), c, wrap);
count += 1.0;
}
}
}
let avg = if count > 0.0 { sum / count } else { 0.0 };
row[x * channels as usize + c as usize] = T::from_linear(avg, enc);
}
}
});
}
fn resample_weights(old_res: usize, new_res: usize) -> Vec<ResampleWeight> { fn resample_weights(old_res: usize, new_res: usize) -> Vec<ResampleWeight> {
let filter_radius = 2.0; let filter_radius = 2.0;
let tau = 2.0; let tau = 2.0;
@ -416,7 +261,6 @@ fn compute_resize_tile(
let mut x_buf = vec![0.0; n_channels * ny_in * nx_out]; let mut x_buf = vec![0.0; n_channels * ny_in * nx_out];
// Resize X
for y in 0..ny_in { for y in 0..ny_in {
for x in 0..nx_out { for x in 0..nx_out {
let x_global = out_extent.p_min.x() + x as i32; let x_global = out_extent.p_min.x() + x as i32;
@ -438,7 +282,6 @@ fn compute_resize_tile(
let mut out_buf = vec![0.0; n_channels * nx_out * ny_out]; let mut out_buf = vec![0.0; n_channels * nx_out * ny_out];
// Resize Y
for x in 0..nx_out { for x in 0..nx_out {
for y in 0..ny_out { for y in 0..ny_out {
let y_global = out_extent.p_min.y() + y as i32; let y_global = out_extent.p_min.y() + y as i32;

View file

@ -36,7 +36,7 @@ pub struct SceneLookup<'a> {
pub media: &'a HashMap<String, Arc<Medium>>, pub media: &'a HashMap<String, Arc<Medium>>,
pub named_materials: &'a HashMap<String, Material>, pub named_materials: &'a HashMap<String, Material>,
pub materials: &'a Vec<Material>, pub materials: &'a Vec<Material>,
pub shape_lights: &'a HashMap<usize, Vec<Light>>, pub shape_lights: &'a HashMap<usize, Vec<Ptr<Light>>>,
} }
impl<'a> SceneLookup<'a> { impl<'a> SceneLookup<'a> {
@ -154,7 +154,6 @@ impl BasicScene {
job: None, job: None,
}; };
let arena_sampler = Arc::clone(&arena);
let sampler_film = Arc::clone(&film_instance); let sampler_film = Arc::clone(&film_instance);
let sampler_job = run_async(move || { let sampler_job = run_async(move || {
let res = sampler_film.as_ref().base().full_resolution; let res = sampler_film.as_ref().base().full_resolution;
@ -163,7 +162,7 @@ impl BasicScene {
&sampler.parameters, &sampler.parameters,
res, res,
&sampler.loc, &sampler.loc,
&arena_sampler, &arena,
) )
.map_err(|e| anyhow!("Failed to create sampler: {}", e)) .map_err(|e| anyhow!("Failed to create sampler: {}", e))
}); });
@ -556,9 +555,9 @@ impl BasicScene {
shape_entities: &[ShapeSceneEntity], shape_entities: &[ShapeSceneEntity],
textures: &NamedTextures, textures: &NamedTextures,
arena: &Arena, arena: &Arena,
) -> HashMap<usize, Vec<Light>> { ) -> HashMap<usize, Vec<Ptr<Light>>> {
let light_state = self.light_state.lock(); let light_state = self.light_state.lock();
let mut shape_lights: HashMap<usize, Vec<Light>> = HashMap::new(); let mut shape_lights: HashMap<usize, Vec<Ptr<Light>>> = HashMap::new();
for (i, entity) in shape_entities.iter().enumerate() { for (i, entity) in shape_entities.iter().enumerate() {
let light_idx = match entity.light_index { let light_idx = match entity.light_index {
@ -592,7 +591,7 @@ impl BasicScene {
let render_from_light = *entity.render_from_object; let render_from_light = *entity.render_from_object;
let lights: Vec<Light> = shapes let lights: Vec<Ptr<Light>> = shapes
.iter() .iter()
.filter_map(|shape| { .filter_map(|shape| {
match crate::core::light::create_area_light( match crate::core::light::create_area_light(
@ -676,7 +675,7 @@ impl BasicScene {
mtl: Material, mtl: Material,
alpha_tex: &Option<Arc<FloatTexture>>, alpha_tex: &Option<Arc<FloatTexture>>,
mi: MediumInterface, mi: MediumInterface,
al_params: Option<&AreaLightEntity>, al_params: Option<&SceneEntity>,
render_from_light: Transform, render_from_light: Transform,
film_cs: Option<&RGBColorSpace>, film_cs: Option<&RGBColorSpace>,
arena: &mut Arena, arena: &mut Arena,
@ -938,16 +937,15 @@ impl BasicScene {
.get(&shape_ctx.entity_index) .get(&shape_ctx.entity_index)
.and_then(|lights| lights.get(shape_ctx.shape_index)); .and_then(|lights| lights.get(shape_ctx.shape_index));
let light_ptr = arena.alloc_opt(shape_lights_opt); let light_ptr = shape_lights_opt.copied().unwrap_or(Ptr::null());
let shape_ptr = shape_ctx.shape; let shape_ptr = shape_ctx.shape;
let prim = if area_light.is_null() && !mi.is_medium_transition() && alpha_tex.is_none() let prim = if light_ptr.is_null() && !mi.is_medium_transition() && alpha_tex.is_none() {
{
Primitive::Simple(SimplePrimitive::new(shape_ptr, Ptr::from(&mtl))) Primitive::Simple(SimplePrimitive::new(shape_ptr, Ptr::from(&mtl)))
} else { } else {
Primitive::Geometric(GeometricPrimitive::new( Primitive::Geometric(GeometricPrimitive::new(
shape_ptr, shape_ptr,
arena.alloc(mtl), arena.alloc(mtl),
area_light, light_ptr,
mi.clone(), mi.clone(),
arena.upload(alpha_tex), arena.upload(alpha_tex),
)) ))

View file

@ -1,9 +1,9 @@
use super::*; use super::*;
use crate::core::film::{CreateFilmBase, PixelSensor}; use crate::core::film::{CreateFilmBase, CreatePixelSensor};
use crate::Arena; use crate::Arena;
use anyhow::Result; use anyhow::Result;
use shared::core::camera::CameraTransform; use shared::core::camera::CameraTransform;
use shared::core::film::{Film, FilmBase, RGBFilm, RGBPixel}; use shared::core::film::{Film, FilmBase, RGBFilm, RGBPixel, PixelSensor};
use shared::spectra::RGBColorSpace; use shared::spectra::RGBColorSpace;
impl CreateFilm for RGBFilm { impl CreateFilm for RGBFilm {
@ -13,13 +13,13 @@ impl CreateFilm for RGBFilm {
filter: Filter, filter: Filter,
_camera_transform: Option<CameraTransform>, _camera_transform: Option<CameraTransform>,
loc: &FileLoc, loc: &FileLoc,
_arena: &Arena, arena: &Arena,
) -> Result<Film> { ) -> Result<Film> {
let colorspace = params.color_space.as_ref().cloned().unwrap_or_else(crate::spectra::default_colorspace_arc); let colorspace = params.color_space.as_ref().cloned().unwrap_or_else(crate::spectra::default_colorspace_arc);
let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY)?; let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY)?;
let write_fp16 = params.get_one_bool("savefp16", true)?; let write_fp16 = params.get_one_bool("savefp16", true)?;
let sensor = PixelSensor::create(params, colorspace.clone(), exposure_time, loc)?; let sensor = PixelSensor::create(params, colorspace.clone(), exposure_time, loc, arena)?;
let film_base = FilmBase::create(params, filter, Some(&sensor.device()), loc)?; let film_base = FilmBase::create(params, filter, Some(&sensor), loc)?;
let film = RGBFilm::new(film_base, &colorspace, max_component_value, write_fp16); let film = RGBFilm::new(film_base, &colorspace, max_component_value, write_fp16);
Ok(Film::RGB(film)) Ok(Film::RGB(film))
} }

View file

@ -11,7 +11,7 @@ use shared::core::light::{Light, LightBase, LightType};
use shared::core::medium::{Medium, MediumInterface}; use shared::core::medium::{Medium, MediumInterface};
use shared::core::shape::{Shape, ShapeTrait}; use shared::core::shape::{Shape, ShapeTrait};
use shared::core::spectrum::Spectrum; use shared::core::spectrum::Spectrum;
use shared::core::texture::{SpectrumType, TextureEvalContext}; use shared::core::texture::{SpectrumType, TextureEvalContext, GPUFloatTexture};
use shared::lights::DiffuseAreaLight; use shared::lights::DiffuseAreaLight;
use shared::spectra::RGBColorSpace; use shared::spectra::RGBColorSpace;
use shared::utils::Transform; use shared::utils::Transform;

View file

@ -1,13 +1,10 @@
use std::path::Path;
use crate::core::image::{HostImage, ImageIO}; use crate::core::image::{HostImage, ImageIO};
use crate::core::light::lookup_spectrum; use crate::core::light::lookup_spectrum;
use crate::core::spectrum::spectrum_to_photometric; use crate::core::spectrum::spectrum_to_photometric;
use crate::core::texture::FloatTexture; use crate::core::texture::FloatTexture;
use crate::utils::sampling::PiecewiseConstant2D;
use crate::utils::resolve_filename; use crate::utils::resolve_filename;
use crate::{Arena, FileLoc, ParameterDictionary}; use crate::{Arena, FileLoc, ParameterDictionary};
use anyhow::{Result, anyhow}; use anyhow::{anyhow, Result};
use shared::core::geometry::Point2i; use shared::core::geometry::Point2i;
use shared::core::light::{Light, LightBase, LightType}; use shared::core::light::{Light, LightBase, LightType};
use shared::core::medium::{Medium, MediumInterface}; use shared::core::medium::{Medium, MediumInterface};
@ -16,8 +13,10 @@ use shared::core::spectrum::Spectrum;
use shared::core::texture::SpectrumType; use shared::core::texture::SpectrumType;
use shared::lights::GoniometricLight; use shared::lights::GoniometricLight;
use shared::spectra::RGBColorSpace; use shared::spectra::RGBColorSpace;
use shared::utils::sampling::PiecewiseConstant2D;
use shared::utils::{Ptr, Transform}; use shared::utils::{Ptr, Transform};
use shared::{Float, PI}; use shared::{Float, PI};
use std::path::Path;
pub fn create( pub fn create(
render_from_light: Transform, render_from_light: Transform,
@ -25,11 +24,10 @@ pub fn create(
params: &ParameterDictionary, params: &ParameterDictionary,
loc: &FileLoc, loc: &FileLoc,
_shape: &Shape, _shape: &Shape,
_alpha_text: &FloatTexture, _alpha_tex: &FloatTexture,
colorspace: Option<&RGBColorSpace>, colorspace: Option<&RGBColorSpace>,
arena: &Arena, arena: &Arena,
) -> Result<Light> { ) -> Result<Light> {
let default_cs = crate::spectra::default_colorspace(); let default_cs = crate::spectra::default_colorspace();
let cs = colorspace.unwrap_or(&default_cs); let cs = colorspace.unwrap_or(&default_cs);
let i = params let i = params
@ -39,14 +37,13 @@ pub fn create(
SpectrumType::Illuminant, SpectrumType::Illuminant,
) )
.expect("Could not retrieve spectrum"); .expect("Could not retrieve spectrum");
let mut scale = params.get_one_float("scale", 1.)?; let mut scale = params.get_one_float("scale", 1.)?;
let filename = resolve_filename(&params.get_one_string("filename", "")?); let filename = resolve_filename(&params.get_one_string("filename", "")?);
let image: Ptr<HostImage> = if filename.is_empty() {
Ptr::null() let host_image = if !filename.is_empty() {
} else {
let im = HostImage::read(Path::new(&filename), None) let im = HostImage::read(Path::new(&filename), None)
.map_err(|e| anyhow!("could not load image '{}': {}", filename, e))?; .map_err(|e| anyhow!("could not load image '{}': {}", filename, e))?;
let loaded = im.image; let loaded = im.image;
let res = loaded.resolution(); let res = loaded.resolution();
@ -56,7 +53,6 @@ pub fn create(
filename filename
)); ));
} }
if res.x() != res.y() { if res.x() != res.y() {
return Err(anyhow!( return Err(anyhow!(
"image resolution ({}, {}) is non-square; unlikely to be an equal-area map", "image resolution ({}, {}) is non-square; unlikely to be an equal-area map",
@ -65,15 +61,19 @@ pub fn create(
)); ));
} }
Ptr::from(&convert_to_luminance_image(&loaded, &filename, loc)?) Some(convert_to_luminance_image(&loaded, &filename, loc)?)
} else {
None
}; };
scale /= spectrum_to_photometric(i); scale /= spectrum_to_photometric(i);
let phi_v = params.get_one_float("power", -1.0)?;
let phi_v = params.get_one_float("power", -1.0)?;
if phi_v > 0.0 { if phi_v > 0.0 {
let k_e = compute_emissive_power(&image); if let Some(ref img) = host_image {
scale *= phi_v / k_e; let k_e = compute_emissive_power(img);
scale *= phi_v / k_e;
}
} }
let swap_yz: [Float; 16] = [ let swap_yz: [Float; 16] = [
@ -81,7 +81,7 @@ pub fn create(
]; ];
let t = let t =
Transform::from_flat(&swap_yz).expect("Could not create transform for GoniometricLight"); Transform::from_flat(&swap_yz).expect("Could not create transform for GoniometricLight");
let _final_render_from_light = render_from_light * t; let final_render_from_light = render_from_light * t;
let mi = match medium { let mi = match medium {
Some(m) => { Some(m) => {
@ -93,31 +93,35 @@ pub fn create(
} }
None => MediumInterface::default(), None => MediumInterface::default(),
}; };
let base = LightBase::new(LightType::DeltaPosition, render_from_light, mi);
let base = LightBase::new(LightType::DeltaPosition, final_render_from_light, mi);
let iemit = lookup_spectrum(&i); let iemit = lookup_spectrum(&i);
let image_ptr = if !image.is_null() { // Build distribution from host image, then upload both to arena
let distrib = PiecewiseConstant2D::from_image(&image); let (image_ptr, distrib_ptr) = match host_image {
let distrib_ptr = arena.alloc(distrib); Some(img) => {
let img_ptr = arena.alloc(image); let distrib = PiecewiseConstant2D::from_image(&img.inner);
(img_ptr, distrib_ptr) (arena.alloc(img.inner), arena.alloc(distrib))
} else { }
(Ptr::null(), Ptr::null()) None => (Ptr::null(), Ptr::null()),
}; };
let specific = GoniometricLight { let specific = GoniometricLight {
base, base,
iemit: arena.alloc(*iemit), iemit: arena.alloc((*iemit).clone()),
scale, scale,
image: image_ptr.0, image: image_ptr,
distrib: image_ptr.1, distrib: distrib_ptr,
}; };
Ok(Light::Goniometric(specific)) Ok(Light::Goniometric(specific))
} }
fn convert_to_luminance_image(image: &HostImage, filename: &str, loc: &FileLoc) -> Result<HostImage> { fn convert_to_luminance_image(
image: &HostImage,
filename: &str,
loc: &FileLoc,
) -> Result<HostImage> {
let res = image.resolution(); let res = image.resolution();
let rgb_desc = image.get_channel_desc(&["R", "G", "B"]); let rgb_desc = image.get_channel_desc(&["R", "G", "B"]);
let y_desc = image.get_channel_desc(&["Y"]); let y_desc = image.get_channel_desc(&["Y"]);

View file

@ -3,7 +3,6 @@ use crate::core::light::lookup_spectrum;
use crate::core::spectrum::spectrum_to_photometric; use crate::core::spectrum::spectrum_to_photometric;
use crate::spectra::get_spectra_context; use crate::spectra::get_spectra_context;
use crate::utils::resolve_filename; use crate::utils::resolve_filename;
use crate::utils::sampling::{PiecewiseConstant2D, WindowedPiecewiseConstant2D};
use crate::{Arena, FileLoc, ParameterDictionary, ArenaUpload, Upload}; use crate::{Arena, FileLoc, ParameterDictionary, ArenaUpload, Upload};
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use rayon::prelude::*; use rayon::prelude::*;

View file

@ -1,13 +1,11 @@
use crate::core::image::{HostImage, ImageIO}; use crate::core::image::{HostImage, ImageIO};
use crate::core::spectrum::spectrum_to_photometric; use crate::core::spectrum::spectrum_to_photometric;
use crate::core::texture::FloatTexture; use crate::core::texture::FloatTexture;
use crate::utils::sampling::PiecewiseConstant2D;
use crate::utils::resolve_filename; use crate::utils::resolve_filename;
use crate::{Arena, FileLoc, ParameterDictionary, ArenaUpload}; use crate::{Arena, ArenaUpload, FileLoc, ParameterDictionary};
use anyhow::{Result, anyhow}; use anyhow::{anyhow, Result};
use shared::Float;
use shared::core::geometry::{ use shared::core::geometry::{
Bounds2f, Point2f, Point2i, Point3f, Vector3f, VectorLike, cos_theta, cos_theta, Bounds2f, Point2f, Point2i, Point3f, Vector3f, VectorLike,
}; };
use shared::core::light::{Light, LightBase, LightType}; use shared::core::light::{Light, LightBase, LightType};
use shared::core::medium::{Medium, MediumInterface}; use shared::core::medium::{Medium, MediumInterface};
@ -15,8 +13,10 @@ use shared::core::shape::Shape;
use shared::core::spectrum::Spectrum; use shared::core::spectrum::Spectrum;
use shared::lights::ProjectionLight; use shared::lights::ProjectionLight;
use shared::spectra::RGBColorSpace; use shared::spectra::RGBColorSpace;
use shared::utils::Transform;
use shared::utils::math::{radians, square}; use shared::utils::math::{radians, square};
use shared::utils::sampling::PiecewiseConstant2D;
use shared::utils::Transform;
use shared::Float;
use std::path::Path; use std::path::Path;
pub fn create( pub fn create(

View file

@ -1,7 +1,7 @@
use crate::core::light::lookup_spectrum; use crate::core::light::lookup_spectrum;
use crate::core::spectrum::spectrum_to_photometric; use crate::core::spectrum::spectrum_to_photometric;
use crate::core::texture::FloatTexture; use crate::core::texture::FloatTexture;
use crate::utils::{Arena, FileLoc, ParameterDictionary}; use crate::{Arena, FileLoc, ParameterDictionary};
use anyhow::Result; use anyhow::Result;
use shared::core::geometry::{Frame, Point3f, VectorLike}; use shared::core::geometry::{Frame, Point3f, VectorLike};
use shared::core::light::{Light, LightBase, LightType}; use shared::core::light::{Light, LightBase, LightType};
@ -22,6 +22,7 @@ trait CreateSpotLight {
scale: Float, scale: Float,
cos_falloff_start: Float, cos_falloff_start: Float,
total_width: Float, total_width: Float,
arena: &Arena
) -> Self; ) -> Self;
} }
@ -33,6 +34,7 @@ impl CreateSpotLight for SpotLight {
scale: Float, scale: Float,
cos_falloff_start: Float, cos_falloff_start: Float,
total_width: Float, total_width: Float,
arena: &Arena
) -> Self { ) -> Self {
let base = LightBase::new( let base = LightBase::new(
LightType::DeltaPosition, LightType::DeltaPosition,
@ -41,7 +43,7 @@ impl CreateSpotLight for SpotLight {
); );
let i = lookup_spectrum(&le); let i = lookup_spectrum(&le);
let iemit = arena.alloc(i); let iemit = arena.alloc_arc(i);
Self { Self {
base, base,
iemit, iemit,
@ -100,7 +102,7 @@ pub fn create(
None => MediumInterface::default(), None => MediumInterface::default(),
}; };
let specific = SpotLight::new(final_render, mi, i, scale, coneangle, coneangle - conedelta); let specific = SpotLight::new(final_render, mi, i, scale, coneangle, coneangle - conedelta, arena);
arena.alloc(specific); arena.alloc(specific);
Ok(Light::Spot(specific)) Ok(Light::Spot(specific))
} }

View file

@ -1,12 +1,12 @@
use crate::core::image::{HostImage, ImageIO}; use crate::core::image::{HostImage, ImageIO};
use crate::core::shape::{CreateShape, ALL_BILINEAR_MESHES}; use crate::core::shape::{CreateShape, ALL_BILINEAR_MESHES};
use crate::core::texture::FloatTexture; use crate::core::texture::FloatTexture;
use crate::utils::sampling::PiecewiseConstant2D;
use crate::{Arena, FileLoc, ParameterDictionary}; use crate::{Arena, FileLoc, ParameterDictionary};
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use log::warn; use log::warn;
use shared::core::shape::Shape; use shared::core::shape::Shape;
use shared::shapes::{BilinearPatchMesh, BilinearPatchShape}; use shared::shapes::{BilinearPatchMesh, BilinearPatchShape};
use shared::utils::sampling::PiecewiseConstant2D;
use shared::{Ptr, Transform}; use shared::{Ptr, Transform};
use std::collections::HashMap; use std::collections::HashMap;
use std::path::Path; use std::path::Path;
@ -95,7 +95,7 @@ impl CreateShape for BilinearPatchShape {
let im = HostImage::read(Path::new(&filename), None)?; let im = HostImage::read(Path::new(&filename), None)?;
let mut img = im.image; let mut img = im.image;
img.flip_y(); img.flip_y();
Some(PiecewiseConstant2D::from_image(&img)) Some(PiecewiseConstant2D::from_image(&img.inner))
} }
} else { } else {
None None
@ -108,7 +108,7 @@ impl CreateShape for BilinearPatchShape {
&p, &p,
&n, &n,
&uv, &uv,
image_dist, image_dist.as_ref(),
); );
let host_arc = Arc::new(host); let host_arc = Arc::new(host);

View file

@ -1,9 +1,9 @@
use crate::utils::sampling::PiecewiseConstant2D;
use anyhow::{bail, Context, Result as AnyResult}; use anyhow::{bail, Context, Result as AnyResult};
use ply_rs::parser::Parser; use ply_rs::parser::Parser;
use ply_rs::ply::{DefaultElement, Property}; use ply_rs::ply::{DefaultElement, Property};
use shared::core::geometry::{Normal3f, Point2f, Point3f, VectorLike}; use shared::core::geometry::{Normal3f, Point2f, Point3f, VectorLike};
use shared::shapes::mesh::TriangleMesh; use shared::shapes::mesh::TriangleMesh;
use shared::utils::sampling::PiecewiseConstant2D;
use shared::Transform; use shared::Transform;
use std::fs::File; use std::fs::File;
use std::path::Path; use std::path::Path;

View file

@ -1,7 +1,7 @@
use shared::core::spectrum::Spectrum; use shared::core::spectrum::Spectrum;
use shared::spectra::cie::*; use shared::spectra::cie::*;
use shared::spectra::{DenselySampledSpectrum, PiecewiseLinearSpectrum}; use shared::spectra::{DenselySampledSpectrum, PiecewiseLinearSpectrum};
use shared::{gbox, leak, Float, Ptr}; use shared::{gbox, gvec_from_slice, leak, Float, Ptr};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::LazyLock; use std::sync::LazyLock;
@ -11,13 +11,11 @@ pub fn create_cie(data: &[Float]) -> DenselySampledSpectrum {
95 => (300.0, 5.0), 95 => (300.0, 5.0),
n => panic!("Unexpected CIE data length: {}", n), n => panic!("Unexpected CIE data length: {}", n),
}; };
let lambdas: Vec<Float> = (0..data.len()) let lambdas: Vec<Float> = (0..data.len())
.map(|i| start_lambda + i as Float * step) .map(|i| start_lambda + i as Float * step)
.collect(); .collect();
let buffer = PiecewiseLinearSpectrum::new(gvec_from_slice(&lambdas), gvec_from_slice(data));
let buffer = PiecewiseLinearSpectrum::new(lambdas, data.to_vec()); let spec = Spectrum::Piecewise(leak(buffer));
let spec = Spectrum::Piecewise(Ptr::from(&*buffer));
DenselySampledSpectrum::from_spectrum(&spec) DenselySampledSpectrum::from_spectrum(&spec)
} }

View file

@ -134,7 +134,8 @@ pub fn default_colorspace_arc() -> Arc<RGBColorSpace> {
pub fn default_colorspace_ref() -> &'static RGBColorSpace { pub fn default_colorspace_ref() -> &'static RGBColorSpace {
static CS: OnceLock<RGBColorSpace> = OnceLock::new(); static CS: OnceLock<RGBColorSpace> = OnceLock::new();
CS.get_or_init(|| stdcs.srgb) let stdcs = get_colorspace_device();
CS.get_or_init(|| *stdcs.srgb)
} }
pub fn default_illuminant() -> Spectrum { pub fn default_illuminant() -> Spectrum {