use std::f32::consts::PI; use crate::{ray::Ray, vec3::Vec3}; #[derive(Debug)] pub struct Camera { // raytracing pub anti_alias_rate: u32, pub pixel00_loc: Vec3, pub pixel_delta_u: Vec3, pub pixel_delta_v: Vec3, // camera dirty: bool, pub fov: f32, pub look_from: Vec3, pub look_at: Vec3, vup: Vec3, pub defocus_angle: f32, pub focus_dist: f32, // camera helpers u: Vec3, v: Vec3, w: Vec3, defocus_disk_u: Vec3, defocus_disk_v: Vec3, } // FIXME: kinda out of place in this file. fn deg_to_rad(deg: f32) -> f32 { deg * PI / 180. } impl Camera { pub fn new() -> Self { Self { anti_alias_rate: 1, dirty: true, fov: 60., pixel00_loc: Vec3::default(), pixel_delta_u: Vec3::default(), pixel_delta_v: Vec3::default(), look_from: Vec3::nil(), look_at: Vec3::new(0., 0., -1.), vup: Vec3::new(0., 1., 0.), defocus_angle: 0., focus_dist: 10., u: Vec3::default(), v: Vec3::default(), w: Vec3::default(), defocus_disk_u: Vec3::default(), defocus_disk_v: Vec3::default(), } } pub fn new_full( anti_alias_rate: u32, fov: f32, look_from: Vec3, look_at: Vec3, vup: Vec3, defocus_angle: f32, focus_dist: f32, ) -> Self { Self { anti_alias_rate, pixel00_loc: Vec3::default(), pixel_delta_u: Vec3::default(), pixel_delta_v: Vec3::default(), dirty: true, fov, look_from, look_at, vup, defocus_angle, focus_dist, u: Vec3::default(), v: Vec3::default(), w: Vec3::default(), defocus_disk_u: Vec3::default(), defocus_disk_v: Vec3::default(), } } pub fn init(&mut self, w: f32, hi: f32) { if !self.dirty { return; } // camera let theta = deg_to_rad(self.fov); let h = (theta / 2.).tan(); self.w = (self.look_from - self.look_at).get_unit(); self.u = self.vup.cross(&self.w).get_unit(); self.v = self.w.cross(&self.u); // viewport let viewport_height = 2. * h * self.focus_dist; let viewport_width = viewport_height * (w / hi); let viewport_u = viewport_width * self.u; let viewport_v = viewport_height * -self.v; // variables self.pixel_delta_u = viewport_u / w; self.pixel_delta_v = viewport_v / hi; self.pixel00_loc = self.look_from - (self.focus_dist * self.w) - viewport_u / 2. - viewport_v / 2.; self.dirty = false; if self.defocus_angle != 0. { let defocus_radius = self.focus_dist * deg_to_rad(self.defocus_angle / 2.).tan(); self.defocus_disk_u = self.u * defocus_radius; self.defocus_disk_v = self.v * defocus_radius; } } pub fn get_ray(&self, pixel_tl: Vec3, x: u32, y: u32) -> Ray { let pixel_loc = pixel_tl + (x * self.pixel_delta_u / (self.anti_alias_rate + 1) as f32) + (y * self.pixel_delta_v / (self.anti_alias_rate + 1) as f32); let ray_orig = if self.defocus_angle > 0. { self.defocus_disk_sample() } else { self.look_from }; let ray_dir = pixel_loc - ray_orig; Ray::new(ray_orig, ray_dir) } pub fn set_fov(&mut self, fov: f32) { if self.fov != fov { self.fov = fov; self.dirty = true; } } pub fn set_anti_alias_rate(&mut self, rate: u32) { if self.anti_alias_rate != rate { self.anti_alias_rate = rate; self.dirty = true; } } pub fn set_look_from(&mut self, look_from: Vec3) { if self.look_from != look_from { self.look_from = look_from; self.dirty = true; } } pub fn set_look_at(&mut self, look_at: Vec3) { if self.look_at != look_at { self.look_at = look_at; self.dirty = true; } } pub fn set_vup(&mut self, vup: Vec3) { if self.vup != vup { self.vup = vup; self.dirty = true; } } pub fn add_defocus_blur(&mut self, defocus_angle: f32, focus_dist: f32) { if self.defocus_angle != defocus_angle || self.focus_dist != focus_dist { self.defocus_angle = defocus_angle; self.focus_dist = focus_dist; self.dirty = true; } } pub fn defocus_disk_sample(&self) -> Vec3 { let p = Vec3::random_in_unit_disk(); self.look_from + (p.x() * self.defocus_disk_u) + (p.y() * self.defocus_disk_v) } }