pbrt/src/camera/perspective.rs

123 lines
4.3 KiB
Rust

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<Float>,
pub camera_from_raster: Transform<Float>,
pub raster_from_screen: Transform<Float>,
pub screen_from_raster: Transform<Float>,
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<Float>,
screen_window: Bounds2f,
lens_radius: Float,
focal_distance: Float,
) -> Self {
let ndc_from_screen: Transform<Float> = 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<CameraRay> {
// 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(),
})
}
}