Working basic triangle intersection.

This commit is contained in:
Bill Thiede 2023-01-17 21:32:28 -08:00
parent f8ec874d13
commit 9e81acfda9
2 changed files with 185 additions and 66 deletions

View File

@ -11,13 +11,14 @@ use crate::{
renderer::{Opt, Scene},
sphere::Sphere,
texture::ConstantTexture,
translate::Translate,
triangles::Triangles,
vec3::Vec3,
};
pub fn new(opt: &Opt) -> Scene {
let lookfrom = Vec3::new(-100., 200., 200.);
let lookat = Vec3::new(0., 1., 0.);
let lookfrom = Vec3::new(-20., 100., -100.);
let lookat = Vec3::new(0., 10., 0.);
let dist_to_focus = 10.0;
let aperture = 0.0;
let time_min = 0.;
@ -46,12 +47,6 @@ pub fn new(opt: &Opt) -> Scene {
.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., 1., 1.))),
1.,
);
dbg!(tri_mesh.triangles.len(), tri_mesh.bbox);
let objects: Vec<Box<dyn Hit>> = vec![
// Light from above
Box::new(XZRect::new(
@ -98,7 +93,15 @@ pub fn new(opt: &Opt) -> Scene {
20.,
Metal::new(Vec3::new(0.8, 0.8, 0.8), 0.2),
)),
Box::new(tri_mesh),
// STL Mesh
Box::new(Translate::new(
Triangles::new(
&stl_cube,
Lambertian::new(ConstantTexture::new(Vec3::new(1., 1.0, 1.0))),
1.,
),
[-10., 0., 0.],
)),
];
let world: Box<dyn Hit> = if opt.use_accel {
Box::new(KDTree::new(objects, time_min, time_max))

View File

@ -1,3 +1,5 @@
use std::f32::EPSILON;
use stl::STL;
use crate::{
@ -13,11 +15,6 @@ pub struct Triangle {
normal: Vec3,
verts: [Vec3; 3],
// Precomputed data
// TODO(wathiede): precompute `d` on load.
d: f32,
edge0: Vec3,
edge1: Vec3,
edge2: Vec3,
}
#[derive(Debug)]
@ -42,13 +39,17 @@ where
let v0 = t.verts[0] * scale_factor;
let v1 = t.verts[1] * scale_factor;
let v2 = t.verts[2] * scale_factor;
assert_eq!(
t.normal,
cross(v1 - v0, v2 - v0).unit_vector(),
"v1 {} v2 {} v3 {}",
v0,
v1,
v2
);
Triangle {
normal: t.normal,
verts: [v0, v1, v2],
d: -dot(t.normal, t.verts[0]),
edge0: v1 - v0,
edge1: v2 - v1,
edge2: v0 - v2,
}
})
.collect();
@ -77,62 +78,172 @@ where
}
}
/// Based on https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/moller-trumbore-ray-triangle-intersection.html
fn ray_triangle_intersect_moller_trumbore1(r: Ray, tri: &Triangle) -> Option<RayTriangleResult> {
// #ifdef MOLLER_TRUMBORE
// Vec3f v0v1 = v1 - v0;
// Vec3f v0v2 = v2 - v0;
// Vec3f pvec = dir.crossProduct(v0v2);
// float det = v0v1.dotProduct(pvec);
// #ifdef CULLING
// // if the determinant is negative, the triangle is 'back facing'
// // if the determinant is close to 0, the ray misses the triangle
// if (det < kEpsilon) return false;
// #else
// // ray and triangle are parallel if det is close to 0
// if (fabs(det) < kEpsilon) return false;
// #endif
// float invDet = 1 / det;
//
// Vec3f tvec = orig - v0;
// u = tvec.dotProduct(pvec) * invDet;
// if (u < 0 || u > 1) return false;
//
// Vec3f qvec = tvec.crossProduct(v0v1);
// v = dir.dotProduct(qvec) * invDet;
// if (v < 0 || u + v > 1) return false;
//
// t = v0v2.dotProduct(qvec) * invDet;
//
let v0 = tri.verts[0];
let v1 = tri.verts[1];
let v2 = tri.verts[2];
let v0v1 = v1 - v0;
let v0v2 = v2 - v0;
let p = cross(r.direction, v0v2);
let det = dot(v0v1, p);
if det < EPSILON {
return None;
}
let inv_det = 1. / det;
let t = r.origin - v0;
let u = dot(t, p) * inv_det;
if u < 0. || u > 1. {
return None;
}
let q = cross(t, v0v1);
let v = dot(r.direction, q) * inv_det;
if v < 0. || u + v > 1. {
return None;
}
let t = dot(v0v2, q) * inv_det;
if t > EPSILON {
return Some(RayTriangleResult {
t,
p: r.origin + r.direction * t,
});
}
None
}
// From https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm
fn ray_triangle_intersect_moller_trumbore2(r: Ray, tri: &Triangle) -> Option<RayTriangleResult> {
let v0 = tri.verts[0];
let v1 = tri.verts[1];
let v2 = tri.verts[2];
let edge1 = v1 - v0;
let edge2 = v2 - v0;
let h = cross(r.direction, edge2);
let a = dot(edge1, h);
if a < EPSILON {
return None;
}
let f = 1. / a;
let s = r.origin - v0;
let u = f * dot(s, h);
if u < 0. || u > 1. {
return None;
}
let q = cross(s, edge1);
let v = f * dot(r.direction, q);
if v < 0. || u + v > 1. {
return None;
}
// At this stage we can compute t to find out where the intersection point is on the line.
let t = f * dot(edge2, q);
// ray intersection
if t > EPSILON {
return Some(RayTriangleResult {
t,
p: r.origin + r.direction * t,
});
}
// This means that there is a line intersection but not a ray intersection.
None
}
/// Based on https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/ray-triangle-intersection-geometric-solution.html
fn ray_triangle_intersect_geometric(r: Ray, tri: &Triangle) -> Option<RayTriangleResult> {
let n = tri.normal;
// close enough to parallel, won't hit
let n_dot_dir = dot(n, r.direction);
if n_dot_dir < EPSILON {
return None;
}
let v0 = tri.verts[0];
let v1 = tri.verts[1];
let v2 = tri.verts[2];
let d = -dot(n, v0);
let t = -(dot(n, r.origin) + d) / n_dot_dir;
// check if the triangle is behind the ray
if t < 0. {
return None;
}
let p = r.origin + t * r.direction;
let edge0 = v1 - v0;
let edge1 = v2 - v1;
let edge2 = v0 - v2;
let vp0 = p - v0;
let c = cross(edge0, vp0);
if dot(n, c) < 0. {
return None;
}
let vp1 = p - v1;
let c = cross(edge1, vp1);
if dot(n, c) < 0. {
return None;
}
let vp2 = p - v2;
let c = cross(edge2, vp2);
if dot(n, c) < 0. {
return None;
}
Some(RayTriangleResult { t, p })
}
impl<M> Hit for Triangles<M>
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<HitRecord> {
// TODO(wathiede): add an acceleration structure to more cheaply skip some triangles.
for tri in &self.triangles {
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;
if let Some(RayTriangleResult { t, p }) =
ray_triangle_intersect_moller_trumbore1(r, tri)
{
//if let Some(RayTriangleResult { t, p }) = ray_triangle_intersect_geometric(r, tri) {
// We don't support UV (yet?).
let uv = (0.5, 0.5);
return Some(HitRecord {
t,
uv,
p,
normal: tri.normal,
material: &self.material,
});
}
let t = -(dot(n, r.origin) + tri.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 vp0 = p - v0;
let c = cross(tri.edge0, vp0);
if dot(n, c) < 0. {
continue;
}
let vp1 = p - v1;
let c = cross(tri.edge1, vp1);
if dot(n, c) < 0. {
continue;
}
let vp2 = p - v2;
let c = cross(tri.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
}
@ -141,3 +252,8 @@ where
Some(self.bbox)
}
}
struct RayTriangleResult {
t: f32,
p: Vec3,
}