Changing behaviour of Ray
This commit is contained in:
parent
82255e5046
commit
1ea327cb6c
11 changed files with 223 additions and 201 deletions
|
|
@ -98,7 +98,7 @@ impl CameraTrait for PerspectiveCamera {
|
||||||
Point3f::new(0., 0., 0.),
|
Point3f::new(0., 0., 0.),
|
||||||
p_vector.normalize(),
|
p_vector.normalize(),
|
||||||
Some(self.sample_time(sample.time)),
|
Some(self.sample_time(sample.time)),
|
||||||
&*self.base().medium,
|
self.base().medium,
|
||||||
);
|
);
|
||||||
// Modify ray for depth of field
|
// Modify ray for depth of field
|
||||||
if self.lens_radius > 0. {
|
if self.lens_radius > 0. {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use crate::core::shape::ShapeIntersection;
|
||||||
use crate::{Float, Ptr, GVec, gvec};
|
use crate::{Float, Ptr, GVec, gvec};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Default, Debug, Clone, Copy)]
|
||||||
pub struct LinearBVHNode {
|
pub struct LinearBVHNode {
|
||||||
pub bounds: Bounds3f,
|
pub bounds: Bounds3f,
|
||||||
pub primitives_offset: usize,
|
pub primitives_offset: usize,
|
||||||
|
|
|
||||||
|
|
@ -798,7 +798,11 @@ impl DigitPermutation {
|
||||||
inv_base_m *= inv_base;
|
inv_base_m *= inv_base;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut permutations = gvec_with_capacity(n_digits as usize * base as usize);
|
let mut permutations = {
|
||||||
|
let mut v = gvec_with_capacity(n_digits as usize * base as usize);
|
||||||
|
v.resize(n_digits as usize * base as usize, 0u16);
|
||||||
|
v
|
||||||
|
};
|
||||||
|
|
||||||
for digit_index in 0..n_digits {
|
for digit_index in 0..n_digits {
|
||||||
let hash_input = [base as u64, digit_index as u64, seed];
|
let hash_input = [base as u64, digit_index as u64, seed];
|
||||||
|
|
@ -829,7 +833,11 @@ impl DigitPermutation {
|
||||||
|
|
||||||
pub fn compute_radical_inverse_permutations(seed: u64) -> GVec<DigitPermutation> {
|
pub fn compute_radical_inverse_permutations(seed: u64) -> GVec<DigitPermutation> {
|
||||||
let mut result = gvec();
|
let mut result = gvec();
|
||||||
result.extend(PRIMES.iter().map(|&base| DigitPermutation::new(base as i32, seed)));
|
result.extend(
|
||||||
|
PRIMES
|
||||||
|
.iter()
|
||||||
|
.map(|&base| DigitPermutation::new(base as i32, seed)),
|
||||||
|
);
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,9 @@ use shared::core::camera::CameraTransform;
|
||||||
use shared::core::light::Light;
|
use shared::core::light::Light;
|
||||||
use shared::core::medium::Medium;
|
use shared::core::medium::Medium;
|
||||||
use shared::core::shape::Shape;
|
use shared::core::shape::Shape;
|
||||||
use shared::spectra::DenselySampledSpectrum;
|
|
||||||
use shared::core::spectrum::Spectrum;
|
use shared::core::spectrum::Spectrum;
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::{DenselySampledSpectrum, RGBColorSpace};
|
||||||
use shared::{Ptr, Transform};
|
use shared::Transform;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub fn lookup_spectrum(s: &Spectrum) -> Arc<DenselySampledSpectrum> {
|
pub fn lookup_spectrum(s: &Spectrum) -> Arc<DenselySampledSpectrum> {
|
||||||
|
|
@ -18,9 +17,6 @@ pub fn lookup_spectrum(s: &Spectrum) -> Arc<DenselySampledSpectrum> {
|
||||||
cache.lookup(dense_spectrum).into()
|
cache.lookup(dense_spectrum).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Placeholders for non-area lights that never inspect these arguments.
|
|
||||||
// TODO: refactor each light to only take what it actually needs,
|
|
||||||
// then delete this bullshit
|
|
||||||
fn dummy_shape() -> Shape {
|
fn dummy_shape() -> Shape {
|
||||||
Shape::default()
|
Shape::default()
|
||||||
}
|
}
|
||||||
|
|
@ -29,6 +25,8 @@ fn dummy_alpha() -> FloatTexture {
|
||||||
FloatTexture::default()
|
FloatTexture::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a non-area light. Returns a Light value — the caller decides
|
||||||
|
/// whether to wrap it in Arc (host ownership) or arena.alloc (GPU).
|
||||||
pub fn create_light(
|
pub fn create_light(
|
||||||
name: &str,
|
name: &str,
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
|
|
@ -36,79 +34,47 @@ pub fn create_light(
|
||||||
parameters: &ParameterDictionary,
|
parameters: &ParameterDictionary,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
camera_transform: CameraTransform,
|
camera_transform: CameraTransform,
|
||||||
arena: &mut Arena,
|
arena: &Arena,
|
||||||
) -> Result<Light> {
|
) -> Result<Light> {
|
||||||
let shape = dummy_shape();
|
let shape = dummy_shape();
|
||||||
let alpha = dummy_alpha();
|
let alpha = dummy_alpha();
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
"point" => crate::lights::point::create(
|
"point" => crate::lights::point::create(
|
||||||
render_from_light,
|
render_from_light, medium, parameters, loc,
|
||||||
medium,
|
&shape, &alpha, None, arena,
|
||||||
parameters,
|
|
||||||
loc,
|
|
||||||
&shape,
|
|
||||||
&alpha,
|
|
||||||
None,
|
|
||||||
arena,
|
|
||||||
),
|
),
|
||||||
"spot" => crate::lights::spot::create(
|
"spot" => crate::lights::spot::create(
|
||||||
render_from_light,
|
render_from_light, medium, parameters, loc,
|
||||||
medium,
|
&shape, &alpha, None, arena,
|
||||||
parameters,
|
|
||||||
loc,
|
|
||||||
&shape,
|
|
||||||
&alpha,
|
|
||||||
None,
|
|
||||||
arena,
|
|
||||||
),
|
),
|
||||||
"distant" => crate::lights::distant::create(
|
"distant" => crate::lights::distant::create(
|
||||||
render_from_light,
|
render_from_light, medium, parameters, loc,
|
||||||
medium,
|
&shape, &alpha, None, arena,
|
||||||
parameters,
|
|
||||||
loc,
|
|
||||||
&shape,
|
|
||||||
&alpha,
|
|
||||||
None,
|
|
||||||
arena,
|
|
||||||
),
|
),
|
||||||
"goniometric" => crate::lights::goniometric::create(
|
"goniometric" => crate::lights::goniometric::create(
|
||||||
render_from_light,
|
render_from_light, medium, parameters, loc,
|
||||||
medium,
|
&shape, &alpha, None, arena,
|
||||||
parameters,
|
|
||||||
loc,
|
|
||||||
&shape,
|
|
||||||
&alpha,
|
|
||||||
None,
|
|
||||||
arena,
|
|
||||||
),
|
),
|
||||||
"projection" => crate::lights::projection::create(
|
"projection" => crate::lights::projection::create(
|
||||||
render_from_light,
|
render_from_light, medium, parameters, loc,
|
||||||
medium,
|
&shape, &alpha, None, arena,
|
||||||
parameters,
|
|
||||||
loc,
|
|
||||||
&shape,
|
|
||||||
&alpha,
|
|
||||||
None,
|
|
||||||
arena,
|
|
||||||
),
|
),
|
||||||
"infinite" => crate::lights::infinite::create(
|
"infinite" => crate::lights::infinite::create(
|
||||||
render_from_light,
|
render_from_light, medium.into(), camera_transform,
|
||||||
medium.into(),
|
parameters, None, loc, arena,
|
||||||
camera_transform,
|
|
||||||
parameters,
|
|
||||||
None,
|
|
||||||
loc,
|
|
||||||
arena,
|
|
||||||
),
|
),
|
||||||
"diffuse" => Err(anyhow!(
|
"diffuse" => Err(anyhow!(
|
||||||
"{}: \"diffuse\" is an area light. Use create_area_light with a shape",
|
"{}: \"diffuse\" is an area light; use create_area_light with a shape",
|
||||||
loc
|
loc
|
||||||
)),
|
)),
|
||||||
_ => Err(anyhow!("{}: unknown light type \"{}\"", loc, name)),
|
_ => Err(anyhow!("{}: unknown light type \"{}\"", loc, name)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a diffuse area light bound to a specific shape.
|
||||||
|
/// Returns a Light value. The individual light constructor still uses
|
||||||
|
/// the arena internally for sub-allocations (shape, image, spectra),
|
||||||
|
/// but the Light itself is returned as a value for the caller to place.
|
||||||
pub fn create_area_light(
|
pub fn create_area_light(
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
medium: Option<Medium>,
|
medium: Option<Medium>,
|
||||||
|
|
@ -118,10 +84,9 @@ pub fn create_area_light(
|
||||||
alpha_tex: &FloatTexture,
|
alpha_tex: &FloatTexture,
|
||||||
colorspace: Option<&RGBColorSpace>,
|
colorspace: Option<&RGBColorSpace>,
|
||||||
arena: &Arena,
|
arena: &Arena,
|
||||||
) -> Result<Ptr<Light>> {
|
) -> Result<Light> {
|
||||||
let light = crate::lights::diffuse::create(
|
crate::lights::diffuse::create(
|
||||||
render_from_light, medium, parameters, loc,
|
render_from_light, medium, parameters, loc,
|
||||||
shape, alpha_tex, colorspace, arena,
|
shape, alpha_tex, colorspace, arena,
|
||||||
)?;
|
)
|
||||||
Ok(arena.alloc(light))
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ pub struct SceneLookup<'a> {
|
||||||
pub media: &'a HashMap<String, Arc<Medium>>,
|
pub media: &'a HashMap<String, Arc<Medium>>,
|
||||||
pub named_materials: &'a HashMap<String, Material>,
|
pub named_materials: &'a HashMap<String, Material>,
|
||||||
pub materials: &'a Vec<Material>,
|
pub materials: &'a Vec<Material>,
|
||||||
pub shape_lights: &'a HashMap<usize, Vec<Ptr<Light>>>,
|
pub shape_lights: &'a HashMap<usize, Vec<Light>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SceneLookup<'a> {
|
impl<'a> SceneLookup<'a> {
|
||||||
|
|
@ -130,8 +130,9 @@ impl BasicScene {
|
||||||
*self.film_colorspace.lock() = Some(Arc::clone(cs));
|
*self.film_colorspace.lock() = Some(Arc::clone(cs));
|
||||||
}
|
}
|
||||||
|
|
||||||
let filter = Filter::create(&filter.name, &filter.parameters, &filter.loc, &arena)
|
let filter = Filter::create(&filter.name, &filter.parameters, &filter.loc, arena)
|
||||||
.map_err(|e| anyhow!("Failed to create filter: {}", e))?;
|
.map_err(|e| anyhow!("Failed to create filter: {}", e))?;
|
||||||
|
|
||||||
let shutter_close = camera.base.parameters.get_one_float("shutterclose", 1.)?;
|
let shutter_close = camera.base.parameters.get_one_float("shutterclose", 1.)?;
|
||||||
let shutter_open = camera.base.parameters.get_one_float("shutteropen", 0.)?;
|
let shutter_open = camera.base.parameters.get_one_float("shutteropen", 0.)?;
|
||||||
let exposure_time = shutter_close - shutter_open;
|
let exposure_time = shutter_close - shutter_open;
|
||||||
|
|
@ -144,7 +145,7 @@ impl BasicScene {
|
||||||
filter,
|
filter,
|
||||||
Some(camera.camera_transform.clone()),
|
Some(camera.camera_transform.clone()),
|
||||||
&film.loc,
|
&film.loc,
|
||||||
&arena,
|
arena,
|
||||||
)
|
)
|
||||||
.map_err(|e| anyhow!("Failed to create film: {}", e))?,
|
.map_err(|e| anyhow!("Failed to create film: {}", e))?,
|
||||||
);
|
);
|
||||||
|
|
@ -154,36 +155,33 @@ impl BasicScene {
|
||||||
job: None,
|
job: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let sampler_film = Arc::clone(&film_instance);
|
let res = film_instance.as_ref().base().full_resolution;
|
||||||
let sampler_job = run_async(move || {
|
let sampler_result =
|
||||||
let res = sampler_film.as_ref().base().full_resolution;
|
Sampler::create(&sampler.name, &sampler.parameters, res, &sampler.loc, arena)
|
||||||
Sampler::create(
|
.map_err(|e| anyhow!("Failed to create sampler: {}", e))?;
|
||||||
&sampler.name,
|
|
||||||
&sampler.parameters,
|
*self.sampler_state.lock() = SingletonState {
|
||||||
res,
|
result: Some(Arc::new(sampler_result)),
|
||||||
&sampler.loc,
|
job: None,
|
||||||
&arena,
|
};
|
||||||
)
|
|
||||||
.map_err(|e| anyhow!("Failed to create sampler: {}", e))
|
let medium = self.get_medium(&camera.medium, &camera.base.loc);
|
||||||
});
|
let camera_result = Camera::create(
|
||||||
self.sampler_state.lock().job = Some(sampler_job);
|
&camera.base.name,
|
||||||
|
&camera.base.parameters,
|
||||||
|
&camera.camera_transform,
|
||||||
|
medium,
|
||||||
|
Arc::clone(&film_instance),
|
||||||
|
&camera.base.loc,
|
||||||
|
arena,
|
||||||
|
)
|
||||||
|
.map_err(|e| anyhow!("Failed to create camera: {}", e))?;
|
||||||
|
|
||||||
|
*self.camera_state.lock() = SingletonState {
|
||||||
|
result: Some(Arc::new(camera_result)),
|
||||||
|
job: None,
|
||||||
|
};
|
||||||
|
|
||||||
let camera_film = Arc::clone(&film_instance);
|
|
||||||
let scene_ptr = Arc::clone(self);
|
|
||||||
let camera_job = run_async(move || {
|
|
||||||
let medium = scene_ptr.get_medium(&camera.medium, &camera.base.loc);
|
|
||||||
Camera::create(
|
|
||||||
&camera.base.name,
|
|
||||||
&camera.base.parameters,
|
|
||||||
&camera.camera_transform,
|
|
||||||
medium,
|
|
||||||
camera_film,
|
|
||||||
&camera.base.loc,
|
|
||||||
&arena,
|
|
||||||
)
|
|
||||||
.map_err(|e| anyhow!("Failed to create camera: {}", e))
|
|
||||||
});
|
|
||||||
self.camera_state.lock().job = Some(camera_job);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -506,11 +504,7 @@ impl BasicScene {
|
||||||
Ok((named_materials, materials))
|
Ok((named_materials, materials))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_lights(
|
pub fn create_lights(&self, camera_transform: &CameraTransform, arena: &Arena) -> Vec<Light> {
|
||||||
&self,
|
|
||||||
camera_transform: &CameraTransform,
|
|
||||||
arena: &mut Arena,
|
|
||||||
) -> Vec<Light> {
|
|
||||||
let state = self.light_state.lock();
|
let state = self.light_state.lock();
|
||||||
|
|
||||||
state
|
state
|
||||||
|
|
@ -555,9 +549,9 @@ impl BasicScene {
|
||||||
shape_entities: &[ShapeSceneEntity],
|
shape_entities: &[ShapeSceneEntity],
|
||||||
textures: &NamedTextures,
|
textures: &NamedTextures,
|
||||||
arena: &Arena,
|
arena: &Arena,
|
||||||
) -> HashMap<usize, Vec<Ptr<Light>>> {
|
) -> HashMap<usize, Vec<Light>> {
|
||||||
let light_state = self.light_state.lock();
|
let light_state = self.light_state.lock();
|
||||||
let mut shape_lights: HashMap<usize, Vec<Ptr<Light>>> = HashMap::new();
|
let mut shape_lights: HashMap<usize, Vec<Light>> = HashMap::new();
|
||||||
|
|
||||||
for (i, entity) in shape_entities.iter().enumerate() {
|
for (i, entity) in shape_entities.iter().enumerate() {
|
||||||
let light_idx = match entity.light_index {
|
let light_idx = match entity.light_index {
|
||||||
|
|
@ -581,7 +575,6 @@ impl BasicScene {
|
||||||
let default_alpha = Arc::new(FloatTexture::default());
|
let default_alpha = Arc::new(FloatTexture::default());
|
||||||
let alpha_ref = alpha_tex.as_ref().unwrap_or(&default_alpha);
|
let alpha_ref = alpha_tex.as_ref().unwrap_or(&default_alpha);
|
||||||
|
|
||||||
// Use the film colorspace as fallback for area light emission
|
|
||||||
let film_cs = self.film_colorspace.lock();
|
let film_cs = self.film_colorspace.lock();
|
||||||
let colorspace_ref = al_entity
|
let colorspace_ref = al_entity
|
||||||
.parameters
|
.parameters
|
||||||
|
|
@ -591,7 +584,7 @@ impl BasicScene {
|
||||||
|
|
||||||
let render_from_light = *entity.render_from_object;
|
let render_from_light = *entity.render_from_object;
|
||||||
|
|
||||||
let lights: Vec<Ptr<Light>> = shapes
|
let lights: Vec<Light> = shapes
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|shape| {
|
.filter_map(|shape| {
|
||||||
match crate::core::light::create_area_light(
|
match crate::core::light::create_area_light(
|
||||||
|
|
@ -678,7 +671,7 @@ impl BasicScene {
|
||||||
al_params: Option<&SceneEntity>,
|
al_params: Option<&SceneEntity>,
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
film_cs: Option<&RGBColorSpace>,
|
film_cs: Option<&RGBColorSpace>,
|
||||||
arena: &mut Arena,
|
arena: &Arena,
|
||||||
area_lights: &mut Vec<Arc<Light>>,
|
area_lights: &mut Vec<Arc<Light>>,
|
||||||
) -> Vec<(Ptr<Shape>, Ptr<Light>, Ptr<GPUFloatTexture>)> {
|
) -> Vec<(Ptr<Shape>, Ptr<Light>, Ptr<GPUFloatTexture>)> {
|
||||||
shapes
|
shapes
|
||||||
|
|
@ -689,7 +682,7 @@ impl BasicScene {
|
||||||
let cs = al_entity.parameters.color_space.as_deref().or(film_cs);
|
let cs = al_entity.parameters.color_space.as_deref().or(film_cs);
|
||||||
let default_alpha = Arc::new(FloatTexture::default());
|
let default_alpha = Arc::new(FloatTexture::default());
|
||||||
let alpha_ref = alpha_tex.as_ref().unwrap_or(&default_alpha);
|
let alpha_ref = alpha_tex.as_ref().unwrap_or(&default_alpha);
|
||||||
crate::core::light::create_area_light(
|
match crate::core::light::create_area_light(
|
||||||
render_from_light,
|
render_from_light,
|
||||||
None,
|
None,
|
||||||
&al_entity.parameters,
|
&al_entity.parameters,
|
||||||
|
|
@ -698,15 +691,21 @@ impl BasicScene {
|
||||||
alpha_ref,
|
alpha_ref,
|
||||||
cs,
|
cs,
|
||||||
arena,
|
arena,
|
||||||
)
|
) {
|
||||||
.ok()
|
Ok(light) => {
|
||||||
|
// Keep an Arc copy for the host-side light list
|
||||||
|
area_lights.push(Arc::new(light));
|
||||||
|
// Alloc into arena for the GPU primitive
|
||||||
|
Some(arena.alloc(light))
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to create area light: {}", e);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.unwrap_or(Ptr::null());
|
.unwrap_or(Ptr::null());
|
||||||
|
|
||||||
if !area_light_ptr.is_null() {
|
|
||||||
area_lights.push(Arc::new(unsafe { &*area_light_ptr.as_raw() }.clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
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()))
|
||||||
|
|
@ -932,13 +931,16 @@ impl BasicScene {
|
||||||
.unwrap_or(Ptr::null()),
|
.unwrap_or(Ptr::null()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let shape_lights_opt = lookup
|
// Light is &Light from the HashMap — alloc into arena for the Ptr
|
||||||
|
let light_ptr = lookup
|
||||||
.shape_lights
|
.shape_lights
|
||||||
.get(&shape_ctx.entity_index)
|
.get(&shape_ctx.entity_index)
|
||||||
.and_then(|lights| lights.get(shape_ctx.shape_index));
|
.and_then(|lights| lights.get(shape_ctx.shape_index))
|
||||||
|
.map(|l| arena.alloc(*l))
|
||||||
|
.unwrap_or(Ptr::null());
|
||||||
|
|
||||||
let light_ptr = shape_lights_opt.copied().unwrap_or(Ptr::null());
|
|
||||||
let shape_ptr = shape_ctx.shape;
|
let shape_ptr = shape_ctx.shape;
|
||||||
|
|
||||||
let prim = if light_ptr.is_null() && !mi.is_medium_transition() && alpha_tex.is_none() {
|
let prim = if light_ptr.is_null() && !mi.is_medium_transition() && alpha_tex.is_none() {
|
||||||
Primitive::Simple(SimplePrimitive::new(shape_ptr, Ptr::from(&mtl)))
|
Primitive::Simple(SimplePrimitive::new(shape_ptr, Ptr::from(&mtl)))
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -947,7 +949,10 @@ impl BasicScene {
|
||||||
arena.alloc(mtl),
|
arena.alloc(mtl),
|
||||||
light_ptr,
|
light_ptr,
|
||||||
mi.clone(),
|
mi.clone(),
|
||||||
arena.upload(alpha_tex),
|
alpha_tex
|
||||||
|
.as_ref()
|
||||||
|
.map(|t| arena.upload(t.as_ref()))
|
||||||
|
.unwrap_or(Ptr::null()),
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use shared::core::light::{Light, LightBase, LightType};
|
||||||
use shared::core::medium::{Medium, MediumInterface};
|
use shared::core::medium::{Medium, MediumInterface};
|
||||||
use shared::core::shape::{Shape, ShapeTrait};
|
use shared::core::shape::{Shape, ShapeTrait};
|
||||||
use shared::core::spectrum::Spectrum;
|
use shared::core::spectrum::Spectrum;
|
||||||
use shared::core::texture::{SpectrumType, TextureEvalContext, GPUFloatTexture};
|
use shared::core::texture::{GPUFloatTexture, 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;
|
||||||
|
|
@ -101,7 +101,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.as_ref() {
|
let light_type = match unsafe { alpha_ptr.as_ref() } {
|
||||||
GPUFloatTexture::Constant(t) if t.evaluate(&TextureEvalContext::default()) == 0.0 => {
|
GPUFloatTexture::Constant(t) if t.evaluate(&TextureEvalContext::default()) == 0.0 => {
|
||||||
LightType::DeltaPosition
|
LightType::DeltaPosition
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,60 +1,84 @@
|
||||||
use crate::Arena;
|
use crate::Arena;
|
||||||
use shared::core::light::{Light, LightTrait};
|
use shared::core::light::{Light, LightTrait};
|
||||||
use shared::lights::sampler::PowerLightSampler;
|
use shared::lights::sampler::{
|
||||||
|
BVHLightSampler, LightSampler, PowerLightSampler, UniformLightSampler,
|
||||||
|
};
|
||||||
use shared::utils::sampling::AliasTable;
|
use shared::utils::sampling::AliasTable;
|
||||||
use shared::spectra::{SampledSpectrum, SampledWavelengths};
|
use shared::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use std::collections::HashMap;
|
use shared::utils::Ptr;
|
||||||
|
use shared::Float;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub struct PowerSamplerHost {
|
/// Top-level dispatcher matching the C++ LightSampler::Create.
|
||||||
pub lights: Vec<Arc<Light>>,
|
pub fn create_light_sampler(
|
||||||
pub light_to_index: HashMap<usize, usize>,
|
name: &str,
|
||||||
pub alias_table: AliasTable,
|
lights: &[Arc<Light>],
|
||||||
}
|
arena: &Arena,
|
||||||
|
) -> LightSampler {
|
||||||
impl PowerSamplerHost {
|
let device_lights = lights_to_slice(lights, arena);
|
||||||
pub fn new(lights: &[Arc<Light>]) -> Self {
|
match name {
|
||||||
if lights.is_empty() {
|
"uniform" => LightSampler::Uniform(create_uniform(device_lights, lights.len())),
|
||||||
return Self {
|
"power" => LightSampler::Power(create_power(lights, device_lights, arena)),
|
||||||
lights: Vec::new(),
|
"bvh" => {
|
||||||
light_to_index: HashMap::new(),
|
log::warn!("BVH light sampler not yet implemented, falling back to power");
|
||||||
alias_table: AliasTable::new(&[]),
|
LightSampler::Power(create_power(lights, device_lights, arena))
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
_ => {
|
||||||
let mut lights_vec = Vec::with_capacity(lights.len());
|
log::error!("Unknown light sampler \"{}\", using power", name);
|
||||||
let mut light_to_index = HashMap::with_capacity(lights.len());
|
LightSampler::Power(create_power(lights, device_lights, arena))
|
||||||
let mut light_power = Vec::with_capacity(lights.len());
|
|
||||||
|
|
||||||
let lambda = SampledWavelengths::sample_visible(0.5);
|
|
||||||
|
|
||||||
for (i, light) in lights.iter().enumerate() {
|
|
||||||
lights_vec.push(light.clone());
|
|
||||||
|
|
||||||
let ptr = Arc::as_ptr(light) as usize;
|
|
||||||
light_to_index.insert(ptr, i);
|
|
||||||
|
|
||||||
let phi = SampledSpectrum::safe_div(&light.phi(lambda), &lambda.pdf());
|
|
||||||
light_power.push(phi.average());
|
|
||||||
}
|
|
||||||
|
|
||||||
let alias_table = AliasTable::new(&light_power);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
lights: lights_vec,
|
|
||||||
light_to_index,
|
|
||||||
alias_table,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// pub fn to_device(&self, arena: &Arena) -> PowerLightSampler {
|
}
|
||||||
// let device_lights: Vec<Light> = self.lights.iter().map(|l| (**l).clone()).collect();
|
|
||||||
// let (lights_ptr, _) = arena.alloc_slice(&device_lights);
|
fn lights_to_slice(lights: &[Arc<Light>], arena: &Arena) -> (Ptr<Light>, u32) {
|
||||||
// let alias_device = self.alias_table.to_device(arena);
|
if lights.is_empty() {
|
||||||
//
|
return (Ptr::null(), 0);
|
||||||
// PowerLightSampler {
|
}
|
||||||
// lights: lights_ptr,
|
let vals: Vec<Light> = lights.iter().map(|l| **l).collect();
|
||||||
// lights_len: self.lights.len() as u32,
|
let (ptr, _) = arena.alloc_slice(&vals);
|
||||||
// alias_table: alias_device,
|
(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 {
|
||||||
|
lights: Ptr::null(),
|
||||||
|
lights_len: 0,
|
||||||
|
alias_table: Ptr::null(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let lambda = SampledWavelengths::sample_visible(0.5);
|
||||||
|
let mut light_power: Vec<Float> = host_lights
|
||||||
|
.iter()
|
||||||
|
.map(|l| {
|
||||||
|
let phi = SampledSpectrum::safe_div(&l.phi(lambda), &lambda.pdf());
|
||||||
|
phi.average()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// If all lights have zero power, treat as uniform
|
||||||
|
if light_power.iter().sum::<Float>() == 0.0 {
|
||||||
|
light_power.fill(1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let alias_table = AliasTable::new(&light_power);
|
||||||
|
let alias_ptr = arena.alloc(alias_table);
|
||||||
|
|
||||||
|
PowerLightSampler {
|
||||||
|
lights,
|
||||||
|
lights_len,
|
||||||
|
alias_table: alias_ptr,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,42 @@
|
||||||
use crate::Arena;
|
|
||||||
use crate::core::image::HostImage;
|
use crate::core::image::HostImage;
|
||||||
use crate::core::material::CreateMaterial;
|
use crate::core::material::CreateMaterial;
|
||||||
|
use crate::core::texture::SpectrumTexture;
|
||||||
|
use crate::utils::upload::ArenaUpload;
|
||||||
use crate::utils::{FileLoc, TextureParameterDictionary};
|
use crate::utils::{FileLoc, TextureParameterDictionary};
|
||||||
|
use crate::Arena;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use shared::core::material::Material;
|
use shared::core::material::Material;
|
||||||
|
use shared::core::spectrum::Spectrum;
|
||||||
|
use shared::core::texture::SpectrumType;
|
||||||
use shared::materials::{DiffuseMaterial, DiffuseTransmissionMaterial};
|
use shared::materials::{DiffuseMaterial, DiffuseTransmissionMaterial};
|
||||||
|
use shared::spectra::ConstantSpectrum;
|
||||||
|
use shared::textures::SpectrumConstantTexture;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
impl CreateMaterial for DiffuseMaterial {
|
impl CreateMaterial for DiffuseMaterial {
|
||||||
fn create(
|
fn create(
|
||||||
_parameters: &TextureParameterDictionary,
|
parameters: &TextureParameterDictionary,
|
||||||
_normal_map: Option<Arc<HostImage>>,
|
normal_map: Option<Arc<HostImage>>,
|
||||||
_named_materials: &HashMap<String, Material>,
|
_named_materials: &HashMap<String, Material>,
|
||||||
_loc: &FileLoc,
|
_loc: &FileLoc,
|
||||||
_arena: &Arena,
|
arena: &Arena,
|
||||||
) -> Result<Material> {
|
) -> Result<Material> {
|
||||||
todo!()
|
let reflectance = parameters
|
||||||
|
.get_spectrum_texture("reflectance", None, SpectrumType::Albedo)
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
Arc::new(SpectrumTexture::Constant(
|
||||||
|
SpectrumConstantTexture::new(Spectrum::Constant(ConstantSpectrum::new(0.5))),
|
||||||
|
))
|
||||||
|
});
|
||||||
|
let displacement = parameters.get_float_texture_or_null("displacement")?;
|
||||||
|
|
||||||
|
let specific = DiffuseMaterial {
|
||||||
|
reflectance: arena.upload(reflectance),
|
||||||
|
displacement: arena.upload(displacement),
|
||||||
|
normal_map: arena.upload(normal_map),
|
||||||
|
};
|
||||||
|
Ok(Material::Diffuse(specific))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@ pub trait CreateRGBColorSpace {
|
||||||
r: Point2f,
|
r: Point2f,
|
||||||
g: Point2f,
|
g: Point2f,
|
||||||
b: Point2f,
|
b: Point2f,
|
||||||
illuminant: &DenselySampledSpectrum,
|
illuminant: Ptr<DenselySampledSpectrum>,
|
||||||
rgb_to_spectrum_table: &RGBToSpectrumTable,
|
rgb_to_spectrum_table: Ptr<RGBToSpectrumTable>,
|
||||||
) -> Self;
|
) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -22,19 +22,16 @@ impl CreateRGBColorSpace for RGBColorSpace {
|
||||||
r: Point2f,
|
r: Point2f,
|
||||||
g: Point2f,
|
g: Point2f,
|
||||||
b: Point2f,
|
b: Point2f,
|
||||||
illuminant: &DenselySampledSpectrum,
|
illuminant: Ptr<DenselySampledSpectrum>,
|
||||||
rgb_to_spectrum_table: &RGBToSpectrumTable,
|
rgb_to_spectrum_table: Ptr<RGBToSpectrumTable>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let stdspec = get_spectra_context();
|
let stdspec = get_spectra_context();
|
||||||
let illum_ptr = Ptr::from(illuminant);
|
let illum_spectrum = Spectrum::Dense(illuminant);
|
||||||
let illum_spectrum = Spectrum::Dense(illum_ptr);
|
|
||||||
let w_xyz: XYZ = illum_spectrum.to_xyz(&stdspec);
|
let w_xyz: XYZ = illum_spectrum.to_xyz(&stdspec);
|
||||||
let w = w_xyz.xy();
|
let w = w_xyz.xy();
|
||||||
|
|
||||||
let r_xyz = XYZ::from_xyy(r, Some(1.0));
|
let r_xyz = XYZ::from_xyy(r, Some(1.0));
|
||||||
let g_xyz = XYZ::from_xyy(g, Some(1.0));
|
let g_xyz = XYZ::from_xyy(g, Some(1.0));
|
||||||
let b_xyz = XYZ::from_xyy(b, Some(1.0));
|
let b_xyz = XYZ::from_xyy(b, Some(1.0));
|
||||||
|
|
||||||
let rgb_values = [
|
let rgb_values = [
|
||||||
[r_xyz.x(), g_xyz.x(), b_xyz.x()],
|
[r_xyz.x(), g_xyz.x(), b_xyz.x()],
|
||||||
[r_xyz.y(), g_xyz.y(), b_xyz.y()],
|
[r_xyz.y(), g_xyz.y(), b_xyz.y()],
|
||||||
|
|
@ -44,14 +41,13 @@ impl CreateRGBColorSpace for RGBColorSpace {
|
||||||
let c: RGB = rgb.inverse().unwrap() * w_xyz;
|
let c: RGB = rgb.inverse().unwrap() * w_xyz;
|
||||||
let xyz_from_rgb = rgb * SquareMatrix::diag(&[c.r, c.g, c.b]);
|
let xyz_from_rgb = rgb * SquareMatrix::diag(&[c.r, c.g, c.b]);
|
||||||
let rgb_from_xyz = xyz_from_rgb.inverse().expect("singular");
|
let rgb_from_xyz = xyz_from_rgb.inverse().expect("singular");
|
||||||
|
|
||||||
RGBColorSpace {
|
RGBColorSpace {
|
||||||
r,
|
r,
|
||||||
g,
|
g,
|
||||||
b,
|
b,
|
||||||
w,
|
w,
|
||||||
illuminant: illum_ptr,
|
illuminant,
|
||||||
rgb_to_spectrum_table: Ptr::from(rgb_to_spectrum_table),
|
rgb_to_spectrum_table,
|
||||||
xyz_from_rgb,
|
xyz_from_rgb,
|
||||||
rgb_from_xyz,
|
rgb_from_xyz,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use shared::core::spectrum::{Spectrum, StandardSpectra};
|
||||||
use shared::spectra::cie::{CIE_D65, CIE_X, CIE_Y, CIE_Z};
|
use shared::spectra::cie::{CIE_D65, CIE_X, CIE_Y, CIE_Z};
|
||||||
use shared::spectra::{DenselySampledSpectrum, DeviceStandardColorSpaces, RGBColorSpace};
|
use shared::spectra::{DenselySampledSpectrum, DeviceStandardColorSpaces, RGBColorSpace};
|
||||||
use shared::Ptr;
|
use shared::Ptr;
|
||||||
use std::sync::{Arc, OnceLock, LazyLock};
|
use std::sync::{Arc, LazyLock, OnceLock};
|
||||||
|
|
||||||
pub mod colorspace;
|
pub mod colorspace;
|
||||||
pub mod data;
|
pub mod data;
|
||||||
|
|
@ -21,10 +21,6 @@ pub static CIE_Z_DATA: LazyLock<DenselySampledSpectrum> =
|
||||||
pub static CIE_D65_DATA: LazyLock<DenselySampledSpectrum> =
|
pub static CIE_D65_DATA: LazyLock<DenselySampledSpectrum> =
|
||||||
LazyLock::new(|| data::create_cie(&CIE_D65));
|
LazyLock::new(|| data::create_cie(&CIE_D65));
|
||||||
|
|
||||||
fn get_d65_illuminant_buffer() -> Arc<DenselySampledSpectrum> {
|
|
||||||
Arc::new(CIE_D65_DATA.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cie_x() -> Spectrum {
|
pub fn cie_x() -> Spectrum {
|
||||||
Spectrum::Dense(Ptr::from(&*CIE_X_DATA))
|
Spectrum::Dense(Ptr::from(&*CIE_X_DATA))
|
||||||
}
|
}
|
||||||
|
|
@ -47,42 +43,46 @@ pub fn get_spectra_context() -> StandardSpectra {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static D65_ILLUMINANT: LazyLock<DenselySampledSpectrum> = LazyLock::new(|| CIE_D65_DATA.clone());
|
||||||
|
|
||||||
pub static SRGB: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| {
|
pub static SRGB: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| {
|
||||||
let illum = get_d65_illuminant_buffer();
|
|
||||||
let r = Point2f::new(0.64, 0.33);
|
let r = Point2f::new(0.64, 0.33);
|
||||||
let g = Point2f::new(0.3, 0.6);
|
let g = Point2f::new(0.3, 0.6);
|
||||||
let b = Point2f::new(0.15, 0.06);
|
let b = Point2f::new(0.15, 0.06);
|
||||||
let table_ptr = Ptr::from(&*SRGB_TABLE);
|
let table_ptr = Ptr::from(&*SRGB_TABLE);
|
||||||
|
let illum_ptr = Ptr::from(&*D65_ILLUMINANT);
|
||||||
|
|
||||||
Arc::new(RGBColorSpace::new(r, g, b, &illum, &table_ptr))
|
Arc::new(RGBColorSpace::new(r, g, b, illum_ptr, table_ptr))
|
||||||
});
|
});
|
||||||
|
|
||||||
pub static DCI_P3: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| {
|
pub static DCI_P3: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| {
|
||||||
let illum = get_d65_illuminant_buffer();
|
|
||||||
let r = Point2f::new(0.680, 0.320);
|
let r = Point2f::new(0.680, 0.320);
|
||||||
let g = Point2f::new(0.265, 0.690);
|
let g = Point2f::new(0.265, 0.690);
|
||||||
let b = Point2f::new(0.150, 0.060);
|
let b = Point2f::new(0.150, 0.060);
|
||||||
let table_ptr = Ptr::from(&*DCI_P3_TABLE);
|
let table_ptr = Ptr::from(&*DCI_P3_TABLE);
|
||||||
Arc::new(RGBColorSpace::new(r, g, b, &illum, &table_ptr))
|
let illum_ptr = Ptr::from(&*D65_ILLUMINANT);
|
||||||
|
|
||||||
|
Arc::new(RGBColorSpace::new(r, g, b, illum_ptr, table_ptr))
|
||||||
});
|
});
|
||||||
|
|
||||||
pub static REC2020: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| {
|
pub static REC2020: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| {
|
||||||
let illum = get_d65_illuminant_buffer();
|
|
||||||
let r = Point2f::new(0.708, 0.292);
|
let r = Point2f::new(0.708, 0.292);
|
||||||
let g = Point2f::new(0.170, 0.797);
|
let g = Point2f::new(0.170, 0.797);
|
||||||
let b = Point2f::new(0.131, 0.046);
|
let b = Point2f::new(0.131, 0.046);
|
||||||
let table_ptr = Ptr::from(&*REC2020_TABLE);
|
let table_ptr = Ptr::from(&*REC2020_TABLE);
|
||||||
Arc::new(RGBColorSpace::new(r, g, b, &illum, &table_ptr))
|
let illum_ptr = Ptr::from(&*D65_ILLUMINANT);
|
||||||
|
|
||||||
|
Arc::new(RGBColorSpace::new(r, g, b, illum_ptr, table_ptr))
|
||||||
});
|
});
|
||||||
|
|
||||||
pub static ACES: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| {
|
pub static ACES: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| {
|
||||||
let illum = get_d65_illuminant_buffer();
|
|
||||||
let r = Point2f::new(0.7347, 0.2653);
|
let r = Point2f::new(0.7347, 0.2653);
|
||||||
let g = Point2f::new(0.0000, 1.0000);
|
let g = Point2f::new(0.0000, 1.0000);
|
||||||
let b = Point2f::new(0.0001, -0.0770);
|
let b = Point2f::new(0.0001, -0.0770);
|
||||||
let table_ptr = Ptr::from(&ACES_TABLE);
|
let table_ptr = Ptr::from(&*ACES_TABLE);
|
||||||
Arc::new(RGBColorSpace::new(r, g, b, &illum, &table_ptr))
|
let illum_ptr = Ptr::from(&*D65_ILLUMINANT);
|
||||||
|
|
||||||
|
Arc::new(RGBColorSpace::new(r, g, b, illum_ptr, table_ptr))
|
||||||
});
|
});
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -141,4 +141,3 @@ pub fn default_colorspace_ref() -> &'static RGBColorSpace {
|
||||||
pub fn default_illuminant() -> Spectrum {
|
pub fn default_illuminant() -> Spectrum {
|
||||||
Spectrum::Dense(default_colorspace().illuminant)
|
Spectrum::Dense(default_colorspace().illuminant)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -915,7 +915,12 @@ impl TextureParameterDictionary {
|
||||||
panic!("[{:?}] Negative RGB values for '{}'", p.loc, name);
|
panic!("[{:?}] Negative RGB values for '{}'", p.loc, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
let cs = self.dict.color_space.as_ref().unwrap();
|
let cs = self
|
||||||
|
.dict
|
||||||
|
.color_space
|
||||||
|
.as_ref()
|
||||||
|
.map(|arc| arc.as_ref())
|
||||||
|
.unwrap_or_else(|| crate::spectra::default_colorspace_ref());
|
||||||
let s: Spectrum = match stype {
|
let s: Spectrum = match stype {
|
||||||
SpectrumType::Illuminant => {
|
SpectrumType::Illuminant => {
|
||||||
Spectrum::RGBIlluminant(RGBIlluminantSpectrum::new(cs, rgb))
|
Spectrum::RGBIlluminant(RGBIlluminantSpectrum::new(cs, rgb))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue