From a0fb4637b5e81c0e9a2a7dc504b797c129265429 Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Sun, 15 Jan 2023 15:15:23 -0800 Subject: [PATCH] rtiow: add ability to render single material triangle mesh. --- rtiow/renderer/src/renderer.rs | 4 + rtiow/renderer/src/scenes/mod.rs | 1 + rtiow/renderer/src/scenes/stltest.rs | 116 +++++++++++++++++++++++++++ rtiow/renderer/src/triangles.rs | 63 ++++++++++++++- rtiow/renderer/stls/20mm cube.stl | Bin 0 -> 684 bytes rtiow/tracer/Cargo.toml | 2 +- 6 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 rtiow/renderer/src/scenes/stltest.rs create mode 100644 rtiow/renderer/stls/20mm cube.stl diff --git a/rtiow/renderer/src/renderer.rs b/rtiow/renderer/src/renderer.rs index fd5b356..21aeb04 100644 --- a/rtiow/renderer/src/renderer.rs +++ b/rtiow/renderer/src/renderer.rs @@ -41,6 +41,7 @@ pub enum Model { Mandelbrot, PerlinDebug, Spheramid, + Stltest, Test, Tron, Tutorial, @@ -58,6 +59,7 @@ impl Model { Model::Mandelbrot => scenes::mandelbrot::new(opt), Model::PerlinDebug => scenes::perlin_debug::new(opt), Model::Spheramid => scenes::spheramid::new(opt), + Model::Stltest => scenes::stltest::new(opt), Model::Test => scenes::test::new(opt), Model::Tron => scenes::tron::new(opt), Model::Tutorial => scenes::tutorial::new(opt), @@ -87,6 +89,7 @@ impl str::FromStr for Model { "mandelbrot" => Ok(Model::Mandelbrot), "perlin_debug" => Ok(Model::PerlinDebug), "spheramid" => Ok(Model::Spheramid), + "stltest" => Ok(Model::Stltest), "test" => Ok(Model::Test), "tron" => Ok(Model::Tron), "tutorial" => Ok(Model::Tutorial), @@ -107,6 +110,7 @@ impl std::string::ToString for Model { Model::Mandelbrot => "mandelbrot".to_string(), Model::PerlinDebug => "perlin_debug".to_string(), Model::Spheramid => "spheramid".to_string(), + Model::Stltest => "stltest".to_string(), Model::Test => "test".to_string(), Model::Tron => "tron".to_string(), Model::Tutorial => "tutorial".to_string(), diff --git a/rtiow/renderer/src/scenes/mod.rs b/rtiow/renderer/src/scenes/mod.rs index 4bf9ced..1d71f34 100644 --- a/rtiow/renderer/src/scenes/mod.rs +++ b/rtiow/renderer/src/scenes/mod.rs @@ -7,6 +7,7 @@ pub mod final_scene; pub mod mandelbrot; pub mod perlin_debug; pub mod spheramid; +pub mod stltest; pub mod test; pub mod tron; pub mod tutorial; diff --git a/rtiow/renderer/src/scenes/stltest.rs b/rtiow/renderer/src/scenes/stltest.rs new file mode 100644 index 0000000..a334fca --- /dev/null +++ b/rtiow/renderer/src/scenes/stltest.rs @@ -0,0 +1,116 @@ +use std::io::{BufReader, Cursor}; +use stl::STL; + +use crate::{ + camera::Camera, + hitable::Hit, + hitable_list::HitableList, + kdtree::KDTree, + material::{DiffuseLight, Lambertian, Metal}, + rect::{XYRect, XZRect}, + renderer::{Opt, Scene}, + sphere::Sphere, + texture::ConstantTexture, + triangles::Triangles, + vec3::Vec3, +}; + +pub fn new(opt: &Opt) -> Scene { + let lookfrom = Vec3::new(200., 200., 200.); + let lookat = Vec3::new(0., 1., 0.); + let dist_to_focus = 10.0; + let aperture = 0.0; + let time_min = 0.; + let time_max = 1.; + let camera = Camera::new( + lookfrom, + lookat, + Vec3::new(0., 1., 0.), + 20., + opt.width as f32 / opt.height as f32, + aperture, + dist_to_focus, + time_min, + time_max, + ); + let ground_color = if opt.use_accel { + ConstantTexture::new(Vec3::new(1.0, 0.4, 0.4)) + } else { + ConstantTexture::new(Vec3::new(0.4, 1.0, 0.4)) + }; + + let stl_cube = STL::parse( + BufReader::new(Cursor::new(include_bytes!("../../stls/20mm cube.stl"))), + false, + ) + .expect("failed to parse cube"); + let light_size = 50.; + let light_height = 200.; + let tri_mesh = Triangles::new( + &stl_cube, + Lambertian::new(ConstantTexture::new(Vec3::new(1., 0., 0.))), + ); + dbg!(&tri_mesh); + let objects: Vec> = vec![ + // Light from above + Box::new(XZRect::new( + -light_size, + light_size, + -light_size, + light_size, + light_height, + DiffuseLight::new(ConstantTexture::new(Vec3::new(15., 15., 15.))), + )), + // Light from back + Box::new(XYRect::new( + -light_size, + light_size, + -light_size, + light_size, + -light_height, + DiffuseLight::new(ConstantTexture::new(Vec3::new(1., 15., 1.))), + )), + // Light from front + Box::new(XYRect::new( + -light_size, + light_size, + -light_size, + light_size, + light_height, + DiffuseLight::new(ConstantTexture::new(Vec3::new(1., 1., 15.))), + )), + // Earth sized sphere + Box::new(Sphere::new( + Vec3::new(0., -1200., 0.), + 1000., + Lambertian::new(ground_color), + )), + // Blue sphere + Box::new(Sphere::new( + Vec3::new(-40., 20., 0.), + 20., + Lambertian::new(ConstantTexture::new(Vec3::new(0.1, 0.2, 0.5))), + )), + // Shiny sphere + Box::new(Sphere::new( + Vec3::new(40., 20., 0.), + 20., + Metal::new(Vec3::new(0.8, 0.8, 0.8), 0.2), + )), + Box::new(tri_mesh), + ]; + let world: Box = if opt.use_accel { + Box::new(KDTree::new(objects, time_min, time_max)) + } else { + Box::new(HitableList::new(objects)) + }; + Scene { + camera, + world, + subsamples: opt.subsamples, + num_threads: opt.num_threads, + width: opt.width, + height: opt.height, + ..Default::default() + } +} diff --git a/rtiow/renderer/src/triangles.rs b/rtiow/renderer/src/triangles.rs index 2133e96..9cbc2e8 100644 --- a/rtiow/renderer/src/triangles.rs +++ b/rtiow/renderer/src/triangles.rs @@ -5,12 +5,13 @@ use crate::{ hitable::{Hit, HitRecord}, material::Material, ray::Ray, - vec3::{dot, Vec3}, + vec3::{cross, dot, Vec3}, }; #[derive(Debug)] pub struct Triangle { pub normal: Vec3, + // TODO(wathiede): precompute `d` on load. pub verts: [Vec3; 3], } @@ -28,7 +29,7 @@ impl Triangles where M: Material, { - pub fn new(stl: &STL, material: M) -> Triangles { + pub fn new(stl: &STL, material: M) -> Triangles { let triangles: Vec<_> = stl .triangles .iter() @@ -66,7 +67,65 @@ impl Hit for Triangles where M: Material, { + // Based on https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/ray-triangle-intersection-geometric-solution.html fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option { + for tri in &self.triangles { + let v0 = tri.verts[0]; + let v1 = tri.verts[1]; + let v2 = tri.verts[2]; + let n = tri.normal; + + // close enough to parallel, won't hit + // TODO(wathiede): verify EPSILON + const EPSILON: f32 = 0.00001; + let n_dot_dir = dot(n, r.direction); + if n_dot_dir < EPSILON { + continue; + } + + let d = -dot(n, tri.verts[0]); + let t = -(dot(n, r.origin) + d) / n_dot_dir; + // check if the triangle is behind the ray + if t < 0. { + continue; + } + + let p = r.origin + t * r.direction; + let v0 = tri.verts[0]; + let v1 = tri.verts[1]; + let v2 = tri.verts[2]; + + let edge0 = v1 - v0; + let vp0 = p - v0; + let c = cross(edge0, vp0); + if dot(n, c) < 0. { + continue; + } + + let edge1 = v2 - v1; + let vp1 = p - v1; + let c = cross(edge1, vp1); + if dot(n, c) < 0. { + continue; + } + + let edge2 = v0 - v2; + let vp2 = p - v2; + let c = cross(edge2, vp2); + if dot(n, c) < 0. { + continue; + } + + // We don't support UV (yet?). + let uv = (0., 0.); + return Some(HitRecord { + t, + uv, + p, + normal: n, + material: &self.material, + }); + } None } diff --git a/rtiow/renderer/stls/20mm cube.stl b/rtiow/renderer/stls/20mm cube.stl new file mode 100644 index 0000000000000000000000000000000000000000..fb95ed5b01a7163b883d3a08ffa295621857ed39 GIT binary patch literal 684 zcmZReGT=cmuVFt57tC4U2&NcdG=v8x8yFa1I$*LO8iYaeFdD>WU|?u~&<*ww8YBzC z5FP{Ad=MXop*lczKy`prBFW;=0d`RX#5|Y|5Dmh}x