began writing spheretracer and realized I'll have to reimplement cgmath

(badly)
This commit is contained in:
CDaut 2025-10-02 16:12:16 +02:00
parent c6290f93fd
commit 6c88c8ddfe
Signed by: clara
GPG key ID: 223391B52FAD4463
8 changed files with 136 additions and 48 deletions

35
raytracer/Cargo.lock generated
View file

@ -2,21 +2,6 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 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]] [[package]]
name = "bit_field" name = "bit_field"
version = "0.10.3" version = "0.10.3"
@ -35,16 +20,6 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" 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]] [[package]]
name = "libc" name = "libc"
version = "0.2.176" version = "0.2.176"
@ -57,15 +32,6 @@ version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" 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]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.101" version = "1.0.101"
@ -108,7 +74,6 @@ dependencies = [
name = "raytracer" name = "raytracer"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"cgmath",
"libc", "libc",
"log", "log",
"uefi", "uefi",

View file

@ -4,7 +4,6 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
cgmath = "0.18.0"
libc = "0.2.176" libc = "0.2.176"
log = "0.4.28" log = "0.4.28"
uefi = { version = "0.35.0", features = ["logger", "panic_handler"] } uefi = { version = "0.35.0", features = ["logger", "panic_handler"] }

View file

@ -22,10 +22,15 @@ impl Buffer {
} }
/// Get a single pixel. /// 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) 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. /// Blit the buffer to the framebuffer.
pub fn blit(&self, gop: &mut GraphicsOutput) -> Result<(), Error> { pub fn blit(&self, gop: &mut GraphicsOutput) -> Result<(), Error> {
gop.blt(BltOp::BufferToVideo { gop.blt(BltOp::BufferToVideo {

View file

@ -2,11 +2,14 @@
#![no_std] #![no_std]
mod buffer; mod buffer;
mod math;
mod ray; mod ray;
mod renderer; mod renderer;
mod spheretracer;
extern crate alloc; extern crate alloc;
use crate::buffer::Buffer; use crate::buffer::Buffer;
use crate::renderer::render;
use uefi::allocator::Allocator; use uefi::allocator::Allocator;
use uefi::boot::ScopedProtocol; use uefi::boot::ScopedProtocol;
use uefi::prelude::*; use uefi::prelude::*;
@ -32,7 +35,8 @@ fn init_buffer(gop: &GraphicsOutput) -> Buffer {
fn main() -> Status { fn main() -> Status {
uefi::helpers::init().unwrap(); uefi::helpers::init().unwrap();
let mut gop = init_gop().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(); buffer.blit(&mut gop).unwrap();
Status::SUCCESS Status::SUCCESS
} }

24
raytracer/src/math.rs Normal file
View file

@ -0,0 +1,24 @@
pub struct Matrix4<T> {
x: T,
}
pub struct Vector3<T> {
x: T,
}
pub struct Vector4<T> {
x: T,
y: T,
z: T,
w: T,
}
impl<T> Vector4<T> {
pub fn new(x: T, y: T, z: T, w: T) -> Vector4<T> {
Vector4 {
x: x,
y: y,
z: z,
w: w,
}
}
}

View file

@ -1,19 +1,19 @@
use crate::math::{Matrix4, Vector3, Vector4};
use alloc::vec::Vec; use alloc::vec::Vec;
use cgmath::{Matrix4, Vector3, Vector4, num_traits::Num};
pub struct Ray<T> { pub struct Ray<T> {
pub(crate) origin: Vector3<T>, pub(crate) origin: Vector3<T>,
pub(crate) direction: Vector3<T>, pub(crate) direction: Vector3<T>,
} }
pub fn construct_primary_rays( pub fn construct_primary_rays_for_pixel(
(width, height): (usize, usize), (width, height): (usize, usize),
(pixel_x_coord, pixel_y_coord): (usize, usize), (pixel_x_coord, pixel_y_coord): (usize, usize),
cam_to_world_matrix: &Matrix4<f32>, cam_to_world_matrix: &Matrix4<f32>,
focal_length: f32, focal_length: f32,
rays_per_pixel: usize, rays_per_pixel: usize,
) -> Vec<Ray> { ) -> Vec<Ray<f32>> {
let mut rays: Vec<Ray> = Vec::with_capacity(rays_per_pixel); let mut rays: Vec<Ray<f32>> = Vec::with_capacity(rays_per_pixel);
//generate all rays for this pixel and add them to the rays vector //generate all rays for this pixel and add them to the rays vector
for _ in 0..rays_per_pixel { for _ in 0..rays_per_pixel {
@ -24,8 +24,8 @@ pub fn construct_primary_rays(
pixel_x_coord, pixel_x_coord,
pixel_y_coord, pixel_y_coord,
//no noise //no noise
0.0f, 0.0f32,
0.0f, 0.0f32,
)); ));
} }
@ -40,7 +40,7 @@ fn generate_single_primary_ray(
v: usize, v: usize,
u_offset: f32, u_offset: f32,
v_offset: f32, v_offset: f32,
) -> Ray { ) -> Ray<f32> {
//calculate the ray direction and translate it to world space //calculate the ray direction and translate it to world space
let direction_camera_space: Vector4<f32> = Vector4::new( let direction_camera_space: Vector4<f32> = Vector4::new(
u as f32 - (buffer_width as f32 / 2.0) + u_offset, u as f32 - (buffer_width as f32 / 2.0) + u_offset,

View file

@ -1,12 +1,16 @@
use core::cmp::{max, min};
use alloc::vec::Vec; use alloc::vec::Vec;
use cgmath::{Matrix4, Vector4, Zero}; use cgmath::{Matrix4, Vector4, Zero};
use uefi::proto::console::gop::BltPixel;
use crate::{ use crate::{
buffer::Buffer, 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<f32> = Matrix4 { let camera_matrix: Matrix4<f32> = Matrix4 {
x: Vector4::unit_x(), x: Vector4::unit_x(),
y: Vector4::unit_y(), y: Vector4::unit_y(),
@ -16,16 +20,53 @@ pub fn render(buffer: Buffer) {
let focal_length = 1.0f; let focal_length = 1.0f;
let mut radiosiy_buffer: Vec<Radiosity> = Vec::with_capacity(buffer.width * buffer.height);
//this reeeeally wants to run in parallel… //this reeeeally wants to run in parallel…
(0..buffer.width).for_each(|x| { (0..buffer.width).for_each(|x| {
(0..buffer.height).for_each(|y| { (0..buffer.height).for_each(|y| {
let rays: Vec<Ray<f32>> = construct_primary_rays( //construct rays for each pixel
let rays: Vec<Ray<f32>> = construct_primary_rays_for_pixel(
(buffer.width, buffer.height), (buffer.width, buffer.height),
(x, y), (x, y),
camera_matrix, camera_matrix,
focal_length, focal_length,
1, 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<Radiosity>) -> Vec<Radiosity> {
//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)
}

View file

@ -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<P>(
ray: Ray<P>,
max_ray_length: P,
epsilon: P,
signed_distance_function: Fn(Vector3<P>) -> (P, Vector3<P>),
) -> 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<P>(sample_point: Vector3<P>) -> (P, Vector3<P>) {
(1.0, Vector3::unit_x())
}