diff --git a/src/camera.rs b/src/camera.rs index 0047283..274059a 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,6 +1,6 @@ -use std::cmp::max; +use std::f32::consts::PI; -use log::info; +use log::{info, warn}; use crate::{ objects::{hit::Hit, traits::Hittable}, @@ -8,50 +8,129 @@ use crate::{ vec3::{Colour, Vec3}, }; +#[derive(Debug)] pub struct Camera { + // output image_width: u32, image_height: u32, + + // raytracing anti_alias_rate: u32, max_depth: u32, - center: Vec3, pixel00_loc: Vec3, pixel_delta_u: Vec3, pixel_delta_v: Vec3, + + // camera + dirty: bool, + fov: f32, + look_from: Vec3, + look_at: Vec3, + vup: Vec3, + + // camera helpers + u: Vec3, + v: Vec3, + w: Vec3, +} + +// FIXME: kinda out of place in this file. +fn deg_to_rad(deg: f32) -> f32 { + deg * PI / 180. } impl Camera { - pub fn new(aspect_ratio: f32, image_width: u32, anti_alias_rate: u32, max_depth: u32) -> Self { - let image_height = max(1, (image_width as f32 / aspect_ratio) as u32); - - //camera - let focal_length = 1.; - let viewport_height = 2.; - let viewport_width = viewport_height * (image_width as f32 / image_height as f32); - let camera_center = Vec3::nil(); - - //viewport - let viewport_u = Vec3::new(viewport_width as f32, 0., 0.); - let viewport_v = Vec3::new(0., -viewport_height as f32, 0.); - - let pixel_delta_u = viewport_u / image_width as f32; - let pixel_delta_v = viewport_v / image_height as f32; - - let pixel00_loc = - camera_center - Vec3::new(0., 0., focal_length) - viewport_u / 2 - viewport_v / 2; - + pub fn new(image_width: u32, image_height: u32) -> Self { Self { image_width: image_width, image_height: image_height, - anti_alias_rate: anti_alias_rate, - max_depth: max_depth, - center: camera_center, - pixel00_loc: pixel00_loc, - pixel_delta_u: pixel_delta_u, - pixel_delta_v: pixel_delta_v, + anti_alias_rate: 1, + max_depth: 10, + dirty: true, + fov: 60., + pixel00_loc: Vec3::nil(), + pixel_delta_u: Vec3::nil(), + pixel_delta_v: Vec3::nil(), + look_from: Vec3::nil(), + look_at: Vec3::new(0., 0., -1.), + vup: Vec3::new(0., 1., 0.), + u: Vec3::nil(), + v: Vec3::nil(), + w: Vec3::nil(), } } - pub fn render(&self, hittables: &Vec) { + fn init(&mut self) { + // camera + let focal_length = (self.look_from - self.look_at).length(); + 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 * focal_length; + let viewport_width = viewport_height * (self.image_width as f32 / self.image_height as f32); + let viewport_u = viewport_width * self.u; + let viewport_v = viewport_height * -self.v; + + // variables + self.pixel_delta_u = viewport_u / self.image_width as f32; + self.pixel_delta_v = viewport_v / self.image_height as f32; + self.pixel00_loc = + self.look_from - (focal_length * self.w) - viewport_u / 2. - viewport_v / 2.; + self.dirty = false; + } + + pub fn set_fov(&mut self, fov: f32) { + if self.fov != fov { + self.fov = fov; + self.dirty = true; + } + } + + pub fn set_max_depth(&mut self, depth: u32) { + if self.max_depth != depth { + self.max_depth = depth; + 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 render(&mut self, hittables: &Vec) { + if self.dirty { + self.init() + } + warn!("{}", format!("{self:#?}")); + let mut imgbuf = image::ImageBuffer::new(self.image_width, self.image_height); // render @@ -67,8 +146,8 @@ impl Camera { 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_dir = pixel_loc - self.center; - let r = Ray::new(self.center, ray_dir); + let ray_dir = pixel_loc - self.look_from; + let r = Ray::new(self.look_from, ray_dir); pixel_colour += self.ray_colour(&hittables, &r, self.max_depth); } } diff --git a/src/main.rs b/src/main.rs index 908463e..2bca79d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,6 +37,12 @@ fn main() { world.push(Sphere::xyz(1., 0., -1.0, 0.5, right.clone())); world.push(Sphere::xyz(0., 0.7, -0.4, 0.2, blue.clone())); - let c = Camera::new(16. / 9., 1920, 3, 50); + let mut c = Camera::new(400, 300); + // let mut c = Camera::new(1920, 1080); + c.set_fov(20.); + c.set_anti_alias_rate(2); + c.set_max_depth(90); + c.set_look_from(Vec3::new(-2., 2., 1.)); + c.set_look_at(Vec3::new(0., 0., -1.)); c.render(&world); } diff --git a/src/vec3.rs b/src/vec3.rs index e70b723..dcb6d10 100644 --- a/src/vec3.rs +++ b/src/vec3.rs @@ -6,7 +6,7 @@ use std::{ ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}, }; -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub struct Vec3 { r: f32, g: f32,