From 99ad9ea260bc359bddd71aef3d298c2b82aa3133 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Wed, 20 Mar 2019 15:56:29 +0100 Subject: [PATCH] Move tracer objects into modules --- src/aabb.rs | 65 ++++++++++++++++ src/main.rs | 203 ++++++++++++------------------------------------ src/ray.rs | 41 ++++++++++ src/triangle.rs | 48 ++++++++++++ src/view.rs | 28 +++++++ 5 files changed, 232 insertions(+), 153 deletions(-) create mode 100644 src/aabb.rs create mode 100644 src/ray.rs create mode 100644 src/triangle.rs create mode 100644 src/view.rs diff --git a/src/aabb.rs b/src/aabb.rs new file mode 100644 index 0000000..41cc9fe --- /dev/null +++ b/src/aabb.rs @@ -0,0 +1,65 @@ +use cgmath::{Vector3, Zero}; + +use super::ray::Ray; + +pub struct AABB { + pub bounds: [Vector3; 2], + pub start_index: usize, + pub end_index: usize, +} + +impl AABB { + pub fn new() -> AABB { + AABB { + bounds: [Vector3::zero(), Vector3::zero()], + start_index: 0, + end_index: 0, + } + } + + pub fn intersect(&self, ray: &Ray) -> bool { + let mut tmin = (self.bounds[ray.signs.x].x - ray.origin.x) * ray.inv_direction.x; + let mut tmax = (self.bounds[1 - ray.signs.x].x - ray.origin.x) * ray.inv_direction.x; + let tymin = (self.bounds[ray.signs.y].y - ray.origin.y) * ray.inv_direction.y; + let tymax = (self.bounds[1 - ray.signs.y].y - ray.origin.y) * ray.inv_direction.y; + + if tmin > tymax || tymin > tmax { + return false; + } + + if tymin > tmin { + tmin = tymin; + } + + if tymax < tmax { + tmax = tymax; + } + + let tzmin = (self.bounds[ray.signs.z].z - ray.origin.z) * ray.inv_direction.z; + let tzmax = (self.bounds[1 - ray.signs.z].z - ray.origin.z) * ray.inv_direction.z; + + if tmin > tzmax || tzmin > tmax { + return false; + } + + if tzmin > tmin { + tmin = tzmin; + } + + if tzmax < tmax { + tmax = tzmax; + } + + let mut t = tmin; + + if t < 0.0 { + t = tmax; + + if t < 0.0 { + return false; + } + } + + true + } +} diff --git a/src/main.rs b/src/main.rs index d5d1a26..532cd91 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,115 +1,21 @@ -use cgmath::prelude::*; - -use cgmath::{vec3, vec4, InnerSpace, Matrix4, Point3, Vector3, Zero}; +use cgmath::{vec3, Vector3}; use image::ImageBuffer; use utilities::prelude::*; -use std::f32::{consts::PI as M_PI, MAX, MIN}; +use std::f32::{MAX, MIN}; -struct Triangle { - points: [Vector3; 3], -} +mod triangle; +use triangle::Triangle; -impl Triangle { - pub fn new(v0: Vector3, v1: Vector3, v2: Vector3) -> Triangle { - Triangle { - points: [v0, v1, v2], - } - } -} +mod aabb; +use aabb::AABB; -struct View { - position: Vector3, - look_at: Vector3, - fov: f32, -} +mod ray; +use ray::Ray; -struct Ray { - origin: Vector3, - direction: Vector3, - inv_direction: Vector3, - signs: Vector3, -} - -impl Ray { - pub fn new(origin: Vector3, direction: Vector3) -> Ray { - let inv_direction = 1.0 / direction; - - let x = (inv_direction.x < 0.0) as usize; - let y = (inv_direction.y < 0.0) as usize; - let z = (inv_direction.z < 0.0) as usize; - - Ray { - origin, - direction, - inv_direction, - signs: Vector3::new(x, y, z), - } - } -} - -struct AABB { - bounds: [Vector3; 2], - start_index: usize, - end_index: usize, -} - -impl AABB { - fn new() -> AABB { - AABB { - bounds: [Vector3::zero(), Vector3::zero()], - start_index: 0, - end_index: 0, - } - } - - fn intersect(&self, ray: &Ray) -> bool { - let mut tmin = (self.bounds[ray.signs.x].x - ray.origin.x) * ray.inv_direction.x; - let mut tmax = (self.bounds[1 - ray.signs.x].x - ray.origin.x) * ray.inv_direction.x; - let tymin = (self.bounds[ray.signs.y].y - ray.origin.y) * ray.inv_direction.y; - let tymax = (self.bounds[1 - ray.signs.y].y - ray.origin.y) * ray.inv_direction.y; - - if tmin > tymax || tymin > tmax { - return false; - } - - if tymin > tmin { - tmin = tymin; - } - - if tymax < tmax { - tmax = tymax; - } - - let tzmin = (self.bounds[ray.signs.z].z - ray.origin.z) * ray.inv_direction.z; - let tzmax = (self.bounds[1 - ray.signs.z].z - ray.origin.z) * ray.inv_direction.z; - - if tmin > tzmax || tzmin > tmax { - return false; - } - - if tzmin > tmin { - tmin = tzmin; - } - - if tzmax < tmax { - tmax = tzmax; - } - - let mut t = tmin; - - if t < 0.0 { - t = tmax; - - if t < 0.0 { - return false; - } - } - - true - } -} +mod view; +use view::View; fn main() { let input_data = [Triangle::new( @@ -121,6 +27,7 @@ fn main() { let view = View { position: vec3(0.0, -4.0, 4.0), look_at: vec3(0.0, 0.0, 0.0), + up: vec3(0.0, 0.0, 1.0), fov: 45.0, }; @@ -131,33 +38,65 @@ fn f_to_u(color: f32) -> u8 { ((color * 255.0) / 1.0) as u8 } +/* fn calculate_ray(x: u32, y: u32, dim_x: u32, dim_y: u32, view: &View) -> Ray { let aspect_ratio = dim_x as f32 / dim_y as f32; - let fov = view.fov / 180.0 * M_PI * 2.0; + let fov = view.fov / M_PI * 2.0 / 180.0; let p_x = (1.0 - 2.0 * ((x as f32 + 0.5) / dim_x as f32)) * fov * aspect_ratio; let p_y = (2.0 * (((y) as f32 + 0.5) / dim_y as f32) - 1.0) * fov; - let camera_to_world = Matrix4::look_at( + let look_at = Matrix4::look_at( Point3::from_vec(view.position), Point3::from_vec(view.look_at), vec3(0.0, 0.0, 1.0), - ) - .invert() - .unwrap(); + ); + + let test = look_at_test(view.position, view.look_at, vec3(0.0, 0.0, 1.0)); + + let camera_to_world = look_at.invert().unwrap(); let origin = camera_to_world * vec4(0.0, 0.0, 0.0, 1.0); let direction = camera_to_world * vec4(p_x, p_y, -1.0, 1.0); Ray::new(origin.truncate(), -direction.truncate().normalize()) } +*/ + +/* +fn look_at_test(eye: Vector3, center: Vector3, up: Vector3) -> Matrix4 { + let mut look_at_matrix = Matrix4::identity(); + + let forward = (eye - center).normalize(); + let right = up.cross(forward).normalize(); + let up = forward.cross(right); + + look_at_matrix[0][0] = right.x; + look_at_matrix[0][1] = right.y; + look_at_matrix[0][2] = right.z; + + look_at_matrix[1][0] = up.x; + look_at_matrix[1][1] = up.y; + look_at_matrix[1][2] = up.z; + + look_at_matrix[2][0] = forward.x; + look_at_matrix[2][1] = forward.y; + look_at_matrix[2][2] = forward.z; + + look_at_matrix[3][0] = eye.x; + look_at_matrix[3][1] = eye.y; + look_at_matrix[3][2] = eye.z; + + look_at_matrix +} +*/ fn debug_raytracer(dim_x: u32, dim_y: u32, view: &View, data: &[Triangle]) { let mut imgbuf = ImageBuffer::new(dim_x, dim_y); for (x, y, pixel) in imgbuf.enumerate_pixels_mut() { - let ray = calculate_ray(x, y, dim_x, dim_y, view); + let ray = Ray::primary_ray(x, y, dim_x, dim_y, view); let acceleration_data = create_acceleration_data(data); @@ -226,12 +165,9 @@ fn pixel_color(ray: &Ray, data: &[Triangle], acceleration_data: Vec) -> Ve for accel in acceleration_data.iter() { if accel.intersect(ray) { for i in accel.start_index..accel.end_index { - let v0 = data[i].points[0]; - let v1 = data[i].points[1]; - let v2 = data[i].points[2]; let mut t = 0.0; - if ray_triangle_intersect_mt(ray, v0, v1, v2, &mut t) { + if data[i].intersect_mt(ray, &mut t) { if t < closest_value { closest_index = i as i32; closest_value = t; @@ -247,42 +183,3 @@ fn pixel_color(ray: &Ray, data: &[Triangle], acceleration_data: Vec) -> Ve return final_color; } - -// source: https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/moller-trumbore-ray-triangle-intersection -fn ray_triangle_intersect_mt( - ray: &Ray, - v0: Vector3, - v1: Vector3, - v2: Vector3, - t: &mut f32, -) -> bool { - let v0v1 = v1 - v0; - let v0v2 = v2 - v0; - let pvec = ray.direction.cross(v0v2); - let det = v0v1.dot(pvec); - - // ray and triangle are parallel if det is close to 0 - if det.abs() < 0.001 { - return false; - } - - let inv_det = 1.0 / det; - - let tvec = ray.origin - v0; - let u = tvec.dot(pvec) * inv_det; - - if u < 0.0 || u > 1.0 { - return false; - } - - let qvec = tvec.cross(v0v1); - let v = ray.direction.dot(qvec) * inv_det; - - if v < 0.0 || u + v > 1.0 { - return false; - } - - *t = v0v2.dot(qvec) * inv_det; - - true -} diff --git a/src/ray.rs b/src/ray.rs new file mode 100644 index 0000000..da27b6b --- /dev/null +++ b/src/ray.rs @@ -0,0 +1,41 @@ +use cgmath::{vec2, vec3, InnerSpace, Vector3}; + +use super::view::View; + +pub struct Ray { + pub origin: Vector3, + pub direction: Vector3, + pub inv_direction: Vector3, + pub signs: Vector3, +} + +impl Ray { + pub fn new(origin: Vector3, direction: Vector3) -> Ray { + let inv_direction = 1.0 / direction; + + let x = (inv_direction.x < 0.0) as usize; + let y = (inv_direction.y < 0.0) as usize; + let z = (inv_direction.z < 0.0) as usize; + + Ray { + origin, + direction, + inv_direction, + signs: Vector3::new(x, y, z), + } + } + + pub fn primary_ray(x: u32, y: u32, dim_y: u32, dim_x: u32, view: &View) -> Ray { + let aspect_ratio = dim_x as f32 / dim_y as f32; + + let uv = vec2(x as f32, y as f32); + + let (up, right) = view.axises(); + + let trans = 2.0 * uv - vec2(1.0, 1.0); + let mut dir = view.look_at + right * trans.x + up * trans.y; + dir = vec3(dir.x * aspect_ratio, dir.y, dir.z * aspect_ratio); + + return Self::new(view.position, dir.normalize()); + } +} diff --git a/src/triangle.rs b/src/triangle.rs new file mode 100644 index 0000000..a18ef37 --- /dev/null +++ b/src/triangle.rs @@ -0,0 +1,48 @@ +use cgmath::{InnerSpace, Vector3}; + +use super::ray::Ray; + +pub struct Triangle { + pub points: [Vector3; 3], +} + +impl Triangle { + pub fn new(v0: Vector3, v1: Vector3, v2: Vector3) -> Triangle { + Triangle { + points: [v0, v1, v2], + } + } + + // source: https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/moller-trumbore-ray-triangle-intersection + pub fn intersect_mt(&self, ray: &Ray, t: &mut f32) -> bool { + let v0v1 = self.points[1] - self.points[0]; + let v0v2 = self.points[2] - self.points[0]; + let pvec = ray.direction.cross(v0v2); + let det = v0v1.dot(pvec); + + // ray and triangle are parallel if det is close to 0 + if det.abs() < 0.001 { + return false; + } + + let inv_det = 1.0 / det; + + let tvec = ray.origin - self.points[0]; + let u = tvec.dot(pvec) * inv_det; + + if u < 0.0 || u > 1.0 { + return false; + } + + let qvec = tvec.cross(v0v1); + let v = ray.direction.dot(qvec) * inv_det; + + if v < 0.0 || u + v > 1.0 { + return false; + } + + *t = v0v2.dot(qvec) * inv_det; + + true + } +} diff --git a/src/view.rs b/src/view.rs new file mode 100644 index 0000000..b4ca7cc --- /dev/null +++ b/src/view.rs @@ -0,0 +1,28 @@ +use cgmath::{InnerSpace, Vector3}; + +use std::f32::consts::PI; + +pub struct View { + pub position: Vector3, + pub look_at: Vector3, + pub up: Vector3, + pub fov: f32, +} + +impl View { + /// returns `up` and `right` + pub fn axises(&self) -> (Vector3, Vector3) { + let view_dir = self.look_at - self.position; + + let horiz_axis = view_dir.cross(self.up).normalize(); + let vert_axis = horiz_axis.cross(view_dir).normalize(); + let right = horiz_axis * Self::scale(self.fov); + let up = vert_axis * Self::scale(-self.fov); + + (up, right) + } + + fn scale(fov: f32) -> f32 { + (0.5 * fov * PI / 180.0).tan() + } +}