From b9372cde62332ebf9b1f788f78d708d7ae5af786 Mon Sep 17 00:00:00 2001 From: djairoh Date: Wed, 15 Apr 2026 14:09:25 +0200 Subject: [PATCH] ft: 7: camera class --- src/camera.rs | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 70 +++++------------------------------------- 2 files changed, 93 insertions(+), 62 deletions(-) create mode 100644 src/camera.rs diff --git a/src/camera.rs b/src/camera.rs new file mode 100644 index 0000000..1a7f8ca --- /dev/null +++ b/src/camera.rs @@ -0,0 +1,85 @@ +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, + center: Vec3, + pixel00_loc: Vec3, + pixel_delta_u: Vec3, + pixel_delta_v: Vec3, +} + +impl Camera { + pub fn new(aspect_ratio: f32, image_width: u32) -> Camera { + 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 viewport_upper_left = + camera_center - Vec3::new(0., 0., focal_length) - viewport_u / 2 - viewport_v / 2; + let pixel00_loc = viewport_upper_left + 0.5 * (pixel_delta_u + pixel_delta_v); + + Camera { + image_width: image_width, + image_height: image_height, + 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_center = + self.pixel00_loc + (i * self.pixel_delta_u) + (j * self.pixel_delta_v); + let ray_dir = pixel_center - self.center; + let r = Ray::new(self.center, ray_dir); + + let pixel = imgbuf.get_pixel_mut(i, j); + *pixel = self.ray_colour(&hittables, &r).output(); + } + } + + info!("Writing image file..."); + imgbuf.save("output.png").unwrap(); + } + + fn ray_colour(&self, hittables: &Vec, r: &Ray) -> Colour { + let closest = Hit::hit_list(hittables, r); + if let Some(hit) = closest { + let n = hit.n(); + return 0.5 * Colour::new(n.x() + 1., n.y() + 1., n.z() + 1.); + } + + // 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) + } +} diff --git a/src/main.rs b/src/main.rs index fd03b8c..c3e9028 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,78 +1,24 @@ #![feature(core_float_math)] +mod camera; mod objects; mod ray; mod vec3; -use core::f32; -use std::cmp::max; - -use crate::objects::hit::Hit; +use crate::camera::Camera; use crate::objects::sphere::Sphere; -use crate::objects::traits::Hittable; use crate::ray::Ray; -use crate::vec3::{Colour, Vec3}; -use log::info; +use crate::vec3::Vec3; use pretty_env_logger; -fn ray_colour(hittables: &Vec, r: &Ray) -> Colour { - let closest = Hit::hit_list(hittables, r); - if let Some(hit) = closest { - let n = hit.n(); - return 0.5 * Colour::new(n.x() + 1., n.y() + 1., n.z() + 1.); - } - - // 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) -} - fn main() { pretty_env_logger::init(); // setup objects - let mut objects = vec![Sphere::new(Vec3::new(0., 0., -1.), 0.5)]; - objects.push(Sphere::new(Vec3::new(0., 0., -0.1), 0.01)); - objects.push(Sphere::new(Vec3::new(0., -100.5, -1.), 100.)); + let mut world = vec![Sphere::new(Vec3::new(0., 0., -1.), 0.5)]; + world.push(Sphere::new(Vec3::new(0., 0., -0.1), 0.01)); + world.push(Sphere::new(Vec3::new(0., -100.5, -1.), 100.)); - let aspect_ratio = 16. / 9.; - let image_width = 400; - 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 viewport_upper_left = - camera_center - Vec3::new(0., 0., focal_length) - viewport_u / 2 - viewport_v / 2; - let pixel00_loc = viewport_upper_left + 0.5 * (pixel_delta_u + pixel_delta_v); - - let mut imgbuf = image::ImageBuffer::new(image_width, image_height); - - // render - for j in 0..image_height { - info!("{}\tScanlines remaining.", (image_height - j)); - for i in 0..image_width { - let pixel_center = pixel00_loc + (i * pixel_delta_u) + (j * pixel_delta_v); - let ray_dir = pixel_center - camera_center; - let r = Ray::new(camera_center, ray_dir); - - let pixel = imgbuf.get_pixel_mut(i, j); - *pixel = ray_colour(&objects, &r).output(); - } - } - - info!("Writing image file..."); - imgbuf.save("output.png").unwrap(); + let c = Camera::new(16. / 9., 400); + c.render(&world); }