179 lines
4.8 KiB
Rust
179 lines
4.8 KiB
Rust
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)
|
|
}
|
|
}
|