76 lines
2 KiB
Rust
76 lines
2 KiB
Rust
#[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<Shape>,
|
|
}
|
|
|
|
impl Scene {
|
|
pub fn new(camera: Camera, contents: Vec<Shape>) -> 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<Hit> {
|
|
let mut hit: Option<Hit> = 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
|
|
}
|
|
}
|