Files
raytracing/src/camera.rs
2026-04-16 14:04:55 +02:00

106 lines
3.5 KiB
Rust

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<T: Hittable>(&self, hittables: &Vec<T>) {
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<T: Hittable>(&self, hittables: &Vec<T>, 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)
}
}