pbrt/src/lights/goniometric.rs

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)
}
}