summaryrefslogtreecommitdiff
path: root/racer-tracer/src
diff options
context:
space:
mode:
authorSakarias Johansson <sakarias.johansson@goodbyekansas.com>2023-01-06 22:59:27 +0100
committerSakarias Johansson <sakarias.johansson@goodbyekansas.com>2023-01-06 22:59:27 +0100
commit928b4191bf5a0d27da6d680ccaade7f94860359e (patch)
treeb2c1e301c1c5c7b5d8cb9a92cc9d4c925e54a7b9 /racer-tracer/src
parent372bc5b09c50cf2440e8f7762dd260cefd7bba7f (diff)
downloadracer-tracer-928b4191bf5a0d27da6d680ccaade7f94860359e.tar.gz
racer-tracer-928b4191bf5a0d27da6d680ccaade7f94860359e.tar.xz
racer-tracer-928b4191bf5a0d27da6d680ccaade7f94860359e.zip
☁ Add sky
Diffstat (limited to 'racer-tracer/src')
-rw-r--r--racer-tracer/src/camera.rs34
-rw-r--r--racer-tracer/src/image.rs16
-rw-r--r--racer-tracer/src/main.rs63
-rw-r--r--racer-tracer/src/ray.rs24
-rw-r--r--racer-tracer/src/vec3.rs204
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(),
+ )
+ }
+}