diff --git a/raytracer/Cargo.lock b/raytracer/Cargo.lock index 7379194..bb02073 100644 --- a/raytracer/Cargo.lock +++ b/raytracer/Cargo.lock @@ -2,21 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "approx" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f2a05fd1bd10b2527e20a2cd32d8873d115b8b39fe219ee25f42a8aca6ba278" -dependencies = [ - "num-traits", -] - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - [[package]] name = "bit_field" version = "0.10.3" @@ -35,16 +20,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" -[[package]] -name = "cgmath" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317" -dependencies = [ - "approx", - "num-traits", -] - [[package]] name = "libc" version = "0.2.176" @@ -57,15 +32,6 @@ version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - [[package]] name = "proc-macro2" version = "1.0.101" @@ -108,7 +74,6 @@ dependencies = [ name = "raytracer" version = "0.1.0" dependencies = [ - "cgmath", "libc", "log", "uefi", diff --git a/raytracer/Cargo.toml b/raytracer/Cargo.toml index 2ffdce9..b900071 100644 --- a/raytracer/Cargo.toml +++ b/raytracer/Cargo.toml @@ -4,7 +4,6 @@ version = "0.1.0" edition = "2024" [dependencies] -cgmath = "0.18.0" libc = "0.2.176" log = "0.4.28" uefi = { version = "0.35.0", features = ["logger", "panic_handler"] } diff --git a/raytracer/src/buffer.rs b/raytracer/src/buffer.rs index e8a3db2..d5a2960 100644 --- a/raytracer/src/buffer.rs +++ b/raytracer/src/buffer.rs @@ -22,10 +22,15 @@ impl Buffer { } /// Get a single pixel. - pub fn pixel(&mut self, x: usize, y: usize) -> Option<&mut BltPixel> { + pub fn get_pixel(&mut self, x: usize, y: usize) -> Option<&mut BltPixel> { self.pixels.get_mut(y * self.width + x) } + /// Set a single pixel. + pub fn set_pixel(&mut self, x: usize, y: usize, pixel: BltPixel) { + self.pixels[y * self.width + x] = pixel; + } + /// Blit the buffer to the framebuffer. pub fn blit(&self, gop: &mut GraphicsOutput) -> Result<(), Error> { gop.blt(BltOp::BufferToVideo { diff --git a/raytracer/src/main.rs b/raytracer/src/main.rs index 23af95f..c0bf055 100644 --- a/raytracer/src/main.rs +++ b/raytracer/src/main.rs @@ -2,11 +2,14 @@ #![no_std] mod buffer; +mod math; mod ray; mod renderer; +mod spheretracer; extern crate alloc; use crate::buffer::Buffer; +use crate::renderer::render; use uefi::allocator::Allocator; use uefi::boot::ScopedProtocol; use uefi::prelude::*; @@ -32,7 +35,8 @@ fn init_buffer(gop: &GraphicsOutput) -> Buffer { fn main() -> Status { uefi::helpers::init().unwrap(); let mut gop = init_gop().unwrap(); - let buffer = init_buffer(&gop); + let mut buffer = init_buffer(&gop); + render(&mut buffer); buffer.blit(&mut gop).unwrap(); Status::SUCCESS } diff --git a/raytracer/src/math.rs b/raytracer/src/math.rs new file mode 100644 index 0000000..1b05b39 --- /dev/null +++ b/raytracer/src/math.rs @@ -0,0 +1,24 @@ +pub struct Matrix4 { + x: T, +} +pub struct Vector3 { + x: T, +} + +pub struct Vector4 { + x: T, + y: T, + z: T, + w: T, +} + +impl Vector4 { + pub fn new(x: T, y: T, z: T, w: T) -> Vector4 { + Vector4 { + x: x, + y: y, + z: z, + w: w, + } + } +} diff --git a/raytracer/src/ray.rs b/raytracer/src/ray.rs index 85b3178..9e33d32 100644 --- a/raytracer/src/ray.rs +++ b/raytracer/src/ray.rs @@ -1,19 +1,19 @@ +use crate::math::{Matrix4, Vector3, Vector4}; use alloc::vec::Vec; -use cgmath::{Matrix4, Vector3, Vector4, num_traits::Num}; pub struct Ray { pub(crate) origin: Vector3, pub(crate) direction: Vector3, } -pub fn construct_primary_rays( +pub fn construct_primary_rays_for_pixel( (width, height): (usize, usize), (pixel_x_coord, pixel_y_coord): (usize, usize), cam_to_world_matrix: &Matrix4, focal_length: f32, rays_per_pixel: usize, -) -> Vec { - let mut rays: Vec = Vec::with_capacity(rays_per_pixel); +) -> Vec> { + let mut rays: Vec> = Vec::with_capacity(rays_per_pixel); //generate all rays for this pixel and add them to the rays vector for _ in 0..rays_per_pixel { @@ -24,8 +24,8 @@ pub fn construct_primary_rays( pixel_x_coord, pixel_y_coord, //no noise - 0.0f, - 0.0f, + 0.0f32, + 0.0f32, )); } @@ -40,7 +40,7 @@ fn generate_single_primary_ray( v: usize, u_offset: f32, v_offset: f32, -) -> Ray { +) -> Ray { //calculate the ray direction and translate it to world space let direction_camera_space: Vector4 = Vector4::new( u as f32 - (buffer_width as f32 / 2.0) + u_offset, diff --git a/raytracer/src/renderer.rs b/raytracer/src/renderer.rs index 5fa22f7..0d2b110 100644 --- a/raytracer/src/renderer.rs +++ b/raytracer/src/renderer.rs @@ -1,12 +1,16 @@ +use core::cmp::{max, min}; + use alloc::vec::Vec; use cgmath::{Matrix4, Vector4, Zero}; +use uefi::proto::console::gop::BltPixel; use crate::{ buffer::Buffer, - ray::{Ray, construct_primary_rays}, + ray::{Ray, construct_primary_rays_for_pixel}, + spheretracer::{Radiosity, sdf, spheretrace}, }; -pub fn render(buffer: Buffer) { +pub fn render(buffer: &mut Buffer) { let camera_matrix: Matrix4 = Matrix4 { x: Vector4::unit_x(), y: Vector4::unit_y(), @@ -16,16 +20,53 @@ pub fn render(buffer: Buffer) { let focal_length = 1.0f; + let mut radiosiy_buffer: Vec = Vec::with_capacity(buffer.width * buffer.height); + //this reeeeally wants to run in parallel… (0..buffer.width).for_each(|x| { (0..buffer.height).for_each(|y| { - let rays: Vec> = construct_primary_rays( + //construct rays for each pixel + let rays: Vec> = construct_primary_rays_for_pixel( (buffer.width, buffer.height), (x, y), camera_matrix, focal_length, 1, ); + + //spheretrace the ray and add the determined radiosity to the buffer + radiosiy_buffer.push( + rays.iter() + .for_each(|ray| spheretrace(ray, 100.00f, 0.01, sdf)), + ); + }); + }); + + //remap radiosities to colors + radiosiy_buffer = normalize_radiosity(&mut radiosiy_buffer) + + //copy vector over to buffer + (0..buffer.width) + .for_each(|x| { + (0..buffer.height).for_each(|y| { + let rad = radiosiy_buffer[y * buffer.width + x]; + buffer.set_pixel(x, y, BltPixel::new(rad, rad, rad)); }); }); } + +fn normalize_radiosity(radiosities: &mut Vec) -> Vec { + //find the minimum and maximum radiosities in he buffer + let (min_rad, max_rad) = + radiosities + .iter() + .zip(radiosities) + .reduce(|(acc_min, acc_max), (curr_min, curr_max)| { + acc_min = min(acc_min, curr_min); + acc_max = max(acc_max, curr_max); + }); + //remap radiosities to [0,255] for framebuffer + radiosities + .iter_mut() + .for_each(|r| r = (r - min_rad) / (max_rad - min_rad) * 255.0) +} diff --git a/raytracer/src/spheretracer.rs b/raytracer/src/spheretracer.rs new file mode 100644 index 0000000..a52d4b8 --- /dev/null +++ b/raytracer/src/spheretracer.rs @@ -0,0 +1,50 @@ +use cgmath::{Vector3, num_traits::abs}; + +use crate::ray::Ray; + +pub type Radiosity = f32; + +/// +/// Spheretraces a ray in a certain signed distance function up to a maximum ray length +/// and with respect to a certain floating point error epsilon +/// +pub fn spheretrace

( + ray: Ray

, + max_ray_length: P, + epsilon: P, + signed_distance_function: Fn(Vector3

) -> (P, Vector3

), +) -> Radiosity { + //accumulate the length this ray has already travelled + let mut distance_travelled: P = 0.0; + + //accumulate radiosity + let mut accumulated_radiosity: P = 0.f; + + //if we still have some distance left to walk + while abs(distance_travelled - max_ray_length) < epsilon { + //walk a bit + let current_point = ray.origin + distance_travelled * ray.direction; + let (distance, normal) = signed_distance_function(current_point); + + //we have a collision + if abs(distance) < epsilon || distance < 0.f { + //TODO: we wanna pathtrace recursively here + accumulated_radiosity += 1.f; + break; + } + } + + accumulated_radiosity +} + +/// +/// A signed distance function that for each sample point +/// returns the Euclidean distance to the next geometric intersection +/// as well as a normal direction (gradient vector). Distance is negative +/// iff point is inside geometry. +/// +/// P is the precision +/// +pub fn sdf

(sample_point: Vector3

) -> (P, Vector3

) { + (1.0, Vector3::unit_x()) +}