use std::cmp::max; use log::info; use crate::{ objects::{hit::Hit, traits::Hittable}, ray::Ray, vec3::{Colour, Vec3}, }; pub struct Camera { image_width: u32, image_height: u32, anti_alias_rate: u32, max_depth: u32, center: Vec3, pixel00_loc: Vec3, pixel_delta_u: Vec3, pixel_delta_v: Vec3, } 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; 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, } } pub fn render(&self, hittables: &Vec) { let mut imgbuf = image::ImageBuffer::new(self.image_width, self.image_height); // render for j in 0..self.image_height { info!("{}\tScanlines remaining.", (self.image_height - j)); for i in 0..self.image_width { let pixel_tl = self.pixel00_loc + (i * self.pixel_delta_u) + (j * self.pixel_delta_v); let mut pixel_colour = Colour::nil(); for y in 1..(self.anti_alias_rate + 1) { for x in 1..(self.anti_alias_rate + 1) { 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); pixel_colour += self.ray_colour(&hittables, &r, self.max_depth); } } let pixel = imgbuf.get_pixel_mut(i, j); *pixel = (pixel_colour / (self.anti_alias_rate * self.anti_alias_rate) as f32).output(); } } info!("Writing image file..."); imgbuf.save("output.png").unwrap(); } fn ray_colour(&self, hittables: &Vec, r: &Ray, depth: u32) -> Colour { if depth <= 0 { return Colour::nil(); } let closest = Hit::hit_list(hittables, r); if let Some(hit) = closest { if let Some((scattered, att)) = hit.mat().scatter(&hit, r) { return att * self.ray_colour(hittables, &scattered, depth - 1); } return Colour::nil(); } // background let unit_dir = r.dir().get_unit(); let a = 0.5 * (unit_dir.y() + 1.); (1.0 - a) * Colour::new(1., 1., 1.) + a * Colour::new(0.5, 0.7, 1.0) } }