From c8ce9060b1555b22d986fe56b5aef70a0872bd57 Mon Sep 17 00:00:00 2001 From: JonOfUs Date: Tue, 12 Jul 2022 17:55:16 +0200 Subject: [PATCH] Add Hittable Triangle --- src/hittable.rs | 60 +++++++++++++++++++++++++++++++++++-------------- src/main.rs | 51 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 88 insertions(+), 23 deletions(-) diff --git a/src/hittable.rs b/src/hittable.rs index 982a59f..42a2908 100644 --- a/src/hittable.rs +++ b/src/hittable.rs @@ -1,11 +1,11 @@ use super::{Arc, Color, Material, Metal, Point3, Ray, Vec3}; pub struct HitRecord { - pub p: Point3, - pub normal: Vec3, + pub p: Point3, // hit location + pub normal: Vec3, // normal of hit location pub mat_ptr: Arc, - pub t: f64, - pub front_face: bool, + pub t: f64, // ray steps (distance) from camera to hit + pub front_face: bool, // whether object was hitted from front or back } pub struct Sphere { @@ -113,41 +113,67 @@ impl Triangle { impl Hittable for Triangle { fn hit(&self, r: &Ray, t_min: f64, t_max: f64, rec: &mut HitRecord) -> bool { + let epsilon = 0.0000001; + let a_to_b = self.b - self.a; let a_to_c = self.c - self.a; - let u_vec = Vec3::cross(r.direction(), a_to_c); + let h = Vec3::cross(r.direction(), a_to_c); - let det = Vec3::dot(a_to_b, u_vec); + let a = Vec3::dot(a_to_b, h); - if det < 0.0 { // only positive bound + if a > -epsilon && a < epsilon { + // ray is parallel to triangle return false; } - let inv_det = 1.0 / det; + let f = 1.0 / a; let a_to_origin = r.origin() - self.a; - let u = Vec3::dot(a_to_origin, u_vec) * inv_det; + let u = Vec3::dot(a_to_origin, h) * f; - if u < 0.0 || u > 1.0 { //check if outside of triangle + if u < 0.0 || u > 1.0 { + //check if outside of triangle return false; } - let v_vec = Vec3::cross(a_to_origin, a_to_b); + let q = Vec3::cross(a_to_origin, a_to_b); - let v = Vec3::dot(r.direction(), v_vec) * inv_det; + let v = Vec3::dot(r.direction(), q) * f; - if v < 0.0 || u+v > 1.0 { // intersection outside of triangle + if v < 0.0 || u + v > 1.0 { + // intersection outside of triangle return false; } - let dist = Vec3::dot(a_to_c, v_vec) * inv_det; + let t = Vec3::dot(a_to_c, q) * f; // where is intersection on ray? - if dist > 0.0 { - + if t <= epsilon { + // line intersection but no ray intersection + return false; + } + + // ray intersection + rec.t = t; + rec.p = r.at(t); + rec.mat_ptr = self.mat_ptr.clone(); + + // triangle normal alg from https://stackoverflow.com/questions/19350792/calculate-normal-of-a-single-triangle-in-3d-space + let normal_to_camera = Vec3::new( + a_to_b.y() * a_to_c.z() - a_to_b.z() * a_to_c.y(), + a_to_b.z() * a_to_c.x() - a_to_b.x() * a_to_c.z(), + a_to_b.x() * a_to_c.y() - a_to_b.y() * a_to_c.x(), + ); + let unit_normal = normal_to_camera / normal_to_camera.length(); + if Vec3::dot(unit_normal, r.direction()) >= 0.0 { + // normal towards camera + rec.set_face_normal(r, unit_normal); + } else { + // normal not towards camera, has to be flipped + rec.set_face_normal(r, -1.0 * unit_normal); } return true; } -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index dffb2cb..ddf42ad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,7 @@ mod utility; mod vec3; use camera::Camera; -use hittable::{HitRecord, Hittable, Sphere}; +use hittable::{HitRecord, Hittable, Sphere, Triangle}; use hittable_list::HittableList; use image::{Rgb, RgbImage}; use material::{Dielectric, Lambertian, Material, Metal}; @@ -50,14 +50,13 @@ fn ray_color(r: &Ray, world: &HittableList, depth: u32) -> Color { fn random_world() -> HittableList { let mut world = HittableList::new(); - let material_ground = Arc::new(Lambertian::new(&Color::new(0.05, 0.05,0.05))); + let material_ground = Arc::new(Lambertian::new(&Color::new(0.05, 0.05, 0.05))); world.add(Box::::new(Sphere::new( Point3::new(0.0, -50000.0, 0.0), 50000.0, material_ground.clone(), ))); - (-6..5).into_iter().for_each(|a| { (-6..5).into_iter().for_each(|b| { let choose_mat = utility::random_f64(); @@ -138,18 +137,58 @@ fn random_world() -> HittableList { material5.clone(), ))); + world.add(Box::::new(Triangle::new( + Point3::new(0.0, 1.0, 5.0), + Point3::new(3.0, 2.0, 0.0), + Point3::new(0.0, 4.0, 0.0), + material2.clone(), + ))); + + world.add(Box::::new(Triangle::new( + Point3::new(5.0, 1.0, -6.0), + Point3::new(1.0, 3.0, -5.0), + Point3::new(6.0, 4.0, -6.0), + material5.clone(), + ))); + world.add(Box::::new(Triangle::new( + Point3::new(5.0, 1.0, -6.0), + Point3::new(8.0, 1.0, -7.0), + Point3::new(6.0, 4.0, -6.0), + material5.clone(), + ))); + world.add(Box::::new(Triangle::new( + Point3::new(8.0, 4.0, -5.0), + Point3::new(8.0, 1.0, -7.0), + Point3::new(6.0, 4.0, -6.0), + material5.clone(), + ))); + return world; } +/* +Current world view: + + I y + I + I + I + / \ + / \ + / \ + / z \ x + +*/ + fn main() { // File let mut default_file = "image.ppm"; // Image let aspect_ratio = 16.0 / 9.0; - let image_width = 600; + let image_width = 1200; let image_height = (image_width as f64 / aspect_ratio) as u32; - let samples_per_pixel = 50_u32; + let samples_per_pixel = 100_u32; let max_depth = 50; let vfov = 40.0; @@ -183,7 +222,7 @@ fn main() { let atomic_counter = Arc::new(AtomicU32::new(0)); let color_lines: Vec<_> = (0..image_height) - .into_par_iter() // threadded/parallel variant + .into_par_iter() // threadded/parallel variant //.into_iter() // iterative variant .rev() .map(|j| {