Compare commits

...

3 commits

Author SHA1 Message Date
7db434535b Very, utterly broken 2026-06-08 16:32:59 +01:00
dac5d2c28f Very broken 2026-06-07 18:53:23 +01:00
4e1b3619f4 Major overhaul to Texture handling 2026-06-06 18:34:29 +01:00
42 changed files with 887 additions and 757 deletions

View file

@ -6,8 +6,8 @@ use crate::core::shape::Shape;
use crate::spectra::{SampledSpectrum, N_SPECTRUM_SAMPLES}; use crate::spectra::{SampledSpectrum, N_SPECTRUM_SAMPLES};
use crate::utils::math::{catmull_rom_weights, square}; use crate::utils::math::{catmull_rom_weights, square};
use crate::utils::sampling::sample_catmull_rom_2d; use crate::utils::sampling::sample_catmull_rom_2d;
use crate::utils::Ptr; use crate::core::{LightIdx, MaterialIdx};
use crate::{gvec_with_capacity, Float, GVec, PI}; use crate::{gvec_with_capacity, Float, GVec, PI, Ptr};
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use num_traits::Float as NumFloat; use num_traits::Float as NumFloat;
@ -78,8 +78,8 @@ impl From<&SubsurfaceInteraction> for SurfaceInteraction {
dndv: Normal3f::zero(), dndv: Normal3f::zero(),
}, },
face_index: 0, face_index: 0,
area_light: Ptr::null(), area_light: LightIdx::default(),
material: Ptr::null(), material: MaterialIdx::default(),
dpdx: Vector3f::zero(), dpdx: Vector3f::zero(),
dpdy: Vector3f::zero(), dpdy: Vector3f::zero(),
dudx: 0., dudx: 0.,

34
shared/src/core/handle.rs Normal file
View file

