Compare commits
No commits in common. "2d696932e39653dd16323fe166da36a463284458" and "a12938db9556d5cc2c39f4bba420a059cd72f8f3" have entirely different histories.
2d696932e3
...
a12938db95
658
rtiow/Cargo.lock
generated
658
rtiow/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -5,8 +5,9 @@ members = [
|
|||||||
"noise_explorer_warp",
|
"noise_explorer_warp",
|
||||||
"renderer",
|
"renderer",
|
||||||
"tracer",
|
"tracer",
|
||||||
"vec3",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[profile]
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
|
|||||||
@ -2,19 +2,19 @@
|
|||||||
name = "noise_explorer"
|
name = "noise_explorer"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Bill Thiede <git@xinu.tv>"]
|
authors = ["Bill Thiede <git@xinu.tv>"]
|
||||||
edition = "2021"
|
edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = "0.7.19"
|
actix-web = "0.7.8"
|
||||||
askama = "0.7.2"
|
askama = "0.7.1"
|
||||||
image = "0.22.5"
|
image = "0.22.3"
|
||||||
log = "0.4.17"
|
log = "0.4.5"
|
||||||
rand = "0.8.5"
|
rand = "0.8.3"
|
||||||
rand_xorshift = "0.3.0"
|
rand_xorshift = "0.3.0"
|
||||||
renderer = { path = "../renderer" }
|
renderer = { path = "../renderer" }
|
||||||
serde = "1.0.152"
|
serde = "1.0.79"
|
||||||
serde_derive = "1.0.152"
|
serde_derive = "1.0.79"
|
||||||
stderrlog = "0.4.3"
|
stderrlog = "0.4.1"
|
||||||
structopt = "0.2.18"
|
structopt = "0.2.10"
|
||||||
|
|||||||
@ -2,18 +2,18 @@
|
|||||||
name = "noise_explorer_warp"
|
name = "noise_explorer_warp"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Bill Thiede <git@xinu.tv>"]
|
authors = ["Bill Thiede <git@xinu.tv>"]
|
||||||
edition = "2021"
|
edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
askama = "0.7.2"
|
askama = "0.7.1"
|
||||||
image = "0.22.5"
|
image = "0.22.3"
|
||||||
log = "0.4.17"
|
log = "0.4.5"
|
||||||
rand = "0.5.6"
|
rand = "0.5.5"
|
||||||
renderer = { path = "../renderer" }
|
renderer = { path = "../renderer" }
|
||||||
serde = "1.0.152"
|
serde = "1.0.79"
|
||||||
serde_derive = "1.0.152"
|
serde_derive = "1.0.79"
|
||||||
stderrlog = "0.4.3"
|
stderrlog = "0.4.1"
|
||||||
structopt = "0.2.18"
|
structopt = "0.2.10"
|
||||||
warp = "0.1.23"
|
warp = "0.1.20"
|
||||||
|
|||||||
@ -2,35 +2,29 @@
|
|||||||
name = "renderer"
|
name = "renderer"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Bill Thiede <git@xinu.tv>"]
|
authors = ["Bill Thiede <git@xinu.tv>"]
|
||||||
edition = "2021"
|
edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
[[bench]]
|
[[bench]]
|
||||||
harness = false
|
harness = false
|
||||||
name = "spheres"
|
name = "spheres"
|
||||||
[[bench]]
|
|
||||||
harness = false
|
|
||||||
name = "aabb"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "*"
|
chrono = "*"
|
||||||
core_affinity = "0.5"
|
core_affinity = "0.5"
|
||||||
cpuprofiler = { version = "0.0.3", optional = true }
|
cpuprofiler = { version = "0.0.3", optional = true }
|
||||||
image = "0.22.5"
|
image = "0.22.3"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.1.0"
|
||||||
log = "0.4.17"
|
log = "0.4.5"
|
||||||
num_cpus = "1.15.0"
|
num_cpus = "1.8.0"
|
||||||
rand = "0.8.5"
|
rand = "0.8.3"
|
||||||
serde = "1.0.152"
|
serde = "1.0.79"
|
||||||
serde_derive = "1.0.152"
|
serde_derive = "1.0.79"
|
||||||
serde_json = "1.0.91"
|
serde_json = "1.0.41"
|
||||||
structopt = "0.2.18"
|
structopt = "0.2.10"
|
||||||
vec3 = {path = "../vec3"}
|
|
||||||
stl = {path = "../../../stl"}
|
|
||||||
#stl = {git = "https://git-private.z.xinu.tv/wathiede/stl"}
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.4"
|
criterion = "0.2"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
profile = ["cpuprofiler"]
|
profile = ["cpuprofiler"]
|
||||||
|
|||||||
@ -1,47 +0,0 @@
|
|||||||
use criterion::*;
|
|
||||||
use renderer::{aabb::AABB, ray::Ray};
|
|
||||||
|
|
||||||
fn bench(c: &mut Criterion) {
|
|
||||||
let bb = AABB::new([1., -1., -1.], [3., 1., 1.]);
|
|
||||||
let r_hit = Ray::new([0., 0., 0.], [1., 0., 0.], 0.);
|
|
||||||
let r_miss = Ray::new([0., 0., 0.], [-1., 0., 0.], 0.);
|
|
||||||
let t_min = 0.001;
|
|
||||||
let t_max = f32::MAX;
|
|
||||||
|
|
||||||
let mut group = c.benchmark_group("aabb");
|
|
||||||
group.throughput(Throughput::Elements(1));
|
|
||||||
group.bench_with_input(BenchmarkId::new("hit_naive", "r_hit"), &r_hit, |b, r| {
|
|
||||||
b.iter(|| bb.hit_naive(*r, t_min, t_max))
|
|
||||||
});
|
|
||||||
group.bench_with_input(BenchmarkId::new("hit2", "r_hit"), &r_hit, |b, r| {
|
|
||||||
b.iter(|| bb.hit2(*r, t_min, t_max))
|
|
||||||
});
|
|
||||||
//group.bench_with_input(BenchmarkId::new("hit_precompute", "r_hit"), &r_hit, |b, r| { b.iter(|| bb.hit_precompute(*r, t_min, t_max)) });
|
|
||||||
group.bench_with_input(BenchmarkId::new("hit_fast", "r_hit"), &r_hit, |b, r| {
|
|
||||||
b.iter(|| bb.hit_fast(*r, t_min, t_max))
|
|
||||||
});
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
group.bench_with_input(BenchmarkId::new("hit_simd", "r_hit"), &r_hit, |b, r| {
|
|
||||||
b.iter(|| bb.hit_simd(*r, t_min, t_max))
|
|
||||||
});
|
|
||||||
|
|
||||||
group.bench_with_input(BenchmarkId::new("hit_naive", "r_miss"), &r_miss, |b, r| {
|
|
||||||
b.iter(|| bb.hit_naive(*r, t_min, t_max))
|
|
||||||
});
|
|
||||||
group.bench_with_input(BenchmarkId::new("hit2", "r_miss"), &r_miss, |b, r| {
|
|
||||||
b.iter(|| bb.hit2(*r, t_min, t_max))
|
|
||||||
});
|
|
||||||
//group.bench_with_input(BenchmarkId::new("hit_precompute", "r_miss"), &r_miss, |b, r| { b.iter(|| bb.hit_precompute(*r, t_min, t_max)) });
|
|
||||||
group.bench_with_input(BenchmarkId::new("hit_fast", "r_miss"), &r_miss, |b, r| {
|
|
||||||
b.iter(|| bb.hit_fast(*r, t_min, t_max))
|
|
||||||
});
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
group.bench_with_input(BenchmarkId::new("hit_simd", "r_miss"), &r_miss, |b, r| {
|
|
||||||
b.iter(|| bb.hit_simd(*r, t_min, t_max))
|
|
||||||
});
|
|
||||||
|
|
||||||
group.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
criterion_group!(benches, bench);
|
|
||||||
criterion_main!(benches);
|
|
||||||
@ -1,6 +1,9 @@
|
|||||||
use criterion::*;
|
#[macro_use]
|
||||||
|
extern crate criterion;
|
||||||
|
|
||||||
use renderer::{
|
use criterion::{Criterion, ParameterizedBenchmark};
|
||||||
|
|
||||||
|
use rtiow::{
|
||||||
hitable::Hit, material::Lambertian, ray::Ray, sphere::Sphere, texture::ConstantTexture,
|
hitable::Hit, material::Lambertian, ray::Ray, sphere::Sphere, texture::ConstantTexture,
|
||||||
vec3::Vec3,
|
vec3::Vec3,
|
||||||
};
|
};
|
||||||
@ -17,15 +20,14 @@ fn criterion_benchmark(c: &mut Criterion) {
|
|||||||
// Miss
|
// Miss
|
||||||
Ray::new([0., 0., -2.], [0., 0., -1.], 0.),
|
Ray::new([0., 0., -2.], [0., 0., -1.], 0.),
|
||||||
];
|
];
|
||||||
let mut group = c.benchmark_group("sphere");
|
c.bench(
|
||||||
group.throughput(Throughput::Elements(1));
|
"sphere",
|
||||||
group.bench_with_input(BenchmarkId::new("Sphere", "hit"), &rays[0], |b, r| {
|
ParameterizedBenchmark::new(
|
||||||
b.iter(|| sphere.hit(*r, 0., 1.))
|
"Sphere",
|
||||||
});
|
move |b, r| b.iter(|| sphere.hit(*r, 0., 1.)),
|
||||||
group.bench_with_input(BenchmarkId::new("Sphere", "miss"), &rays[1], |b, r| {
|
rays,
|
||||||
b.iter(|| sphere.hit(*r, 0., 1.))
|
),
|
||||||
});
|
);
|
||||||
group.finish()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
criterion_group!(benches, criterion_benchmark);
|
criterion_group!(benches, criterion_benchmark);
|
||||||
|
|||||||
@ -2,7 +2,7 @@ use std::fmt;
|
|||||||
|
|
||||||
use crate::{ray::Ray, vec3::Vec3};
|
use crate::{ray::Ray, vec3::Vec3};
|
||||||
|
|
||||||
#[derive(Default, Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct AABB {
|
pub struct AABB {
|
||||||
bounds: [Vec3; 2],
|
bounds: [Vec3; 2],
|
||||||
}
|
}
|
||||||
@ -30,12 +30,7 @@ fn max(x: f32, y: f32) -> f32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AABB {
|
impl AABB {
|
||||||
pub fn new<V: Into<Vec3>>(min: V, max: V) -> AABB {
|
pub fn new(min: Vec3, max: Vec3) -> AABB {
|
||||||
let min: Vec3 = min.into();
|
|
||||||
let max: Vec3 = max.into();
|
|
||||||
assert!(min.x < max.x);
|
|
||||||
assert!(min.y < max.y);
|
|
||||||
assert!(min.z < max.z);
|
|
||||||
AABB { bounds: [min, max] }
|
AABB { bounds: [min, max] }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,33 +61,10 @@ impl AABB {
|
|||||||
pub fn min(&self) -> Vec3 {
|
pub fn min(&self) -> Vec3 {
|
||||||
self.bounds[0]
|
self.bounds[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn max(&self) -> Vec3 {
|
pub fn max(&self) -> Vec3 {
|
||||||
self.bounds[1]
|
self.bounds[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> bool {
|
|
||||||
self.hit_simd(r, t_min, t_max)
|
|
||||||
//self.hit_naive(r, t_min, t_max)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hit_naive(&self, r: Ray, t_min: f32, t_max: f32) -> bool {
|
|
||||||
let mut t_min = t_min;
|
|
||||||
let mut t_max = t_max;
|
|
||||||
for axis in 0..3 {
|
|
||||||
let t0 = ((self.min()[axis] - r.origin[axis]) * r.inv_direction[axis])
|
|
||||||
.min((self.max()[axis] - r.origin[axis]) * r.inv_direction[axis]);
|
|
||||||
let t1 = ((self.min()[axis] - r.origin[axis]) * r.inv_direction[axis])
|
|
||||||
.max((self.max()[axis] - r.origin[axis]) * r.inv_direction[axis]);
|
|
||||||
t_min = t0.max(t_min);
|
|
||||||
t_max = t1.min(t_max);
|
|
||||||
if t_max <= t_min {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hit2(&self, r: Ray, t_min: f32, t_max: f32) -> bool {
|
pub fn hit2(&self, r: Ray, t_min: f32, t_max: f32) -> bool {
|
||||||
let mut t_min = t_min;
|
let mut t_min = t_min;
|
||||||
let mut t_max = t_max;
|
let mut t_max = t_max;
|
||||||
@ -147,26 +119,21 @@ impl AABB {
|
|||||||
t_min < t1 && t_max > t0
|
t_min < t1 && t_max > t0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hit_simd(&self, r: Ray, t_min: f32, t_max: f32) -> bool {
|
pub fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> bool {
|
||||||
#[cfg(target_arch = "x86_64")]
|
let mut t_min = t_min;
|
||||||
unsafe {
|
let mut t_max = t_max;
|
||||||
use std::arch::x86_64::*;
|
for axis in 0..3 {
|
||||||
let o4 = _mm_set_ps(0., r.origin.z, r.origin.y, r.origin.x);
|
let t0 = ((self.min()[axis] - r.origin[axis]) * r.inv_direction[axis])
|
||||||
let d4 = _mm_set_ps(0., r.direction.z, r.direction.y, r.direction.x);
|
.min((self.max()[axis] - r.origin[axis]) * r.inv_direction[axis]);
|
||||||
let bmin4 = _mm_set_ps(0., self.min().z, self.min().y, self.min().x);
|
let t1 = ((self.min()[axis] - r.origin[axis]) * r.inv_direction[axis])
|
||||||
let bmax4 = _mm_set_ps(0., self.max().z, self.max().y, self.max().x);
|
.max((self.max()[axis] - r.origin[axis]) * r.inv_direction[axis]);
|
||||||
let mask4 = _mm_cmpeq_ps(_mm_setzero_ps(), _mm_set_ps(1., 0., 0., 0.));
|
t_min = t0.max(t_min);
|
||||||
let t1 = _mm_mul_ps(_mm_sub_ps(_mm_and_ps(bmin4, mask4), o4), d4);
|
t_max = t1.min(t_max);
|
||||||
let t2 = _mm_mul_ps(_mm_sub_ps(_mm_and_ps(bmax4, mask4), o4), d4);
|
if t_max <= t_min {
|
||||||
let vmax4 = _mm_max_ps(t1, t2);
|
return false;
|
||||||
let vmin4 = _mm_min_ps(t1, t2);
|
}
|
||||||
let vmax4: (f32, f32, f32, f32) = std::mem::transmute(vmax4);
|
|
||||||
let vmin4: (f32, f32, f32, f32) = std::mem::transmute(vmin4);
|
|
||||||
let tmax = min(vmax4.0, min(vmax4.1, vmax4.2));
|
|
||||||
let tmin = max(vmin4.0, max(vmin4.1, vmin4.2));
|
|
||||||
//tmax >= tmin && tmin < r.time && tmax > t_min
|
|
||||||
t_min <= tmin && tmin <= t_max
|
|
||||||
}
|
}
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hit_fast(&self, r: Ray, _t_min: f32, _t_max: f32) -> bool {
|
pub fn hit_fast(&self, r: Ray, _t_min: f32, _t_max: f32) -> bool {
|
||||||
@ -202,48 +169,3 @@ pub fn surrounding_box(box0: &AABB, box1: &AABB) -> AABB {
|
|||||||
);
|
);
|
||||||
AABB::new(min, max)
|
AABB::new(min, max)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
macro_rules! hit_test {
|
|
||||||
($($name:ident,)*) => {
|
|
||||||
mod hit {
|
|
||||||
use super::*;
|
|
||||||
$(
|
|
||||||
#[test]
|
|
||||||
fn $name() {
|
|
||||||
let t_min = 0.001;
|
|
||||||
let t_max = f32::MAX;
|
|
||||||
let bb = AABB::new([1., -1., -1.], [3., 1., 1.]);
|
|
||||||
// Hit
|
|
||||||
let r = Ray::new([0., 0., 0.], [1., 0., 0.], 0.5);
|
|
||||||
assert!(bb.$name(r, t_min, t_max));
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
mod miss {
|
|
||||||
use super::*;
|
|
||||||
$(
|
|
||||||
#[test]
|
|
||||||
fn $name() {
|
|
||||||
let t_min = 0.001;
|
|
||||||
let t_max = f32::MAX;
|
|
||||||
let bb = AABB::new([1., -1., -1.], [3., 1., 1.]);
|
|
||||||
// Miss
|
|
||||||
let r = Ray::new([0., 0., 0.], [-1., 0., 0.], 0.5);
|
|
||||||
assert!(!bb.$name(r, t_min, t_max));
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hit_test! {
|
|
||||||
hit_naive,
|
|
||||||
hit2,
|
|
||||||
hit_fast,
|
|
||||||
hit_simd,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,289 +0,0 @@
|
|||||||
/// Implementation based on blog post @
|
|
||||||
/// https://jacco.ompf2.com/2022/04/13/how-to-build-a-bvh-part-1-basics/
|
|
||||||
use std::f32::EPSILON;
|
|
||||||
|
|
||||||
use stl::STL;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
aabb::AABB,
|
|
||||||
hitable::{Hit, HitRecord},
|
|
||||||
material::Material,
|
|
||||||
ray::Ray,
|
|
||||||
vec3::{cross, dot, Vec3},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct BVHNode {
|
|
||||||
aabb: AABB,
|
|
||||||
left_child: usize,
|
|
||||||
first_prim: usize,
|
|
||||||
prim_count: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BVHNode {
|
|
||||||
fn is_leaf(&self) -> bool {
|
|
||||||
self.prim_count > 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Triangle {
|
|
||||||
centroid: Vec3,
|
|
||||||
verts: [Vec3; 3],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct BVHTriangles<M>
|
|
||||||
where
|
|
||||||
M: Material,
|
|
||||||
{
|
|
||||||
pub triangles: Vec<Triangle>,
|
|
||||||
material: M,
|
|
||||||
bvh_nodes: Vec<BVHNode>,
|
|
||||||
}
|
|
||||||
|
|
||||||
const ROOT_NODE_IDX: usize = 0;
|
|
||||||
impl<M> BVHTriangles<M>
|
|
||||||
where
|
|
||||||
M: Material,
|
|
||||||
{
|
|
||||||
pub fn new(stl: &STL, material: M) -> BVHTriangles<M> {
|
|
||||||
let triangles: Vec<_> = stl
|
|
||||||
.triangles
|
|
||||||
.iter()
|
|
||||||
.map(|t| {
|
|
||||||
let v0 = t.verts[0];
|
|
||||||
let v1 = t.verts[1];
|
|
||||||
let v2 = t.verts[2];
|
|
||||||
let centroid = (v0 + v1 + v2) * 0.3333;
|
|
||||||
Triangle {
|
|
||||||
centroid,
|
|
||||||
verts: [v0, v1, v2],
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let n = 2 * triangles.len() - 2;
|
|
||||||
let bvh_nodes = Vec::with_capacity(n);
|
|
||||||
let mut bvh = BVHTriangles {
|
|
||||||
triangles,
|
|
||||||
bvh_nodes,
|
|
||||||
material,
|
|
||||||
};
|
|
||||||
bvh.build_bvh();
|
|
||||||
bvh
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_bvh(&mut self) {
|
|
||||||
// assign all triangles to root node
|
|
||||||
let root = BVHNode {
|
|
||||||
aabb: AABB::default(),
|
|
||||||
left_child: 0,
|
|
||||||
first_prim: 0,
|
|
||||||
prim_count: self.triangles.len() - 1,
|
|
||||||
};
|
|
||||||
self.bvh_nodes.push(root);
|
|
||||||
self.update_node_bounds(ROOT_NODE_IDX);
|
|
||||||
// subdivide recursively
|
|
||||||
self.subdivide(ROOT_NODE_IDX);
|
|
||||||
}
|
|
||||||
fn update_node_bounds(&mut self, node_idx: usize) {
|
|
||||||
let node = &mut self.bvh_nodes[node_idx];
|
|
||||||
let mut aabb_min: Vec3 = f32::MAX.into();
|
|
||||||
let mut aabb_max: Vec3 = f32::MIN.into();
|
|
||||||
for i in node.first_prim..node.prim_count {
|
|
||||||
let leaf_tri = &self.triangles[i];
|
|
||||||
aabb_min = vec3::min(aabb_min, leaf_tri.verts[0]);
|
|
||||||
aabb_min = vec3::min(aabb_min, leaf_tri.verts[1]);
|
|
||||||
aabb_min = vec3::min(aabb_min, leaf_tri.verts[2]);
|
|
||||||
aabb_max = vec3::max(aabb_max, leaf_tri.verts[0]);
|
|
||||||
aabb_max = vec3::max(aabb_max, leaf_tri.verts[1]);
|
|
||||||
aabb_max = vec3::max(aabb_max, leaf_tri.verts[2]);
|
|
||||||
}
|
|
||||||
node.aabb = AABB::new(aabb_min, aabb_max);
|
|
||||||
}
|
|
||||||
fn subdivide(&mut self, idx: usize) {
|
|
||||||
// Early out if we're down to just 2 or less triangles
|
|
||||||
if self.bvh_nodes[idx].prim_count <= 2 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let (first_prim, prim_count, left_count, i) = {
|
|
||||||
let node = &self.bvh_nodes[idx];
|
|
||||||
|
|
||||||
// Compute split plane and position.
|
|
||||||
let extent = node.aabb.min() - node.aabb.max();
|
|
||||||
let axis = node.aabb.longest_axis();
|
|
||||||
let split_pos = node.aabb.min()[axis] + extent[axis] * 0.5;
|
|
||||||
|
|
||||||
// Split the group in two halves.
|
|
||||||
let mut i = node.first_prim as isize;
|
|
||||||
let mut j = i + node.prim_count as isize - 1;
|
|
||||||
while i <= j {
|
|
||||||
if self.triangles[i as usize].centroid[axis] < split_pos {
|
|
||||||
i += 1;
|
|
||||||
} else {
|
|
||||||
self.triangles.swap(i as usize, j as usize);
|
|
||||||
j -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create child nodes for each half.
|
|
||||||
let left_count = i as usize - node.first_prim;
|
|
||||||
if left_count == 0 || left_count == node.prim_count {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
(node.first_prim, node.prim_count, left_count, i)
|
|
||||||
};
|
|
||||||
|
|
||||||
// create child nodes
|
|
||||||
let left_child_idx = self.bvh_nodes.len();
|
|
||||||
let right_child_idx = left_child_idx + 1;
|
|
||||||
let left = BVHNode {
|
|
||||||
aabb: AABB::default(),
|
|
||||||
left_child: 0,
|
|
||||||
first_prim: first_prim,
|
|
||||||
prim_count: left_count,
|
|
||||||
};
|
|
||||||
let right = BVHNode {
|
|
||||||
aabb: AABB::default(),
|
|
||||||
left_child: 0,
|
|
||||||
first_prim: i as usize,
|
|
||||||
prim_count: prim_count - left_count,
|
|
||||||
};
|
|
||||||
self.bvh_nodes.push(left);
|
|
||||||
self.bvh_nodes.push(right);
|
|
||||||
let node = &mut self.bvh_nodes[idx];
|
|
||||||
node.left_child = left_child_idx;
|
|
||||||
node.prim_count = 0;
|
|
||||||
|
|
||||||
// Recurse
|
|
||||||
self.update_node_bounds(left_child_idx);
|
|
||||||
self.update_node_bounds(right_child_idx);
|
|
||||||
self.subdivide(left_child_idx);
|
|
||||||
self.subdivide(right_child_idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn intersect_bvh(&self, r: Ray, node_idx: usize, t_min: f32, t_max: f32) -> Option<HitRecord> {
|
|
||||||
let node = &self.bvh_nodes[node_idx];
|
|
||||||
if !node.aabb.hit(r, t_min, t_max) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.is_leaf() {
|
|
||||||
for tri in &self.triangles {
|
|
||||||
if let Some(RayTriangleResult { t, p }) =
|
|
||||||
ray_triangle_intersect_moller_trumbore(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);
|
|
||||||
let v0 = tri.verts[0];
|
|
||||||
let v1 = tri.verts[1];
|
|
||||||
let v2 = tri.verts[2];
|
|
||||||
|
|
||||||
let v0v1 = v1 - v0;
|
|
||||||
let v0v2 = v2 - v0;
|
|
||||||
let normal = cross(v0v1, v0v2);
|
|
||||||
return Some(HitRecord {
|
|
||||||
t,
|
|
||||||
uv,
|
|
||||||
p,
|
|
||||||
normal,
|
|
||||||
material: &self.material,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let hr = self.intersect_bvh(r, node.left_child, t_min, t_max);
|
|
||||||
if hr.is_some() {
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
let hr = self.intersect_bvh(r, node.left_child + 1, t_min, t_max);
|
|
||||||
if hr.is_some() {
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<M> Hit for BVHTriangles<M>
|
|
||||||
where
|
|
||||||
M: Material,
|
|
||||||
{
|
|
||||||
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
|
|
||||||
self.intersect_bvh(r, 0, t_min, t_max)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bounding_box(&self, _t_min: f32, _t_max: f32) -> Option<AABB> {
|
|
||||||
Some(self.bvh_nodes[0].aabb)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RayTriangleResult {
|
|
||||||
t: f32,
|
|
||||||
p: Vec3,
|
|
||||||
}
|
|
||||||
///
|
|
||||||
/// 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_trumbore(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
|
|
||||||
}
|
|
||||||
@ -1,6 +1,5 @@
|
|||||||
pub mod aabb;
|
pub mod aabb;
|
||||||
pub mod bvh;
|
pub mod bvh;
|
||||||
pub mod bvh_triangles;
|
|
||||||
pub mod camera;
|
pub mod camera;
|
||||||
pub mod constant_medium;
|
pub mod constant_medium;
|
||||||
pub mod cuboid;
|
pub mod cuboid;
|
||||||
@ -18,10 +17,8 @@ pub mod ray;
|
|||||||
pub mod rect;
|
pub mod rect;
|
||||||
pub mod renderer;
|
pub mod renderer;
|
||||||
pub mod rotate;
|
pub mod rotate;
|
||||||
pub mod scale;
|
|
||||||
pub mod scenes;
|
pub mod scenes;
|
||||||
pub mod sphere;
|
pub mod sphere;
|
||||||
pub mod texture;
|
pub mod texture;
|
||||||
pub mod translate;
|
pub mod translate;
|
||||||
pub mod triangles;
|
|
||||||
pub mod vec3;
|
pub mod vec3;
|
||||||
|
|||||||
@ -7,8 +7,7 @@ use std::{
|
|||||||
mpsc::{sync_channel, Receiver, SyncSender},
|
mpsc::{sync_channel, Receiver, SyncSender},
|
||||||
Arc, Mutex,
|
Arc, Mutex,
|
||||||
},
|
},
|
||||||
thread,
|
thread, time,
|
||||||
time::{self, Instant},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use core_affinity;
|
use core_affinity;
|
||||||
@ -42,7 +41,6 @@ pub enum Model {
|
|||||||
Mandelbrot,
|
Mandelbrot,
|
||||||
PerlinDebug,
|
PerlinDebug,
|
||||||
Spheramid,
|
Spheramid,
|
||||||
Stltest,
|
|
||||||
Test,
|
Test,
|
||||||
Tron,
|
Tron,
|
||||||
Tutorial,
|
Tutorial,
|
||||||
@ -60,7 +58,6 @@ 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),
|
||||||
@ -90,7 +87,6 @@ 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),
|
||||||
@ -111,7 +107,6 @@ 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(),
|
||||||
@ -399,7 +394,6 @@ impl AddAssign for RenderStats {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn progress(
|
fn progress(
|
||||||
start_time: Instant,
|
|
||||||
last_stat: &RenderStats,
|
last_stat: &RenderStats,
|
||||||
current_stat: &RenderStats,
|
current_stat: &RenderStats,
|
||||||
time_diff: time::Duration,
|
time_diff: time::Duration,
|
||||||
@ -408,18 +402,13 @@ fn progress(
|
|||||||
let human = human::Formatter::new();
|
let human = human::Formatter::new();
|
||||||
let pixel_diff = current_stat.pixels - last_stat.pixels;
|
let pixel_diff = current_stat.pixels - last_stat.pixels;
|
||||||
let ray_diff = current_stat.rays - last_stat.rays;
|
let ray_diff = current_stat.rays - last_stat.rays;
|
||||||
let now = time::Instant::now();
|
|
||||||
let start_diff = now - start_time;
|
|
||||||
let percent = 100. * current_stat.pixels as f32 / pixel_total as f32;
|
|
||||||
let eta = 100. * start_diff.as_secs_f32() / percent;
|
|
||||||
format!(
|
format!(
|
||||||
"{:7} / {:7}pixels ({:2.0}%) {:7}pixels/s {:7}rays/s eta {:.0}s",
|
"{:7} / {:7}pixels ({:2}%) {:7}pixels/s {:7}rays/s",
|
||||||
human.format(current_stat.pixels as f64),
|
human.format(current_stat.pixels as f64),
|
||||||
human.format(pixel_total as f64),
|
human.format(pixel_total as f64),
|
||||||
percent,
|
100 * current_stat.pixels / pixel_total,
|
||||||
human.format(pixel_diff as f64 / time_diff.as_secs_f64()),
|
human.format(pixel_diff as f64 / time_diff.as_secs_f64()),
|
||||||
human.format(ray_diff as f64 / time_diff.as_secs_f64()),
|
human.format(ray_diff as f64 / time_diff.as_secs_f64())
|
||||||
eta
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -587,9 +576,10 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
|
|||||||
drop(pixel_req_rx);
|
drop(pixel_req_rx);
|
||||||
drop(pixel_resp_tx);
|
drop(pixel_resp_tx);
|
||||||
|
|
||||||
|
let start_time = time::Instant::now();
|
||||||
let (w, h) = (scene.width, scene.height);
|
let (w, h) = (scene.width, scene.height);
|
||||||
handles.push(thread::spawn(move || {
|
handles.push(thread::spawn(move || {
|
||||||
let batch_line_requests = false;
|
let batch_line_requests = true;
|
||||||
if batch_line_requests {
|
if batch_line_requests {
|
||||||
for y in 0..h {
|
for y in 0..h {
|
||||||
pixel_req_tx
|
pixel_req_tx
|
||||||
@ -614,7 +604,6 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
|
|||||||
let mut last_time = time::Instant::now();
|
let mut last_time = time::Instant::now();
|
||||||
let mut last_stat: RenderStats = Default::default();
|
let mut last_stat: RenderStats = Default::default();
|
||||||
let mut current_stat: RenderStats = Default::default();
|
let mut current_stat: RenderStats = Default::default();
|
||||||
let start_time = time::Instant::now();
|
|
||||||
for resp in pixel_resp_rx {
|
for resp in pixel_resp_rx {
|
||||||
match resp {
|
match resp {
|
||||||
Response::Pixel { x, y, pixel, rs } => {
|
Response::Pixel { x, y, pixel, rs } => {
|
||||||
@ -632,15 +621,9 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
|
|||||||
let now = time::Instant::now();
|
let now = time::Instant::now();
|
||||||
let time_diff = now - last_time;
|
let time_diff = now - last_time;
|
||||||
if time_diff > time::Duration::from_secs(1) {
|
if time_diff > time::Duration::from_secs(1) {
|
||||||
println!(
|
info!(
|
||||||
"{}",
|
"{}",
|
||||||
progress(
|
progress(&last_stat, ¤t_stat, time_diff, pixel_total)
|
||||||
start_time,
|
|
||||||
&last_stat,
|
|
||||||
¤t_stat,
|
|
||||||
time_diff,
|
|
||||||
pixel_total
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
last_stat = current_stat;
|
last_stat = current_stat;
|
||||||
last_time = now;
|
last_time = now;
|
||||||
@ -650,16 +633,10 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
|
|||||||
thr.join().expect("thread join");
|
thr.join().expect("thread join");
|
||||||
}
|
}
|
||||||
let time_diff = time::Instant::now() - start_time;
|
let time_diff = time::Instant::now() - start_time;
|
||||||
println!(
|
info!(
|
||||||
"Runtime {} seconds {}",
|
"Runtime {} seconds {}",
|
||||||
time_diff.as_secs_f32(),
|
time_diff.as_secs_f32(),
|
||||||
progress(
|
progress(&Default::default(), ¤t_stat, time_diff, pixel_total)
|
||||||
start_time,
|
|
||||||
&Default::default(),
|
|
||||||
¤t_stat,
|
|
||||||
time_diff,
|
|
||||||
pixel_total
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
output::write_images(&scene, time_diff, output_dir)
|
output::write_images(&scene, time_diff, output_dir)
|
||||||
|
|||||||
@ -1,53 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
aabb::AABB,
|
|
||||||
hitable::{Hit, HitRecord},
|
|
||||||
ray::Ray,
|
|
||||||
vec3::Vec3,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Scale<H>
|
|
||||||
where
|
|
||||||
H: Hit,
|
|
||||||
{
|
|
||||||
hitable: H,
|
|
||||||
scale: Vec3,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<H> Scale<H>
|
|
||||||
where
|
|
||||||
H: Hit,
|
|
||||||
{
|
|
||||||
pub fn new<V>(hitable: H, scale: V) -> Scale<H>
|
|
||||||
where
|
|
||||||
V: Into<Vec3>,
|
|
||||||
{
|
|
||||||
Scale {
|
|
||||||
hitable,
|
|
||||||
scale: scale.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<H> Hit for Scale<H>
|
|
||||||
where
|
|
||||||
H: Hit,
|
|
||||||
{
|
|
||||||
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
|
|
||||||
let moved_r = Ray::new(r.origin / self.scale, r.direction, r.time);
|
|
||||||
if let Some(rec) = self.hitable.hit(moved_r, t_min, t_max) {
|
|
||||||
return Some(HitRecord {
|
|
||||||
p: rec.p * self.scale,
|
|
||||||
..rec
|
|
||||||
});
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bounding_box(&self, t_min: f32, t_max: f32) -> Option<AABB> {
|
|
||||||
if let Some(bbox) = self.hitable.bounding_box(t_min, t_max) {
|
|
||||||
return Some(AABB::new(bbox.min() * self.scale, bbox.max() * self.scale));
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -7,7 +7,6 @@ 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;
|
||||||
|
|||||||
@ -1,126 +0,0 @@
|
|||||||
use std::io::{BufReader, Cursor};
|
|
||||||
use stl::STL;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
bvh_triangles::BVHTriangles,
|
|
||||||
camera::Camera,
|
|
||||||
hitable::Hit,
|
|
||||||
hitable_list::HitableList,
|
|
||||||
kdtree::KDTree,
|
|
||||||
material::{DiffuseLight, Lambertian, Metal},
|
|
||||||
rect::{XYRect, XZRect},
|
|
||||||
renderer::{Opt, Scene},
|
|
||||||
scale::Scale,
|
|
||||||
sphere::Sphere,
|
|
||||||
texture::ConstantTexture,
|
|
||||||
vec3::Vec3,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn new(opt: &Opt) -> Scene {
|
|
||||||
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.;
|
|
||||||
let time_max = 1.;
|
|
||||||
let camera = Camera::new(
|
|
||||||
lookfrom,
|
|
||||||
lookat,
|
|
||||||
Vec3::new(0., 1., 0.),
|
|
||||||
45.,
|
|
||||||
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, 0.4, 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 objects: Vec<Box<dyn Hit>> = vec![
|
|
||||||
// Light from above - white
|
|
||||||
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 - green
|
|
||||||
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 - blue
|
|
||||||
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., -40.),
|
|
||||||
20.,
|
|
||||||
Metal::new(Vec3::new(0.8, 0.8, 0.8), 0.2),
|
|
||||||
)),
|
|
||||||
Box::new(Sphere::new(
|
|
||||||
Vec3::new(-40., 20., 40.),
|
|
||||||
20.,
|
|
||||||
Metal::new(Vec3::new(0.8, 0.8, 0.8), 0.2),
|
|
||||||
)),
|
|
||||||
// STL Mesh
|
|
||||||
Box::new(Scale::new(
|
|
||||||
BVHTriangles::new(
|
|
||||||
&stl_cube,
|
|
||||||
Lambertian::new(ConstantTexture::new(Vec3::new(0.6, 0.6, 0.6))),
|
|
||||||
),
|
|
||||||
200.,
|
|
||||||
)),
|
|
||||||
];
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,168 +0,0 @@
|
|||||||
use std::f32::EPSILON;
|
|
||||||
|
|
||||||
use stl::STL;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
aabb::AABB,
|
|
||||||
hitable::{Hit, HitRecord},
|
|
||||||
material::Material,
|
|
||||||
ray::Ray,
|
|
||||||
vec3::{cross, dot, Vec3},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Triangle {
|
|
||||||
normal: Vec3,
|
|
||||||
verts: [Vec3; 3],
|
|
||||||
// Precomputed data
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Triangles<M>
|
|
||||||
where
|
|
||||||
M: Material,
|
|
||||||
{
|
|
||||||
pub triangles: Vec<Triangle>,
|
|
||||||
pub bbox: AABB,
|
|
||||||
material: M,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<M> Triangles<M>
|
|
||||||
where
|
|
||||||
M: Material,
|
|
||||||
{
|
|
||||||
pub fn new(stl: &STL, material: M, scale_factor: f32) -> Triangles<M> {
|
|
||||||
let triangles: Vec<_> = stl
|
|
||||||
.triangles
|
|
||||||
.iter()
|
|
||||||
.map(|t| {
|
|
||||||
let v0 = t.verts[0] * scale_factor;
|
|
||||||
let v1 = t.verts[1] * scale_factor;
|
|
||||||
let v2 = t.verts[2] * scale_factor;
|
|
||||||
Triangle {
|
|
||||||
normal: t.normal,
|
|
||||||
verts: [v0, v1, v2],
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let (min, max) = triangles.iter().fold(
|
|
||||||
(Vec3::from(f32::MAX), Vec3::from(f32::MIN)),
|
|
||||||
|(min, max), t| {
|
|
||||||
let t_min_x = t.verts[0].x.min(t.verts[1].x.min(t.verts[2].x));
|
|
||||||
let t_min_y = t.verts[0].y.min(t.verts[1].y.min(t.verts[2].y));
|
|
||||||
let t_min_z = t.verts[0].z.min(t.verts[1].z.min(t.verts[2].z));
|
|
||||||
let t_max_x = t.verts[0].x.max(t.verts[1].x.max(t.verts[2].x));
|
|
||||||
let t_max_y = t.verts[0].y.max(t.verts[1].y.max(t.verts[2].y));
|
|
||||||
let t_max_z = t.verts[0].z.max(t.verts[1].z.max(t.verts[2].z));
|
|
||||||
|
|
||||||
(
|
|
||||||
Vec3::from([min.x.min(t_min_x), min.y.min(t_min_y), min.z.min(t_min_z)]),
|
|
||||||
Vec3::from([max.x.max(t_max_x), max.y.max(t_max_y), max.z.max(t_max_z)]),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let bbox = AABB::new(min, max);
|
|
||||||
Triangles {
|
|
||||||
triangles,
|
|
||||||
bbox,
|
|
||||||
material,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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_trumbore(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
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<M> Hit for Triangles<M>
|
|
||||||
where
|
|
||||||
M: Material,
|
|
||||||
{
|
|
||||||
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 {
|
|
||||||
if let Some(RayTriangleResult { t, p }) = ray_triangle_intersect_moller_trumbore(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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bounding_box(&self, _t_min: f32, _t_max: f32) -> Option<AABB> {
|
|
||||||
Some(self.bbox)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RayTriangleResult {
|
|
||||||
t: f32,
|
|
||||||
p: Vec3,
|
|
||||||
}
|
|
||||||
@ -1 +1,274 @@
|
|||||||
pub use vec3::*;
|
use std::{
|
||||||
|
convert::From,
|
||||||
|
fmt,
|
||||||
|
num::ParseFloatError,
|
||||||
|
ops::{Add, Div, Index, Mul, Neg, Sub},
|
||||||
|
str,
|
||||||
|
};
|
||||||
|
|
||||||
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Deserialize, Serialize, PartialEq, Copy, Clone)]
|
||||||
|
pub struct Vec3 {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
pub z: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cross(v1: Vec3, v2: Vec3) -> Vec3 {
|
||||||
|
Vec3 {
|
||||||
|
x: v1.y * v2.z - v1.z * v2.y,
|
||||||
|
y: v1.z * v2.x - v1.x * v2.z,
|
||||||
|
z: v1.x * v2.y - v1.y * v2.x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dot(v1: Vec3, v2: Vec3) -> f32 {
|
||||||
|
v1.x * v2.x + v1.y * v2.y + v1.z * v2.z
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vec3 {
|
||||||
|
pub fn new(x: f32, y: f32, z: f32) -> Vec3 {
|
||||||
|
Vec3 { x, y, z }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn min(self) -> f32 {
|
||||||
|
self.x.min(self.y).min(self.z)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn max(self) -> f32 {
|
||||||
|
self.x.max(self.y).max(self.z)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn length(self) -> f32 {
|
||||||
|
self.squared_length().sqrt()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn squared_length(self) -> f32 {
|
||||||
|
self.x * self.x + self.y * self.y + self.z * self.z
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unit_vector(self) -> Vec3 {
|
||||||
|
self / self.length()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_unit_vector(&mut self) {
|
||||||
|
*self = self.unit_vector();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<[f32; 3]> for Vec3 {
|
||||||
|
fn from(v: [f32; 3]) -> Self {
|
||||||
|
Vec3 {
|
||||||
|
x: v[0],
|
||||||
|
y: v[1],
|
||||||
|
z: v[2],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Vec3 {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{} {} {}", self.x, self.y, self.z)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl str::FromStr for Vec3 {
|
||||||
|
type Err = ParseFloatError;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let coords: Vec<&str> = s.split(' ').collect();
|
||||||
|
Ok(Vec3 {
|
||||||
|
x: coords[0].parse::<f32>()?,
|
||||||
|
y: coords[1].parse::<f32>()?,
|
||||||
|
z: coords[2].parse::<f32>()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<f32> for Vec3 {
|
||||||
|
type Output = Vec3;
|
||||||
|
|
||||||
|
fn add(self, r: f32) -> Vec3 {
|
||||||
|
Vec3 {
|
||||||
|
x: self.x + r,
|
||||||
|
y: self.y + r,
|
||||||
|
z: self.z + r,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for Vec3 {
|
||||||
|
type Output = Vec3;
|
||||||
|
|
||||||
|
fn add(self, r: Vec3) -> Vec3 {
|
||||||
|
Vec3 {
|
||||||
|
x: self.x + r.x,
|
||||||
|
y: self.y + r.y,
|
||||||
|
z: self.z + r.z,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<Vec3> for f32 {
|
||||||
|
type Output = Vec3;
|
||||||
|
|
||||||
|
fn div(self, r: Vec3) -> Vec3 {
|
||||||
|
Vec3 {
|
||||||
|
x: self / r.x,
|
||||||
|
y: self / r.y,
|
||||||
|
z: self / r.z,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<f32> for Vec3 {
|
||||||
|
type Output = Vec3;
|
||||||
|
|
||||||
|
fn div(self, r: f32) -> Vec3 {
|
||||||
|
Vec3 {
|
||||||
|
x: self.x / r,
|
||||||
|
y: self.y / r,
|
||||||
|
z: self.z / r,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<usize> for Vec3 {
|
||||||
|
type Output = f32;
|
||||||
|
fn index(&self, idx: usize) -> &f32 {
|
||||||
|
match idx {
|
||||||
|
0 => &self.x,
|
||||||
|
1 => &self.y,
|
||||||
|
2 => &self.z,
|
||||||
|
_ => panic!("idx {} out of range for vec3", idx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul for Vec3 {
|
||||||
|
type Output = Vec3;
|
||||||
|
|
||||||
|
fn mul(self, r: Vec3) -> Vec3 {
|
||||||
|
Vec3 {
|
||||||
|
x: self.x * r.x,
|
||||||
|
y: self.y * r.y,
|
||||||
|
z: self.z * r.z,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<Vec3> for f32 {
|
||||||
|
type Output = Vec3;
|
||||||
|
|
||||||
|
fn mul(self, v: Vec3) -> Vec3 {
|
||||||
|
Vec3 {
|
||||||
|
x: v.x * self,
|
||||||
|
y: v.y * self,
|
||||||
|
z: v.z * self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<f32> for Vec3 {
|
||||||
|
type Output = Vec3;
|
||||||
|
|
||||||
|
fn mul(self, r: f32) -> Vec3 {
|
||||||
|
Vec3 {
|
||||||
|
x: self.x * r,
|
||||||
|
y: self.y * r,
|
||||||
|
z: self.z * r,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Neg for Vec3 {
|
||||||
|
type Output = Vec3;
|
||||||
|
|
||||||
|
fn neg(self) -> Vec3 {
|
||||||
|
-1. * self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub for Vec3 {
|
||||||
|
type Output = Vec3;
|
||||||
|
|
||||||
|
fn sub(self, r: Vec3) -> Vec3 {
|
||||||
|
Vec3 {
|
||||||
|
x: self.x - r.x,
|
||||||
|
y: self.y - r.y,
|
||||||
|
z: self.z - r.z,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::{f32::consts::PI, str::FromStr};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vec_add() {
|
||||||
|
let v0 = Vec3::new(1., 2., 3.);
|
||||||
|
let v1 = Vec3::new(1., 1., 1.);
|
||||||
|
assert_eq!(v0 + v1, Vec3::new(2., 3., 4.));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vec_div() {
|
||||||
|
let v0 = Vec3::new(1., 2., 4.);
|
||||||
|
assert_eq!(v0 / 2., Vec3::new(0.5, 1., 2.));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vec_mul() {
|
||||||
|
let v0 = Vec3::new(1., 2., 4.);
|
||||||
|
assert_eq!(v0 * 0.5, Vec3::new(0.5, 1., 2.));
|
||||||
|
assert_eq!(v0 * v0, Vec3::new(1., 4., 16.));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vec_sub() {
|
||||||
|
let v0 = Vec3::new(1., 2., 3.);
|
||||||
|
let v1 = Vec3::new(1., 1., 1.);
|
||||||
|
assert_eq!(v0 - v1, Vec3::new(0., 1., 2.));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vec_idx() {
|
||||||
|
let v0 = Vec3::new(1., 2., 3.);
|
||||||
|
assert_eq!(v0[2], 3.);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vec_display() {
|
||||||
|
let v0 = Vec3::new(1., 2., 3.);
|
||||||
|
assert_eq!(format!("{}", v0), "1 2 3".to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vec_from_str() {
|
||||||
|
assert_eq!(Vec3::from_str("1. 2. 3.").unwrap(), Vec3::new(1., 2., 3.));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vec_str_roundtrip() {
|
||||||
|
let v = Vec3::from_str("1 2 3").unwrap();
|
||||||
|
let s = format!("{}", v);
|
||||||
|
assert_eq!(v, Vec3::from_str(&s).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vec_dot() {
|
||||||
|
let v0 = Vec3::new(1., 0., 0.);
|
||||||
|
let v1 = Vec3::new(-1., 0., 0.);
|
||||||
|
assert_eq!(dot(v0, v1), v0.length() * v1.length() * PI.cos());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vec_cross() {
|
||||||
|
let v0 = Vec3::new(1., 0., 0.);
|
||||||
|
let v1 = Vec3::new(0., 1., 0.);
|
||||||
|
assert_eq!(cross(v0, v1), Vec3::new(0., 0., 1.));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Binary file not shown.
2
rtiow/rustfmt.toml
Normal file
2
rtiow/rustfmt.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
imports_granularity = "Crate"
|
||||||
|
format_code_in_doc_comments = true
|
||||||
@ -2,12 +2,12 @@
|
|||||||
name = "tracer"
|
name = "tracer"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Bill Thiede <git@xinu.tv>"]
|
authors = ["Bill Thiede <git@xinu.tv>"]
|
||||||
edition = "2021"
|
edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4.17"
|
log = "0.4.5"
|
||||||
renderer = { path = "../renderer" }
|
renderer = { path = "../renderer" }
|
||||||
stderrlog = "0.4.3"
|
stderrlog = "0.4.1"
|
||||||
structopt = "0.2.18"
|
structopt = "0.2.10"
|
||||||
|
|||||||
@ -1,10 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "vec3"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
serde = { version = "1.0.152", features = ["serde_derive"] }
|
|
||||||
serde_derive = "1.0.152"
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
mod vec3;
|
|
||||||
pub use vec3::*;
|
|
||||||
@ -1,309 +0,0 @@
|
|||||||
use std::{
|
|
||||||
convert::From,
|
|
||||||
fmt,
|
|
||||||
num::ParseFloatError,
|
|
||||||
ops::{Add, Div, Index, Mul, Neg, Sub},
|
|
||||||
str,
|
|
||||||
};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Deserialize, Serialize, PartialEq, Copy, Clone)]
|
|
||||||
pub struct Vec3 {
|
|
||||||
pub x: f32,
|
|
||||||
pub y: f32,
|
|
||||||
pub z: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a `Vec3` with the lowest of each component in v1 or v2.
|
|
||||||
pub fn min(v1: Vec3, v2: Vec3) -> Vec3 {
|
|
||||||
Vec3 {
|
|
||||||
x: v1.x.min(v2.x),
|
|
||||||
y: v1.y.min(v2.y),
|
|
||||||
z: v1.z.min(v2.z),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Return a `Vec3` with the greatest of each component in v1 or v2.
|
|
||||||
pub fn max(v1: Vec3, v2: Vec3) -> Vec3 {
|
|
||||||
Vec3 {
|
|
||||||
x: v1.x.max(v2.x),
|
|
||||||
y: v1.y.max(v2.y),
|
|
||||||
z: v1.z.max(v2.z),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cross(v1: Vec3, v2: Vec3) -> Vec3 {
|
|
||||||
Vec3 {
|
|
||||||
x: v1.y * v2.z - v1.z * v2.y,
|
|
||||||
y: v1.z * v2.x - v1.x * v2.z,
|
|
||||||
z: v1.x * v2.y - v1.y * v2.x,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dot(v1: Vec3, v2: Vec3) -> f32 {
|
|
||||||
v1.x * v2.x + v1.y * v2.y + v1.z * v2.z
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Vec3 {
|
|
||||||
pub fn new(x: f32, y: f32, z: f32) -> Vec3 {
|
|
||||||
Vec3 { x, y, z }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn min(self) -> f32 {
|
|
||||||
self.x.min(self.y).min(self.z)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn max(self) -> f32 {
|
|
||||||
self.x.max(self.y).max(self.z)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn length(self) -> f32 {
|
|
||||||
self.squared_length().sqrt()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn squared_length(self) -> f32 {
|
|
||||||
self.x * self.x + self.y * self.y + self.z * self.z
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unit_vector(self) -> Vec3 {
|
|
||||||
self / self.length()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn make_unit_vector(&mut self) {
|
|
||||||
*self = self.unit_vector();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<f32> for Vec3 {
|
|
||||||
fn from(v: f32) -> Self {
|
|
||||||
Vec3 { x: v, y: v, z: v }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<[f32; 3]> for Vec3 {
|
|
||||||
fn from(v: [f32; 3]) -> Self {
|
|
||||||
Vec3 {
|
|
||||||
x: v[0],
|
|
||||||
y: v[1],
|
|
||||||
z: v[2],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Vec3 {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{} {} {}", self.x, self.y, self.z)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl str::FromStr for Vec3 {
|
|
||||||
type Err = ParseFloatError;
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
let coords: Vec<&str> = s.split(' ').collect();
|
|
||||||
Ok(Vec3 {
|
|
||||||
x: coords[0].parse::<f32>()?,
|
|
||||||
y: coords[1].parse::<f32>()?,
|
|
||||||
z: coords[2].parse::<f32>()?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Add<f32> for Vec3 {
|
|
||||||
type Output = Vec3;
|
|
||||||
|
|
||||||
fn add(self, r: f32) -> Vec3 {
|
|
||||||
Vec3 {
|
|
||||||
x: self.x + r,
|
|
||||||
y: self.y + r,
|
|
||||||
z: self.z + r,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Add for Vec3 {
|
|
||||||
type Output = Vec3;
|
|
||||||
|
|
||||||
fn add(self, r: Vec3) -> Vec3 {
|
|
||||||
Vec3 {
|
|
||||||
x: self.x + r.x,
|
|
||||||
y: self.y + r.y,
|
|
||||||
z: self.z + r.z,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Div<Vec3> for Vec3 {
|
|
||||||
type Output = Vec3;
|
|
||||||
|
|
||||||
fn div(self, r: Vec3) -> Vec3 {
|
|
||||||
Vec3 {
|
|
||||||
x: self.x / r.x,
|
|
||||||
y: self.y / r.y,
|
|
||||||
z: self.z / r.z,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Div<Vec3> for f32 {
|
|
||||||
type Output = Vec3;
|
|
||||||
|
|
||||||
fn div(self, r: Vec3) -> Vec3 {
|
|
||||||
Vec3 {
|
|
||||||
x: self / r.x,
|
|
||||||
y: self / r.y,
|
|
||||||
z: self / r.z,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Div<f32> for Vec3 {
|
|
||||||
type Output = Vec3;
|
|
||||||
|
|
||||||
fn div(self, r: f32) -> Vec3 {
|
|
||||||
Vec3 {
|
|
||||||
x: self.x / r,
|
|
||||||
y: self.y / r,
|
|
||||||
z: self.z / r,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Index<usize> for Vec3 {
|
|
||||||
type Output = f32;
|
|
||||||
fn index(&self, idx: usize) -> &f32 {
|
|
||||||
match idx {
|
|
||||||
0 => &self.x,
|
|
||||||
1 => &self.y,
|
|
||||||
2 => &self.z,
|
|
||||||
_ => panic!("idx {} out of range for vec3", idx),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul for Vec3 {
|
|
||||||
type Output = Vec3;
|
|
||||||
|
|
||||||
fn mul(self, r: Vec3) -> Vec3 {
|
|
||||||
Vec3 {
|
|
||||||
x: self.x * r.x,
|
|
||||||
y: self.y * r.y,
|
|
||||||
z: self.z * r.z,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<Vec3> for f32 {
|
|
||||||
type Output = Vec3;
|
|
||||||
|
|
||||||
fn mul(self, v: Vec3) -> Vec3 {
|
|
||||||
Vec3 {
|
|
||||||
x: v.x * self,
|
|
||||||
y: v.y * self,
|
|
||||||
z: v.z * self,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<f32> for Vec3 {
|
|
||||||
type Output = Vec3;
|
|
||||||
|
|
||||||
fn mul(self, r: f32) -> Vec3 {
|
|
||||||
Vec3 {
|
|
||||||
x: self.x * r,
|
|
||||||
y: self.y * r,
|
|
||||||
z: self.z * r,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Neg for Vec3 {
|
|
||||||
type Output = Vec3;
|
|
||||||
|
|
||||||
fn neg(self) -> Vec3 {
|
|
||||||
-1. * self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sub for Vec3 {
|
|
||||||
type Output = Vec3;
|
|
||||||
|
|
||||||
fn sub(self, r: Vec3) -> Vec3 {
|
|
||||||
Vec3 {
|
|
||||||
x: self.x - r.x,
|
|
||||||
y: self.y - r.y,
|
|
||||||
z: self.z - r.z,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use std::{f32::consts::PI, str::FromStr};
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn vec_add() {
|
|
||||||
let v0 = Vec3::new(1., 2., 3.);
|
|
||||||
let v1 = Vec3::new(1., 1., 1.);
|
|
||||||
assert_eq!(v0 + v1, Vec3::new(2., 3., 4.));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn vec_div() {
|
|
||||||
let v0 = Vec3::new(1., 2., 4.);
|
|
||||||
assert_eq!(v0 / 2., Vec3::new(0.5, 1., 2.));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn vec_mul() {
|
|
||||||
let v0 = Vec3::new(1., 2., 4.);
|
|
||||||
assert_eq!(v0 * 0.5, Vec3::new(0.5, 1., 2.));
|
|
||||||
assert_eq!(v0 * v0, Vec3::new(1., 4., 16.));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn vec_sub() {
|
|
||||||
let v0 = Vec3::new(1., 2., 3.);
|
|
||||||
let v1 = Vec3::new(1., 1., 1.);
|
|
||||||
assert_eq!(v0 - v1, Vec3::new(0., 1., 2.));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn vec_idx() {
|
|
||||||
let v0 = Vec3::new(1., 2., 3.);
|
|
||||||
assert_eq!(v0[2], 3.);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn vec_display() {
|
|
||||||
let v0 = Vec3::new(1., 2., 3.);
|
|
||||||
assert_eq!(format!("{}", v0), "1 2 3".to_owned());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn vec_from_str() {
|
|
||||||
assert_eq!(Vec3::from_str("1. 2. 3.").unwrap(), Vec3::new(1., 2., 3.));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn vec_str_roundtrip() {
|
|
||||||
let v = Vec3::from_str("1 2 3").unwrap();
|
|
||||||
let s = format!("{}", v);
|
|
||||||
assert_eq!(v, Vec3::from_str(&s).unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn vec_dot() {
|
|
||||||
let v0 = Vec3::new(1., 0., 0.);
|
|
||||||
let v1 = Vec3::new(-1., 0., 0.);
|
|
||||||
assert_eq!(dot(v0, v1), v0.length() * v1.length() * PI.cos());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn vec_cross() {
|
|
||||||
let v0 = Vec3::new(1., 0., 0.);
|
|
||||||
let v1 = Vec3::new(0., 1., 0.);
|
|
||||||
assert_eq!(cross(v0, v1), Vec3::new(0., 0., 1.));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const Thread = std.Thread;
|
|
||||||
const info = std.log.info;
|
|
||||||
const Queue = std.atomic.Queue;
|
|
||||||
|
|
||||||
const Task = struct {
|
|
||||||
row_idx: usize,
|
|
||||||
row_pixels: []u8,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn worker(q: *Queue(Task)) void {
|
|
||||||
while (true) {
|
|
||||||
if (q.get()) |node| {
|
|
||||||
const task = node.data;
|
|
||||||
info("starting thread: {}", .{task.row_idx});
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main() anyerror!void {
|
|
||||||
const allocator = std.heap.page_allocator;
|
|
||||||
const cpus = try Thread.getCpuCount();
|
|
||||||
const width = 512;
|
|
||||||
const height = 256;
|
|
||||||
var pixels: [width * height * 3]u8 = undefined;
|
|
||||||
|
|
||||||
var threads: std.ArrayList(std.Thread) = std.ArrayList(std.Thread).init(allocator);
|
|
||||||
var q = Queue(Task).init();
|
|
||||||
const Node = Queue(Task).Node;
|
|
||||||
var row: usize = 0;
|
|
||||||
while (row < height) : (row += 1) {
|
|
||||||
const node = try allocator.create(Node);
|
|
||||||
node.* = .{
|
|
||||||
.prev = undefined,
|
|
||||||
.next = undefined,
|
|
||||||
.data = Task{
|
|
||||||
.row_idx = row,
|
|
||||||
.row_pixels = pixels[row * width * 3 .. (row + 1) * width * 3],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
q.put(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
var t: usize = 0;
|
|
||||||
while (t < cpus) : (t += 1) {
|
|
||||||
try threads.append(try Thread.spawn(.{}, worker, .{&q}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for all threads to finish.
|
|
||||||
for (threads.items) |thread| {
|
|
||||||
Thread.join(thread);
|
|
||||||
}
|
|
||||||
info("main done", .{});
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user