174 lines
5.8 KiB
Rust
174 lines
5.8 KiB
Rust
use crate::core::image::{Image, ImageIO, ImageMetadata};
|
|
use crate::core::light::{CreateLight, lookup_spectrum};
|
|
use crate::core::spectrum::spectrum_to_photometric;
|
|
use crate::core::texture::FloatTexture;
|
|
use crate::utils::sampling::PiecewiseConstant2D;
|
|
use crate::utils::{Arena, FileLoc, ParameterDictionary, resolve_filename};
|
|
use shared::core::image::ImageBase;
|
|
use shared::core::light::{Light, LightBase, LightType};
|
|
use shared::core::medium::{Medium, MediumInterface};
|
|
use shared::core::spectrum::Spectrum;
|
|
use shared::core::texture::SpectrumType;
|
|
use shared::lights::GoniometricLight;
|
|
use shared::spectra::RGBColorSpace;
|
|
use shared::utils::Transform;
|
|
use shared::utils::containers::Array2D;
|
|
|
|
impl CreateLight for GoniometricLight {
|
|
fn new(
|
|
render_from_light: Transform,
|
|
medium_interface: MediumInterface,
|
|
le: Spectrum,
|
|
scale: shared::Float,
|
|
shape: Option<shared::utils::Ptr<Shape>>,
|
|
alpha: Option<shared::utils::Ptr<FloatTexture>>,
|
|
image: Option<shared::utils::Ptr<Image>>,
|
|
image_color_space: Option<shared::utils::Ptr<RGBColorSpace>>,
|
|
two_sided: Option<bool>,
|
|
fov: Option<shared::Float>,
|
|
) -> Self {
|
|
let base = LightBase::new(
|
|
LightType::DeltaPosition,
|
|
render_from_light,
|
|
medium_interface,
|
|
);
|
|
|
|
let iemit = lookup_spectrum(le);
|
|
let d = image.unwrap().get_sampling_distribution_uniform();
|
|
let distrib = PiecewiseConstant2D::new_with_data(d);
|
|
Self {
|
|
base,
|
|
iemit,
|
|
scale,
|
|
image,
|
|
distrib,
|
|
}
|
|
}
|
|
|
|
fn create(
|
|
arena: &mut Arena,
|
|
render_from_light: Transform,
|
|
medium: Medium,
|
|
params: &ParameterDictionary,
|
|
loc: &FileLoc,
|
|
shape: &Shape,
|
|
alpha_text: &FloatTexture,
|
|
colorspace: Option<&RGBColorSpace>,
|
|
) -> Light {
|
|
let i = params.get_one_spectrum(
|
|
"I",
|
|
colorspace.unwrap().illuminant,
|
|
SpectrumType::Illuminant,
|
|
);
|
|
let lemit_data = lookup_spectrum(&i_spectrum_def);
|
|
let sc = params.get_one_float("scale", 1.);
|
|
let filename = resolve_filename(params.get_one_string("filename", ""));
|
|
let mut image: Option<Image> = None;
|
|
|
|
if filename.is_empty() {
|
|
println!(
|
|
"{}: Both \"L\" and \"filename\" specified for DiffuseAreaLight.",
|
|
loc
|
|
)
|
|
} else {
|
|
let im = Image::read(filename, None).expect("Could not load image");
|
|
let loaded_img: Image = im.image;
|
|
let res = loaded_img.resolution();
|
|
let metadata: ImageMetadata = im.metadata;
|
|
if loaded_img.has_any_infinite_pixels() {
|
|
panic!(
|
|
"{:?}: Image '{}' has NaN pixels. Not suitable for light.",
|
|
loc, filename
|
|
);
|
|
}
|
|
if res.x() != res.y() {
|
|
panic!(
|
|
"{:?}: Image resolution ({}, {}) is non square. Unlikely that this is an equal area map.",
|
|
loc,
|
|
res.x(),
|
|
res.y(),
|
|
y()
|
|
);
|
|
}
|
|
let rgb_desc = loaded_img.get_channel_desc(&["R", "G", "B"]);
|
|
let y_desc = loaded_img.get_channel_desc(&["Y"]);
|
|
if let Ok(rgb) = rgb_desc {
|
|
if y_desc.is_ok() {
|
|
panic!(
|
|
"{:?}: Image '{}' has both RGB and Y channels. Ambiguous.",
|
|
loc, filename
|
|
);
|
|
}
|
|
|
|
let mut y_pixels = Vec::with_capacity((res.x * res.y) as usize);
|
|
|
|
for y in 0..res.y {
|
|
for x in 0..res.x {
|
|
let r = loaded_img.get_channel(Point2i::new(x, y), 0);
|
|
let g = loaded_img.get_channel(Point2i::new(x, y), 1);
|
|
let b = loaded_img.get_channel(Point2i::new(x, y), 2);
|
|
|
|
y_pixels.push((r + g + b) / 3.0);
|
|
}
|
|
}
|
|
|
|
loaded_img = Some(Image::new(
|
|
PixelFormat::F32,
|
|
res,
|
|
&["Y"],
|
|
ColorEncoding::Linear,
|
|
));
|
|
} else if y_desc.is_ok() {
|
|
image = Some(loaded_img);
|
|
} else {
|
|
panic!(
|
|
"{:?}: Image '{}' has neither RGB nor Y channels.",
|
|
loc, filename
|
|
);
|
|
}
|
|
}
|
|
|
|
scale /= spectrum_to_photometric(&lemit_data);
|
|
let phi_v = params.get_one_float("power", -1.0);
|
|
|
|
if phi_v > 0.0 {
|
|
if let Some(ref img) = image {
|
|
let mut sum_y = 0.0;
|
|
let res = img.resolution();
|
|
|
|
for y in 0..res.y {
|
|
for x in 0..res.x {
|
|
sum_y += img.get_channel(Point2i::new(x, y), 0);
|
|
}
|
|
}
|
|
|
|
let k_e = 4.0 * PI * sum_y / (res.x * res.y) as Float;
|
|
scale *= phi_v / k_e;
|
|
}
|
|
}
|
|
|
|
let swap_yz: [Float; 16] = [
|
|
1., 0., 0., 0., 0., 0., 1., 0., 0., 1., 0., 0., 0., 0., 0., 1.,
|
|
];
|
|
let t = Transform::from_flat(swap_yz);
|
|
let final_render_from_light = render_from_light * t;
|
|
|
|
let d: Array2D<Float> = image.get_sampling_distribution();
|
|
distrib = PiecewiseConstant2D::new_with_data(d);
|
|
|
|
let specific = GoniometricLight::new(
|
|
render_from_light,
|
|
medium_interface,
|
|
le,
|
|
scale,
|
|
None,
|
|
None,
|
|
Some(image),
|
|
None,
|
|
None,
|
|
None,
|
|
);
|
|
|
|
Light::Goniometric(specific)
|
|
}
|
|
}
|