rtiow: add ability to render single material triangle mesh.
This commit is contained in:
parent
6069bf9a65
commit
a0fb4637b5
@ -41,6 +41,7 @@ pub enum Model {
|
|||||||
Mandelbrot,
|
Mandelbrot,
|
||||||
PerlinDebug,
|
PerlinDebug,
|
||||||
Spheramid,
|
Spheramid,
|
||||||
|
Stltest,
|
||||||
Test,
|
Test,
|
||||||
Tron,
|
Tron,
|
||||||
Tutorial,
|
Tutorial,
|
||||||
@ -58,6 +59,7 @@ impl Model {
|
|||||||
Model::Mandelbrot => scenes::mandelbrot::new(opt),
|
Model::Mandelbrot => scenes::mandelbrot::new(opt),
|
||||||
Model::PerlinDebug => scenes::perlin_debug::new(opt),
|
Model::PerlinDebug => scenes::perlin_debug::new(opt),
|
||||||
Model::Spheramid => scenes::spheramid::new(opt),
|
Model::Spheramid => scenes::spheramid::new(opt),
|
||||||
|
Model::Stltest => scenes::stltest::new(opt),
|
||||||
Model::Test => scenes::test::new(opt),
|
Model::Test => scenes::test::new(opt),
|
||||||
Model::Tron => scenes::tron::new(opt),
|
Model::Tron => scenes::tron::new(opt),
|
||||||
Model::Tutorial => scenes::tutorial::new(opt),
|
Model::Tutorial => scenes::tutorial::new(opt),
|
||||||
@ -87,6 +89,7 @@ impl str::FromStr for Model {
|
|||||||
"mandelbrot" => Ok(Model::Mandelbrot),
|
"mandelbrot" => Ok(Model::Mandelbrot),
|
||||||
"perlin_debug" => Ok(Model::PerlinDebug),
|
"perlin_debug" => Ok(Model::PerlinDebug),
|
||||||
"spheramid" => Ok(Model::Spheramid),
|
"spheramid" => Ok(Model::Spheramid),
|
||||||
|
"stltest" => Ok(Model::Stltest),
|
||||||
"test" => Ok(Model::Test),
|
"test" => Ok(Model::Test),
|
||||||
"tron" => Ok(Model::Tron),
|
"tron" => Ok(Model::Tron),
|
||||||
"tutorial" => Ok(Model::Tutorial),
|
"tutorial" => Ok(Model::Tutorial),
|
||||||
@ -107,6 +110,7 @@ impl std::string::ToString for Model {
|
|||||||
Model::Mandelbrot => "mandelbrot".to_string(),
|
Model::Mandelbrot => "mandelbrot".to_string(),
|
||||||
Model::PerlinDebug => "perlin_debug".to_string(),
|
Model::PerlinDebug => "perlin_debug".to_string(),
|
||||||
Model::Spheramid => "spheramid".to_string(),
|
Model::Spheramid => "spheramid".to_string(),
|
||||||
|
Model::Stltest => "stltest".to_string(),
|
||||||
Model::Test => "test".to_string(),
|
Model::Test => "test".to_string(),
|
||||||
Model::Tron => "tron".to_string(),
|
Model::Tron => "tron".to_string(),
|
||||||
Model::Tutorial => "tutorial".to_string(),
|
Model::Tutorial => "tutorial".to_string(),
|
||||||
|
|||||||
@ -7,6 +7,7 @@ pub mod final_scene;
|
|||||||
pub mod mandelbrot;
|
pub mod mandelbrot;
|
||||||
pub mod perlin_debug;
|
pub mod perlin_debug;
|
||||||
pub mod spheramid;
|
pub mod spheramid;
|
||||||
|
pub mod stltest;
|
||||||
pub mod test;
|
pub mod test;
|
||||||
pub mod tron;
|
pub mod tron;
|
||||||
pub mod tutorial;
|
pub mod tutorial;
|
||||||
|
|||||||
116
rtiow/renderer/src/scenes/stltest.rs
Normal file
116
rtiow/renderer/src/scenes/stltest.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,12 +5,13 @@ use crate::{
|
|||||||
hitable::{Hit, HitRecord},
|
hitable::{Hit, HitRecord},
|
||||||
material::Material,
|
material::Material,
|
||||||
ray::Ray,
|
ray::Ray,
|
||||||
vec3::{dot, Vec3},
|
vec3::{cross, dot, Vec3},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Triangle {
|
pub struct Triangle {
|
||||||
pub normal: Vec3,
|
pub normal: Vec3,
|
||||||
|
// TODO(wathiede): precompute `d` on load.
|
||||||
pub verts: [Vec3; 3],
|
pub verts: [Vec3; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,7 +29,7 @@ impl<M> Triangles<M>
|
|||||||
where
|
where
|
||||||
M: Material,
|
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
|
let triangles: Vec<_> = stl
|
||||||
.triangles
|
.triangles
|
||||||
.iter()
|
.iter()
|
||||||
@ -66,7 +67,65 @@ impl<M> Hit for Triangles<M>
|
|||||||
where
|
where
|
||||||
M: Material,
|
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> {
|
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
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
BIN
rtiow/renderer/stls/20mm cube.stl
Normal file
BIN
rtiow/renderer/stls/20mm cube.stl
Normal file
Binary file not shown.
@ -10,4 +10,4 @@ edition = "2021"
|
|||||||
log = "0.4.5"
|
log = "0.4.5"
|
||||||
renderer = { path = "../renderer" }
|
renderer = { path = "../renderer" }
|
||||||
stderrlog = "0.4.1"
|
stderrlog = "0.4.1"
|
||||||
structopt = "0.2.10"
|
structopt = "0.2.10"
|
||||||
Loading…
x
Reference in New Issue
Block a user