127 lines
3.9 KiB
Rust
127 lines
3.9 KiB
Rust
use crate::Float;
|
|
use crate::core::color::RGB;
|
|
use crate::core::geometry::{
|
|
Bounds2f, Bounds3f, Normal3f, Point2f, Point2i, Point3f, Ray, Vector3f, VectorLike, cos_theta,
|
|
};
|
|
use crate::core::image::DeviceImage;
|
|
use crate::core::light::{
|
|
LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait, LightType,
|
|
};
|
|
use crate::core::medium::MediumInterface;
|
|
use crate::core::spectrum::SpectrumTrait;
|
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
|
use crate::utils::math::{radians, square};
|
|
use crate::utils::ptr::DevicePtr;
|
|
use crate::{
|
|
spectra::{RGBColorSpace, RGBIlluminantSpectrum},
|
|
utils::{Transform, sampling::DevicePiecewiseConstant2D},
|
|
};
|
|
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub struct ProjectionLight {
|
|
pub base: LightBase,
|
|
pub scale: Float,
|
|
pub hither: Float,
|
|
pub screen_bounds: Bounds2f,
|
|
pub screen_from_light: Transform,
|
|
pub light_from_screen: Transform,
|
|
pub a: Float,
|
|
pub image: DevicePtr<DeviceImage>,
|
|
pub distrib: DevicePtr<DevicePiecewiseConstant2D>,
|
|
pub image_color_space: DevicePtr<RGBColorSpace>,
|
|
}
|
|
|
|
impl ProjectionLight {
|
|
pub fn i(&self, w: Vector3f, lambda: SampledWavelengths) -> SampledSpectrum {
|
|
if w.z() < self.hither {
|
|
return SampledSpectrum::new(0.);
|
|
}
|
|
let ps = self.screen_from_light.apply_to_point(w.into());
|
|
if !self.screen_bounds.contains(Point2f::new(ps.x(), ps.y())) {
|
|
return SampledSpectrum::new(0.);
|
|
}
|
|
let uv = Point2f::from(self.screen_bounds.offset(&Point2f::new(ps.x(), ps.y())));
|
|
let mut rgb = RGB::default();
|
|
for c in 0..3 {
|
|
rgb[c] = self.image.lookup_nearest_channel(uv, c as i32);
|
|
}
|
|
let s = RGBIlluminantSpectrum::new(&*self.image_color_space, rgb.clamp_zero());
|
|
self.scale * s.sample(&lambda)
|
|
}
|
|
}
|
|
|
|
impl LightTrait for ProjectionLight {
|
|
fn base(&self) -> &LightBase {
|
|
&self.base
|
|
}
|
|
|
|
fn sample_li(
|
|
&self,
|
|
_ctx: &LightSampleContext,
|
|
_u: Point2f,
|
|
_lambda: &SampledWavelengths,
|
|
_allow_incomplete_pdf: bool,
|
|
) -> Option<LightLiSample> {
|
|
todo!()
|
|
}
|
|
|
|
fn pdf_li(
|
|
&self,
|
|
_ctx: &LightSampleContext,
|
|
_wi: Vector3f,
|
|
_allow_incomplete_pdf: bool,
|
|
) -> Float {
|
|
todo!()
|
|
}
|
|
|
|
fn l(
|
|
&self,
|
|
_p: Point3f,
|
|
_n: Normal3f,
|
|
_uv: Point2f,
|
|
_w: Vector3f,
|
|
_lambda: &SampledWavelengths,
|
|
) -> SampledSpectrum {
|
|
todo!()
|
|
}
|
|
|
|
fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
todo!()
|
|
}
|
|
|
|
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
|
|
let mut sum = SampledSpectrum::new(0.);
|
|
for y in 0..self.image.resolution.y() {
|
|
for x in 0..self.image.resolution.x() {
|
|
let ps = self.screen_bounds.lerp(Point2f::new(
|
|
(x as Float + 0.5) / self.image.resolution.x() as Float,
|
|
(y as Float + 0.5) / self.image.resolution.y() as Float,
|
|
));
|
|
let w_raw = Vector3f::from(self.light_from_screen.apply_to_point(Point3f::new(
|
|
ps.x(),
|
|
ps.y(),
|
|
0.,
|
|
)));
|
|
let w = w_raw.normalize();
|
|
let dwda = cos_theta(w).powi(3);
|
|
let mut rgb = RGB::default();
|
|
for c in 0..3 {
|
|
rgb[c] = self.image.get_channel(Point2i::new(x, y), c as i32);
|
|
}
|
|
|
|
let s = RGBIlluminantSpectrum::new(&*self.image_color_space, rgb.clamp_zero());
|
|
sum += s.sample(&lambda) * dwda;
|
|
}
|
|
}
|
|
self.scale * self.a * sum / (self.image.resolution.x() * self.image.resolution.y()) as Float
|
|
}
|
|
|
|
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {
|
|
todo!()
|
|
}
|
|
|
|
fn bounds(&self) -> Option<LightBounds> {
|
|
todo!()
|
|
}
|
|
}
|