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.),
|
||||
p_vector.normalize(),
|
||||
Some(self.sample_time(sample.time)),
|
||||
&*self.base().medium,
|
||||
self.base().medium,
|
||||
);
|
||||
// Modify ray for depth of field
|
||||
if self.lens_radius > 0. {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use crate::core::shape::ShapeIntersection;
|
|||
use crate::{Float, Ptr, GVec, gvec};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Default, Debug, Clone, Copy)]
|
||||
pub struct LinearBVHNode {
|
||||
pub bounds: Bounds3f,
|
||||
pub primitives_offset: usize,
|
||||
|
|
|
|||
|
|
@ -798,7 +798,11 @@ impl DigitPermutation {
|
|||
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 {
|
||||
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> {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,10 +6,9 @@ use shared::core::camera::CameraTransform;
|
|||
use shared::core::light::Light;
|
||||
use shared::core::medium::Medium;
|
||||
use shared::core::shape::Shape;
|
||||
use shared::spectra::DenselySampledSpectrum;
|
||||
use shared::core::spectrum::Spectrum;
|
||||
use shared::spectra::RGBColorSpace;
|
||||
use shared::{Ptr, Transform};
|
||||
use shared::spectra::{DenselySampledSpectrum, RGBColorSpace};
|
||||
use shared::Transform;
|
||||
use std::sync::Arc;
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
// 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 {
|
||||
Shape::default()
|
||||
}
|
||||
|
|
@ -29,6 +25,8 @@ fn dummy_alpha() -> FloatTexture {
|
|||
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(
|
||||
name: &str,
|
||||
render_from_light: Transform,
|
||||
|
|
@ -36,79 +34,47 @@ pub fn create_light(
|
|||
parameters: &ParameterDictionary,
|
||||
loc: &FileLoc,
|
||||
camera_transform: CameraTransform,
|
||||
arena: &mut Arena,
|
||||
arena: &Arena,
|
||||
) -> Result<Light> {
|
||||
let shape = dummy_shape();
|
||||
let alpha = dummy_alpha();
|
||||
|
||||
match name {
|
||||
"point" => crate::lights::point::create(
|
||||
render_from_light,
|
||||
medium,
|
||||
parameters,
|
||||
loc,
|
||||
&shape,
|
||||
&alpha,
|
||||
None,
|
||||
arena,
|
||||
render_from_light, medium, parameters, loc,
|
||||
&shape, &alpha, None, arena,
|
||||
),
|
||||
"spot" => crate::lights::spot::create(
|
||||
render_from_light,
|
||||
medium,
|
||||
parameters,
|
||||
loc,
|
||||
&shape,
|
||||
&alpha,
|
||||
None,
|
||||
arena,
|
||||
render_from_light, medium, parameters, loc,
|
||||
&shape, &alpha, None, arena,
|
||||
),
|
||||
"distant" => crate::lights::distant::create(
|
||||
render_from_light,
|
||||
medium,
|
||||
parameters,
|
||||
loc,
|
||||
&shape,
|
||||
&alpha,
|
||||
None,
|
||||
arena,
|
||||
render_from_light, medium, parameters, loc,
|
||||
&shape, &alpha, None, arena,
|
||||
),
|
||||
"goniometric" => crate::lights::goniometric::create(
|
||||
render_from_light,
|
||||
medium,
|
||||
parameters,
|
||||
loc,
|
||||
&shape,
|
||||
&alpha,
|
||||
None,
|
||||
arena,
|
||||
render_from_light, medium, parameters, loc,
|
||||
&shape, &alpha, None, arena,
|
||||
),
|
||||
"projection" => crate::lights::projection::create(
|
||||
render_from_light,
|
||||
medium,
|
||||
parameters,
|
||||
loc,
|
||||
&shape,
|
||||
&alpha,
|
||||
None,
|
||||
arena,
|
||||
render_from_light, medium, parameters, loc,
|
||||
&shape, &alpha, None, arena,
|
||||
),
|
||||
"infinite" => crate::lights::infinite::create(
|
||||
render_from_light,
|
||||
medium.into(),
|
||||
camera_transform,
|
||||
parameters,
|
||||
None,
|
||||
loc,
|
||||
arena,
|
||||
render_from_light, medium.into(), camera_transform,
|
||||
parameters, None, loc, arena,
|
||||
),
|
||||
"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
|
||||
)),
|
||||
_ => 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(
|
||||
render_from_light: Transform,
|
||||
medium: Option<Medium>,
|
||||
|
|
@ -118,10 +84,9 @@ pub fn create_area_light(
|
|||
alpha_tex: &FloatTexture,
|
||||
colorspace: Option<&RGBColorSpace>,
|
||||
arena: &Arena,
|
||||
) -> Result<Ptr<Light>> {
|
||||
let light = crate::lights::diffuse::create(
|
||||
) -> Result<Light> {
|
||||
crate::lights::diffuse::create(
|
||||
render_from_light, medium, parameters, loc,
|
||||
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 named_materials: &'a HashMap<String, 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> {
|
||||
|
|
@ -130,8 +130,9 @@ impl BasicScene {
|
|||
*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))?;
|
||||
|
||||
let shutter_close = camera.base.parameters.get_one_float("shutterclose", 1.)?;
|
||||
let shutter_open = camera.base.parameters.get_one_float("shutteropen", 0.)?;
|
||||
let exposure_time = shutter_close - shutter_open;
|
||||
|
|
@ -144,7 +145,7 @@ impl BasicScene {
|
|||
filter,
|
||||
Some(camera.camera_transform.clone()),
|
||||
&film.loc,
|
||||
&arena,
|
||||
arena,
|
||||
)
|
||||
.map_err(|e| anyhow!("Failed to create film: {}", e))?,
|
||||
);
|
||||
|
|
@ -154,36 +155,33 @@ impl BasicScene {
|
|||
job: None,
|
||||
};
|
||||
|
||||
let sampler_film = Arc::clone(&film_instance);
|
||||
let sampler_job = run_async(move || {
|
||||
let res = sampler_film.as_ref().base().full_resolution;
|
||||
Sampler::create(
|
||||
&sampler.name,
|
||||
&sampler.parameters,
|
||||
res,
|
||||
&sampler.loc,
|
||||
&arena,
|
||||
)
|
||||
.map_err(|e| anyhow!("Failed to create sampler: {}", e))
|
||||
});
|
||||
self.sampler_state.lock().job = Some(sampler_job);
|
||||
let res = film_instance.as_ref().base().full_resolution;
|
||||
let sampler_result =
|
||||
Sampler::create(&sampler.name, &sampler.parameters, res, &sampler.loc, arena)
|
||||
.map_err(|e| anyhow!("Failed to create sampler: {}", e))?;
|
||||
|
||||
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(
|
||||
*self.sampler_state.lock() = SingletonState {
|
||||
result: Some(Arc::new(sampler_result)),
|
||||
job: None,
|
||||
};
|
||||
|
||||
let medium = self.get_medium(&camera.medium, &camera.base.loc);
|
||||
let camera_result = Camera::create(
|
||||
&camera.base.name,
|
||||
&camera.base.parameters,
|
||||
&camera.camera_transform,
|
||||
medium,
|
||||
camera_film,
|
||||
Arc::clone(&film_instance),
|
||||
&camera.base.loc,
|
||||
&arena,
|
||||
arena,
|
||||
)
|
||||
.map_err(|e| anyhow!("Failed to create camera: {}", e))
|
||||
});
|
||||
self.camera_state.lock().job = Some(camera_job);
|
||||
.map_err(|e| anyhow!("Failed to create camera: {}", e))?;
|
||||
|
||||
*self.camera_state.lock() = SingletonState {
|
||||
result: Some(Arc::new(camera_result)),
|
||||
job: None,
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -506,11 +504,7 @@ impl BasicScene {
|
|||
Ok((named_materials, materials))
|
||||
}
|
||||
|
||||
pub fn create_lights(
|
||||
&self,
|
||||
camera_transform: &CameraTransform,
|
||||
arena: &mut Arena,
|
||||
) -> Vec<Light> {
|
||||
pub fn create_lights(&self, camera_transform: &CameraTransform, arena: &Arena) -> Vec<Light> {
|
||||
let state = self.light_state.lock();
|
||||
|
||||
state
|
||||
|
|
@ -555,9 +549,9 @@ impl BasicScene {
|
|||
shape_entities: &[ShapeSceneEntity],
|
||||
textures: &NamedTextures,
|
||||
arena: &Arena,
|
||||
) -> HashMap<usize, Vec<Ptr<Light>>> {
|
||||
) -> HashMap<usize, Vec<Light>> {
|
||||
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() {
|
||||
let light_idx = match entity.light_index {
|
||||
|
|
@ -581,7 +575,6 @@ impl BasicScene {
|
|||
let default_alpha = Arc::new(FloatTexture::default());
|
||||
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 colorspace_ref = al_entity
|
||||
.parameters
|
||||
|
|
@ -591,7 +584,7 @@ impl BasicScene {
|
|||
|
||||
let render_from_light = *entity.render_from_object;
|
||||
|
||||
let lights: Vec<Ptr<Light>> = shapes
|
||||
let lights: Vec<Light> = shapes
|
||||
.iter()
|
||||
.filter_map(|shape| {
|
||||
match crate::core::light::create_area_light(
|
||||
|
|
@ -678,7 +671,7 @@ impl BasicScene {
|
|||
al_params: Option<&SceneEntity>,
|
||||
render_from_light: Transform,
|
||||
film_cs: Option<&RGBColorSpace>,
|
||||
arena: &mut Arena,
|
||||
arena: &Arena,
|
||||
area_lights: &mut Vec<Arc<Light>>,
|
||||
) -> Vec<(Ptr<Shape>, Ptr<Light>, Ptr<GPUFloatTexture>)> {
|
||||
shapes
|
||||
|
|
@ -689,7 +682,7 @@ impl BasicScene {
|
|||
let cs = al_entity.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);
|
||||
crate::core::light::create_area_light(
|
||||
match crate::core::light::create_area_light(
|
||||
render_from_light,
|
||||
None,
|
||||
&al_entity.parameters,
|
||||
|
|
@ -698,15 +691,21 @@ impl BasicScene {
|
|||
alpha_ref,
|
||||
cs,
|
||||
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());
|
||||
|
||||
if !area_light_ptr.is_null() {
|
||||
area_lights.push(Arc::new(unsafe { &*area_light_ptr.as_raw() }.clone()));
|
||||
}
|
||||
|
||||
let alpha_ptr = alpha_tex
|
||||
.as_ref()
|
||||
.map(|t| arena.upload(t.as_ref()))
|
||||
|
|
@ -932,13 +931,16 @@ impl BasicScene {
|
|||
.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
|
||||
.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 prim = if light_ptr.is_null() && !mi.is_medium_transition() && alpha_tex.is_none() {
|
||||
Primitive::Simple(SimplePrimitive::new(shape_ptr, Ptr::from(&mtl)))
|
||||
} else {
|
||||
|
|
@ -947,7 +949,10 @@ impl BasicScene {
|
|||
arena.alloc(mtl),
|
||||
light_ptr,
|
||||
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::shape::{Shape, ShapeTrait};
|
||||
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::spectra::RGBColorSpace;
|
||||
use shared::utils::Transform;
|
||||
|
|
@ -101,7 +101,7 @@ pub fn create(
|
|||
|
||||
// Upload alpha texture to GPU and check for null texture
|
||||
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 => {
|
||||
LightType::DeltaPosition
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,60 +1,84 @@
|
|||
use crate::Arena;
|
||||
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::spectra::{SampledSpectrum, SampledWavelengths};
|
||||
use std::collections::HashMap;
|
||||
use shared::utils::Ptr;
|
||||
use shared::Float;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct PowerSamplerHost {
|
||||
pub lights: Vec<Arc<Light>>,
|
||||
pub light_to_index: HashMap<usize, usize>,
|
||||
pub alias_table: AliasTable,
|
||||
/// Top-level dispatcher matching the C++ LightSampler::Create.
|
||||
pub fn create_light_sampler(
|
||||
name: &str,
|
||||
lights: &[Arc<Light>],
|
||||
arena: &Arena,
|
||||
) -> LightSampler {
|
||||
let device_lights = lights_to_slice(lights, arena);
|
||||
match name {
|
||||
"uniform" => LightSampler::Uniform(create_uniform(device_lights, lights.len())),
|
||||
"power" => LightSampler::Power(create_power(lights, device_lights, arena)),
|
||||
"bvh" => {
|
||||
log::warn!("BVH light sampler not yet implemented, falling back to power");
|
||||
LightSampler::Power(create_power(lights, device_lights, arena))
|
||||
}
|
||||
_ => {
|
||||
log::error!("Unknown light sampler \"{}\", using power", name);
|
||||
LightSampler::Power(create_power(lights, device_lights, arena))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PowerSamplerHost {
|
||||
pub fn new(lights: &[Arc<Light>]) -> Self {
|
||||
fn lights_to_slice(lights: &[Arc<Light>], arena: &Arena) -> (Ptr<Light>, u32) {
|
||||
if lights.is_empty() {
|
||||
return Self {
|
||||
lights: Vec::new(),
|
||||
light_to_index: HashMap::new(),
|
||||
alias_table: AliasTable::new(&[]),
|
||||
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 {
|
||||
lights: Ptr::null(),
|
||||
lights_len: 0,
|
||||
alias_table: Ptr::null(),
|
||||
};
|
||||
}
|
||||
|
||||
let mut lights_vec = Vec::with_capacity(lights.len());
|
||||
let mut light_to_index = HashMap::with_capacity(lights.len());
|
||||
let mut light_power = Vec::with_capacity(lights.len());
|
||||
|
||||
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();
|
||||
|
||||
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());
|
||||
// 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);
|
||||
|
||||
Self {
|
||||
lights: lights_vec,
|
||||
light_to_index,
|
||||
alias_table,
|
||||
PowerLightSampler {
|
||||
lights,
|
||||
lights_len,
|
||||
alias_table: alias_ptr,
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
// let alias_device = self.alias_table.to_device(arena);
|
||||
//
|
||||
// PowerLightSampler {
|
||||
// lights: lights_ptr,
|
||||
// lights_len: self.lights.len() as u32,
|
||||
// alias_table: alias_device,
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,42 @@
|
|||
use crate::Arena;
|
||||
use crate::core::image::HostImage;
|
||||
use crate::core::material::CreateMaterial;
|
||||
use crate::core::texture::SpectrumTexture;
|
||||
use crate::utils::upload::ArenaUpload;
|
||||
use crate::utils::{FileLoc, TextureParameterDictionary};
|
||||
use crate::Arena;
|
||||
use anyhow::Result;
|
||||
use shared::core::material::Material;
|
||||
use shared::core::spectrum::Spectrum;
|
||||
use shared::core::texture::SpectrumType;
|
||||
use shared::materials::{DiffuseMaterial, DiffuseTransmissionMaterial};
|
||||
use shared::spectra::ConstantSpectrum;
|
||||
use shared::textures::SpectrumConstantTexture;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
impl CreateMaterial for DiffuseMaterial {
|
||||
fn create(
|
||||
_parameters: &TextureParameterDictionary,
|
||||
_normal_map: Option<Arc<HostImage>>,
|
||||
parameters: &TextureParameterDictionary,
|
||||
normal_map: Option<Arc<HostImage>>,
|
||||
_named_materials: &HashMap<String, Material>,
|
||||
_loc: &FileLoc,
|
||||
_arena: &Arena,
|
||||
arena: &Arena,
|
||||
) -> 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,
|
||||
g: Point2f,
|
||||
b: Point2f,
|
||||
illuminant: &DenselySampledSpectrum,
|
||||
rgb_to_spectrum_table: &RGBToSpectrumTable,
|
||||
illuminant: Ptr<DenselySampledSpectrum>,
|
||||
rgb_to_spectrum_table: Ptr<RGBToSpectrumTable>,
|
||||
) -> Self;
|
||||
}
|
||||
|
||||
|
|
@ -22,19 +22,16 @@ impl CreateRGBColorSpace for RGBColorSpace {
|
|||
r: Point2f,
|
||||
g: Point2f,
|
||||
b: Point2f,
|
||||
illuminant: &DenselySampledSpectrum,
|
||||
rgb_to_spectrum_table: &RGBToSpectrumTable,
|
||||
illuminant: Ptr<DenselySampledSpectrum>,
|
||||
rgb_to_spectrum_table: Ptr<RGBToSpectrumTable>,
|
||||
) -> Self {
|
||||
let stdspec = get_spectra_context();
|
||||
let illum_ptr = Ptr::from(illuminant);
|
||||
let illum_spectrum = Spectrum::Dense(illum_ptr);
|
||||
let illum_spectrum = Spectrum::Dense(illuminant);
|
||||
let w_xyz: XYZ = illum_spectrum.to_xyz(&stdspec);
|
||||
let w = w_xyz.xy();
|
||||
|
||||
let r_xyz = XYZ::from_xyy(r, Some(1.0));
|
||||
let g_xyz = XYZ::from_xyy(g, Some(1.0));
|
||||
let b_xyz = XYZ::from_xyy(b, Some(1.0));
|
||||
|
||||
let rgb_values = [
|
||||
[r_xyz.x(), g_xyz.x(), b_xyz.x()],
|
||||
[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 xyz_from_rgb = rgb * SquareMatrix::diag(&[c.r, c.g, c.b]);
|
||||
let rgb_from_xyz = xyz_from_rgb.inverse().expect("singular");
|
||||
|
||||
RGBColorSpace {
|
||||
r,
|
||||
g,
|
||||
b,
|
||||
w,
|
||||
illuminant: illum_ptr,
|
||||
rgb_to_spectrum_table: Ptr::from(rgb_to_spectrum_table),
|
||||
illuminant,
|
||||
rgb_to_spectrum_table,
|
||||
xyz_from_rgb,
|
||||
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::{DenselySampledSpectrum, DeviceStandardColorSpaces, RGBColorSpace};
|
||||
use shared::Ptr;
|
||||
use std::sync::{Arc, OnceLock, LazyLock};
|
||||
use std::sync::{Arc, LazyLock, OnceLock};
|
||||
|
||||
pub mod colorspace;
|
||||
pub mod data;
|
||||
|
|
@ -21,10 +21,6 @@ pub static CIE_Z_DATA: LazyLock<DenselySampledSpectrum> =
|
|||
pub static CIE_D65_DATA: LazyLock<DenselySampledSpectrum> =
|
||||
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 {
|
||||
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(|| {
|
||||
let illum = get_d65_illuminant_buffer();
|
||||
let r = Point2f::new(0.64, 0.33);
|
||||
let g = Point2f::new(0.3, 0.6);
|
||||
let b = Point2f::new(0.15, 0.06);
|
||||
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(|| {
|
||||
let illum = get_d65_illuminant_buffer();
|
||||
let r = Point2f::new(0.680, 0.320);
|
||||
let g = Point2f::new(0.265, 0.690);
|
||||
let b = Point2f::new(0.150, 0.060);
|
||||
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(|| {
|
||||
let illum = get_d65_illuminant_buffer();
|
||||
let r = Point2f::new(0.708, 0.292);
|
||||
let g = Point2f::new(0.170, 0.797);
|
||||
let b = Point2f::new(0.131, 0.046);
|
||||
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(|| {
|
||||
let illum = get_d65_illuminant_buffer();
|
||||
let r = Point2f::new(0.7347, 0.2653);
|
||||
let g = Point2f::new(0.0000, 1.0000);
|
||||
let b = Point2f::new(0.0001, -0.0770);
|
||||
let table_ptr = Ptr::from(&ACES_TABLE);
|
||||
Arc::new(RGBColorSpace::new(r, g, b, &illum, &table_ptr))
|
||||
let table_ptr = Ptr::from(&*ACES_TABLE);
|
||||
let illum_ptr = Ptr::from(&*D65_ILLUMINANT);
|
||||
|
||||
Arc::new(RGBColorSpace::new(r, g, b, illum_ptr, table_ptr))
|
||||
});
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -141,4 +141,3 @@ pub fn default_colorspace_ref() -> &'static RGBColorSpace {
|
|||
pub fn default_illuminant() -> Spectrum {
|
||||
Spectrum::Dense(default_colorspace().illuminant)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -915,7 +915,12 @@ impl TextureParameterDictionary {
|
|||
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 {
|
||||
SpectrumType::Illuminant => {
|
||||
Spectrum::RGBIlluminant(RGBIlluminantSpectrum::new(cs, rgb))
|
||||
|
|
|
|||
Loading…
Reference in a new issue