#[cfg(feature = "bytemuck")] pub mod impl_bytemuck; pub mod color; mod camera; mod geometry; mod sampling; use core::f32; pub use camera::Camera; pub use geometry::{Point, Transform, Vector, shapes::Shape}; use color::Rgba; use geometry::{ Ray, shapes::{Hit, Hittable}, }; use sampling::Sampler; pub struct Scene { camera: Camera, contents: Vec, } impl Scene { pub fn new(camera: Camera, contents: Vec) -> Self { Self { camera, contents } } pub fn render(&self, width: u32, height: u32, samples: u32) -> Box<[Rgba]> { let mut sampler = Sampler::new(width, height, samples, 0); let mut data = Box::new_uninit_slice(width as usize * height as usize); let w = width as f32; let h = height as f32; let aspect_ratio = h / w; for (i, pixel) in data.iter_mut().enumerate() { let bx = i as u32 % width; let by = i as u32 / width; let mut rgb = Vector::ZERO; for mut pixel_samples in sampler.pixel(bx, by) { let (x_offset, y_offset) = pixel_samples.get_2d(); let x = bx as f32 + x_offset; let y = by as f32 + y_offset; let cx = 2.0 * x / w - 1.0; let cy = (1.0 - 2.0 * y / h) * aspect_ratio; if let Some(h) = self.intersect(self.camera.ray(cx, cy)) { rgb += h.normal.normalize() * 0.5 + Vector::new(0.5, 0.5, 0.5); } } rgb /= samples as f32; pixel.write(Rgba::new(rgb.x, rgb.y, rgb.z, 1.0)); } unsafe { data.assume_init() } } fn intersect(&self, ray: Ray) -> Option { let mut hit: Option = None; for shape in &self.contents { let t_max = hit.as_ref().map(|h| h.t).unwrap_or(f32::INFINITY); if let Some(h) = shape.intersect(&ray, t_max) { hit = Some(h); } } hit } }