rtiow: add ability to render single material triangle mesh.

This commit is contained in:
Bill Thiede 2023-01-15 15:15:23 -08:00
parent 6069bf9a65
commit a0fb4637b5
6 changed files with 183 additions and 3 deletions

View File

@ -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(),

View File

@ -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;

View File

@ -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<Box<dyn Hit>> = 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<dyn Hit> = 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()
}
}

View File

@ -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<M> Triangles<M>
where
M: Material,
{
pub fn new<V>(stl: &STL, material: M) -> Triangles<M> {
pub fn new(stl: &STL, material: M) -> Triangles<M> {
let triangles: Vec<_> = stl
.triangles
.iter()
@ -66,7 +67,65 @@ 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> {
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
}

Binary file not shown.

View File

@ -10,4 +10,4 @@ edition = "2021"
log = "0.4.5"
renderer = { path = "../renderer" }
stderrlog = "0.4.1"
structopt = "0.2.10"
structopt = "0.2.10"