use super::{CameraBase, CameraRay, CameraTrait}; use crate::camera; use crate::core::film::FilmTrait; use crate::core::filter::FilterTrait; use crate::core::pbrt::Float; use crate::core::sampler::CameraSample; use crate::geometry::{ Bounds2f, Point2f, Point3f, Ray, RayDifferential, Vector2f, Vector3f, VectorLike, }; use crate::spectra::{SampledSpectrum, SampledWavelengths}; use crate::utils::sampling::sample_uniform_disk_concentric; use crate::utils::transform::Transform; #[derive(Debug)] pub struct PerspectiveCamera { pub base: CameraBase, pub screen_from_camera: Transform, pub camera_from_raster: Transform, pub raster_from_screen: Transform, pub screen_from_raster: Transform, pub lens_radius: Float, pub focal_distance: Float, pub dx_camera: Vector3f, pub dy_camera: Vector3f, pub cos_total_width: Float, } impl PerspectiveCamera { pub fn new( base: CameraBase, screen_from_camera: &Transform, screen_window: Bounds2f, lens_radius: Float, focal_distance: Float, ) -> Self { let ndc_from_screen: Transform = Transform::scale( 1. / (screen_window.p_max.x() - screen_window.p_min.x()), 1. / (screen_window.p_max.y() - screen_window.p_min.y()), 1., ) * Transform::translate(Vector3f::new( -screen_window.p_min.x(), -screen_window.p_max.y(), 0., )); let raster_from_ndc = Transform::scale( base.film.full_resolution().x() as Float, -base.film.full_resolution().y() as Float, 1., ); let raster_from_screen = raster_from_ndc * ndc_from_screen; let screen_from_raster = raster_from_screen.inverse(); let camera_from_raster = screen_from_camera.inverse() * screen_from_raster; let dx_camera = camera_from_raster.apply_to_point(Point3f::new(1., 0., 0.)) - camera_from_raster.apply_to_point(Point3f::new(0., 0., 0.)); let dy_camera = camera_from_raster.apply_to_point(Point3f::new(0., 1., 0.)) - camera_from_raster.apply_to_point(Point3f::new(0., 0., 0.)); let radius = base.film.get_filter().radius(); let p_corner = Point3f::new(-radius.x(), -radius.y(), 0.); let w_corner_camera = (camera_from_raster.apply_to_point(p_corner) - Point3f::new(0., 0., 0.)).normalize(); let cos_total_width = w_corner_camera.z(); Self { base, screen_from_camera: *screen_from_camera, camera_from_raster, raster_from_screen, screen_from_raster, lens_radius, focal_distance, dx_camera, dy_camera, cos_total_width, } } } impl CameraTrait for PerspectiveCamera { fn base(&self) -> &CameraBase { &self.base } fn init_metadata(&self, metadata: &mut crate::image::ImageMetadata) { self.base.init_metadata(metadata) } fn generate_ray( &self, sample: CameraSample, _lambda: &SampledWavelengths, ) -> Option { // Compute raster and camera sample positions let p_film = Point3f::new(sample.p_film.x(), sample.p_film.y(), 0.); let p_camera = self.camera_from_raster.apply_to_point(p_film); let p_vector = p_camera - Point3f::new(0., 0., 0.); let mut r = Ray::new( Point3f::new(0., 0., 0.), p_vector.normalize(), Some(self.sample_time(sample.time)), self.base().medium.clone(), ); // Modify ray for depth of field if self.lens_radius > 0. { // Sample point on lens let p_lens = self.lens_radius * Vector2f::from(sample_uniform_disk_concentric(sample.p_lens)); // Compute point on plane of focus let ft = self.focal_distance / r.d.z(); let p_focus = r.at(ft); // Update ray for effect of lens r.o = Point3f::new(p_lens.x(), p_lens.y(), 0.); r.d = (p_focus - r.o).normalize(); } let ray = self.render_from_camera(&r, &mut None); Some(CameraRay { ray, weight: SampledSpectrum::default(), }) } }