@ -0,0 +1,34 @@
use crate::core::light::Light;
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct LightIdx(pub u32);
impl LightIdx {
pub const NONE: Self = LightIdx(u32::MAX);
pub fn is_none(self) -> bool { self.0 == u32::MAX }
}
impl Default for LightIdx {
fn default() -> Self { Self::NONE }
}
impl LightIdx {
#[inline]
pub fn get(self, lights: &[Light]) -> &Light {
&lights[self.0 as usize]
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct MaterialIdx(pub u32);
impl MaterialIdx {
pub const NONE: Self = MaterialIdx(u32::MAX);
pub fn is_none(self) -> bool { self.0 == u32::MAX }
}
impl Default for MaterialIdx {
fn default() -> Self { Self::NONE }
}

View file

@ -1,8 +1,8 @@
use crate::core::color::{ColorEncoding, ColorEncodingTrait, LINEAR}; use crate::core::color::{ColorEncoding, ColorEncodingTrait, LINEAR};
use crate::core::geometry::{Bounds2f, Point2f, Point2fi, Point2i}; use crate::core::geometry::{Bounds2f, Point2f, Point2fi, Point2i};
use crate::utils::math::{f16_to_f32_software, lerp, square}; use crate::utils::math::{f16_to_f32_software, lerp, square};
use crate::{gvec_with_capacity, Float, GVec}; use crate::{gvec_with_capacity, Float, GVec, Ptr};
use anyhow::{Result, bail}; use anyhow::{bail, Result};
use core::hash; use core::hash;
use core::ops::{Deref, DerefMut}; use core::ops::{Deref, DerefMut};
use num_traits::Float as NumFloat; use num_traits::Float as NumFloat;
@ -23,7 +23,7 @@ impl WrapMode {
"black" => Ok(WrapMode::Black), "black" => Ok(WrapMode::Black),
"repeat" => Ok(WrapMode::Repeat), "repeat" => Ok(WrapMode::Repeat),
"octahedralsphere" => Ok(WrapMode::OctahedralSphere), "octahedralsphere" => Ok(WrapMode::OctahedralSphere),
_ => bail!("{:?}: wrap mode unknown", name) _ => bail!("{:?}: wrap mode unknown", name),
} }
} }
} }
@ -236,6 +236,17 @@ pub struct Image {
pub pixels: Pixels, pub pixels: Pixels,
} }
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct ImageView {
pub pixels: *const u8,
pub byte_len: usize,
pub resolution: Point2i,
pub n_channels: i32,
pub format: PixelFormat,
pub encoding: ColorEncoding,
}
impl Image { impl Image {
pub fn new( pub fn new(
format: PixelFormat, format: PixelFormat,
@ -472,3 +483,34 @@ impl Image {
false false
} }
} }
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FilterFunction {
Point,
Bilinear,
Trilinear,
Ewa,
}
impl FilterFunction {
pub fn parse(name: &str) -> Result<FilterFunction> {
match name {
"ewa" | "EWA" => Ok(FilterFunction::Ewa),
"trilinear" => Ok(FilterFunction::Trilinear),
"bilinear" => Ok(FilterFunction::Bilinear),
"point" => Ok(FilterFunction::Point),
_ => bail!("Filter function unknown"),
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct ImagePyramid {
pub levels: *const Ptr<Image>,
pub level_count: u32,
pub wrap_mode: WrapMode,
pub filter: FilterFunction,
pub max_aniso: f32,
}

View file

@ -1,4 +1,3 @@
use crate::Float;
use crate::bxdfs::DiffuseBxDF; use crate::bxdfs::DiffuseBxDF;
use crate::core::bsdf::BSDF; use crate::core::bsdf::BSDF;
use crate::core::bssrdf::BSSRDF; use crate::core::bssrdf::BSSRDF;
@ -10,15 +9,16 @@ use crate::core::geometry::{
use crate::core::image::Image; use crate::core::image::Image;
use crate::core::light::{Light, LightTrait}; use crate::core::light::{Light, LightTrait};
use crate::core::material::{ use crate::core::material::{
Material, MaterialEvalContext, MaterialTrait, NormalBumpEvalContext, bump_map, normal_map, bump_map, normal_map, Material, MaterialEvalContext, MaterialTrait, NormalBumpEvalContext,
}; };
use crate::core::medium::{Medium, MediumInterface, PhaseFunction}; use crate::core::medium::{Medium, MediumInterface, PhaseFunction};
use crate::core::sampler::{Sampler, SamplerTrait}; use crate::core::sampler::{Sampler, SamplerTrait};
use crate::core::shape::Shape; use crate::core::shape::Shape;
use crate::core::texture::{GPUFloatTexture, UniversalTextureEvaluator}; use crate::core::texture::{FloatTexture, UniversalTextureEvaluator};
use crate::core::{LightIdx, MaterialIdx};
use crate::spectra::{SampledSpectrum, SampledWavelengths}; use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::Ptr;
use crate::utils::math::{clamp, difference_of_products, square}; use crate::utils::math::{clamp, difference_of_products, square};
use crate::{GVec, Ptr, Float};
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
#[repr(C)] #[repr(C)]
@ -217,8 +217,8 @@ pub struct ShadingGeom {
#[repr(C)] #[repr(C)]
#[derive(Debug, Default, Clone, Copy)] #[derive(Debug, Default, Clone, Copy)]
pub struct SurfaceInteraction { pub struct SurfaceInteraction {
pub area_light: Ptr<Light>, pub area_light: LightIdx,
pub material: Ptr<Material>, pub material: MaterialIdx,
pub shape: Ptr<Shape>, pub shape: Ptr<Shape>,
pub common: InteractionBase, pub common: InteractionBase,
pub shading: ShadingGeom, pub shading: ShadingGeom,
@ -239,13 +239,17 @@ unsafe impl Send for SurfaceInteraction {}
unsafe impl Sync for SurfaceInteraction {} unsafe impl Sync for SurfaceInteraction {}
impl SurfaceInteraction { impl SurfaceInteraction {
pub fn le(&self, w: Vector3f, lambda: &SampledWavelengths) -> SampledSpectrum { pub fn le(
if !self.area_light.is_null() { &self,
self.area_light w: Vector3f,
.l(self.p(), self.n(), self.common.uv, w, lambda) lambda: &SampledWavelengths,
} else { lights: &GVec<Light>,
SampledSpectrum::new(0.) ) -> SampledSpectrum {
if self.area_light.is_none() {
return SampledSpectrum::new(0.);
} }
let light = self.area_light.get(lights);
light.l(self.p(), self.n(), self.common.uv, w, lambda)
} }
pub fn compute_differentials(&mut self, r: &Ray, camera: &Camera, samples_per_pixel: i32) { pub fn compute_differentials(&mut self, r: &Ray, camera: &Camera, samples_per_pixel: i32) {
@ -347,7 +351,7 @@ impl SurfaceInteraction {
pub fn compute_bump_geom( pub fn compute_bump_geom(
&mut self, &mut self,
tex_eval: &UniversalTextureEvaluator, tex_eval: &UniversalTextureEvaluator,
displacement: Ptr<GPUFloatTexture>, displacement: Ptr<FloatTexture>,
normal_image: Ptr<Image>, normal_image: Ptr<Image>,
) { ) {
let ctx = NormalBumpEvalContext::from(&*self); let ctx = NormalBumpEvalContext::from(&*self);
@ -519,9 +523,9 @@ impl SurfaceInteraction {
dndu, dndu,
dndv, dndv,
}, },
material: Ptr::null(), material: MaterialIdx::default(),
face_index: 0, face_index: 0,
area_light: Ptr::null(), area_light: LightIdx::default(),
dpdx: Vector3f::zero(), dpdx: Vector3f::zero(),
dpdy: Vector3f::zero(), dpdy: Vector3f::zero(),
dudx: 0.0, dudx: 0.0,
@ -591,8 +595,8 @@ impl SurfaceInteraction {
#[cfg(not(target_os = "cuda"))] #[cfg(not(target_os = "cuda"))]
pub fn set_intersection_properties( pub fn set_intersection_properties(
&mut self, &mut self,
mtl: Ptr<Material>, mtl: MaterialIdx,
area: Ptr<Light>, area: LightIdx,
ray_medium: Ptr<Medium>, ray_medium: Ptr<Medium>,
prim_medium_interface: MediumInterface, prim_medium_interface: MediumInterface,
) { ) {

View file

@ -14,7 +14,7 @@ use crate::core::interaction::{Interaction, InteractionTrait, ShadingGeom, Surfa
use crate::core::scattering::TrowbridgeReitzDistribution; use crate::core::scattering::TrowbridgeReitzDistribution;
use crate::core::spectrum::{Spectrum, SpectrumTrait}; use crate::core::spectrum::{Spectrum, SpectrumTrait};
use crate::core::texture::{ use crate::core::texture::{
GPUFloatTexture, GPUSpectrumTexture, TextureEvalContext, TextureEvaluator, FloatTexture, SpectrumTexture, TextureEvalContext, TextureEvaluator,
}; };
use crate::materials::*; use crate::materials::*;
use crate::spectra::{SampledSpectrum, SampledWavelengths}; use crate::spectra::{SampledSpectrum, SampledWavelengths};
@ -122,7 +122,7 @@ pub fn normal_map(normal_map: &Image, ctx: &NormalBumpEvalContext) -> (Vector3f,
pub fn bump_map<T: TextureEvaluator>( pub fn bump_map<T: TextureEvaluator>(
tex_eval: &T, tex_eval: &T,
displacement: &GPUFloatTexture, displacement: &FloatTexture,
ctx: &NormalBumpEvalContext, ctx: &NormalBumpEvalContext,
) -> (Vector3f, Vector3f) { ) -> (Vector3f, Vector3f) {
debug_assert!(tex_eval.can_evaluate(&[Ptr::from(displacement)], &[])); debug_assert!(tex_eval.can_evaluate(&[Ptr::from(displacement)], &[]));
@ -174,7 +174,7 @@ pub trait MaterialTrait {
fn can_evaluate_textures(&self, tex_eval: &dyn TextureEvaluator) -> bool; fn can_evaluate_textures(&self, tex_eval: &dyn TextureEvaluator) -> bool;
fn get_normal_map(&self) -> Option<&Image>; fn get_normal_map(&self) -> Option<&Image>;
fn get_displacement(&self) -> Ptr<GPUFloatTexture>; fn get_displacement(&self) -> Ptr<FloatTexture>;
fn has_subsurface_scattering(&self) -> bool; fn has_subsurface_scattering(&self) -> bool;
} }

View file

@ -7,6 +7,7 @@ pub mod color;
pub mod film; pub mod film;
pub mod filter; pub mod filter;
pub mod geometry; pub mod geometry;
pub mod handle;
pub mod image; pub mod image;
pub mod interaction; pub mod interaction;
pub mod light; pub mod light;
@ -19,3 +20,5 @@ pub mod scattering;
pub mod shape; pub mod shape;
pub mod spectrum; pub mod spectrum;
pub mod texture; pub mod texture;
pub use handle::{LightIdx, MaterialIdx};

View file

@ -4,7 +4,7 @@ use num_traits::{Float as NumFloat, Num, NumCast, PrimInt};
use crate::core::light::LightTrait; use crate::core::light::LightTrait;
use crate::core::shape::Shape; use crate::core::shape::Shape;
use crate::core::texture::GPUFloatTexture; use crate::core::texture::FloatTexture;
use crate::lights::*; use crate::lights::*;
use crate::spectra::{DenselySampledSpectrum, RGBColorSpace}; use crate::spectra::{DenselySampledSpectrum, RGBColorSpace};
use crate::utils::Ptr; use crate::utils::Ptr;

View file

@ -4,12 +4,12 @@ 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;
use crate::core::medium::{Medium, MediumInterface}; use crate::core::medium::{Medium, MediumInterface};
use crate::core::pbrt::Float;
use crate::core::shape::{Shape, ShapeIntersection, ShapeTrait}; use crate::core::shape::{Shape, ShapeIntersection, ShapeTrait};
use crate::core::texture::{GPUFloatTexture, TextureEvalContext}; use crate::core::texture::{FloatTexture, TextureEvalContext};
use crate::core::{LightIdx, MaterialIdx};
use crate::utils::hash::hash_float; use crate::utils::hash::hash_float;
use crate::utils::transform::{AnimatedTransform, Transform}; use crate::utils::transform::{AnimatedTransform, Transform};
use crate::utils::Ptr; use crate::{Float, Ptr};
use alloc::boxed::Box; use alloc::boxed::Box;
use alloc::sync::Arc; use alloc::sync::Arc;
@ -26,10 +26,10 @@ pub trait PrimitiveTrait: Send + Sync {
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct GeometricPrimitive { pub struct GeometricPrimitive {
pub shape: Ptr<Shape>, pub shape: Ptr<Shape>,
pub material: Ptr<Material>, pub material: MaterialIdx,
pub area_light: Ptr<Light>, pub area_light: LightIdx,
pub medium_interface: MediumInterface, pub medium_interface: MediumInterface,
pub alpha: Ptr<GPUFloatTexture>, pub alpha: Ptr<FloatTexture>,
} }
unsafe impl Send for GeometricPrimitive {} unsafe impl Send for GeometricPrimitive {}
@ -91,7 +91,7 @@ impl PrimitiveTrait for GeometricPrimitive {
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct SimplePrimitive { pub struct SimplePrimitive {
pub shape: Ptr<Shape>, pub shape: Ptr<Shape>,
pub material: Ptr<Material>, pub material: MaterialIdx,
} }
impl PrimitiveTrait for SimplePrimitive { impl PrimitiveTrait for SimplePrimitive {
@ -103,7 +103,7 @@ impl PrimitiveTrait for SimplePrimitive {
let mut si = self.shape.intersect(r, t_max)?; let mut si = self.shape.intersect(r, t_max)?;
si.set_intersection_properties( si.set_intersection_properties(
self.material, self.material,
Ptr::null(), LightIdx::default(),
MediumInterface::default(), MediumInterface::default(),
r.medium, r.medium,
); );

View file

@ -5,6 +5,7 @@ use crate::core::geometry::{
use crate::core::interaction::{ use crate::core::interaction::{
Interaction, InteractionTrait, MediumInteraction, SurfaceInteraction, Interaction, InteractionTrait, MediumInteraction, SurfaceInteraction,
}; };
use crate::core::{MaterialIdx, LightIdx};
use crate::core::light::Light; use crate::core::light::Light;
use crate::core::material::Material; use crate::core::material::Material;
use crate::core::medium::{Medium, MediumInterface}; use crate::core::medium::{Medium, MediumInterface};
@ -37,8 +38,8 @@ impl ShapeIntersection {
pub fn set_intersection_properties( pub fn set_intersection_properties(
&mut self, &mut self,
mtl: Ptr<Material>, mtl: MaterialIdx,
area: Ptr<Light>, area: LightIdx,
prim_medium_interface: MediumInterface, prim_medium_interface: MediumInterface,
ray_medium: Ptr<Medium>, ray_medium: Ptr<Medium>,
) { ) {

View file

@ -78,7 +78,7 @@ impl UVMapping {
let dsdy = self.su * ctx.dudy; let dsdy = self.su * ctx.dudy;
let dtdx = self.sv * ctx.dvdx; let dtdx = self.sv * ctx.dvdx;
let dtdy = self.sv * ctx.dvdy; let dtdy = self.sv * ctx.dvdy;
let st = Point2f::new(self.su * ctx.uv[0] + self.du, self.sv * ctx.uv[1] * self.dv); let st = Point2f::new(self.su * ctx.uv[0] + self.du, self.sv * ctx.uv[1] + self.dv);
TexCoord2D { TexCoord2D {
st, st,
dsdx, dsdx,
@ -107,7 +107,7 @@ impl SphericalMapping {
let x2y2 = square(pt.x()) + square(pt.y()); let x2y2 = square(pt.x()) + square(pt.y());
let sqrtx2y2 = x2y2.sqrt(); let sqrtx2y2 = x2y2.sqrt();
let dsdp = Vector3f::new(-pt.y(), pt.x(), 0.) / (2. * PI * x2y2); let dsdp = Vector3f::new(-pt.y(), pt.x(), 0.) / (2. * PI * x2y2);
let dtdp = 1. / (PI * (x2y2 * square(pt.z()))) let dtdp = 1. / (PI * (x2y2 + square(pt.z())))
* Vector3f::new( * Vector3f::new(
pt.x() * pt.z() / sqrtx2y2, pt.x() * pt.z() / sqrtx2y2,
pt.y() * pt.z() / sqrtx2y2, pt.y() * pt.z() / sqrtx2y2,
@ -148,7 +148,7 @@ impl CylindricalMapping {
let pt = self.texture_from_render.apply_to_point(ctx.p); let pt = self.texture_from_render.apply_to_point(ctx.p);
let x2y2 = square(pt.x()) + square(pt.y()); let x2y2 = square(pt.x()) + square(pt.y());
let dsdp = Vector3f::new(-pt.y(), pt.x(), 0.) / (2. * PI * x2y2); let dsdp = Vector3f::new(-pt.y(), pt.x(), 0.) / (2. * PI * x2y2);
let dtdp = Vector3f::new(1., 0., 0.); let dtdp = Vector3f::new(0., 0., 1.);
let dpdx = self.texture_from_render.apply_to_vector(ctx.dpdx); let dpdx = self.texture_from_render.apply_to_vector(ctx.dpdx);
let dpdy = self.texture_from_render.apply_to_vector(ctx.dpdy); let dpdy = self.texture_from_render.apply_to_vector(ctx.dpdy);
let dsdx = dsdp.dot(dpdx); let dsdx = dsdp.dot(dpdx);
@ -341,34 +341,34 @@ impl From<&Interaction> for TextureEvalContext {
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub enum GPUFloatTexture { pub enum FloatTexture {
Constant(FloatConstantTexture), Constant(FloatConstantTexture),
DirectionMix(GPUFloatDirectionMixTexture), DirectionMix(FloatDirectionMixTexture),
Scaled(GPUFloatScaledTexture), Scaled(FloatScaledTexture),
Bilerp(FloatBilerpTexture), Bilerp(FloatBilerpTexture),
Checkerboard(FloatCheckerboardTexture), Checkerboard(FloatCheckerboardTexture),
Dots(FloatDotsTexture), Dots(FloatDotsTexture),
FBm(FBmTexture), FBm(FBmTexture),
Windy(WindyTexture), Windy(WindyTexture),
Wrinkled(WrinkledTexture), Wrinkled(WrinkledTexture),
Image(GPUFloatImageTexture), Image(FloatImageTexture),
Mix(GPUFloatMixTexture), Mix(FloatMixTexture),
} }
impl GPUFloatTexture { impl FloatTexture {
pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float { pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
match self { match self {
GPUFloatTexture::Constant(t) => t.evaluate(ctx), FloatTexture::Constant(t) => t.evaluate(ctx),
GPUFloatTexture::DirectionMix(t) => t.evaluate(ctx), FloatTexture::DirectionMix(t) => t.evaluate(ctx),
GPUFloatTexture::Scaled(t) => t.evaluate(ctx), FloatTexture::Scaled(t) => t.evaluate(ctx),
GPUFloatTexture::Bilerp(t) => t.evaluate(ctx), FloatTexture::Bilerp(t) => t.evaluate(ctx),
GPUFloatTexture::Checkerboard(t) => t.evaluate(ctx), FloatTexture::Checkerboard(t) => t.evaluate(ctx),
GPUFloatTexture::Dots(t) => t.evaluate(ctx), FloatTexture::Dots(t) => t.evaluate(ctx),
GPUFloatTexture::FBm(t) => t.evaluate(ctx), FloatTexture::FBm(t) => t.evaluate(ctx),
GPUFloatTexture::Windy(t) => t.evaluate(ctx), FloatTexture::Windy(t) => t.evaluate(ctx),
GPUFloatTexture::Wrinkled(t) => t.evaluate(ctx), FloatTexture::Wrinkled(t) => t.evaluate(ctx),
GPUFloatTexture::Image(t) => t.evaluate(ctx), FloatTexture::Image(t) => t.evaluate(ctx),
GPUFloatTexture::Mix(t) => t.evaluate(ctx), FloatTexture::Mix(t) => t.evaluate(ctx),
} }
} }
} }
@ -384,51 +384,51 @@ pub enum SpectrumType {
#[repr(C)] #[repr(C)]
#[enum_dispatch] #[enum_dispatch]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub enum GPUSpectrumTexture { pub enum SpectrumTexture {
Constant(SpectrumConstantTexture), Constant(SpectrumConstantTexture),
Bilerp(SpectrumBilerpTexture), Bilerp(SpectrumBilerpTexture),
Checkerboard(SpectrumCheckerboardTexture), Checkerboard(SpectrumCheckerboardTexture),
Marble(MarbleTexture), Marble(MarbleTexture),
DirectionMix(GPUSpectrumDirectionMixTexture), DirectionMix(SpectrumDirectionMixTexture),
Dots(SpectrumDotsTexture), Dots(SpectrumDotsTexture),
Scaled(GPUSpectrumScaledTexture), Scaled(SpectrumScaledTexture),
Image(GPUSpectrumImageTexture), Image(SpectrumImageTexture),
Mix(GPUSpectrumMixTexture), Mix(SpectrumMixTexture),
} }
impl GPUSpectrumTexture { impl SpectrumTexture {
pub fn evaluate( pub fn evaluate(
&self, &self,
ctx: &TextureEvalContext, ctx: &TextureEvalContext,
lambda: &SampledWavelengths, lambda: &SampledWavelengths,
) -> SampledSpectrum { ) -> SampledSpectrum {
match self { match self {
GPUSpectrumTexture::Constant(t) => t.evaluate(ctx, lambda), SpectrumTexture::Constant(t) => t.evaluate(ctx, lambda),
GPUSpectrumTexture::Bilerp(t) => t.evaluate(ctx, lambda), SpectrumTexture::Bilerp(t) => t.evaluate(ctx, lambda),
GPUSpectrumTexture::Checkerboard(t) => t.evaluate(ctx, lambda), SpectrumTexture::Checkerboard(t) => t.evaluate(ctx, lambda),
GPUSpectrumTexture::Marble(t) => t.evaluate(ctx, lambda), SpectrumTexture::Marble(t) => t.evaluate(ctx, lambda),
GPUSpectrumTexture::DirectionMix(t) => t.evaluate(ctx, lambda), SpectrumTexture::DirectionMix(t) => t.evaluate(ctx, lambda),
GPUSpectrumTexture::Dots(t) => t.evaluate(ctx, lambda), SpectrumTexture::Dots(t) => t.evaluate(ctx, lambda),
GPUSpectrumTexture::Scaled(t) => t.evaluate(ctx, lambda), SpectrumTexture::Scaled(t) => t.evaluate(ctx, lambda),
GPUSpectrumTexture::Image(t) => t.evaluate(ctx, lambda), SpectrumTexture::Image(t) => t.evaluate(ctx, lambda),
GPUSpectrumTexture::Mix(t) => t.evaluate(ctx, lambda), SpectrumTexture::Mix(t) => t.evaluate(ctx, lambda),
} }
} }
} }
pub trait TextureEvaluator: Send + Sync { pub trait TextureEvaluator: Send + Sync {
fn evaluate_float(&self, tex: &GPUFloatTexture, ctx: &TextureEvalContext) -> Float; fn evaluate_float(&self, tex: &FloatTexture, ctx: &TextureEvalContext) -> Float;
fn evaluate_spectrum( fn evaluate_spectrum(
&self, &self,
tex: &GPUSpectrumTexture, tex: &SpectrumTexture,
ctx: &TextureEvalContext, ctx: &TextureEvalContext,
lambda: &SampledWavelengths, lambda: &SampledWavelengths,
) -> SampledSpectrum; ) -> SampledSpectrum;
fn can_evaluate( fn can_evaluate(
&self, &self,
_ftex: &[Ptr<GPUFloatTexture>], _ftex: &[Ptr<FloatTexture>],
_stex: &[Ptr<GPUSpectrumTexture>], _stex: &[Ptr<SpectrumTexture>],
) -> bool; ) -> bool;
} }
@ -437,13 +437,13 @@ pub trait TextureEvaluator: Send + Sync {
pub struct UniversalTextureEvaluator; pub struct UniversalTextureEvaluator;
impl TextureEvaluator for UniversalTextureEvaluator { impl TextureEvaluator for UniversalTextureEvaluator {
fn evaluate_float(&self, tex: &GPUFloatTexture, ctx: &TextureEvalContext) -> Float { fn evaluate_float(&self, tex: &FloatTexture, ctx: &TextureEvalContext) -> Float {
tex.evaluate(ctx) tex.evaluate(ctx)
} }
fn evaluate_spectrum( fn evaluate_spectrum(
&self, &self,
tex: &GPUSpectrumTexture, tex: &SpectrumTexture,
ctx: &TextureEvalContext, ctx: &TextureEvalContext,
lambda: &SampledWavelengths, lambda: &SampledWavelengths,
) -> SampledSpectrum { ) -> SampledSpectrum {
@ -452,8 +452,8 @@ impl TextureEvaluator for UniversalTextureEvaluator {
fn can_evaluate( fn can_evaluate(
&self, &self,
_float_textures: &[Ptr<GPUFloatTexture>], _float_textures: &[Ptr<FloatTexture>],
_spectrum_textures: &[Ptr<GPUSpectrumTexture>], _spectrum_textures: &[Ptr<SpectrumTexture>],
) -> bool { ) -> bool {
true true
} }
@ -464,39 +464,39 @@ impl TextureEvaluator for UniversalTextureEvaluator {
pub struct BasicTextureEvaluator; pub struct BasicTextureEvaluator;
impl TextureEvaluator for BasicTextureEvaluator { impl TextureEvaluator for BasicTextureEvaluator {
fn evaluate_float(&self, tex: &GPUFloatTexture, ctx: &TextureEvalContext) -> Float { fn evaluate_float(&self, tex: &FloatTexture, ctx: &TextureEvalContext) -> Float {
match tex { match tex {
GPUFloatTexture::Constant(t) => t.evaluate(ctx), FloatTexture::Constant(t) => t.evaluate(ctx),
GPUFloatTexture::Image(t) => t.evaluate(ctx), FloatTexture::Image(t) => t.evaluate(ctx),
_ => 0.0, _ => 0.0,
} }
} }
fn evaluate_spectrum( fn evaluate_spectrum(
&self, &self,
tex: &GPUSpectrumTexture, tex: &SpectrumTexture,
ctx: &TextureEvalContext, ctx: &TextureEvalContext,
lambda: &SampledWavelengths, lambda: &SampledWavelengths,
) -> SampledSpectrum { ) -> SampledSpectrum {
match tex { match tex {
GPUSpectrumTexture::Constant(t) => t.evaluate(ctx, lambda), SpectrumTexture::Constant(t) => t.evaluate(ctx, lambda),
GPUSpectrumTexture::Image(t) => t.evaluate(ctx, lambda), SpectrumTexture::Image(t) => t.evaluate(ctx, lambda),
_ => SampledSpectrum::new(0.0), _ => SampledSpectrum::new(0.0),
} }
} }
fn can_evaluate( fn can_evaluate(
&self, &self,
ftex: &[Ptr<GPUFloatTexture>], ftex: &[Ptr<FloatTexture>],
stex: &[Ptr<GPUSpectrumTexture>], stex: &[Ptr<SpectrumTexture>],
) -> bool { ) -> bool {
for t in ftex { for t in ftex {
if t.is_null() { if t.is_null() {
continue; continue;
} }
match t.get().unwrap() { match t.get().unwrap() {
GPUFloatTexture::Constant(_) FloatTexture::Constant(_)
| GPUFloatTexture::Image(_) => {} | FloatTexture::Image(_) => {}
_ => return false, _ => return false,
} }
} }
@ -505,8 +505,8 @@ impl TextureEvaluator for BasicTextureEvaluator {
continue; continue;
} }
match t.get().unwrap() { match t.get().unwrap() {
GPUSpectrumTexture::Constant(_) SpectrumTexture::Constant(_)
| GPUSpectrumTexture::Image(_) => {} | SpectrumTexture::Image(_) => {}
_ => return false, _ => return false,
} }
} }

View file

@ -11,7 +11,7 @@ use crate::core::medium::MediumInterface;
use crate::core::shape::{Shape, ShapeSampleContext, ShapeTrait}; use crate::core::shape::{Shape, ShapeSampleContext, ShapeTrait};
use crate::core::spectrum::{Spectrum, SpectrumTrait}; use crate::core::spectrum::{Spectrum, SpectrumTrait};
use crate::core::texture::{ use crate::core::texture::{
GPUFloatTexture, TextureEvalContext, TextureEvaluator, UniversalTextureEvaluator, FloatTexture, TextureEvalContext, TextureEvaluator, UniversalTextureEvaluator,
}; };
use crate::spectra::*; use crate::spectra::*;
use crate::utils::hash::hash_float; use crate::utils::hash::hash_float;
@ -24,7 +24,7 @@ use num_traits::Float as NumFloat;
pub struct DiffuseAreaLight { pub struct DiffuseAreaLight {
pub base: LightBase, pub base: LightBase,
pub shape: Ptr<Shape>, pub shape: Ptr<Shape>,
pub alpha: Ptr<GPUFloatTexture>, pub alpha: Ptr<FloatTexture>,
pub colorspace: Ptr<RGBColorSpace>, pub colorspace: Ptr<RGBColorSpace>,
pub lemit: Ptr<DenselySampledSpectrum>, pub lemit: Ptr<DenselySampledSpectrum>,
pub image: Ptr<Image>, pub image: Ptr<Image>,

View file

@ -5,8 +5,8 @@ use crate::core::interaction::{Interaction, InteractionBase, SimpleInteraction};
use crate::core::light::{LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait}; use crate::core::light::{LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait};
use crate::core::spectrum::SpectrumTrait; use crate::core::spectrum::SpectrumTrait;
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths}; use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
use crate::utils::Ptr; use crate::utils::math::square;
use crate::{Float, PI}; use crate::{Float, Ptr, PI};
use num_traits::Float as NumFloat; use num_traits::Float as NumFloat;
#[repr(C)] #[repr(C)]
@ -42,7 +42,7 @@ impl LightTrait for DistantLight {
} }
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum { fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
self.scale * self.lemit.sample(&lambda) * PI * self.scene_radius.sqrt() self.scale * self.lemit.sample(&lambda) * PI * square(self.scene_radius)
} }
fn sample_li( fn sample_li(

View file

@ -1,8 +1,7 @@
use crate::core::geometry::primitives::OctahedralVector; use crate::core::geometry::primitives::OctahedralVector;
use crate::core::geometry::{Bounds3f, Normal3f, Point3f, Vector3f, VectorLike}; use crate::core::geometry::{Bounds3f, DirectionCone, Normal3f, Point3f, Vector3f, VectorLike};
use crate::core::geometry::{DirectionCone, Normal}; use crate::core::light::{Light, LightBounds, LightSampleContext};
use crate::core::light::Light; use crate::core::LightIdx;
use crate::core::light::{LightBounds, LightSampleContext};
use crate::spectra::{SampledSpectrum, SampledWavelengths}; use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::math::{clamp, lerp, sample_discrete}; use crate::utils::math::{clamp, lerp, sample_discrete};
use crate::utils::math::{safe_sqrt, square}; use crate::utils::math::{safe_sqrt, square};
@ -167,25 +166,25 @@ impl CompactLightBounds {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SampledLight { pub struct SampledLight {
pub light: Ptr<Light>, pub light: LightIdx,
pub p: Float, pub p: Float,
} }
impl SampledLight { // impl SampledLight {
pub fn new(light: Light, p: Float) -> Self { // pub fn new(light: Light, p: Float) -> Self {
Self { // Self {
light: Ptr::from(&light), // light: Ptr::from(&light),
p, // p,
} // }
} // }
} // }
//
#[enum_dispatch] #[enum_dispatch]
pub trait LightSamplerTrait { pub trait LightSamplerTrait {
fn sample_with_context(&self, ctx: &LightSampleContext, u: Float) -> Option<SampledLight>; fn sample_with_context(&self, ctx: &LightSampleContext, u: Float) -> Option<SampledLight>;
fn pmf_with_context(&self, ctx: &LightSampleContext, light: &Light) -> Float; fn pmf_with_context(&self, ctx: &LightSampleContext, idx: LightIdx) -> Float;
fn sample(&self, u: Float) -> Option<SampledLight>; fn sample(&self, u: Float) -> Option<SampledLight>;
fn pmf(&self, light: &Light) -> Float; fn pmf(&self, idx: LightIdx) -> Float;
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -198,18 +197,12 @@ pub enum LightSampler {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct UniformLightSampler { pub struct UniformLightSampler {
lights: Ptr<Light>,
lights_len: u32, lights_len: u32,
} }
impl UniformLightSampler { impl UniformLightSampler {
pub fn new(lights: Ptr<Light>, lights_len: u32) -> Self { pub fn new(lights_len: u32) -> Self {
Self { lights, lights_len } Self { lights_len }
}
#[inline(always)]
fn light(&self, idx: usize) -> Light {
unsafe { *self.lights.add(idx) }
} }
} }
@ -217,39 +210,33 @@ impl LightSamplerTrait for UniformLightSampler {
fn sample_with_context(&self, _ctx: &LightSampleContext, u: Float) -> Option<SampledLight> { fn sample_with_context(&self, _ctx: &LightSampleContext, u: Float) -> Option<SampledLight> {
self.sample(u) self.sample(u)
} }
fn pmf_with_context(&self, _ctx: &LightSampleContext, light: &Light) -> Float {
self.pmf(light)
}
fn sample(&self, u: Float) -> Option<SampledLight> { fn sample(&self, u: Float) -> Option<SampledLight> {
if self.lights_len == 0 { if self.lights_len == 0 {
return None; return None;
} }
let light_index = ((u * self.lights_len as Float) as u32).min(self.lights_len - 1);
let light_index = (u as u32 * self.lights_len).min(self.lights_len - 1) as usize;
Some(SampledLight { Some(SampledLight {
light: Ptr::from(&self.light(light_index)), light: LightIdx(light_index),
p: 1. / self.lights_len as Float, p: 1.0 / self.lights_len as Float,
}) })
} }
fn pmf(&self, _light: &Light) -> Float {
if self.lights_len == 0 {
return 0.;
}
1. / self.lights_len as Float
}
}
#[repr(C)] fn pmf_with_context(&self, _ctx: &LightSampleContext, _idx: LightIdx) -> Float {
#[derive(Clone, Copy, Debug)] self.pmf(_idx)
pub struct Alias { }
pub q: Float,
pub alias: u32, fn pmf(&self, _idx: LightIdx) -> Float {
if self.lights_len == 0 {
return 0.0;
}
1.0 / self.lights_len as Float
}
} }
#[repr(C)] #[repr(C)]
#[derive(Clone, Debug, Copy)] #[derive(Clone, Debug, Copy)]
pub struct PowerLightSampler { pub struct PowerLightSampler {
pub lights: Ptr<Light>,
pub lights_len: u32, pub lights_len: u32,
pub alias_table: Ptr<AliasTable>, pub alias_table: Ptr<AliasTable>,
} }
@ -262,32 +249,23 @@ impl LightSamplerTrait for PowerLightSampler {
self.sample(u) self.sample(u)
} }
fn pmf_with_context(&self, _ctx: &LightSampleContext, light: &Light) -> Float { fn pmf_with_context(&self, _ctx: &LightSampleContext, idx: LightIdx) -> Float {
self.pmf(light) self.pmf(idx)
} }
fn sample(&self, u: Float) -> Option<SampledLight> { fn sample(&self, u: Float) -> Option<SampledLight> {
if self.alias_table.size() == 0 { if self.alias_table.size() == 0 {
return None; return None;
} }
let (light_index, pmf, _) = self.alias_table.sample(u); let (light_index, pmf, _) = self.alias_table.sample(u);
let light_ref = unsafe { self.lights.add(light_index as usize) };
Some(SampledLight { Some(SampledLight {
light: light_ref, light: LightIdx(light_index),
p: pmf, p: pmf,
}) })
} }
fn pmf(&self, light: &Light) -> Float { fn pmf(&self, idx: LightIdx) -> Float {
let target = light as *const Light; self.alias_table.pmf(idx.0)
for i in 0..self.lights_len as usize {
if unsafe { self.lights.add(i) }.as_raw() == target {
return self.alias_table.pmf(i as u32);
}
}
0.0
} }
} }
@ -426,15 +404,17 @@ impl LightSamplerTrait for BVHLightSampler {
fn sample_with_context(&self, ctx: &LightSampleContext, mut u: Float) -> Option<SampledLight> { fn sample_with_context(&self, ctx: &LightSampleContext, mut u: Float) -> Option<SampledLight> {
let empty_nodes = if self.nodes_len == 0 { 0. } else { 1. }; let empty_nodes = if self.nodes_len == 0 { 0. } else { 1. };
let inf_size = self.infinite_lights_len as Float; let inf_size = self.infinite_lights_len as Float;
let light_size = self.lights_len as Float;
let p_inf = inf_size / (inf_size + empty_nodes); let p_inf = inf_size / (inf_size + empty_nodes);
if u < p_inf { if u < p_inf {
u /= p_inf; u /= p_inf;
let ind = (u * light_size).min(light_size - 1.) as usize; // sample uniformly from infinite lights; their global index equals their
// position in the infinite_lights array (infinite lights are at 0..n_inf
// in the scene lights array by construction)
let ind = (u * inf_size).min(inf_size - 1.) as u32;
let pmf = p_inf / inf_size; let pmf = p_inf / inf_size;
return Some(SampledLight::new(self.infinite_light(ind), pmf)); return Some(SampledLight { light: LightIdx(ind), p: pmf });
} }
if self.nodes_len == 0 { if self.nodes_len == 0 {
@ -469,28 +449,32 @@ impl LightSamplerTrait for BVHLightSampler {
node_ind = if child == 0 { child0_idx } else { child1_idx }; node_ind = if child == 0 { child0_idx } else { child1_idx };
} else { } else {
if node_ind > 0 || node.light_bounds.importance(p, n, &self.all_light_bounds) > 0. { if node_ind > 0 || node.light_bounds.importance(p, n, &self.all_light_bounds) > 0. {
let light_idx = node.child_or_light_index() as usize; // child_or_light_index() is the global index into the scene lights array
return Some(SampledLight::new(self.light(light_idx), pmf)); return Some(SampledLight {
light: LightIdx(node.child_or_light_index()),
p: pmf,
});
} }
return None; return None;
} }
} }
} }
fn pmf_with_context(&self, ctx: &LightSampleContext, light: &Light) -> Float { fn pmf_with_context(&self, ctx: &LightSampleContext, idx: LightIdx) -> Float {
let empty_nodes = if self.nodes_len == 0 { 0. } else { 1. }; let empty_nodes = if self.nodes_len == 0 { 0. } else { 1. };
let n_infinite = self.infinite_lights_len as Float; let n_infinite = self.infinite_lights_len as Float;
if self
.light_index_in(self.infinite_lights, self.infinite_lights_len, light) // Infinite lights occupy indices 0..infinite_lights_len in the global array
.is_some() if idx.0 < self.infinite_lights_len {
{
return 1.0 / (n_infinite + empty_nodes); return 1.0 / (n_infinite + empty_nodes);
} }
let Some(light_index) = self.light_index_in(self.lights, self.lights_len, light) else { let light_index = idx.0 as usize;
if light_index >= self.lights_len as usize {
return 0.0; return 0.0;
}; }
// bit_trail[light_index] encodes the path from root to the leaf for this light
let mut bit_trail = self.bit_trail(light_index); let mut bit_trail = self.bit_trail(light_index);
let p_inf = n_infinite / (n_infinite + empty_nodes); let p_inf = n_infinite / (n_infinite + empty_nodes);
let mut pmf = 1.0 - p_inf; let mut pmf = 1.0 - p_inf;
@ -516,17 +500,12 @@ impl LightSamplerTrait for BVHLightSampler {
} }
let which_child = (bit_trail & 1) as usize; let which_child = (bit_trail & 1) as usize;
// Update probability: prob of picking the correct child
pmf *= ci[which_child] / sum_importance; pmf *= ci[which_child] / sum_importance;
// Advance
node_ind = if which_child == 1 { node_ind = if which_child == 1 {
node.child_or_light_index() as usize node.child_or_light_index() as usize
} else { } else {
node_ind + 1 node_ind + 1
}; };
bit_trail >>= 1; bit_trail >>= 1;
} }
} }
@ -535,20 +514,17 @@ impl LightSamplerTrait for BVHLightSampler {
if self.lights_len == 0 { if self.lights_len == 0 {
return None; return None;
} }
let light_ind = (u * self.lights_len as Float).min(self.lights_len as Float - 1.) as u32;
let light_ind = (u * self.lights_len as Float).min(self.lights_len as Float - 1.) as usize; Some(SampledLight {
light: LightIdx(light_ind),
Some(SampledLight::new( p: 1. / self.lights_len as Float,
self.light(light_ind), })
1. / self.lights_len as Float,
))
} }
fn pmf(&self, _light: &Light) -> Float { fn pmf(&self, _idx: LightIdx) -> Float {
if self.lights_len == 0 { if self.lights_len == 0 {
return 0.; return 0.;
} }
1. / self.lights_len as Float 1. / self.lights_len as Float
} }
} }

View file

@ -8,7 +8,7 @@ use crate::core::image::Image;
use crate::core::material::{Material, MaterialEvalContext, MaterialTrait}; use crate::core::material::{Material, MaterialEvalContext, MaterialTrait};
use crate::core::scattering::TrowbridgeReitzDistribution; use crate::core::scattering::TrowbridgeReitzDistribution;
use crate::core::spectrum::{Spectrum, SpectrumTrait}; use crate::core::spectrum::{Spectrum, SpectrumTrait};
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator}; use crate::core::texture::{FloatTexture, SpectrumTexture, TextureEvaluator};
use crate::spectra::{SampledSpectrum, SampledWavelengths}; use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::Ptr; use crate::utils::Ptr;
use crate::utils::math::clamp; use crate::utils::math::clamp;
@ -17,13 +17,13 @@ use crate::utils::math::clamp;
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct CoatedDiffuseMaterial { pub struct CoatedDiffuseMaterial {
pub normal_map: Ptr<Image>, pub normal_map: Ptr<Image>,
pub displacement: Ptr<GPUFloatTexture>, pub displacement: Ptr<FloatTexture>,
pub reflectance: Ptr<GPUSpectrumTexture>, pub reflectance: Ptr<SpectrumTexture>,
pub albedo: Ptr<GPUSpectrumTexture>, pub albedo: Ptr<SpectrumTexture>,
pub u_roughness: Ptr<GPUFloatTexture>, pub u_roughness: Ptr<FloatTexture>,
pub v_roughness: Ptr<GPUFloatTexture>, pub v_roughness: Ptr<FloatTexture>,
pub thickness: Ptr<GPUFloatTexture>, pub thickness: Ptr<FloatTexture>,
pub g: Ptr<GPUFloatTexture>, pub g: Ptr<FloatTexture>,
pub eta: Ptr<Spectrum>, pub eta: Ptr<Spectrum>,
pub max_depth: u32, pub max_depth: u32,
pub n_samples: u32, pub n_samples: u32,
@ -34,13 +34,13 @@ pub struct CoatedDiffuseMaterial {
impl CoatedDiffuseMaterial { impl CoatedDiffuseMaterial {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
reflectance: Ptr<GPUSpectrumTexture>, reflectance: Ptr<SpectrumTexture>,
u_roughness: Ptr<GPUFloatTexture>, u_roughness: Ptr<FloatTexture>,
v_roughness: Ptr<GPUFloatTexture>, v_roughness: Ptr<FloatTexture>,
thickness: Ptr<GPUFloatTexture>, thickness: Ptr<FloatTexture>,
albedo: Ptr<GPUSpectrumTexture>, albedo: Ptr<SpectrumTexture>,
g: Ptr<GPUFloatTexture>, g: Ptr<FloatTexture>,
displacement: Ptr<GPUFloatTexture>, displacement: Ptr<FloatTexture>,
eta: Ptr<Spectrum>, eta: Ptr<Spectrum>,
normal_map: Ptr<Image>, normal_map: Ptr<Image>,
remap_roughness: bool, remap_roughness: bool,
@ -141,7 +141,7 @@ impl MaterialTrait for CoatedDiffuseMaterial {
Some(&*self.normal_map) Some(&*self.normal_map)
} }
fn get_displacement(&self) -> Ptr<GPUFloatTexture> { fn get_displacement(&self) -> Ptr<FloatTexture> {
self.displacement self.displacement
} }
@ -153,18 +153,18 @@ impl MaterialTrait for CoatedDiffuseMaterial {
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct CoatedConductorMaterial { pub struct CoatedConductorMaterial {
displacement: Ptr<GPUFloatTexture>, displacement: Ptr<FloatTexture>,
interface_uroughness: Ptr<GPUFloatTexture>, interface_uroughness: Ptr<FloatTexture>,
interface_vroughness: Ptr<GPUFloatTexture>, interface_vroughness: Ptr<FloatTexture>,
thickness: Ptr<GPUFloatTexture>, thickness: Ptr<FloatTexture>,
interface_eta: Ptr<Spectrum>, interface_eta: Ptr<Spectrum>,
g: Ptr<GPUFloatTexture>, g: Ptr<FloatTexture>,
albedo: Ptr<GPUSpectrumTexture>, albedo: Ptr<SpectrumTexture>,
conductor_uroughness: Ptr<GPUFloatTexture>, conductor_uroughness: Ptr<FloatTexture>,
conductor_vroughness: Ptr<GPUFloatTexture>, conductor_vroughness: Ptr<FloatTexture>,
conductor_eta: Ptr<GPUSpectrumTexture>, conductor_eta: Ptr<SpectrumTexture>,
k: Ptr<GPUSpectrumTexture>, k: Ptr<SpectrumTexture>,
reflectance: Ptr<GPUSpectrumTexture>, reflectance: Ptr<SpectrumTexture>,
normal_map: Ptr<Image>, normal_map: Ptr<Image>,
max_depth: u32, max_depth: u32,
n_samples: u32, n_samples: u32,
@ -175,17 +175,17 @@ pub struct CoatedConductorMaterial {
impl CoatedConductorMaterial { impl CoatedConductorMaterial {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
displacement: Ptr<GPUFloatTexture>, displacement: Ptr<FloatTexture>,
interface_uroughness: Ptr<GPUFloatTexture>, interface_uroughness: Ptr<FloatTexture>,
interface_vroughness: Ptr<GPUFloatTexture>, interface_vroughness: Ptr<FloatTexture>,
thickness: Ptr<GPUFloatTexture>, thickness: Ptr<FloatTexture>,
g: Ptr<GPUFloatTexture>, g: Ptr<FloatTexture>,
albedo: Ptr<GPUSpectrumTexture>, albedo: Ptr<SpectrumTexture>,
conductor_uroughness: Ptr<GPUFloatTexture>, conductor_uroughness: Ptr<FloatTexture>,
conductor_vroughness: Ptr<GPUFloatTexture>, conductor_vroughness: Ptr<FloatTexture>,
conductor_eta: Ptr<GPUSpectrumTexture>, conductor_eta: Ptr<SpectrumTexture>,
k: Ptr<GPUSpectrumTexture>, k: Ptr<SpectrumTexture>,
reflectance: Ptr<GPUSpectrumTexture>, reflectance: Ptr<SpectrumTexture>,
normal_map: Ptr<Image>, normal_map: Ptr<Image>,
interface_eta: Ptr<Spectrum>, interface_eta: Ptr<Spectrum>,
max_depth: u32, max_depth: u32,
@ -337,7 +337,7 @@ impl MaterialTrait for CoatedConductorMaterial {
self.normal_map.get() self.normal_map.get()
} }
fn get_displacement(&self) -> Ptr<GPUFloatTexture> { fn get_displacement(&self) -> Ptr<FloatTexture> {
self.displacement self.displacement
} }

View file

@ -10,37 +10,37 @@ use crate::core::image::Image;
use crate::core::material::{Material, MaterialEvalContext, MaterialTrait}; use crate::core::material::{Material, MaterialEvalContext, MaterialTrait};
use crate::core::scattering::TrowbridgeReitzDistribution; use crate::core::scattering::TrowbridgeReitzDistribution;
use crate::core::spectrum::{Spectrum, SpectrumTrait}; use crate::core::spectrum::{Spectrum, SpectrumTrait};
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator}; use crate::core::texture::{FloatTexture, SpectrumTexture, TextureEvaluator};
use crate::spectra::{SampledSpectrum, SampledWavelengths}; use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::textures::GPUSpectrumMixTexture; use crate::textures::SpectrumMixTexture;
use crate::utils::Ptr; use crate::utils::Ptr;
use crate::utils::math::clamp; use crate::utils::math::clamp;
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct HairMaterial { pub struct HairMaterial {
pub sigma_a: Ptr<GPUSpectrumTexture>, pub sigma_a: Ptr<SpectrumTexture>,
pub color: Ptr<GPUSpectrumTexture>, pub color: Ptr<SpectrumTexture>,
pub eumelanin: Ptr<GPUFloatTexture>, pub eumelanin: Ptr<FloatTexture>,
pub pheomelanin: Ptr<GPUFloatTexture>, pub pheomelanin: Ptr<FloatTexture>,
pub eta: Ptr<GPUFloatTexture>, pub eta: Ptr<FloatTexture>,
pub beta_m: Ptr<GPUFloatTexture>, pub beta_m: Ptr<FloatTexture>,
pub beta_n: Ptr<GPUFloatTexture>, pub beta_n: Ptr<FloatTexture>,
pub alpha: Ptr<GPUFloatTexture>, pub alpha: Ptr<FloatTexture>,
} }
impl HairMaterial { impl HairMaterial {
#[cfg(not(target_os = "cuda"))] #[cfg(not(target_os = "cuda"))]
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
sigma_a: Ptr<GPUSpectrumTexture>, sigma_a: Ptr<SpectrumTexture>,
color: Ptr<GPUSpectrumTexture>, color: Ptr<SpectrumTexture>,
eumelanin: Ptr<GPUFloatTexture>, eumelanin: Ptr<FloatTexture>,
pheomelanin: Ptr<GPUFloatTexture>, pheomelanin: Ptr<FloatTexture>,
eta: Ptr<GPUFloatTexture>, eta: Ptr<FloatTexture>,
beta_m: Ptr<GPUFloatTexture>, beta_m: Ptr<FloatTexture>,
beta_n: Ptr<GPUFloatTexture>, beta_n: Ptr<FloatTexture>,
alpha: Ptr<GPUFloatTexture>, alpha: Ptr<FloatTexture>,
) -> Self { ) -> Self {
Self { Self {
sigma_a, sigma_a,
@ -81,7 +81,7 @@ impl MaterialTrait for HairMaterial {
todo!() todo!()
} }
fn get_displacement(&self) -> Ptr<GPUFloatTexture> { fn get_displacement(&self) -> Ptr<FloatTexture> {
Ptr::null() Ptr::null()
} }
@ -93,7 +93,7 @@ impl MaterialTrait for HairMaterial {
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct MeasuredMaterial { pub struct MeasuredMaterial {
pub displacement: Ptr<GPUFloatTexture>, pub displacement: Ptr<FloatTexture>,
pub normal_map: Ptr<Image>, pub normal_map: Ptr<Image>,
pub brdf: Ptr<MeasuredBxDFData>, pub brdf: Ptr<MeasuredBxDFData>,
} }
@ -126,7 +126,7 @@ impl MaterialTrait for MeasuredMaterial {
Some(&*self.normal_map) Some(&*self.normal_map)
} }
fn get_displacement(&self) -> Ptr<GPUFloatTexture> { fn get_displacement(&self) -> Ptr<FloatTexture> {
self.displacement self.displacement
} }
@ -139,15 +139,15 @@ impl MaterialTrait for MeasuredMaterial {
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct SubsurfaceMaterial { pub struct SubsurfaceMaterial {
pub normal_map: Ptr<Image>, pub normal_map: Ptr<Image>,
pub displacement: Ptr<GPUFloatTexture>, pub displacement: Ptr<FloatTexture>,
pub sigma_a: Ptr<GPUSpectrumTexture>, pub sigma_a: Ptr<SpectrumTexture>,
pub sigma_s: Ptr<GPUSpectrumMixTexture>, pub sigma_s: Ptr<SpectrumMixTexture>,
pub reflectance: Ptr<GPUSpectrumMixTexture>, pub reflectance: Ptr<SpectrumMixTexture>,
pub mfp: Ptr<GPUSpectrumMixTexture>, pub mfp: Ptr<SpectrumMixTexture>,
pub eta: Float, pub eta: Float,
pub scale: Float, pub scale: Float,
pub u_roughness: Ptr<GPUFloatTexture>, pub u_roughness: Ptr<FloatTexture>,
pub v_roughness: Ptr<GPUFloatTexture>, pub v_roughness: Ptr<FloatTexture>,
pub remap_roughness: bool, pub remap_roughness: bool,
pub table: Ptr<BSSRDFTable>, pub table: Ptr<BSSRDFTable>,
} }
@ -178,7 +178,7 @@ impl MaterialTrait for SubsurfaceMaterial {
todo!() todo!()
} }
fn get_displacement(&self) -> Ptr<GPUFloatTexture> { fn get_displacement(&self) -> Ptr<FloatTexture> {
todo!() todo!()
} }

View file

@ -8,7 +8,7 @@ use crate::core::image::Image;
use crate::core::material::{Material, MaterialEvalContext, MaterialTrait}; use crate::core::material::{Material, MaterialEvalContext, MaterialTrait};
use crate::core::scattering::TrowbridgeReitzDistribution; use crate::core::scattering::TrowbridgeReitzDistribution;
use crate::core::spectrum::{Spectrum, SpectrumTrait}; use crate::core::spectrum::{Spectrum, SpectrumTrait};
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator}; use crate::core::texture::{FloatTexture, SpectrumTexture, TextureEvaluator};
use crate::spectra::{SampledSpectrum, SampledWavelengths}; use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::math::clamp; use crate::utils::math::clamp;
use crate::utils::Ptr; use crate::utils::Ptr;
@ -17,24 +17,24 @@ use crate::utils::Ptr;
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct ConductorMaterial { pub struct ConductorMaterial {
pub normal_map: Ptr<Image>, pub normal_map: Ptr<Image>,
pub reflectance: Ptr<GPUSpectrumTexture>, pub reflectance: Ptr<SpectrumTexture>,
pub eta: Ptr<GPUSpectrumTexture>, pub eta: Ptr<SpectrumTexture>,
pub k: Ptr<GPUSpectrumTexture>, pub k: Ptr<SpectrumTexture>,
pub u_roughness: Ptr<GPUFloatTexture>, pub u_roughness: Ptr<FloatTexture>,
pub v_roughness: Ptr<GPUFloatTexture>, pub v_roughness: Ptr<FloatTexture>,
pub displacement: Ptr<GPUFloatTexture>, pub displacement: Ptr<FloatTexture>,
pub remap_roughness: bool, pub remap_roughness: bool,
} }
impl ConductorMaterial { impl ConductorMaterial {
pub fn new( pub fn new(
normal_map: Ptr<Image>, normal_map: Ptr<Image>,
reflectance: Ptr<GPUSpectrumTexture>, reflectance: Ptr<SpectrumTexture>,
eta: Ptr<GPUSpectrumTexture>, eta: Ptr<SpectrumTexture>,
k: Ptr<GPUSpectrumTexture>, k: Ptr<SpectrumTexture>,
u_roughness: Ptr<GPUFloatTexture>, u_roughness: Ptr<FloatTexture>,
v_roughness: Ptr<GPUFloatTexture>, v_roughness: Ptr<FloatTexture>,
displacement: Ptr<GPUFloatTexture>, displacement: Ptr<FloatTexture>,
remap_roughness: bool, remap_roughness: bool,
) -> Self { ) -> Self {
Self { Self {
@ -105,7 +105,7 @@ impl MaterialTrait for ConductorMaterial {
self.normal_map.get() self.normal_map.get()
} }
fn get_displacement(&self) -> Ptr<GPUFloatTexture> { fn get_displacement(&self) -> Ptr<FloatTexture> {
self.displacement self.displacement
} }

View file

@ -8,7 +8,7 @@ use crate::core::image::Image;
use crate::core::material::{Material, MaterialEvalContext, MaterialTrait}; use crate::core::material::{Material, MaterialEvalContext, MaterialTrait};
use crate::core::scattering::TrowbridgeReitzDistribution; use crate::core::scattering::TrowbridgeReitzDistribution;
use crate::core::spectrum::{Spectrum, SpectrumTrait}; use crate::core::spectrum::{Spectrum, SpectrumTrait};
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator}; use crate::core::texture::{FloatTexture, SpectrumTexture, TextureEvaluator};
use crate::spectra::{SampledSpectrum, SampledWavelengths}; use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::math::clamp; use crate::utils::math::clamp;
use crate::Ptr; use crate::Ptr;
@ -17,9 +17,9 @@ use crate::Ptr;
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct DielectricMaterial { pub struct DielectricMaterial {
pub normal_map: Ptr<Image>, pub normal_map: Ptr<Image>,
pub displacement: Ptr<GPUFloatTexture>, pub displacement: Ptr<FloatTexture>,
pub u_roughness: Ptr<GPUFloatTexture>, pub u_roughness: Ptr<FloatTexture>,
pub v_roughness: Ptr<GPUFloatTexture>, pub v_roughness: Ptr<FloatTexture>,
pub eta: Ptr<Spectrum>, pub eta: Ptr<Spectrum>,
pub remap_roughness: bool, pub remap_roughness: bool,
} }
@ -71,7 +71,7 @@ impl MaterialTrait for DielectricMaterial {
Some(&*self.normal_map) Some(&*self.normal_map)
} }
fn get_displacement(&self) -> Ptr<GPUFloatTexture> { fn get_displacement(&self) -> Ptr<FloatTexture> {
self.displacement self.displacement
} }
@ -83,7 +83,7 @@ impl MaterialTrait for DielectricMaterial {
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct ThinDielectricMaterial { pub struct ThinDielectricMaterial {
pub displacement: Ptr<GPUFloatTexture>, pub displacement: Ptr<FloatTexture>,
pub normal_map: Ptr<Image>, pub normal_map: Ptr<Image>,
pub eta: Ptr<Spectrum>, pub eta: Ptr<Spectrum>,
} }
@ -114,7 +114,7 @@ impl MaterialTrait for ThinDielectricMaterial {
Some(&*self.normal_map) Some(&*self.normal_map)
} }
fn get_displacement(&self) -> Ptr<GPUFloatTexture> { fn get_displacement(&self) -> Ptr<FloatTexture> {
self.displacement self.displacement
} }

View file

@ -1,4 +1,3 @@
use crate::Float;
use crate::bxdfs::{ use crate::bxdfs::{
CoatedConductorBxDF, CoatedDiffuseBxDF, ConductorBxDF, DielectricBxDF, DiffuseBxDF, HairBxDF, CoatedConductorBxDF, CoatedDiffuseBxDF, ConductorBxDF, DielectricBxDF, DiffuseBxDF, HairBxDF,
}; };
@ -9,17 +8,18 @@ use crate::core::image::Image;
use crate::core::material::{Material, MaterialEvalContext, MaterialTrait}; use crate::core::material::{Material, MaterialEvalContext, MaterialTrait};
use crate::core::scattering::TrowbridgeReitzDistribution; use crate::core::scattering::TrowbridgeReitzDistribution;
use crate::core::spectrum::{Spectrum, SpectrumTrait}; use crate::core::spectrum::{Spectrum, SpectrumTrait};
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator}; use crate::core::texture::{FloatTexture, SpectrumTexture, TextureEvaluator};
use crate::spectra::{SampledSpectrum, SampledWavelengths}; use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::Ptr;
use crate::utils::math::clamp; use crate::utils::math::clamp;
use crate::Float;
use crate::Ptr;
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct DiffuseMaterial { pub struct DiffuseMaterial {
pub normal_map: Ptr<Image>, pub normal_map: Ptr<Image>,
pub displacement: Ptr<GPUFloatTexture>, pub displacement: Ptr<FloatTexture>,
pub reflectance: Ptr<GPUSpectrumTexture>, pub reflectance: Ptr<SpectrumTexture>,
} }
impl MaterialTrait for DiffuseMaterial { impl MaterialTrait for DiffuseMaterial {
@ -29,7 +29,8 @@ impl MaterialTrait for DiffuseMaterial {
ctx: &MaterialEvalContext, ctx: &MaterialEvalContext,
lambda: &SampledWavelengths, lambda: &SampledWavelengths,
) -> BSDF { ) -> BSDF {
let r = tex_eval.evaluate_spectrum(&self.reflectance, ctx, lambda); let spec = tex_eval.evaluate_spectrum(&self.reflectance, ctx, lambda);
let r = SampledSpectrum::clamp(&spec, 0., 1.);
let bxdf = BxDF::Diffuse(DiffuseBxDF::new(r)); let bxdf = BxDF::Diffuse(DiffuseBxDF::new(r));
BSDF::new(ctx.ns, ctx.dpdus, bxdf) BSDF::new(ctx.ns, ctx.dpdus, bxdf)
} }
@ -51,7 +52,7 @@ impl MaterialTrait for DiffuseMaterial {
self.normal_map.get() self.normal_map.get()
} }
fn get_displacement(&self) -> Ptr<GPUFloatTexture> { fn get_displacement(&self) -> Ptr<FloatTexture> {
self.displacement self.displacement
} }
@ -64,9 +65,9 @@ impl MaterialTrait for DiffuseMaterial {
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct DiffuseTransmissionMaterial { pub struct DiffuseTransmissionMaterial {
pub image: Ptr<Image>, pub image: Ptr<Image>,
pub displacement: Ptr<GPUFloatTexture>, pub displacement: Ptr<FloatTexture>,
pub reflectance: Ptr<GPUFloatTexture>, pub reflectance: Ptr<FloatTexture>,
pub transmittance: Ptr<GPUFloatTexture>, pub transmittance: Ptr<FloatTexture>,
pub scale: Float, pub scale: Float,
} }
@ -96,7 +97,7 @@ impl MaterialTrait for DiffuseTransmissionMaterial {
self.image.get() self.image.get()
} }
fn get_displacement(&self) -> Ptr<GPUFloatTexture> { fn get_displacement(&self) -> Ptr<FloatTexture> {
self.displacement self.displacement
} }

View file

@ -8,7 +8,7 @@ use crate::core::image::Image;
use crate::core::material::{Material, MaterialEvalContext, MaterialTrait}; use crate::core::material::{Material, MaterialEvalContext, MaterialTrait};
use crate::core::scattering::TrowbridgeReitzDistribution; use crate::core::scattering::TrowbridgeReitzDistribution;
use crate::core::spectrum::{Spectrum, SpectrumTrait}; use crate::core::spectrum::{Spectrum, SpectrumTrait};
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator}; use crate::core::texture::{FloatTexture, SpectrumTexture, TextureEvaluator};
use crate::spectra::{SampledSpectrum, SampledWavelengths}; use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::Ptr; use crate::utils::Ptr;
use crate::utils::hash::hash_float; use crate::utils::hash::hash_float;
@ -17,7 +17,7 @@ use crate::utils::math::clamp;
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct MixMaterial { pub struct MixMaterial {
pub amount: Ptr<GPUFloatTexture>, pub amount: Ptr<FloatTexture>,
pub materials: [Ptr<Material>; 2], pub materials: [Ptr<Material>; 2],
} }
@ -73,7 +73,7 @@ impl MaterialTrait for MixMaterial {
None None
} }
fn get_displacement(&self) -> Ptr<GPUFloatTexture> { fn get_displacement(&self) -> Ptr<FloatTexture> {
panic!( panic!(
"MixMaterial::get_displacement() shouldn't be called. \ "MixMaterial::get_displacement() shouldn't be called. \
Displacement is not supported on Mix materials directly." Displacement is not supported on Mix materials directly."

View file

@ -1,6 +1,6 @@
use crate::Float; use crate::Float;
use crate::core::texture::{ use crate::core::texture::{
GPUFloatTexture, GPUSpectrumTexture, TextureEvalContext, TextureMapping2D, TextureMapping3D, FloatTexture, SpectrumTexture, TextureEvalContext, TextureMapping2D, TextureMapping3D,
TextureMapping3DTrait, TextureMapping3DTrait,
}; };
use crate::spectra::{SampledSpectrum, SampledWavelengths}; use crate::spectra::{SampledSpectrum, SampledWavelengths};
@ -46,7 +46,7 @@ fn checkerboard(
pub struct FloatCheckerboardTexture { pub struct FloatCheckerboardTexture {
pub map2d: Ptr<TextureMapping2D>, pub map2d: Ptr<TextureMapping2D>,
pub map3d: Ptr<TextureMapping3D>, pub map3d: Ptr<TextureMapping3D>,
pub tex: [Ptr<GPUFloatTexture>; 2], pub tex: [Ptr<FloatTexture>; 2],
} }
impl FloatCheckerboardTexture { impl FloatCheckerboardTexture {
@ -77,7 +77,7 @@ impl FloatCheckerboardTexture {
pub struct SpectrumCheckerboardTexture { pub struct SpectrumCheckerboardTexture {
pub map2d: Ptr<TextureMapping2D>, pub map2d: Ptr<TextureMapping2D>,
pub map3d: Ptr<TextureMapping3D>, pub map3d: Ptr<TextureMapping3D>,
pub tex: [Ptr<GPUSpectrumTexture>; 2], pub tex: [Ptr<SpectrumTexture>; 2],
} }
impl SpectrumCheckerboardTexture { impl SpectrumCheckerboardTexture {

View file

@ -1,7 +1,7 @@
use crate::Float; use crate::Float;
use crate::core::geometry::{Point2f, VectorLike}; use crate::core::geometry::{Point2f, VectorLike};
use crate::core::texture::{ use crate::core::texture::{
GPUFloatTexture, GPUSpectrumTexture, TextureEvalContext, TextureMapping2D, FloatTexture, SpectrumTexture, TextureEvalContext, TextureMapping2D,
}; };
use crate::spectra::sampled::{SampledSpectrum, SampledWavelengths}; use crate::spectra::sampled::{SampledSpectrum, SampledWavelengths};
use crate::utils::Ptr; use crate::utils::Ptr;
@ -29,8 +29,8 @@ fn inside_polka_dot(st: Point2f) -> bool {
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct FloatDotsTexture { pub struct FloatDotsTexture {
pub mapping: TextureMapping2D, pub mapping: TextureMapping2D,
pub outside_dot: Ptr<GPUFloatTexture>, pub outside_dot: Ptr<FloatTexture>,
pub inside_dot: Ptr<GPUFloatTexture>, pub inside_dot: Ptr<FloatTexture>,
} }
impl FloatDotsTexture { impl FloatDotsTexture {
@ -54,8 +54,8 @@ impl FloatDotsTexture {
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct SpectrumDotsTexture { pub struct SpectrumDotsTexture {
pub mapping: TextureMapping2D, pub mapping: TextureMapping2D,
pub outside_dot: Ptr<GPUSpectrumTexture>, pub outside_dot: Ptr<SpectrumTexture>,
pub inside_dot: Ptr<GPUSpectrumTexture>, pub inside_dot: Ptr<SpectrumTexture>,
} }
impl SpectrumDotsTexture { impl SpectrumDotsTexture {

View file

@ -1,5 +1,5 @@
use crate::core::color::{RGB, XYZ}; use crate::core::color::{RGB, XYZ};
use crate::core::image::Image; use crate::core::image::{Image, WrapMode, WrapMode2D};
use crate::core::spectrum::SpectrumTrait; use crate::core::spectrum::SpectrumTrait;
use crate::core::texture::{SpectrumType, TextureEvalContext, TextureMapping2D}; use crate::core::texture::{SpectrumType, TextureEvalContext, TextureMapping2D};
use crate::spectra::{ use crate::spectra::{
@ -13,104 +13,119 @@ use crate::Float;
* Leaving it here isolated, for careful handling */ * Leaving it here isolated, for careful handling */
#[repr(C)] #[repr(C)]
#[derive(Clone, Debug, Copy)] #[derive(Clone, Debug, Copy)]
pub struct GPUSpectrumImageTexture { pub struct SpectrumImageTexture {
pub mapping: TextureMapping2D, pub wrap_mode: WrapMode,
pub tex_obj: u64, pub tex_obj: u64,
pub image: Ptr<Image>,
pub scale: Float, pub scale: Float,
pub invert: bool,
pub is_single_channel: bool,
pub color_space: Ptr<RGBColorSpace>,
pub spectrum_type: SpectrumType, pub spectrum_type: SpectrumType,
pub image: Ptr<Image>,
pub color_space: Ptr<RGBColorSpace>,
pub mapping: TextureMapping2D,
pub is_single_channel: bool,
pub invert: bool,
} }
impl GPUSpectrumImageTexture { impl SpectrumImageTexture {
#[allow(unused)]
pub fn evaluate( pub fn evaluate(
&self, &self,
ctx: &TextureEvalContext, ctx: &TextureEvalContext,
lambda: &SampledWavelengths, lambda: &SampledWavelengths,
) -> SampledSpectrum { ) -> SampledSpectrum {
#[cfg(not(feature = "cuda"))] #[cfg(feature = "cuda")]
{ if self.tex_obj != 0 {
return SampledSpectrum::zero(); // FUTURE: hardware sampling path.
// let c = self.mapping.map(ctx);
// let rgb = tex2d_grad(self.tex_obj, c.st, [c.dsdx,c.dtdx], [c.dsdy,c.dtdy]);
// return spectrum_from_rgb(rgb * self.scale, self.invert, self.spectrum_type, ...);
// Until then, fall through to software path below.
} }
#[cfg(feature = "cuda")] let Some(image) = self.image.get() else {
{ return SampledSpectrum::zero();
use cuda_std::intrinsics; };
let c = self.mapping.map(ctx); let mut c = self.mapping.map(ctx);
let u = c.st.x(); c.st[1] = 1.0 - c.st[1]; // flip V to match pbrt convention
let v = 1.0 - c.st.y();
let d_p_dx = [c.dsdx, c.dtdx]; let wrap = WrapMode2D {
let d_p_dy = [c.dsdy, c.dtdy]; uv: [self.wrap_mode; 2],
};
let tex_color = if self.is_single_channel { let rgb = if image.n_channels == 1 {
let val = 0.; let v = image.bilerp_channel_with_wrap(c.st, 0, wrap);
// let val: Float = RGB::new(v, v, v)
// unsafe { intrinsics::tex2d_grad(self.tex_obj, u, v, d_p_dx, d_p_dy) }; } else {
RGB::new(val, val, val) RGB::new(
} else { image.bilerp_channel_with_wrap(c.st, 0, wrap),
let val = [0., 0., 0., 0.]; image.bilerp_channel_with_wrap(c.st, 1, wrap),
// let val: [Float; 4] = image.bilerp_channel_with_wrap(c.st, 2, wrap),
// unsafe { intrinsics::tex2d_grad(self.tex_obj, u, v, d_p_dx, d_p_dy) }; )
RGB::new(val[0], val[1], val[2]) };
}; let mut rgb = rgb * self.scale;
if self.invert {
let mut rgb = tex_color * self.scale; rgb = (RGB::new(1.0, 1.0, 1.0) - rgb);
if self.invert { }
rgb = (RGB::new(1.0, 1.0, 1.0) - rgb).clamp_zero(); rgb = rgb.clamp_zero();
} let cs = self
.color_space
match self.spectrum_type { .get()
SpectrumType::Unbounded => { .expect("color_space must not be null");
RGBUnboundedSpectrum::new(&self.color_space, rgb).sample(lambda) match self.spectrum_type {
} SpectrumType::Unbounded => RGBUnboundedSpectrum::new(cs, rgb).sample(lambda),
SpectrumType::Albedo => { SpectrumType::Albedo => RGBAlbedoSpectrum::new(cs, rgb.clamp(0.0, 1.0)).sample(lambda),
RGBAlbedoSpectrum::new(&self.color_space, rgb.clamp(0.0, 1.0)).sample(lambda) _ => RGBIlluminantSpectrum::new(cs, rgb).sample(lambda),
}
_ => RGBIlluminantSpectrum::new(&self.color_space, rgb).sample(lambda),
}
} }
} }
} }
#[repr(C)]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct GPUFloatImageTexture { pub struct FloatImageTexture {
pub image: Ptr<Image>,
pub mapping: TextureMapping2D, pub mapping: TextureMapping2D,
pub wrap_mode: WrapMode,
pub tex_obj: u64, pub tex_obj: u64,
pub scale: Float, pub scale: Float,
pub invert: bool, pub invert: bool,
} }
impl GPUFloatImageTexture { impl FloatImageTexture {
#[allow(unused_variables)] #[allow(unused_variables)]
pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float { pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
#[cfg(not(feature = "cuda"))] #[cfg(not(feature = "cuda"))]
{ {
return 0.; let wrap = WrapMode2D {
uv: [self.wrap_mode; 2],
};
let mut c = self.mapping.map(ctx);
c.st[1] = 1.0 - c.st[1];
let Some(image) = self.image.get() else { return 0. };
let v = image.bilerp_channel_with_wrap(c.st, 0, wrap);
let v = if self.invert { (1.0 - v).max(0.0) } else { v };
return v * self.scale;
} }
#[cfg(feature = "cuda")] #[cfg(feature = "cuda")]
#[allow(unused)]
{ {
use cuda_std::intrinsics; if self.tex_obj != 0 {
let c = self.mapping.map(ctx); use cuda_std::intrinsics;
let u = c.st.x(); let c = self.mapping.map(ctx);
let v = 1.0 - c.st.y(); let u = c.st.x();
let d_p_dx = [c.dsdx, c.dtdx]; let v = 1.0 - c.st.y();
let d_p_dy = [c.dsdy, c.dtdy]; let d_p_dx = [c.dsdx, c.dtdx];
// let val: Float = unsafe { intrinsics::tex2d_grad(self.tex_obj, u, v, d_p_dx, d_p_dy) }; let d_p_dy = [c.dsdy, c.dtdy];
let val: Float = 0.; // let val: Float = unsafe { intrinsics::tex2d_grad(self.tex_obj, u, v, d_p_dx, d_p_dy) };
let _ = (u, v, d_p_dx, d_p_dy);
let result = if self.invert { let val: Float = 0.;
// Invert the pixel intensity let result = if self.invert { (1.0 - val).max(0.0) } else { val };
(1.0 - val).max(0.0) return result * self.scale;
} else { }
val // software path (no hardware texture object)
}; let wrap = WrapMode2D { uv: [self.wrap_mode; 2] };
let mut c = self.mapping.map(ctx);
return result * self.scale; c.st[1] = 1.0 - c.st[1];
let Some(image) = self.image.get() else { return 0. };
let v = image.bilerp_channel_with_wrap(c.st, 0, wrap);
let v = if self.invert { (1.0 - v).max(0.0) } else { v };
return v * self.scale;
} }
} }
} }

View file

@ -1,18 +1,18 @@
use crate::Float; use crate::Float;
use crate::core::geometry::{Vector3f, VectorLike}; use crate::core::geometry::{Vector3f, VectorLike};
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvalContext}; use crate::core::texture::{FloatTexture, SpectrumTexture, TextureEvalContext};
use crate::spectra::{SampledSpectrum, SampledWavelengths}; use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::Ptr; use crate::utils::Ptr;
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct GPUFloatMixTexture { pub struct FloatMixTexture {
pub tex1: Ptr<GPUFloatTexture>, pub tex1: Ptr<FloatTexture>,
pub tex2: Ptr<GPUFloatTexture>, pub tex2: Ptr<FloatTexture>,
pub amount: Ptr<GPUFloatTexture>, pub amount: Ptr<FloatTexture>,
} }
impl GPUFloatMixTexture { impl FloatMixTexture {
pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float { pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
let amt = self.amount.get().map(|t| t.evaluate(ctx)).unwrap_or(0.0); let amt = self.amount.get().map(|t| t.evaluate(ctx)).unwrap_or(0.0);
let t1 = if amt != 1.0 { let t1 = if amt != 1.0 {
@ -33,13 +33,13 @@ impl GPUFloatMixTexture {
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct GPUFloatDirectionMixTexture { pub struct FloatDirectionMixTexture {
pub tex1: Ptr<GPUFloatTexture>, pub tex1: Ptr<FloatTexture>,
pub tex2: Ptr<GPUFloatTexture>, pub tex2: Ptr<FloatTexture>,
pub dir: Vector3f, pub dir: Vector3f,
} }
impl GPUFloatDirectionMixTexture { impl FloatDirectionMixTexture {
pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float { pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
let amt = self.dir.abs_dot(ctx.n.into()); let amt = self.dir.abs_dot(ctx.n.into());
let t1 = if amt != 1.0 { let t1 = if amt != 1.0 {
@ -60,13 +60,13 @@ impl GPUFloatDirectionMixTexture {
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct GPUSpectrumMixTexture { pub struct SpectrumMixTexture {
pub tex1: Ptr<GPUSpectrumTexture>, pub tex1: Ptr<SpectrumTexture>,
pub tex2: Ptr<GPUSpectrumTexture>, pub tex2: Ptr<SpectrumTexture>,
pub amount: Ptr<GPUFloatTexture>, pub amount: Ptr<FloatTexture>,
} }
impl GPUSpectrumMixTexture { impl SpectrumMixTexture {
pub fn evaluate( pub fn evaluate(
&self, &self,
ctx: &TextureEvalContext, ctx: &TextureEvalContext,
@ -97,13 +97,13 @@ impl GPUSpectrumMixTexture {
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct GPUSpectrumDirectionMixTexture { pub struct SpectrumDirectionMixTexture {
pub tex1: Ptr<GPUSpectrumTexture>, pub tex1: Ptr<SpectrumTexture>,
pub tex2: Ptr<GPUSpectrumTexture>, pub tex2: Ptr<SpectrumTexture>,
pub dir: Vector3f, pub dir: Vector3f,
} }
impl GPUSpectrumDirectionMixTexture { impl SpectrumDirectionMixTexture {
pub fn evaluate( pub fn evaluate(
&self, &self,
ctx: &TextureEvalContext, ctx: &TextureEvalContext,

View file

@ -1,16 +1,16 @@
use crate::Float; use crate::Float;
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvalContext}; use crate::core::texture::{FloatTexture, SpectrumTexture, TextureEvalContext};
use crate::spectra::{SampledSpectrum, SampledWavelengths}; use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::Ptr; use crate::utils::Ptr;
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct GPUFloatScaledTexture { pub struct FloatScaledTexture {
pub tex: Ptr<GPUFloatTexture>, pub tex: Ptr<FloatTexture>,
pub scale: Ptr<GPUFloatTexture>, pub scale: Ptr<FloatTexture>,
} }
impl GPUFloatScaledTexture { impl FloatScaledTexture {
pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float { pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
let sc = self.scale.get().map(|t| t.evaluate(ctx)).unwrap(); let sc = self.scale.get().map(|t| t.evaluate(ctx)).unwrap();
if sc == 0. { if sc == 0. {
@ -22,12 +22,12 @@ impl GPUFloatScaledTexture {
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct GPUSpectrumScaledTexture { pub struct SpectrumScaledTexture {
pub tex: Ptr<GPUSpectrumTexture>, pub tex: Ptr<SpectrumTexture>,
pub scale: Ptr<GPUFloatTexture>, pub scale: Ptr<FloatTexture>,
} }
impl GPUSpectrumScaledTexture { impl SpectrumScaledTexture {
pub fn evaluate( pub fn evaluate(
&self, &self,
ctx: &TextureEvalContext, ctx: &TextureEvalContext,

View file

@ -3,6 +3,8 @@ use crate::core::film::Film;
use crate::core::filter::Filter; use crate::core::filter::Filter;
use crate::core::light::Light; use crate::core::light::Light;
use crate::core::sampler::Sampler; use crate::core::sampler::Sampler;
use crate::core::material::Material;
use crate::core::LightIdx;
use crate::lights::sampler::LightSampler; use crate::lights::sampler::LightSampler;
use crate::wavefront::aggregate::WavefrontAggregate; use crate::wavefront::aggregate::WavefrontAggregate;
use crate::wavefront::workitems::*; use crate::wavefront::workitems::*;
@ -15,7 +17,9 @@ pub struct WavefrontPathIntegrator<A: WavefrontAggregate> {
pub max_depth: u32, pub max_depth: u32,
pub samples_per_pixel: u32, pub samples_per_pixel: u32,
pub regularize: bool, pub regularize: bool,
pub infinite_lights: GVec<Ptr<Light>>, pub lights: GVec<Light>,
pub materials: GVec<Material>,
pub infinite_lights: GVec<LightIdx>,
pub max_queue_size: u32, pub max_queue_size: u32,
pub scanlines_per_pass: u32, pub scanlines_per_pass: u32,
pub light_sampler: LightSampler, pub light_sampler: LightSampler,
@ -33,4 +37,3 @@ pub struct WavefrontPathIntegrator<A: WavefrontAggregate> {
pub trait WavefrontRenderer { pub trait WavefrontRenderer {
fn render(&mut self); fn render(&mut self);
} }

View file

@ -3,8 +3,8 @@ use crate::core::film::VisibleSurface;
use crate::core::geometry::{ use crate::core::geometry::{
Normal3f, Point2f, Point2i, Point3f, Point3fi, Ray, RayDifferential, Vector3f, Normal3f, Point2f, Point2i, Point3f, Point3fi, Ray, RayDifferential, Vector3f,
}; };
use crate::core::light::Light; use crate::core::light::{Light, LightSampleContext};
use crate::core::light::LightSampleContext; use crate::core::{MaterialIdx, LightIdx};
use crate::core::material::Material; use crate::core::material::Material;
use crate::core::medium::{Medium, MediumInterface}; use crate::core::medium::{Medium, MediumInterface};
use crate::spectra::{SampledSpectrum, SampledWavelengths}; use crate::spectra::{SampledSpectrum, SampledWavelengths};
@ -207,7 +207,7 @@ impl SoA for EscapedRayWorkItemSoA {
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct HitAreaLightWorkItem { pub struct HitAreaLightWorkItem {
pub area_light: Ptr<Light>, pub area_light: LightIdx,
pub p: Point3f, pub p: Point3f,
pub n: Normal3f, pub n: Normal3f,
pub uv: Point2f, pub uv: Point2f,
@ -225,7 +225,7 @@ pub struct HitAreaLightWorkItem {
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct HitAreaLightWorkItemSoA { pub struct HitAreaLightWorkItemSoA {
pub area_light: SoABuffer<Ptr<Light>>, pub area_light: SoABuffer<LightIdx>,
pub p: SoABuffer<Point3f>, pub p: SoABuffer<Point3f>,
pub n: SoABuffer<Normal3f>, pub n: SoABuffer<Normal3f>,
pub uv: SoABuffer<Point2f>, pub uv: SoABuffer<Point2f>,
@ -308,8 +308,8 @@ pub struct MaterialEvalWorkItem {
pub wo: Vector3f, pub wo: Vector3f,
pub time: Float, pub time: Float,
pub face_index: i32, pub face_index: i32,
pub material: Ptr<Material>, pub material: MaterialIdx,
pub area_light: Ptr<Light>, pub area_light: LightIdx,
pub medium_interface: MediumInterface, pub medium_interface: MediumInterface,
pub pixel_index: u32, pub pixel_index: u32,
pub lambda: SampledWavelengths, pub lambda: SampledWavelengths,
@ -336,8 +336,8 @@ pub struct MaterialEvalWorkItemSoA {
pub wo: SoABuffer<Vector3f>, pub wo: SoABuffer<Vector3f>,
pub time: SoABuffer<Float>, pub time: SoABuffer<Float>,
pub face_index: SoABuffer<i32>, pub face_index: SoABuffer<i32>,
pub material: SoABuffer<Ptr<Material>>, pub material: SoABuffer<MaterialIdx>,
pub area_light: SoABuffer<Ptr<Light>>, pub area_light: SoABuffer<LightIdx>,
pub medium_interface: SoABuffer<MediumInterface>, pub medium_interface: SoABuffer<MediumInterface>,
pub pixel_index: SoABuffer<u32>, pub pixel_index: SoABuffer<u32>,
pub lambda: SoABuffer<SampledWavelengths>, pub lambda: SoABuffer<SampledWavelengths>,

View file

@ -19,6 +19,7 @@ pub trait InteractionGetter {
lambda: &SampledWavelengths, lambda: &SampledWavelengths,
camera: &Camera, camera: &Camera,
sampler: &mut Sampler, sampler: &mut Sampler,
materials: &[Material],
) -> Option<BSDF>; ) -> Option<BSDF>;
fn get_bssrdf( fn get_bssrdf(
@ -26,6 +27,7 @@ pub trait InteractionGetter {
_ray: &Ray, _ray: &Ray,
lambda: &SampledWavelengths, lambda: &SampledWavelengths,
_camera: &Camera, _camera: &Camera,
materials: &[Material],
) -> Option<BSSRDF>; ) -> Option<BSSRDF>;
} }
@ -36,18 +38,20 @@ impl InteractionGetter for SurfaceInteraction {
lambda: &SampledWavelengths, lambda: &SampledWavelengths,
camera: &Camera, camera: &Camera,
sampler: &mut Sampler, sampler: &mut Sampler,
materials: &[Material],
) -> Option<BSDF> { ) -> Option<BSDF> {
self.compute_differentials(r, camera, sampler.samples_per_pixel()); self.compute_differentials(r, camera, sampler.samples_per_pixel());
if self.material.is_none() {
return None;
}
let mut active_mat: &Material = &materials[self.material.0 as usize];
let material = { let material = {
let Some(mut active_mat) = self.material.get() else {
return None;
};
let tex_eval = UniversalTextureEvaluator; let tex_eval = UniversalTextureEvaluator;
while let Material::Mix(mix) = active_mat { while let Material::Mix(mix) = active_mat {
let ctx = MaterialEvalContext::from(&*self); let ctx = MaterialEvalContext::from(&*self);
active_mat = mix.choose_material(&tex_eval, &ctx)?; active_mat = mix.choose_material(&tex_eval, &ctx)?;
} }
*active_mat active_mat
}; };
let ctx = MaterialEvalContext::from(&*self); let ctx = MaterialEvalContext::from(&*self);
let tex_eval = UniversalTextureEvaluator; let tex_eval = UniversalTextureEvaluator;
@ -73,10 +77,12 @@ impl InteractionGetter for SurfaceInteraction {
_ray: &Ray, _ray: &Ray,
lambda: &SampledWavelengths, lambda: &SampledWavelengths,
_camera: &Camera, _camera: &Camera,
materials: &[Material],
) -> Option<BSSRDF> { ) -> Option<BSSRDF> {
let Some(mut active_mat) = self.material.get() else { if self.material.is_none() {
return None; return None;
}; }
let mut active_mat: &Material = &materials[self.material.0 as usize];
let tex_eval = UniversalTextureEvaluator; let tex_eval = UniversalTextureEvaluator;
while let Material::Mix(mix) = active_mat { while let Material::Mix(mix) = active_mat {
let ctx = MaterialEvalContext::from(self); let ctx = MaterialEvalContext::from(self);
@ -95,6 +101,7 @@ impl InteractionGetter for MediumInteraction {
_lambda: &SampledWavelengths, _lambda: &SampledWavelengths,
_camera: &Camera, _camera: &Camera,
_sampler: &mut Sampler, _sampler: &mut Sampler,
_materials: &[Material],
) -> Option<BSDF> { ) -> Option<BSDF> {
None None
} }
@ -104,6 +111,7 @@ impl InteractionGetter for MediumInteraction {
_ray: &Ray, _ray: &Ray,
_lambda: &SampledWavelengths, _lambda: &SampledWavelengths,
_camera: &Camera, _camera: &Camera,
_materials: &[Material],
) -> Option<BSSRDF> { ) -> Option<BSSRDF> {
None None
} }
@ -116,6 +124,7 @@ impl InteractionGetter for SimpleInteraction {
_lambda: &SampledWavelengths, _lambda: &SampledWavelengths,
_camera: &Camera, _camera: &Camera,
_sampler: &mut Sampler, _sampler: &mut Sampler,
_materials: &[Material],
) -> Option<BSDF> { ) -> Option<BSDF> {
None None
} }
@ -125,6 +134,7 @@ impl InteractionGetter for SimpleInteraction {
_ray: &Ray, _ray: &Ray,
_lambda: &SampledWavelengths, _lambda: &SampledWavelengths,
_camera: &Camera, _camera: &Camera,
_materials: &[Material],
) -> Option<BSSRDF> { ) -> Option<BSSRDF> {
None None
} }

View file

@ -88,14 +88,14 @@ impl MaterialFactory for Material {
} }
pub fn default_diffuse_material(arena: &Arena) -> Material { pub fn default_diffuse_material(arena: &Arena) -> Material {
use shared::core::texture::GPUSpectrumTexture; use shared::core::texture::SpectrumTexture;
use shared::core::texture::SpectrumConstantTexture; use shared::core::texture::SpectrumConstantTexture;
use shared::core::spectrum::{ConstantSpectrum, Spectrum}; use shared::core::spectrum::{ConstantSpectrum, Spectrum};
use shared::materials::DiffuseMaterial; use shared::materials::DiffuseMaterial;
use shared::utils::Ptr; use shared::utils::Ptr;
let grey = Spectrum::Constant(ConstantSpectrum { c: 0.5 }); let grey = Spectrum::Constant(ConstantSpectrum { c: 0.5 });
let tex = GPUSpectrumTexture::Constant(SpectrumConstantTexture::new(grey)); let tex = SpectrumTexture::Constant(SpectrumConstantTexture::new(grey));
let tex_ptr = arena.alloc(tex); let tex_ptr = arena.alloc(tex);
Material::Diffuse(DiffuseMaterial { Material::Diffuse(DiffuseMaterial {

View file

@ -1,16 +1,16 @@
use shared::core::{ use shared::core::{
LightIdx, MaterialIdx,
light::Light, light::Light,
material::Material, material::Material,
medium::MediumInterface, medium::MediumInterface,
primitive::{GeometricPrimitive, SimplePrimitive}, primitive::{GeometricPrimitive, SimplePrimitive},
shape::Shape, shape::Shape,
texture::GPUFloatTexture, texture::FloatTexture,
}; };
use shared::Ptr;
use shared::utils::Ptr;
pub trait CreateSimplePrimitive { pub trait CreateSimplePrimitive {
fn new(shape: Ptr<Shape>, material: Ptr<Material>) -> SimplePrimitive { fn new(shape: Ptr<Shape>, material: MaterialIdx) -> SimplePrimitive {
SimplePrimitive { shape, material } SimplePrimitive { shape, material }
} }
} }
@ -20,10 +20,10 @@ impl CreateSimplePrimitive for SimplePrimitive {}
pub trait CreateGeometricPrimitive { pub trait CreateGeometricPrimitive {
fn new( fn new(
shape: Ptr<Shape>, shape: Ptr<Shape>,
material: Ptr<Material>, material: MaterialIdx,
area_light: Ptr<Light>, area_light: LightIdx,
medium_interface: MediumInterface, medium_interface: MediumInterface,
alpha: Ptr<GPUFloatTexture>, alpha: Ptr<FloatTexture>,
) -> GeometricPrimitive { ) -> GeometricPrimitive {
GeometricPrimitive { GeometricPrimitive {
shape, shape,

View file

@ -17,7 +17,7 @@ pub fn render_scene(scene: &BasicScene, arena: &Arena) -> Result<()> {
let media = scene.create_media(); let media = scene.create_media();
let textures = scene.create_textures(arena); let textures = scene.create_textures(arena);
let (named_materials, materials) = scene.create_materials(&textures, arena)?; let (named_materials, materials) = scene.create_materials(&textures, arena)?;
let lights = scene.create_lights(&textures, arena); let (lights, al_map) = scene.create_lights(&textures, &media, arena);
let _have_scattering = { let _have_scattering = {
let shapes = scene.shapes.lock(); let shapes = scene.shapes.lock();
@ -30,8 +30,7 @@ pub fn render_scene(scene: &BasicScene, arena: &Arena) -> Result<()> {
.any(|sh| !sh.inside_medium.is_empty() || !sh.outside_medium.is_empty()) .any(|sh| !sh.inside_medium.is_empty() || !sh.outside_medium.is_empty())
}; };
let (aggregate, area_lights) = let aggregate = scene.create_aggregate(&textures, &named_materials, &materials, al_map, &media, arena);
scene.create_aggregate(&textures, &named_materials, &materials, &media, arena);
let mut all_lights = lights; let mut all_lights = lights;
all_lights.extend(area_lights); all_lights.extend(area_lights);
@ -60,7 +59,7 @@ pub fn render_scene(scene: &BasicScene, arena: &Arena) -> Result<()> {
loop { loop {
if let Some(isect) = aggregate.intersect(&ray, Some(Float::INFINITY)) { if let Some(isect) = aggregate.intersect(&ray, Some(Float::INFINITY)) {
let intr = isect.intr; let intr = isect.intr;
if intr.material.is_null() { if intr.material.is_none() {
log::warn!("Ignoring material") log::warn!("Ignoring material")
} else { } else {
let world_from_render = camera.base().camera_transform.world_from_render; let world_from_render = camera.base().camera_transform.world_from_render;
@ -80,7 +79,7 @@ pub fn render_scene(scene: &BasicScene, arena: &Arena) -> Result<()> {
log::debug!("Distance from camera: {}\n", intr.p().distance(cr.ray.o)); log::debug!("Distance from camera: {}\n", intr.p().distance(cr.ray.o));
for (name, mtl) in &named_materials { for (name, mtl) in &named_materials {
if *mtl == *intr.material.get().unwrap() { if *mtl == intr.material {
log::debug!("Named material: {}\n\n", name); log::debug!("Named material: {}\n\n", name);
break; break;
} }
@ -104,6 +103,7 @@ pub fn render_scene(scene: &BasicScene, arena: &Arena) -> Result<()> {
sampler.clone(), sampler.clone(),
aggregate.clone(), aggregate.clone(),
all_lights, all_lights,
materials,
arena, arena,
); );
wf.render(); wf.render();

View file

@ -15,7 +15,7 @@ use crate::lights::sampler::create_light_sampler;
use crate::utils::parallel::{run_async, AsyncJob}; use crate::utils::parallel::{run_async, AsyncJob};
use crate::utils::parameters::{NamedTextures, ParameterDictionary, TextureParameterDictionary}; use crate::utils::parameters::{NamedTextures, ParameterDictionary, TextureParameterDictionary};
use crate::utils::resolve_filename; use crate::utils::resolve_filename;
use crate::wavefront::{CreateWavefront, CpuAggregate, CpuWavefrontRenderer}; use crate::wavefront::{CpuAggregate, CpuWavefrontRenderer, CreateWavefront};
use crate::{Arena, ArenaUpload, FileLoc}; use crate::{Arena, ArenaUpload, FileLoc};
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use parking_lot::Mutex; use parking_lot::Mutex;
@ -32,6 +32,7 @@ use shared::core::primitive::{AnimatedPrimitive, GeometricPrimitive, Primitive,
use shared::core::sampler::{Sampler, SamplerTrait}; use shared::core::sampler::{Sampler, SamplerTrait};
use shared::core::shape::Shape; use shared::core::shape::Shape;
use shared::core::texture::SpectrumType; use shared::core::texture::SpectrumType;
use shared::core::{LightIdx, MaterialIdx};
use shared::spectra::RGBColorSpace; use shared::spectra::RGBColorSpace;
use shared::textures::FloatConstantTexture; use shared::textures::FloatConstantTexture;
use shared::utils::soa::SoA; use shared::utils::soa::SoA;
@ -77,28 +78,30 @@ fn resolve_medium_interface(
fn resolve_material( fn resolve_material(
mat_ref: &MaterialRef, mat_ref: &MaterialRef,
named_materials: &HashMap<String, Material>, named_materials: &HashMap<String, MaterialIdx>,
materials: &[Material], materials: &[Material],
loc: &FileLoc, loc: &FileLoc,
arena: &Arena, _arena: &Arena,
) -> Material { ) -> MaterialIdx {
match mat_ref { match mat_ref {
MaterialRef::Name(name) => match named_materials.get(name) { MaterialRef::Name(name) => match named_materials.get(name) {
Some(m) => *m, Some(m) => *m,
None => { None => {
log::error!("{}: named material '{}' not found", loc, name); MaterialIdx::default()
crate::core::material::default_diffuse_material(arena) // log::error!("{}: named material '{}' not found", loc, name);
// crate::core::material::default_diffuse_material(arena)
} }
}, },
MaterialRef::Index(idx) => { MaterialRef::Index(idx) => {
if *idx < materials.len() { if *idx < materials.len() {
materials[*idx] MaterialIdx(*idx as u32)
} else { } else {
log::error!("{}: material index {} out of bounds", loc, idx); MaterialIdx::default()
crate::core::material::default_diffuse_material(arena) // log::error!("{}: material index {} out of bounds", loc, idx);
// crate::core::material::default_diffuse_material(arena)
} }
} }
MaterialRef::None => crate::core::material::default_diffuse_material(arena), MaterialRef::None => MaterialIdx::default(),
} }
} }
@ -155,6 +158,8 @@ impl Default for BasicScene {
} }
} }
pub type AreaLightMap = HashMap<(usize, usize), LightIdx>;
impl BasicScene { impl BasicScene {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
@ -295,26 +300,53 @@ impl BasicScene {
arena: Arc<Arena>, arena: Arc<Arena>,
) -> Result<()> { ) -> Result<()> {
let mut state = self.texture_state.lock(); let mut state = self.texture_state.lock();
self.add_texture_generic(
name, if texture.render_from_object.is_animated() {
texture, log::info!(
&mut state, "{}: animated world-to-texture not supported, using start transform",
|s| &mut s.serial_spectrum_textures, texture.base.loc
|s| &mut s.spectrum_texture_jobs, );
move |tex| { }
let render_from_texture = tex.render_from_object.start_transform;
let tex_dict = TextureParameterDictionary::new(tex.base.parameters.into(), None); if texture.base.name != "imagemap" && texture.base.name != "ptex" {
state.serial_spectrum_textures.push((name, texture));
return Ok(());
}
let filename = resolve_filename(&texture.base.parameters.get_one_string("filename", "")?);
if !self.validate_texture_file(&filename, &texture.base.loc, &mut state.n_missing_textures)
{
return Ok(());
}
// Avoid duplicate work if the same file is already being loaded
if state.loading_texture_filenames.contains(&filename) {
state.serial_spectrum_textures.push((name, texture));
return Ok(());
}
state.loading_texture_filenames.insert(filename.clone());
state
.async_spectrum_textures
.push((name.clone(), texture.clone()));
let job = run_async(move || {
let render_from_texture = texture.render_from_object.start_transform;
let tex_dict = TextureParameterDictionary::new(texture.base.parameters.into(), None);
Arc::new(
SpectrumTexture::create( SpectrumTexture::create(
&tex.base.name, &texture.base.name,
render_from_texture, render_from_texture,
tex_dict, tex_dict,
SpectrumType::Albedo, SpectrumType::Albedo,
tex.base.loc, texture.base.loc,
&arena, &arena,
) )
.expect("Could not create spectrum texture") .expect("Could not create spectrum texture"),
}, )
) });
state.spectrum_texture_jobs.insert(name, job);
Ok(())
} }
pub fn add_area_light(&self, light: SceneEntity) -> usize { pub fn add_area_light(&self, light: SceneEntity) -> usize {
@ -367,10 +399,39 @@ impl BasicScene {
let mut named = NamedTextures { let mut named = NamedTextures {
float_textures: Arc::new(float_textures.clone()), float_textures: Arc::new(float_textures.clone()),
albedo_spectrum_textures: Arc::new(spectrum_textures.clone()), albedo_spectrum_textures: Arc::new(spectrum_textures.clone()),
illuminant_spectrum_textures: Arc::new(spectrum_textures.clone()), illuminant_spectrum_textures: Arc::new(HashMap::new()),
unbounded_spectrum_textures: Arc::new(spectrum_textures.clone()), unbounded_spectrum_textures: Arc::new(HashMap::new()),
}; };
for (name, entity) in state.async_spectrum_textures.drain(..) {
let render_from_texture = entity.render_from_object.start_transform;
let params = entity.base.parameters.clone();
let unbounded = SpectrumTexture::create(
&entity.base.name,
render_from_texture,
TextureParameterDictionary::new(params.clone().into(), None),
SpectrumType::Unbounded,
entity.base.loc.clone(),
arena,
)
.expect("Could not create unbounded spectrum texture");
let illum = SpectrumTexture::create(
&entity.base.name,
render_from_texture,
TextureParameterDictionary::new(params.into(), None),
SpectrumType::Illuminant,
entity.base.loc,
arena,
)
.expect("Could not create illuminant spectrum texture");
Arc::make_mut(&mut named.unbounded_spectrum_textures)
.insert(name.clone(), Arc::new(unbounded));
Arc::make_mut(&mut named.illuminant_spectrum_textures).insert(name, Arc::new(illum));
}
// Serial float textures may reference already-loaded textures // Serial float textures may reference already-loaded textures
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;
@ -389,18 +450,30 @@ impl BasicScene {
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 make = |st: SpectrumType, named: &NamedTextures, loc| {
TextureParameterDictionary::new(entity.base.parameters.into(), Some(&named)); let tex_dict = TextureParameterDictionary::new(
let tex = SpectrumTexture::create( entity.base.parameters.clone().into(),
&entity.base.name, Some(named),
render_from_texture, );
tex_dict, SpectrumTexture::create(
SpectrumType::Albedo, &entity.base.name,
entity.base.loc, render_from_texture,
arena, tex_dict,
) st,
.expect("Could not create spectrum texture"); loc,
Arc::make_mut(&mut named.albedo_spectrum_textures).insert(name, Arc::new(tex)); arena,
)
.expect("Could not create spectrum texture")
};
let albedo = make(SpectrumType::Albedo, &named, entity.base.loc.clone());
let unbounded = make(SpectrumType::Unbounded, &named, entity.base.loc.clone());
let illum = make(SpectrumType::Illuminant, &named, entity.base.loc);
Arc::make_mut(&mut named.albedo_spectrum_textures)
.insert(name.clone(), Arc::new(albedo));
Arc::make_mut(&mut named.unbounded_spectrum_textures)
.insert(name.clone(), Arc::new(unbounded));
Arc::make_mut(&mut named.illuminant_spectrum_textures).insert(name, Arc::new(illum));
} }
named named
@ -517,25 +590,31 @@ impl BasicScene {
state.map.clone() state.map.clone()
} }
pub fn create_lights(&self, _textures: &NamedTextures, arena: &Arena) -> Vec<Arc<Light>> { pub fn create_lights(
&self,
textures: &NamedTextures,
media: &HashMap<String, Arc<Medium>>,
arena: &Arena,
) -> (Vec<Light>, AreaLightMap) {
let light_state = self.light_state.lock(); let light_state = self.light_state.lock();
let shapes = self.shapes.lock();
let film_cs = self.film_colorspace.lock();
let film_cs_ref = film_cs.as_deref();
let camera = self let camera = self
.get_camera() .get_camera()
.expect("Camera must be initialized before lights"); .expect("Camera must be initialized before lights");
let camera_transform = camera.base().camera_transform; let camera_transform = camera.base().camera_transform;
let mut lights: Vec<Arc<Light>> = Vec::new();
// Non-area lights created from stored entities let mut lights: Vec<Light> = Vec::new();
for entity in &light_state.lights { for entity in &light_state.lights {
let medium = self.get_medium(&entity.medium, &entity.transformed_base.base.loc); let medium = self.get_medium(&entity.medium, &entity.transformed_base.base.loc);
if entity.transformed_base.render_from_object.is_animated() { if entity.transformed_base.render_from_object.is_animated() {
log::warn!( log::warn!(
"{}: animated lights aren't supported, using start transform.", "{}: animated lights aren't supported, using start transform.",
entity.transformed_base.base.loc entity.transformed_base.base.loc
); );
} }
match crate::core::light::create_light( match crate::core::light::create_light(
&entity.transformed_base.base.name, &entity.transformed_base.base.name,
entity.transformed_base.render_from_object.start_transform, entity.transformed_base.render_from_object.start_transform,
@ -545,28 +624,78 @@ impl BasicScene {
camera_transform, camera_transform,
arena, arena,
) { ) {
Ok(light) => lights.push(Arc::new(light)), Ok(light) => lights.push(light), // bare Light, no Arc
Err(e) => { Err(e) => log::error!(
log::error!( "{}: failed to create light: {}",
"{}: failed to create light: {}", entity.transformed_base.base.loc,
entity.transformed_base.base.loc, e
e ),
); }
}
let mut area_map: AreaLightMap = HashMap::new();
for (entity_idx, entity) in shapes.iter().enumerate() {
let Some(al_idx) = entity.light_index else {
continue;
};
let al = &light_state.area_lights[al_idx];
let created_shapes = match Shape::create(
&entity.base.name,
*entity.render_from_object,
*entity.object_from_render,
entity.reverse_orientation,
entity.base.parameters.clone(),
&textures.float_textures,
entity.base.loc.clone(),
arena,
) {
Ok(s) => s,
Err(_) => continue,
};
let alpha_tex = get_alpha_texture(
&entity.base.parameters,
&entity.base.loc,
&textures.float_textures,
);
let cs = al.parameters.color_space.as_deref().or(film_cs_ref);
for (sub_idx, shape) in created_shapes.iter().enumerate() {
let default_alpha = Arc::new(FloatTexture::default());
let alpha_ref = alpha_tex.as_ref().unwrap_or(&default_alpha);
match crate::core::light::create_area_light(
*entity.render_from_object,
None,
&al.parameters,
&al.loc,
shape,
alpha_ref,
cs,
arena,
) {
Ok(light) => {
let idx = LightIdx(lights.len() as u32);
lights.push(light);
area_map.insert((entity_idx, sub_idx), idx);
}
Err(e) => log::error!("{}: area light creation failed: {}", al.loc, e),
} }
} }
} }
lights (lights, area_map)
} }
pub fn create_aggregate( pub fn create_aggregate(
&self, &self,
textures: &NamedTextures, textures: &NamedTextures,
named_materials: &HashMap<String, Material>, named_materials: &HashMap<String, MaterialIdx>,
materials: &[Material], materials: &[Material],
area_map: &AreaLightMap,
media: &HashMap<String, Arc<Medium>>, media: &HashMap<String, Arc<Medium>>,
arena: &Arena, arena: &Arena,
) -> (Arc<Primitive>, Vec<Arc<Light>>) { ) -> Arc<Primitive> {
let mut shapes = self.shapes.lock(); let mut shapes = self.shapes.lock();
let mut animated_shapes = self.animated_shapes.lock(); let mut animated_shapes = self.animated_shapes.lock();
let mut instance_defs = self.instance_definitions.lock(); let mut instance_defs = self.instance_definitions.lock();
@ -575,21 +704,16 @@ impl BasicScene {
let film_cs = self.film_colorspace.lock(); let film_cs = self.film_colorspace.lock();
let film_cs_ref = film_cs.as_deref(); let film_cs_ref = film_cs.as_deref();
let mut all_lights: Vec<Arc<Light>> = Vec::new();
log::info!("Starting shapes"); log::info!("Starting shapes");
let mut primitives = Self::create_primitives_for_shapes( let mut primitives = Self::create_primitives_for_shapes(
&shapes, &shapes,
textures, textures,
named_materials, named_materials,
materials, materials,
&light_state, area_map,
media, media,
film_cs_ref,
arena, arena,
&mut all_lights,
); );
shapes.clear(); shapes.clear();
shapes.shrink_to_fit(); shapes.shrink_to_fit();
@ -598,72 +722,62 @@ impl BasicScene {
textures, textures,
named_materials, named_materials,
materials, materials,
&light_state, area_map,
media, media,
film_cs_ref,
arena, arena,
&mut all_lights,
); );
primitives.extend(animated_primitives); primitives.extend(animated_primitives);
animated_shapes.clear(); animated_shapes.clear();
animated_shapes.shrink_to_fit(); animated_shapes.shrink_to_fit();
log::info!("Finished shapes"); log::info!("Finished shapes");
log::info!("Starting instances"); log::info!("Starting instances");
let mut resolved_defs: HashMap<String, Option<Primitive>> = HashMap::new(); let mut resolved_defs: HashMap<String, Option<Primitive>> = HashMap::new();
for (name, def) in instance_defs.drain() { for (name, def) in instance_defs.drain() {
let mut inst_prims = Self::create_primitives_for_shapes( let mut inst_prims = Self::create_primitives_for_shapes(
&def.shapes, &def.shapes,
textures, textures,
named_materials, named_materials,
materials, materials,
&light_state, area_map,
media, media,
film_cs_ref,
arena, arena,
&mut all_lights,
); );
let animated_inst_prims = Self::create_primitives_for_animated_shapes( let animated_inst_prims = Self::create_primitives_for_animated_shapes(
&def.animated_shapes, &def.animated_shapes,
textures, textures,
named_materials, named_materials,
materials, materials,
&light_state, area_map,
media, media,
film_cs_ref,
arena, arena,
&mut all_lights,
); );
inst_prims.extend(animated_inst_prims); inst_prims.extend(animated_inst_prims);
let aggregate = if inst_prims.len() > 1 { let aggregate = if inst_prims.len() > 1 {
let bvh = BVHAggregate::new(inst_prims, 4, SplitMethod::SAH); Some(Primitive::BVH(arena.alloc(BVHAggregate::new(
Some(Primitive::BVH(arena.alloc(bvh))) inst_prims,
4,
SplitMethod::SAH,
))))
} else if inst_prims.len() == 1 { } else if inst_prims.len() == 1 {
Some(inst_prims.into_iter().next().unwrap()) Some(inst_prims.into_iter().next().unwrap())
} else { } else {
None None
}; };
resolved_defs.insert(name, aggregate); resolved_defs.insert(name, aggregate);
} }
for inst in instances.drain(..) { for inst in instances.drain(..) {
let def = match resolved_defs.get(&inst.name) { let def = match resolved_defs.get(&inst.name) {
Some(Some(prim)) => prim, Some(Some(prim)) => prim,
Some(None) => continue, // empty instance Some(None) => continue,
None => { None => {
log::error!("{}: object instance '{}' not defined", inst.loc, inst.name); log::error!("{}: object instance '{}' not defined", inst.loc, inst.name);
continue; continue;
} }
}; };
let prim = match &inst.transform { let prim = match &inst.transform {
InstanceTransform::Static(xform) => { InstanceTransform::Static(xform) => {
// TransformedPrimitive wraps a primitive with a static transform
Primitive::Transformed(shared::core::primitive::TransformedPrimitive { Primitive::Transformed(shared::core::primitive::TransformedPrimitive {
primitive: arena.alloc(*def), primitive: arena.alloc(*def),
render_from_primitive: arena.alloc(**xform), render_from_primitive: arena.alloc(**xform),
@ -676,7 +790,6 @@ impl BasicScene {
}; };
primitives.push(prim); primitives.push(prim);
} }
log::info!("Finished instances"); log::info!("Finished instances");
log::info!("Starting top-level accelerator"); log::info!("Starting top-level accelerator");
@ -688,7 +801,7 @@ impl BasicScene {
let agg_ptr = arena.alloc(aggregate); let agg_ptr = arena.alloc(aggregate);
log::info!("Finished top-level accelerator"); log::info!("Finished top-level accelerator");
(Arc::new(Primitive::BVH(agg_ptr)), all_lights) Arc::new(Primitive::BVH(agg_ptr))
} }
// Integrator // Integrator
@ -723,12 +836,21 @@ impl BasicScene {
camera: Arc<Camera>, camera: Arc<Camera>,
sampler: Arc<Sampler>, sampler: Arc<Sampler>,
aggregate: Arc<Primitive>, aggregate: Arc<Primitive>,
lights: Vec<Arc<Light>>, lights: Vec<Light>,
materials: Vec<Material>,
arena: &Arena, arena: &Arena,
) -> CpuWavefrontRenderer { ) -> CpuWavefrontRenderer {
let integrator_entity = self.integrator.lock().clone().unwrap(); let integrator_entity = self.integrator.lock().clone().unwrap();
let params = &integrator_entity.parameters; let params = &integrator_entity.parameters;
CpuWavefrontRenderer::create(params.clone(), camera, sampler, aggregate, lights, arena) CpuWavefrontRenderer::create(
params.clone(),
camera,
sampler,
aggregate,
lights,
materials,
arena,
)
} }
// Getters // Getters
@ -769,17 +891,15 @@ impl BasicScene {
fn create_primitives_for_shapes( fn create_primitives_for_shapes(
shapes: &[ShapeSceneEntity], shapes: &[ShapeSceneEntity],
textures: &NamedTextures, textures: &NamedTextures,
named_materials: &HashMap<String, Material>, named_materials: &HashMap<String, MaterialIdx>,
materials: &[Material], materials: &[Material],
light_state: &LightState, area_map: &AreaLightMap,
media: &HashMap<String, Arc<Medium>>, media: &HashMap<String, Arc<Medium>>,
film_cs: Option<&RGBColorSpace>,
arena: &Arena, arena: &Arena,
area_lights: &mut Vec<Arc<Light>>,
) -> Vec<Primitive> { ) -> Vec<Primitive> {
let mut primitives = Vec::new(); let mut primitives = Vec::new();
for entity in shapes { for (entity_idx, entity) in shapes.iter().enumerate() {
let created_shapes = match Shape::create( let created_shapes = match Shape::create(
&entity.base.name, &entity.base.name,
*entity.render_from_object, *entity.render_from_object,
@ -801,22 +921,18 @@ impl BasicScene {
continue; continue;
} }
eprintln!("shape '{}' n={}", entity.base.name, created_shapes.len()); let mtl: MaterialIdx = resolve_material(
let mtl = resolve_material(
&entity.material, &entity.material,
named_materials, named_materials,
materials, materials,
&entity.base.loc, &entity.base.loc,
arena, arena,
); );
let alpha_tex = get_alpha_texture( let alpha_tex = get_alpha_texture(
&entity.base.parameters, &entity.base.parameters,
&entity.base.loc, &entity.base.loc,
&textures.float_textures, &textures.float_textures,
); );
let mi = resolve_medium_interface( let mi = resolve_medium_interface(
media, media,
&entity.inside_medium, &entity.inside_medium,
@ -824,73 +940,39 @@ impl BasicScene {
&entity.base.loc, &entity.base.loc,
); );
let al_entity = entity.light_index.map(|idx| &light_state.area_lights[idx]); for (sub_idx, shape) in created_shapes.into_iter().enumerate() {
// look up the pre-created light index instead of creating one
let light_idx = area_map
.get(&(entity_idx, sub_idx))
.copied()
.unwrap_or(LightIdx::NONE);
for shape in created_shapes {
// Create area light for this shape if the entity has one
let light_ptr = al_entity
.and_then(|al| {
let cs = al.parameters.color_space.as_deref().or(film_cs);
let default_alpha = Arc::new(FloatTexture::default());
let alpha_ref = alpha_tex.as_ref().unwrap_or(&default_alpha);
match crate::core::light::create_area_light(
*entity.render_from_object,
None,
&al.parameters,
&al.loc,
&shape,
alpha_ref,
cs,
arena,
) {
Ok(light) => {
area_lights.push(Arc::new(light));
Some(arena.alloc(light))
}
Err(e) => {
log::error!("{}: area light creation failed: {}", al.loc, e);
None
}
}
})
.unwrap_or(Ptr::null());
// Pick SimplePrimitive when no extras are needed
let prim = let prim =
if light_ptr.is_null() && !mi.is_medium_transition() && alpha_tex.is_none() { if light_idx.is_none() && !mi.is_medium_transition() && alpha_tex.is_none() {
Primitive::Simple(SimplePrimitive::new(shape, arena.alloc(mtl))) Primitive::Simple(SimplePrimitive::new(shape, mtl)) // mtl is MaterialIdx now
} else { } else {
let alpha_ptr = alpha_tex let alpha_ptr = alpha_tex
.as_ref() .as_ref()
.map(|t| arena.upload(t.as_ref())) .map(|t| arena.upload(t.as_ref()))
.unwrap_or(Ptr::null()); .unwrap_or(Ptr::null());
Primitive::Geometric(GeometricPrimitive::new( Primitive::Geometric(GeometricPrimitive::new(
shape, shape, mtl, light_idx, mi, alpha_ptr,
arena.alloc(mtl),
light_ptr,
mi,
alpha_ptr,
)) ))
}; };
primitives.push(prim); primitives.push(prim);
} }
} }
primitives primitives
} }
fn create_primitives_for_animated_shapes( fn create_primitives_for_animated_shapes(
shapes: &[AnimatedShapeSceneEntity], shapes: &[AnimatedShapeSceneEntity],
textures: &NamedTextures, textures: &NamedTextures,
named_materials: &HashMap<String, Material>, named_materials: &HashMap<String, MaterialIdx>,
materials: &[Material], materials: &[Material],
light_state: &LightState, area_map: &AreaLightMap,
media: &HashMap<String, Arc<Medium>>, media: &HashMap<String, Arc<Medium>>,
_film_cs: Option<&RGBColorSpace>,
arena: &Arena, arena: &Arena,
_area_lights: &mut Vec<Arc<Light>>,
) -> Vec<Primitive> { ) -> Vec<Primitive> {
let mut primitives = Vec::new(); let mut primitives = Vec::new();
@ -943,9 +1025,7 @@ impl BasicScene {
&entity.transformed_base.base.loc, &entity.transformed_base.base.loc,
); );
let al_entity = entity.light_index.map(|idx| &light_state.area_lights[idx]); if entity.light_index.is_some() {
if al_entity.is_some() {
log::error!( log::error!(
"{}: animated area lights are not supported.", "{}: animated area lights are not supported.",
entity.transformed_base.base.loc entity.transformed_base.base.loc
@ -956,7 +1036,7 @@ impl BasicScene {
let mut base_prims = Vec::new(); let mut base_prims = Vec::new();
for shape in created_shapes { for shape in created_shapes {
let base = if !mi.is_medium_transition() && alpha_tex.is_none() { let base = if !mi.is_medium_transition() && alpha_tex.is_none() {
Primitive::Simple(SimplePrimitive::new(shape, arena.alloc(mtl))) Primitive::Simple(SimplePrimitive::new(shape, mtl))
} else { } else {
let alpha_ptr = alpha_tex let alpha_ptr = alpha_tex
.as_ref() .as_ref()
@ -965,8 +1045,8 @@ impl BasicScene {
Primitive::Geometric(GeometricPrimitive::new( Primitive::Geometric(GeometricPrimitive::new(
shape, shape,
arena.alloc(mtl), mtl,
Ptr::null(), // no area light on animated shapes LightIdx::default(), // no area light on animated shapes
mi, mi,
alpha_ptr, alpha_ptr,
)) ))

View file

@ -12,7 +12,11 @@ use shared::core::texture::{
UVMapping, UVMapping,
}; };
use shared::spectra::{SampledSpectrum, SampledWavelengths}; use shared::spectra::{SampledSpectrum, SampledWavelengths};
use shared::textures::*; use shared::textures::{
FBmTexture, FloatBilerpTexture, FloatCheckerboardTexture, FloatConstantTexture,
FloatDotsTexture, MarbleTexture, SpectrumBilerpTexture, SpectrumCheckerboardTexture,
SpectrumConstantTexture, SpectrumDotsTexture, WindyTexture, WrinkledTexture,
};
use shared::utils::Transform; use shared::utils::Transform;
use shared::Float; use shared::Float;
use std::collections::HashMap; use std::collections::HashMap;
@ -45,16 +49,6 @@ pub enum FloatTexture {
Bilerp(FloatBilerpTexture), Bilerp(FloatBilerpTexture),
} }
impl FloatTexture {
fn upload_image(inner: &FloatImageTexture, _arena: &Arena) -> GPUFloatImageTexture {
GPUFloatImageTexture {
mapping: inner.base.mapping,
tex_obj: inner.base.mipmap.texture_object(),
scale: inner.base.scale,
invert: inner.base.invert,
}
}
}
impl Default for FloatTexture { impl Default for FloatTexture {
fn default() -> Self { fn default() -> Self {

View file

@ -233,7 +233,7 @@ impl RayIntegratorTrait for PathIntegrator {
if state.depth == 0 || state.specular_bounce { if state.depth == 0 || state.specular_bounce {
state.l += state.beta * le; state.l += state.beta * le;
} else if self.config.use_mis } else if self.config.use_mis
&& !isect.area_light.is_null() { && !isect.area_light.is_none() {
let light = &isect.area_light; let light = &isect.area_light;
let p_l = self.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);

View file

@ -1,7 +1,6 @@
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::utils::resolve_filename; use crate::utils::resolve_filename;
use crate::utils::upload::ArenaUpload; use crate::utils::upload::ArenaUpload;
use crate::{Arena, FileLoc, ParameterDictionary}; use crate::{Arena, FileLoc, ParameterDictionary};
@ -11,7 +10,8 @@ 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::{GPUFloatTexture, SpectrumType, TextureEvalContext}; use crate::core::texture::FloatTexture as HostFloatTexture;
use shared::core::texture::{FloatTexture, SpectrumType, TextureEvalContext};
use shared::lights::DiffuseAreaLight; use shared::lights::DiffuseAreaLight;
use shared::spectra::RGBColorSpace; use shared::spectra::RGBColorSpace;
use shared::utils::Transform; use shared::utils::Transform;
@ -24,7 +24,7 @@ pub fn create(
params: &ParameterDictionary, params: &ParameterDictionary,
loc: &FileLoc, loc: &FileLoc,
shape: &Shape, shape: &Shape,
alpha: &FloatTexture, alpha: &HostFloatTexture,
colorspace: Option<&RGBColorSpace>, colorspace: Option<&RGBColorSpace>,
arena: &Arena, arena: &Arena,
) -> Result<Light> { ) -> Result<Light> {
@ -102,7 +102,7 @@ pub fn create(
// Upload alpha texture to GPU and check for null texture // Upload alpha texture to GPU and check for null texture
let alpha_ptr = arena.upload(alpha); let alpha_ptr = arena.upload(alpha);
let light_type = match alpha_ptr.get().unwrap() { let light_type = match alpha_ptr.get().unwrap() {
GPUFloatTexture::Constant(t) if t.evaluate(&TextureEvalContext::default()) == 0.0 => { FloatTexture::Constant(t) if t.evaluate(&TextureEvalContext::default()) == 0.0 => {
LightType::DeltaPosition LightType::DeltaPosition
} }
_ => LightType::Area, _ => LightType::Area,

View file

@ -1,65 +1,40 @@
use crate::Arena; use crate::Arena;
use shared::core::light::{Light, LightTrait}; use shared::core::light::{Light, LightTrait};
use shared::lights::sampler::{ use shared::lights::sampler::{LightSampler, PowerLightSampler, UniformLightSampler};
LightSampler, PowerLightSampler, UniformLightSampler,
};
use shared::utils::sampling::AliasTable;
use shared::spectra::{SampledSpectrum, SampledWavelengths}; use shared::spectra::{SampledSpectrum, SampledWavelengths};
use shared::utils::sampling::AliasTable;
use shared::utils::Ptr; use shared::utils::Ptr;
use shared::Float; use shared::Float;
use std::sync::Arc;
pub fn create_light_sampler( pub fn create_light_sampler(name: &str, lights: &[Light], arena: &Arena) -> LightSampler {
name: &str,
lights: &[Arc<Light>],
arena: &Arena,
) -> LightSampler {
let device_lights = lights_to_slice(lights, arena);
match name { match name {
"uniform" => LightSampler::Uniform(create_uniform(device_lights, lights.len())), "uniform" => LightSampler::Uniform(create_uniform(lights.len() as u32)),
"power" => LightSampler::Power(create_power(lights, device_lights, arena)), "power" => LightSampler::Power(create_power(lights, arena)),
"bvh" => { "bvh" => {
log::warn!("BVH light sampler not yet implemented, falling back to power"); log::warn!("BVH light sampler not yet implemented, falling back to power");
LightSampler::Power(create_power(lights, device_lights, arena)) LightSampler::Power(create_power(lights, arena))
} }
_ => { _ => {
log::error!("Unknown light sampler \"{}\", using power", name); log::error!("Unknown light sampler \"{}\", using power", name);
LightSampler::Power(create_power(lights, device_lights, arena)) LightSampler::Power(create_power(lights, arena))
} }
} }
} }
fn lights_to_slice(lights: &[Arc<Light>], arena: &Arena) -> (Ptr<Light>, u32) { fn create_uniform(lights_len: u32) -> UniformLightSampler {
UniformLightSampler::new(lights_len)
}
fn create_power(lights: &[Light], arena: &Arena) -> PowerLightSampler {
if lights.is_empty() { if lights.is_empty() {
return (Ptr::null(), 0);
}
let vals: Vec<Light> = lights.iter().map(|l| **l).collect();
let (ptr, _) = arena.alloc_slice(&vals);
(ptr, lights.len() as u32)
}
fn create_uniform(
(lights, lights_len): (Ptr<Light>, u32),
_count: usize,
) -> UniformLightSampler {
UniformLightSampler::new(lights, lights_len)
}
fn create_power(
host_lights: &[Arc<Light>],
(lights, lights_len): (Ptr<Light>, u32),
arena: &Arena,
) -> PowerLightSampler {
if host_lights.is_empty() {
return PowerLightSampler { return PowerLightSampler {
lights: Ptr::null(),
lights_len: 0, lights_len: 0,
alias_table: Ptr::null(), alias_table: Ptr::null(),
}; };
} }
let lambda = SampledWavelengths::sample_visible(0.5); let lambda = SampledWavelengths::sample_visible(0.5);
let mut light_power: Vec<Float> = host_lights let mut light_power: Vec<Float> = lights
.iter() .iter()
.map(|l| { .map(|l| {
let phi = SampledSpectrum::safe_div(&l.phi(lambda), &lambda.pdf()); let phi = SampledSpectrum::safe_div(&l.phi(lambda), &lambda.pdf());
@ -67,7 +42,7 @@ fn create_power(
}) })
.collect(); .collect();
// If all lights have zero power, treat as uniform // If all lights have zero power, treat as uniform.
if light_power.iter().sum::<Float>() == 0.0 { if light_power.iter().sum::<Float>() == 0.0 {
light_power.fill(1.0); light_power.fill(1.0);
} }
@ -76,8 +51,7 @@ fn create_power(
let alias_ptr = arena.alloc(alias_table); let alias_ptr = arena.alloc(alias_table);
PowerLightSampler { PowerLightSampler {
lights, lights_len: lights.len() as u32,
lights_len,
alias_table: alias_ptr, alias_table: alias_ptr,
} }
} }

View file

@ -62,7 +62,6 @@ impl CreateMaterial for ConductorMaterial {
remap_roughness, remap_roughness,
); );
arena.alloc(material);
Ok(Material::Conductor(material)) Ok(Material::Conductor(material))
} }
} }

View file

@ -3,14 +3,14 @@ use crate::core::texture::{
CreateFloatTexture, CreateSpectrumTexture, FloatTexture, FloatTextureTrait, SpectrumTexture, CreateFloatTexture, CreateSpectrumTexture, FloatTexture, FloatTextureTrait, SpectrumTexture,
SpectrumTextureTrait, SpectrumTextureTrait,
}; };
use crate::utils::mipmap::{FilterFunction, MIPMap, MIPMapFilterOptions}; use crate::utils::mipmap::{MIPMap, MIPMapFilterOptions};
use crate::utils::{FileLoc, TextureParameterDictionary, resolve_filename}; use crate::utils::{resolve_filename, FileLoc, TextureParameterDictionary};
use crate::{Arena}; use crate::Arena;
use anyhow::Result; use anyhow::Result;
use shared::core::color::RGB; use shared::core::color::RGB;
use shared::core::color::{ColorEncoding, SRGBEncoding}; use shared::core::color::{ColorEncoding, SRGBEncoding};
use shared::core::geometry::Vector2f; use shared::core::geometry::Vector2f;
use shared::core::image::WrapMode; use shared::core::image::{FilterFunction, WrapMode};
use shared::core::spectrum::SpectrumTrait; use shared::core::spectrum::SpectrumTrait;
use shared::core::texture::{SpectrumType, TexCoord2D, TextureEvalContext, TextureMapping2D}; use shared::core::texture::{SpectrumType, TexCoord2D, TextureEvalContext, TextureMapping2D};
use shared::spectra::{ use shared::spectra::{
@ -143,7 +143,7 @@ impl SpectrumTextureTrait for SpectrumImageTexture {
return RGBUnboundedSpectrum::new(&cs, rgb).sample(lambda); return RGBUnboundedSpectrum::new(&cs, rgb).sample(lambda);
} }
SpectrumType::Albedo => { SpectrumType::Albedo => {
return RGBAlbedoSpectrum::new(&cs, rgb).sample(lambda); return RGBAlbedoSpectrum::new(&cs, rgb.clamp(0., 1.)).sample(lambda);
} }
_ => return RGBIlluminantSpectrum::new(&cs, rgb).sample(lambda), _ => return RGBIlluminantSpectrum::new(&cs, rgb).sample(lambda),
} }

View file

@ -1,7 +1,7 @@
use crate::core::image::{HostImage, ImageIO}; use crate::core::image::{HostImage, ImageIO};
use shared::core::color::{ColorEncoding, RGB}; use shared::core::color::{ColorEncoding, RGB};
use shared::core::geometry::{Point2f, Point2i, Vector2f, VectorLike}; use shared::core::geometry::{Point2f, Point2i, Vector2f, VectorLike};
use shared::core::image::{WrapMode, WrapMode2D}; use shared::core::image::{WrapMode, WrapMode2D, FilterFunction};
use shared::spectra::RGBColorSpace; use shared::spectra::RGBColorSpace;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use shared::utils::math::{lerp, safe_sqrt, square}; use shared::utils::math::{lerp, safe_sqrt, square};
@ -13,39 +13,17 @@ use std::path::Path;
#[cfg(feature = "cuda")] #[cfg(feature = "cuda")]
use std::sync::OnceLock; use std::sync::OnceLock;
#[repr(C)] // impl std::fmt::Display for FilterFunction {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] // fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
pub enum FilterFunction { // let s = match self {
Point, // FilterFunction::Ewa => "EWA",
Bilinear, // FilterFunction::Trilinear => "trilinear",
Trilinear, // FilterFunction::Bilinear => "bilinear",
Ewa, // FilterFunction::Point => "point",
} // };
// write!(f, "{}", s)
impl FilterFunction { // }
pub fn parse(name: &str) -> Result<FilterFunction> { // }
match name {
"ewa" | "EWA" => Ok(FilterFunction::Ewa),
"trilinear" => Ok(FilterFunction::Trilinear),
"bilinear" => Ok(FilterFunction::Bilinear),
"point" => Ok(FilterFunction::Point),
_ => bail!("Filter function unknown")
}
}
}
impl std::fmt::Display for FilterFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
FilterFunction::Ewa => "EWA",
FilterFunction::Trilinear => "trilinear",
FilterFunction::Bilinear => "bilinear",
FilterFunction::Point => "point",
};
write!(f, "{}", s)
}
}
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]

View file

@ -907,6 +907,7 @@ impl TextureParameterDictionary {
if let Some(tex) = map.get(tex_name) { if let Some(tex) = map.get(tex_name) {
return Some(Arc::clone(tex)); return Some(Arc::clone(tex));
} }
panic!( panic!(
"[{:?}] Couldn't find spectrum texture named '{}'", "[{:?}] Couldn't find spectrum texture named '{}'",
p.loc, tex_name p.loc, tex_name

View file

@ -1,8 +1,9 @@
use crate::core::texture::{FloatTexture, SpectrumTexture};
use crate::core::image::HostImage; use crate::core::image::HostImage;
use shared::core::image::Image; use crate::core::texture::{FloatTexture as HostFloatTexture, SpectrumTexture as HostSpectrumTexture};
use crate::spectra::default_colorspace;
use crate::Arena; use crate::Arena;
use shared::core::texture::{GPUFloatTexture, GPUSpectrumTexture}; use shared::core::image::Image;
use shared::core::texture::{FloatTexture, SpectrumTexture};
use shared::textures::*; use shared::textures::*;
use shared::Ptr; use shared::Ptr;
use std::sync::Arc; use std::sync::Arc;
@ -12,43 +13,46 @@ pub trait Upload {
fn upload(self, arena: &Arena) -> Self::Target; fn upload(self, arena: &Arena) -> Self::Target;
} }
fn convert_float(tex: &FloatTexture, arena: &Arena) -> GPUFloatTexture { fn convert_float(tex: &HostFloatTexture, arena: &Arena) -> FloatTexture {
match tex { match tex {
FloatTexture::Constant(t) => GPUFloatTexture::Constant(*t), HostFloatTexture::Constant(t) => FloatTexture::Constant(*t),
FloatTexture::Bilerp(t) => GPUFloatTexture::Bilerp(*t), HostFloatTexture::Bilerp(t) => FloatTexture::Bilerp(*t),
FloatTexture::Checkerboard(t) => GPUFloatTexture::Checkerboard(*t), HostFloatTexture::Checkerboard(t) => FloatTexture::Checkerboard(*t),
FloatTexture::Dots(t) => GPUFloatTexture::Dots(*t), HostFloatTexture::Dots(t) => FloatTexture::Dots(*t),
FloatTexture::FBm(t) => GPUFloatTexture::FBm(*t), HostFloatTexture::FBm(t) => FloatTexture::FBm(*t),
FloatTexture::Windy(t) => GPUFloatTexture::Windy(*t), HostFloatTexture::Windy(t) => FloatTexture::Windy(*t),
FloatTexture::Wrinkled(t) => GPUFloatTexture::Wrinkled(*t), HostFloatTexture::Wrinkled(t) => FloatTexture::Wrinkled(*t),
FloatTexture::Scaled(t) => { HostFloatTexture::Scaled(t) => {
let tex = arena.alloc(convert_float(&t.tex, arena)); let tex = arena.alloc(convert_float(t.tex.as_ref(), arena));
let scale = arena.alloc(convert_float(&t.scale, arena)); let scale = arena.alloc(convert_float(t.scale.as_ref(), arena));
GPUFloatTexture::Scaled(GPUFloatScaledTexture { tex, scale }) FloatTexture::Scaled(FloatScaledTexture { tex, scale })
} }
FloatTexture::Mix(t) => { HostFloatTexture::Mix(t) => {
let tex1 = arena.alloc(convert_float(&t.tex1, arena)); let tex1 = arena.alloc(convert_float(t.tex1.as_ref(), arena));
let tex2 = arena.alloc(convert_float(&t.tex2, arena)); let tex2 = arena.alloc(convert_float(t.tex2.as_ref(), arena));
let amount = arena.alloc(convert_float(&t.amount, arena)); let amount = arena.alloc(convert_float(t.amount.as_ref(), arena));
GPUFloatTexture::Mix(GPUFloatMixTexture { tex1, tex2, amount }) FloatTexture::Mix(FloatMixTexture { tex1, tex2, amount })
} }
FloatTexture::DirectionMix(t) => { HostFloatTexture::DirectionMix(t) => {
let tex1 = arena.alloc(convert_float(&t.tex1, arena)); let tex1 = arena.alloc(convert_float(t.tex1.as_ref(), arena));
let tex2 = arena.alloc(convert_float(&t.tex2, arena)); let tex2 = arena.alloc(convert_float(t.tex2.as_ref(), arena));
GPUFloatTexture::DirectionMix(GPUFloatDirectionMixTexture { FloatTexture::DirectionMix(FloatDirectionMixTexture {
tex1, tex1,
tex2, tex2,
dir: t.dir, dir: t.dir,
}) })
} }
FloatTexture::Image(t) => { HostFloatTexture::Image(t) => {
let image = arena.alloc(t.base.mipmap.base_image().inner.clone());
let tex_obj = arena.get_texture_object(&t.base.mipmap); let tex_obj = arena.get_texture_object(&t.base.mipmap);
GPUFloatTexture::Image(GPUFloatImageTexture { FloatTexture::Image(FloatImageTexture {
image,
mapping: t.base.mapping, mapping: t.base.mapping,
wrap_mode: t.base.mipmap.wrap_mode,
tex_obj, tex_obj,
scale: t.base.scale, scale: t.base.scale,
invert: t.base.invert, invert: t.base.invert,
@ -57,94 +61,92 @@ fn convert_float(tex: &FloatTexture, arena: &Arena) -> GPUFloatTexture {
} }
} }
fn convert_spectrum(tex: &SpectrumTexture, arena: &Arena) -> GPUSpectrumTexture { fn convert_spectrum(tex: &HostSpectrumTexture, arena: &Arena) -> SpectrumTexture {
match tex { match tex {
SpectrumTexture::Constant(t) => GPUSpectrumTexture::Constant(*t), HostSpectrumTexture::Constant(t) => SpectrumTexture::Constant(*t),
SpectrumTexture::Bilerp(t) => GPUSpectrumTexture::Bilerp(*t), HostSpectrumTexture::Bilerp(t) => SpectrumTexture::Bilerp(*t),
SpectrumTexture::Checkerboard(t) => GPUSpectrumTexture::Checkerboard(*t), HostSpectrumTexture::Checkerboard(t) => SpectrumTexture::Checkerboard(*t),
SpectrumTexture::Dots(t) => GPUSpectrumTexture::Dots(*t), HostSpectrumTexture::Dots(t) => SpectrumTexture::Dots(*t),
SpectrumTexture::Marble(t) => GPUSpectrumTexture::Marble(*t), HostSpectrumTexture::Marble(t) => SpectrumTexture::Marble(*t),
SpectrumTexture::Scaled(t) => { HostSpectrumTexture::Scaled(t) => {
let tex = arena.alloc(convert_spectrum(&t.tex, arena)); let tex = arena.alloc(convert_spectrum(t.tex.as_ref(), arena));
let scale = arena.alloc(convert_float(&t.scale, arena)); let scale = arena.alloc(convert_float(t.scale.as_ref(), arena));
GPUSpectrumTexture::Scaled(GPUSpectrumScaledTexture { tex, scale }) SpectrumTexture::Scaled(SpectrumScaledTexture { tex, scale })
} }
SpectrumTexture::Mix(t) => { HostSpectrumTexture::Mix(t) => {
let tex1 = arena.alloc(convert_spectrum(&t.tex1, arena)); let tex1 = arena.alloc(convert_spectrum(t.tex1.as_ref(), arena));
let tex2 = arena.alloc(convert_spectrum(&t.tex2, arena)); let tex2 = arena.alloc(convert_spectrum(t.tex2.as_ref(), arena));
let amount = arena.alloc(convert_float(&t.amount, arena)); let amount = arena.alloc(convert_float(t.amount.as_ref(), arena));
GPUSpectrumTexture::Mix(GPUSpectrumMixTexture { tex1, tex2, amount }) SpectrumTexture::Mix(SpectrumMixTexture { tex1, tex2, amount })
} }
SpectrumTexture::DirectionMix(t) => { HostSpectrumTexture::DirectionMix(t) => {
let tex1 = arena.alloc(convert_spectrum(&t.tex1, arena)); let tex1 = arena.alloc(convert_spectrum(t.tex1.as_ref(), arena));
let tex2 = arena.alloc(convert_spectrum(&t.tex2, arena)); let tex2 = arena.alloc(convert_spectrum(t.tex2.as_ref(), arena));
GPUSpectrumTexture::DirectionMix(GPUSpectrumDirectionMixTexture { SpectrumTexture::DirectionMix(SpectrumDirectionMixTexture {
tex1, tex1,
tex2, tex2,
dir: t.dir, dir: t.dir,
}) })
} }
SpectrumTexture::Image(t) => { HostSpectrumTexture::Image(t) => {
let image = arena.alloc(t.base.mipmap.base_image().inner.clone());
let tex_obj = arena.get_texture_object(&t.base.mipmap); let tex_obj = arena.get_texture_object(&t.base.mipmap);
GPUSpectrumTexture::Image(GPUSpectrumImageTexture { SpectrumTexture::Image(SpectrumImageTexture {
mapping: t.base.mapping, image,
tex_obj, tex_obj,
wrap_mode: t.base.mipmap.wrap_mode,
color_space: arena
.alloc(t.base.mipmap.color_space.unwrap_or_else(default_colorspace)),
mapping: t.base.mapping,
scale: t.base.scale, scale: t.base.scale,
invert: t.base.invert, invert: t.base.invert,
is_single_channel: t.base.mipmap.is_single_channel(), is_single_channel: t.base.mipmap.is_single_channel(),
spectrum_type: t.spectrum_type, spectrum_type: t.spectrum_type,
color_space: arena.alloc(
t.base
.mipmap
.color_space
.unwrap_or_else(crate::spectra::default_colorspace),
),
}) })
} }
} }
} }
impl Upload for Arc<FloatTexture> { impl Upload for Arc<HostFloatTexture> {
type Target = Ptr<GPUFloatTexture>; type Target = Ptr<FloatTexture>;
fn upload(self, arena: &Arena) -> Self::Target { fn upload(self, arena: &Arena) -> Self::Target {
arena.alloc(convert_float(&self, arena)) arena.alloc(convert_float(&self, arena))
} }
} }
impl Upload for Arc<SpectrumTexture> { impl Upload for Arc<HostSpectrumTexture> {
type Target = Ptr<GPUSpectrumTexture>; type Target = Ptr<SpectrumTexture>;
fn upload(self, arena: &Arena) -> Self::Target { fn upload(self, arena: &Arena) -> Self::Target {
arena.alloc(convert_spectrum(&self, arena)) arena.alloc(convert_spectrum(&self, arena))
} }
} }
impl Upload for &FloatTexture { impl Upload for &HostFloatTexture {
type Target = Ptr<GPUFloatTexture>; type Target = Ptr<FloatTexture>;
fn upload(self, arena: &Arena) -> Self::Target { fn upload(self, arena: &Arena) -> Self::Target {
arena.alloc(convert_float(self, arena)) arena.alloc(convert_float(self, arena))
} }
} }
impl Upload for &SpectrumTexture { impl Upload for &HostSpectrumTexture {
type Target = Ptr<GPUSpectrumTexture>; type Target = Ptr<SpectrumTexture>;
fn upload(self, arena: &Arena) -> Self::Target { fn upload(self, arena: &Arena) -> Self::Target {
arena.alloc(convert_spectrum(self, arena)) arena.alloc(convert_spectrum(self, arena))
} }
} }
impl Upload for Option<Arc<FloatTexture>> { impl Upload for Option<Arc<HostFloatTexture>> {
type Target = Ptr<GPUFloatTexture>; type Target = Ptr<FloatTexture>;
fn upload(self, arena: &Arena) -> Self::Target { fn upload(self, arena: &Arena) -> Self::Target {
arena.alloc_opt(self.map(|v| convert_float(&v, arena))) arena.alloc_opt(self.map(|v| convert_float(&v, arena)))
} }
} }
impl Upload for Option<Arc<SpectrumTexture>> { impl Upload for Option<Arc<HostSpectrumTexture>> {
type Target = Ptr<GPUSpectrumTexture>; type Target = Ptr<SpectrumTexture>;
fn upload(self, arena: &Arena) -> Self::Target { fn upload(self, arena: &Arena) -> Self::Target {
arena.alloc_opt(self.map(|v| convert_spectrum(&v, arena))) arena.alloc_opt(self.map(|v| convert_spectrum(&v, arena)))
} }

View file

@ -3,21 +3,22 @@ use log::debug;
use rayon::prelude::*; use rayon::prelude::*;
use shared::core::geometry::{Bounds3f, Ray, VectorLike}; use shared::core::geometry::{Bounds3f, Ray, VectorLike};
use shared::core::interaction::{InteractionTrait, SurfaceInteraction}; use shared::core::interaction::{InteractionTrait, SurfaceInteraction};
use shared::core::material::MaterialTrait; use shared::core::material::{Material, MaterialTrait};
use shared::core::primitive::{Primitive, PrimitiveTrait}; use shared::core::primitive::{Primitive, PrimitiveTrait};
use shared::core::texture::BasicTextureEvaluator; use shared::core::texture::BasicTextureEvaluator;
use shared::core::texture::TextureEvaluator; use shared::core::texture::TextureEvaluator;
use shared::wavefront::workitems::*; use shared::wavefront::workitems::*;
use shared::wavefront::WavefrontAggregate; use shared::wavefront::WavefrontAggregate;
use shared::{Float, Ptr}; use shared::{Float, Ptr, GVec, gvec_from_slice};
pub struct CpuAggregate { pub struct CpuAggregate {
pub aggregate: Primitive, pub aggregate: Primitive,
pub materials: GVec<Material>,
} }
impl CpuAggregate { impl CpuAggregate {
pub fn new(aggregate: Primitive) -> Self { pub fn new(aggregate: Primitive, materials: &[Material]) -> Self {
Self { aggregate } Self { aggregate, materials: gvec_from_slice(materials) }
} }
} }
@ -70,7 +71,7 @@ impl WavefrontAggregate for CpuAggregate {
let intr = &si.intr; let intr = &si.intr;
// Medium transition // Medium transition
if intr.material.is_null() { if intr.material.is_none() {
let mut next = r; let mut next = r;
next.ray = intr.spawn_ray(r.ray.d); next.ray = intr.spawn_ray(r.ray.d);
next_ray_q.push(next); next_ray_q.push(next);
@ -78,7 +79,7 @@ impl WavefrontAggregate for CpuAggregate {
} }
// Area light hit // Area light hit
if !intr.area_light.is_null() { if !intr.area_light.is_none() {
hit_area_light_q.push(HitAreaLightWorkItem { hit_area_light_q.push(HitAreaLightWorkItem {
area_light: intr.area_light, area_light: intr.area_light,
p: intr.p(), p: intr.p(),
@ -97,7 +98,7 @@ impl WavefrontAggregate for CpuAggregate {
} }
// Material eval queue dispatch // Material eval queue dispatch
let material = *intr.material.get().unwrap(); let material = &self.materials[intr.material.0 as usize];
let eval_q = if material.can_evaluate_textures(&BasicTextureEvaluator) { let eval_q = if material.can_evaluate_textures(&BasicTextureEvaluator) {
basic_eval_mtl_q basic_eval_mtl_q
} else { } else {

View file

@ -16,10 +16,11 @@ use shared::core::geometry::{
}; };
use shared::core::interaction::InteractionTrait; use shared::core::interaction::InteractionTrait;
use shared::core::light::{Light, LightSampleContext, LightTrait}; use shared::core::light::{Light, LightSampleContext, LightTrait};
use shared::core::material::{MaterialEvalContext, MaterialTrait}; use shared::core::material::{Material, MaterialEvalContext, MaterialTrait};
use shared::core::primitive::Primitive; use shared::core::primitive::{Primitive, PrimitiveTrait};
use shared::core::sampler::{get_camera_sample, CameraSample, Sampler, SamplerTrait}; use shared::core::sampler::{get_camera_sample, CameraSample, Sampler, SamplerTrait};
use shared::core::texture::{BasicTextureEvaluator, TextureEvalContext, UniversalTextureEvaluator}; use shared::core::texture::{BasicTextureEvaluator, TextureEvalContext, UniversalTextureEvaluator};
use shared::core::LightIdx;
use shared::lights::sampler::{LightSampler, LightSamplerTrait}; use shared::lights::sampler::{LightSampler, LightSamplerTrait};
use shared::spectra::{SampledSpectrum, SampledWavelengths}; use shared::spectra::{SampledSpectrum, SampledWavelengths};
use shared::utils::math::square; use shared::utils::math::square;
@ -27,7 +28,7 @@ use shared::utils::sampling::power_heuristic;
use shared::utils::soa::{SoA, SoAAllocator, WorkQueue}; use shared::utils::soa::{SoA, SoAAllocator, WorkQueue};
use shared::wavefront::workitems::*; use shared::wavefront::workitems::*;
use shared::wavefront::{WavefrontAggregate, WavefrontPathIntegrator, WavefrontRenderer}; use shared::wavefront::{WavefrontAggregate, WavefrontPathIntegrator, WavefrontRenderer};
use shared::{gvec, Ptr, SHADOW_EPSILON}; use shared::{gvec, gvec_from_slice, GVec, Ptr, SHADOW_EPSILON};
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::sync::Arc; use std::sync::Arc;
@ -55,7 +56,8 @@ where
camera: Arc<Camera>, camera: Arc<Camera>,
sampler: Arc<Sampler>, sampler: Arc<Sampler>,
aggregate: Arc<Primitive>, aggregate: Arc<Primitive>,
lights: Vec<Arc<Light>>, mut lights: Vec<Light>,
materials: Vec<Material>,
arena: &Arena, arena: &Arena,
) -> CpuWavefrontRenderer { ) -> CpuWavefrontRenderer {
let max_depth = parameters let max_depth = parameters
@ -75,15 +77,23 @@ where
let scanlines_per_pass = (max_samples / res_x).max(1); let scanlines_per_pass = (max_samples / res_x).max(1);
let max_queue_size = res_x * scanlines_per_pass; let max_queue_size = res_x * scanlines_per_pass;
let mut infinite_lights = gvec(); let mut infinite_lights: GVec<LightIdx> = gvec();
for light in &lights { for (i, light) in lights.iter().enumerate() {
if light.light_type().is_infinite() { if light.light_type().is_infinite() {
infinite_lights.push(arena.alloc(**light)); infinite_lights.push(LightIdx(i as u32));
} }
} }
let cpu_aggregate = CpuAggregate::new(*aggregate); let cpu_aggregate = CpuAggregate::new(*aggregate);
let bounds = aggregate.bounds();
for light in &mut lights {
light.preprocess(&bounds);
}
let lights: GVec<Light> = gvec_from_slice(&lights);
let materials: GVec<Material> = gvec_from_slice(&materials);
CpuWavefrontRenderer(WavefrontPathIntegrator { CpuWavefrontRenderer(WavefrontPathIntegrator {
aggregate: cpu_aggregate, aggregate: cpu_aggregate,
camera: (*camera).clone(), camera: (*camera).clone(),
@ -94,6 +104,8 @@ where
samples_per_pixel: spp, samples_per_pixel: spp,
regularize, regularize,
infinite_lights, infinite_lights,
lights,
materials,
max_queue_size, max_queue_size,
scanlines_per_pass, scanlines_per_pass,
light_sampler, light_sampler,
@ -298,8 +310,8 @@ impl CpuWavefrontRenderer {
let mut l_contrib = SampledSpectrum::new(0.0); let mut l_contrib = SampledSpectrum::new(0.0);
for light_ptr in infinite_lights { for idx in infinite_lights {
let light = light_ptr.get().unwrap(); let light = &self.lights[idx.0 as usize];
let ray = Ray::new(w.ray_o, w.ray_d, None, Ptr::null()); let ray = Ray::new(w.ray_o, w.ray_d, None, Ptr::null());
let le = light.le(&ray, &w.lambda); let le = light.le(&ray, &w.lambda);
if le.is_black() { if le.is_black() {
@ -311,7 +323,7 @@ impl CpuWavefrontRenderer {
} else { } else {
// Compute MIS-weighted radiance contribution from infinite light // Compute MIS-weighted radiance contribution from infinite light
let ctx = w.prev_intr_ctx; let ctx = w.prev_intr_ctx;
let light_choice_pdf = light_sampler.pmf_with_context(&ctx, light); let light_choice_pdf = light_sampler.pmf_with_context(&ctx, *idx);
let r_l = w.r_l * light_choice_pdf * light.pdf_li(&ctx, w.ray_d, true); let r_l = w.r_l * light_choice_pdf * light.pdf_li(&ctx, w.ray_d, true);
l_contrib += w.beta * le / (w.r_u + r_l).average(); l_contrib += w.beta * le / (w.r_u + r_l).average();
} }
@ -333,9 +345,12 @@ impl CpuWavefrontRenderer {
let hit_area_light_queue = &self.hit_area_light_queue; let hit_area_light_queue = &self.hit_area_light_queue;
(0..n as usize).into_par_iter().for_each(|i| { (0..n as usize).into_par_iter().for_each(|i| {
let w = unsafe { hit_area_light_queue.storage.get(i) }; let w = unsafe { hit_area_light_queue.get(i) };
if w.area_light.is_none() {
return;
}
let light = &self.lights[w.area_light.0 as usize];
let light = w.area_light.get().unwrap();
let le = light.l(w.p, w.n, w.uv, w.wo, &w.lambda); let le = light.l(w.p, w.n, w.uv, w.wo, &w.lambda);
if le.is_black() { if le.is_black() {
return; return;
@ -346,7 +361,7 @@ impl CpuWavefrontRenderer {
} else { } else {
let wi = -w.wo; let wi = -w.wo;
let ctx = w.prev_intr_ctx; let ctx = w.prev_intr_ctx;
let light_choice_pdf = light_sampler.pmf_with_context(&ctx, light); let light_choice_pdf = light_sampler.pmf_with_context(&ctx, w.area_light);
// wi from previous interaction to this light hit // wi from previous interaction to this light hit
let light_pdf = light_choice_pdf * light.pdf_li(&ctx, wi, true); let light_pdf = light_choice_pdf * light.pdf_li(&ctx, wi, true);
let r_u = w.r_u; let r_u = w.r_u;
@ -389,13 +404,13 @@ impl CpuWavefrontRenderer {
(0..n as usize).into_par_iter().for_each(|i| { (0..n as usize).into_par_iter().for_each(|i| {
let w = unsafe { queue.storage.get(i) }; let w = unsafe { queue.storage.get(i) };
if w.material.is_none() {
return;
}
let material = &self.materials[w.material.0 as usize];
let pi = w.pixel_index as usize; let pi = w.pixel_index as usize;
let rs = pixel_sample_state.samples.get(pi); let rs = pixel_sample_state.samples.get(pi);
let Some(material) = w.material.get() else {
return;
};
let _is_cond = material.is_conductor(); let _is_cond = material.is_conductor();
// GetMaterialEvalContext // GetMaterialEvalContext
@ -516,12 +531,9 @@ impl CpuWavefrontRenderer {
else { else {
return; return;
}; };
let light = &self.lights[sampled_light.light.0 as usize];
let Some(ls) = let Some(ls) = light.sample_li(&light_ctx, rs.direct.u, &lambda, true) else {
sampled_light
.light
.sample_li(&light_ctx, rs.direct.u, &lambda, true)
else {
return; return;
}; };
@ -540,7 +552,7 @@ impl CpuWavefrontRenderer {
let beta = w.beta * f * wi.abs_dot(ns.into()); let beta = w.beta * f * wi.abs_dot(ns.into());
let light_pdf = ls.pdf * sampled_light.p; let light_pdf = ls.pdf * sampled_light.p;
let bsdf_pdf = if sampled_light.light.light_type().is_delta_light() { let bsdf_pdf = if light.light_type().is_delta_light() {
0.0 0.0
} else { } else {
bsdf.pdf(wo, wi, FArgs::default()) bsdf.pdf(wo, wi, FArgs::default())