diff options
| author | Sakarias Johansson <sakarias.johansson@goodbyekansas.com> | 2023-01-06 22:59:27 +0100 |
|---|---|---|
| committer | Sakarias Johansson <sakarias.johansson@goodbyekansas.com> | 2023-01-06 22:59:27 +0100 |
| commit | 928b4191bf5a0d27da6d680ccaade7f94860359e (patch) | |
| tree | b2c1e301c1c5c7b5d8cb9a92cc9d4c925e54a7b9 /racer-tracer | |
| parent | 372bc5b09c50cf2440e8f7762dd260cefd7bba7f (diff) | |
| download | racer-tracer-928b4191bf5a0d27da6d680ccaade7f94860359e.tar.gz racer-tracer-928b4191bf5a0d27da6d680ccaade7f94860359e.tar.xz racer-tracer-928b4191bf5a0d27da6d680ccaade7f94860359e.zip | |
☁ Add sky
Diffstat (limited to 'racer-tracer')
| -rw-r--r-- | racer-tracer/src/camera.rs | 34 | ||||
| -rw-r--r-- | racer-tracer/src/image.rs | 16 | ||||
| -rw-r--r-- | racer-tracer/src/main.rs | 63 | ||||
| -rw-r--r-- | racer-tracer/src/ray.rs | 24 | ||||
| -rw-r--r-- | racer-tracer/src/vec3.rs | 204 |
5 files changed, 326 insertions, 15 deletions
diff --git a/racer-tracer/src/camera.rs b/racer-tracer/src/camera.rs new file mode 100644 index 0000000..5f0abbb --- /dev/null +++ b/racer-tracer/src/camera.rs @@ -0,0 +1,34 @@ +use crate::image::Image; +use crate::vec3::Vec3; + +#[derive(Clone)] +pub struct Camera { + pub viewport_height: f64, + pub viewport_width: f64, + pub focal_length: f64, + pub origin: Vec3, + pub horizontal: Vec3, + pub vertical: Vec3, + pub lower_left_corner: Vec3, +} + +impl Camera { + pub fn new(image: &Image, viewport_height: f64, focal_length: f64) -> Camera { + let viewport_width = image.aspect_ratio * viewport_height; + let origin = Vec3::new(0.0, 0.0, 0.0); + let horizontal = Vec3::new(viewport_width, 0.0, 0.0); + let vertical = Vec3::new(0.0, viewport_height, 0.0); + Camera { + viewport_height, + viewport_width, + focal_length, + origin, + horizontal, + vertical, + lower_left_corner: origin + - horizontal / 2.0 + - vertical / 2.0 + - Vec3::new(0.0, 0.0, focal_length), + } + } +} diff --git a/racer-tracer/src/image.rs b/racer-tracer/src/image.rs new file mode 100644 index 0000000..1b5f61e --- /dev/null +++ b/racer-tracer/src/image.rs @@ -0,0 +1,16 @@ +#[derive(Clone)] +pub struct Image { + pub aspect_ratio: f64, + pub width: usize, + pub height: usize, +} + +impl Image { + pub fn new(aspect_ratio: f64, width: usize) -> Image { + Image { + aspect_ratio, + width, + height: (width as f64 / aspect_ratio) as usize, + } + } +} diff --git a/racer-tracer/src/main.rs b/racer-tracer/src/main.rs index a35773b..1951da5 100644 --- a/racer-tracer/src/main.rs +++ b/racer-tracer/src/main.rs @@ -1,45 +1,78 @@ #[macro_use] mod error; +mod camera; +mod image; +mod ray; mod util; +mod vec3; +use crate::vec3::Vec3; use std::vec::Vec; use futures::{select, stream::FuturesUnordered, stream::StreamExt}; use minifb::{Key, Window, WindowOptions}; -async fn raytrace(row: usize, width: usize) -> Result<(usize, Vec<u32>), error::TracerError> { - let mut buffer: Vec<u32> = vec![0; width]; +fn ray_color(ray: &ray::Ray) -> Vec3 { + let unit_direction = vec3::unit_vector(ray.direction()); + let t = 0.5 * (unit_direction.y() + 1.0); + (1.0 - t) * Vec3::new(1.0, 1.0, 1.0) + t * Vec3::new(0.5, 0.7, 1.0) +} + +async fn raytrace( + camera: camera::Camera, + image: image::Image, + row: usize, +) -> Result<(usize, Vec<u32>), error::TracerError> { + let mut buffer: Vec<u32> = vec![0; image.width as usize]; - // TODO: Trace geometry for i in 0..buffer.len() { - buffer[i as usize] = util::hsv_to_rgb(((row as u32 + i as u32) % 360) as f64, 100.0, 100.0); + let u: f64 = i as f64 / (image.width - 1) as f64; + let v: f64 = row as f64 / (image.height - 1) as f64; + let ray = ray::Ray::new( + camera.origin, + camera.lower_left_corner + u * camera.horizontal + v * camera.vertical - camera.origin, + ); + let col = ray_color(&ray); + buffer[i] = col.as_color(); } Ok((row, buffer)) } -async fn run(width: usize, height: usize) -> Result<(), error::TracerError> { - let mut screen_buffer: Vec<u32> = vec![0; width * height]; - let mut window = Window::new("racer-tracer", width, height, WindowOptions::default()) - .expect("Unable to create window"); +async fn run( + rows_per_update: u32, + aspect_ratio: f64, + screen_height: usize, +) -> Result<(), error::TracerError> { + let image = image::Image::new(aspect_ratio, screen_height); + let camera = camera::Camera::new(&image, 2.0, 1.0); + + let mut screen_buffer: Vec<u32> = vec![0; image.width * image.height]; + let mut window = Window::new( + "racer-tracer", + image.width, + image.height, + WindowOptions::default(), + ) + .expect("Unable to create window"); window.limit_update_rate(Some(std::time::Duration::from_micros(16600))); let mut futs = FuturesUnordered::new(); // One future per row is a bit high. // Could do something less spammy. - for h in 0..height { - futs.push(raytrace(h, width)); + for h in 0..image.height { + futs.push(raytrace(camera.clone(), image.clone(), h)); } let mut complete = false; while window.is_open() && !window.is_key_down(Key::Escape) { if !complete { - for _ in 1..50 { + for _ in 1..rows_per_update { select! { res = futs.select_next_some() => { let row_buffer = res.expect("Expected to get data"); - let start = row_buffer.0 * width; - let end = start + width; + let start = row_buffer.0 * image.width; + let end = start + image.width; screen_buffer[start..end].copy_from_slice(row_buffer.1.as_slice()); }, complete => { @@ -53,7 +86,7 @@ async fn run(width: usize, height: usize) -> Result<(), error::TracerError> { } window - .update_with_buffer(&screen_buffer, width, height) + .update_with_buffer(&screen_buffer, image.width, image.height) .map_err(|e| error::TracerError::FailedToUpdateWindow(e.to_string()))?; } @@ -62,7 +95,7 @@ async fn run(width: usize, height: usize) -> Result<(), error::TracerError> { #[tokio::main] async fn main() { - if let Err(e) = run(640, 480).await { + if let Err(e) = run(50, 16.0 / 9.0, 1200).await { eprintln!("{}", e); std::process::exit(e.into()) } diff --git a/racer-tracer/src/ray.rs b/racer-tracer/src/ray.rs new file mode 100644 index 0000000..1325cc2 --- /dev/null +++ b/racer-tracer/src/ray.rs @@ -0,0 +1,24 @@ +use crate::vec3::Vec3; + +pub struct Ray { + origin: Vec3, + direction: Vec3, +} + +impl Ray { + pub fn new(origin: Vec3, direction: Vec3) -> Ray { + Ray { origin, direction } + } + + pub fn origin(&self) -> &Vec3 { + &self.origin + } + + pub fn direction(&self) -> &Vec3 { + &self.direction + } + + pub fn at(&self, go_length: f64) -> Vec3 { + self.origin.clone() + go_length * self.direction.clone() + } +} diff --git a/racer-tracer/src/vec3.rs b/racer-tracer/src/vec3.rs new file mode 100644 index 0000000..47df292 --- /dev/null +++ b/racer-tracer/src/vec3.rs @@ -0,0 +1,204 @@ +use std::{fmt, ops}; + +#[derive(Default, Clone, Copy)] +pub struct Vec3 { + data: [f64; 3], +} + +impl Vec3 { + pub fn new(x: f64, y: f64, z: f64) -> Vec3 { + Vec3 { data: [x, y, z] } + } + + pub fn x(&self) -> &f64 { + &self.data[0] + } + + pub fn y(&self) -> &f64 { + &self.data[1] + } + + pub fn z(&self) -> &f64 { + &self.data[2] + } + + pub fn length(&self) -> f64 { + f64::sqrt(self.length_squared()) + } + + pub fn length_squared(&self) -> f64 { + self.data[0] * self.data[0] + self.data[1] * self.data[1] + self.data[2] * self.data[2] + } + + pub fn dot(&self, v: &Vec3) -> f64 { + dot(self, v) + } + + pub fn cross(&self, v: &Vec3) -> Vec3 { + cross(self, v) + } + + pub fn unit_vector(&self) -> Vec3 { + unit_vector(self) + } + + pub fn as_color(&self) -> u32 { + let red: u32 = (self.data[0] * 255.0) as u32; + let green: u32 = (self.data[1] * 255.0) as u32; + let blue: u32 = (self.data[2] * 255.0) as u32; + ((red as u32) << 16) | ((green as u32) << 8) | blue as u32 + } +} + +pub fn dot(v1: &Vec3, v2: &Vec3) -> f64 { + v1.data[0] * v2.data[0] + v1.data[1] * v2.data[1] + v1.data[2] * v2.data[2] +} + +pub fn cross(v1: &Vec3, v2: &Vec3) -> Vec3 { + Vec3::new( + v1.data[1] * v2.data[2] - v1.data[2] * v2.data[1], + v1.data[2] * v2.data[0] - v1.data[0] * v2.data[2], + v1.data[0] * v2.data[1] - v1.data[1] * v2.data[0], + ) +} + +pub fn unit_vector(v: &Vec3) -> Vec3 { + v / v.length() +} + +impl ops::Add<Vec3> for Vec3 { + type Output = Vec3; + + fn add(self, rhs: Vec3) -> Self::Output { + Vec3::new( + self.data[0] + rhs.data[0], + self.data[1] + rhs.data[1], + self.data[2] + rhs.data[2], + ) + } +} + +impl ops::AddAssign<Vec3> for Vec3 { + fn add_assign(&mut self, rhs: Vec3) { + self.data[0] += rhs.data[0]; + self.data[1] += rhs.data[1]; + self.data[2] += rhs.data[2]; + } +} + +impl ops::Sub<Vec3> for Vec3 { + type Output = Vec3; + + fn sub(self, rhs: Vec3) -> Self::Output { + Vec3::new( + rhs.data[0] - self.data[0], + rhs.data[1] - self.data[1], + rhs.data[2] - self.data[2], + ) + } +} + +impl ops::SubAssign for Vec3 { + fn sub_assign(&mut self, rhs: Self) { + self.data[0] -= rhs.data[0]; + self.data[1] -= rhs.data[1]; + self.data[2] -= rhs.data[2]; + } +} + +impl ops::Div<f64> for Vec3 { + type Output = Vec3; + + fn div(self, rhs: f64) -> Self::Output { + (1.0 / rhs) * self + } +} + +impl ops::Div<&Vec3> for f64 { + type Output = Vec3; + + fn div(self, rhs: &Vec3) -> Self::Output { + (1.0 / self) * rhs + } +} + +impl ops::Div<f64> for &Vec3 { + type Output = Vec3; + + fn div(self, rhs: f64) -> Self::Output { + (1.0 / rhs) * self + } +} + +impl ops::DivAssign<f64> for Vec3 { + fn div_assign(&mut self, rhs: f64) { + let t = 1.0 / rhs; + self.data[0] *= t; + self.data[1] *= t; + self.data[2] *= t; + } +} + +impl ops::Mul<f64> for Vec3 { + type Output = Vec3; + + fn mul(self, rhs: f64) -> Self::Output { + Vec3::new(self.data[0] * rhs, self.data[1] * rhs, self.data[2] * rhs) + } +} + +impl ops::Mul<&Vec3> for f64 { + type Output = Vec3; + + fn mul(self, rhs: &Vec3) -> Self::Output { + Vec3::new(rhs.data[0] * self, rhs.data[1] * self, rhs.data[2] * self) + } +} + +impl ops::Mul<Vec3> for Vec3 { + type Output = Vec3; + + fn mul(self, rhs: Vec3) -> Self::Output { + Vec3::new( + self.data[0] * rhs.data[0], + self.data[1] * rhs.data[1], + self.data[2] * rhs.data[2], + ) + } +} + +impl ops::Mul<Vec3> for f64 { + type Output = Vec3; + + fn mul(self, rhs: Vec3) -> Self::Output { + rhs * self + } +} + +impl ops::MulAssign<f64> for Vec3 { + fn mul_assign(&mut self, rhs: f64) { + self.data[0] *= rhs; + self.data[1] *= rhs; + self.data[2] *= rhs; + } +} + +impl ops::Neg for Vec3 { + type Output = Vec3; + + fn neg(self) -> Self::Output { + Vec3::new(-self.data[0], -self.data[1], -self.data[2]) + } +} + +impl fmt::Display for Vec3 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str( + format!( + "x: {}, y: {}, z: {}", + self.data[0], self.data[1], self.data[2] + ) + .as_str(), + ) + } +} |
