Compare commits
22 Commits
a12938db95
...
2d696932e3
| Author | SHA1 | Date | |
|---|---|---|---|
| 2d696932e3 | |||
| 27d6c1280b | |||
| 4506418706 | |||
| 1d8aff7905 | |||
| 585ad4805c | |||
| b7f163c5a9 | |||
| 4ab9425a97 | |||
| 468cba97b3 | |||
| b9ebc186fa | |||
| 3e9d900f1e | |||
| 9e81acfda9 | |||
| f8ec874d13 | |||
| a8756debb8 | |||
| a0fb4637b5 | |||
| 6069bf9a65 | |||
| eeb7813243 | |||
| c644299726 | |||
| e6db61543b | |||
| 39eeb79409 | |||
| 54e72cd81d | |||
| 2d91f781f3 | |||
| 24e8b4f9cf |
654
rtiow/Cargo.lock
generated
654
rtiow/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -5,9 +5,8 @@ members = [
|
||||
"noise_explorer_warp",
|
||||
"renderer",
|
||||
"tracer",
|
||||
"vec3",
|
||||
]
|
||||
|
||||
[profile]
|
||||
[profile.release]
|
||||
debug = true
|
||||
|
||||
|
||||
@ -2,19 +2,19 @@
|
||||
name = "noise_explorer"
|
||||
version = "0.1.0"
|
||||
authors = ["Bill Thiede <git@xinu.tv>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
actix-web = "0.7.8"
|
||||
askama = "0.7.1"
|
||||
image = "0.22.3"
|
||||
log = "0.4.5"
|
||||
rand = "0.8.3"
|
||||
actix-web = "0.7.19"
|
||||
askama = "0.7.2"
|
||||
image = "0.22.5"
|
||||
log = "0.4.17"
|
||||
rand = "0.8.5"
|
||||
rand_xorshift = "0.3.0"
|
||||
renderer = { path = "../renderer" }
|
||||
serde = "1.0.79"
|
||||
serde_derive = "1.0.79"
|
||||
stderrlog = "0.4.1"
|
||||
structopt = "0.2.10"
|
||||
serde = "1.0.152"
|
||||
serde_derive = "1.0.152"
|
||||
stderrlog = "0.4.3"
|
||||
structopt = "0.2.18"
|
||||
|
||||
@ -2,18 +2,18 @@
|
||||
name = "noise_explorer_warp"
|
||||
version = "0.1.0"
|
||||
authors = ["Bill Thiede <git@xinu.tv>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
askama = "0.7.1"
|
||||
image = "0.22.3"
|
||||
log = "0.4.5"
|
||||
rand = "0.5.5"
|
||||
askama = "0.7.2"
|
||||
image = "0.22.5"
|
||||
log = "0.4.17"
|
||||
rand = "0.5.6"
|
||||
renderer = { path = "../renderer" }
|
||||
serde = "1.0.79"
|
||||
serde_derive = "1.0.79"
|
||||
stderrlog = "0.4.1"
|
||||
structopt = "0.2.10"
|
||||
warp = "0.1.20"
|
||||
serde = "1.0.152"
|
||||
serde_derive = "1.0.152"
|
||||
stderrlog = "0.4.3"
|
||||
structopt = "0.2.18"
|
||||
warp = "0.1.23"
|
||||
|
||||
@ -2,29 +2,35 @@
|
||||
name = "renderer"
|
||||
version = "0.1.0"
|
||||
authors = ["Bill Thiede <git@xinu.tv>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[[bench]]
|
||||
harness = false
|
||||
name = "spheres"
|
||||
[[bench]]
|
||||
harness = false
|
||||
name = "aabb"
|
||||
|
||||
[dependencies]
|
||||
chrono = "*"
|
||||
core_affinity = "0.5"
|
||||
cpuprofiler = { version = "0.0.3", optional = true }
|
||||
image = "0.22.3"
|
||||
lazy_static = "1.1.0"
|
||||
log = "0.4.5"
|
||||
num_cpus = "1.8.0"
|
||||
rand = "0.8.3"
|
||||
serde = "1.0.79"
|
||||
serde_derive = "1.0.79"
|
||||
serde_json = "1.0.41"
|
||||
structopt = "0.2.10"
|
||||
image = "0.22.5"
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4.17"
|
||||
num_cpus = "1.15.0"
|
||||
rand = "0.8.5"
|
||||
serde = "1.0.152"
|
||||
serde_derive = "1.0.152"
|
||||
serde_json = "1.0.91"
|
||||
structopt = "0.2.18"
|
||||
vec3 = {path = "../vec3"}
|
||||
stl = {path = "../../../stl"}
|
||||
#stl = {git = "https://git-private.z.xinu.tv/wathiede/stl"}
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.2"
|
||||
criterion = "0.4"
|
||||
|
||||
[features]
|
||||
profile = ["cpuprofiler"]
|
||||
|
||||
47
rtiow/renderer/benches/aabb.rs
Normal file
47
rtiow/renderer/benches/aabb.rs
Normal file
@ -0,0 +1,47 @@
|
||||
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,9 +1,6 @@
|
||||
#[macro_use]
|
||||
extern crate criterion;
|
||||
use criterion::*;
|
||||
|
||||
use criterion::{Criterion, ParameterizedBenchmark};
|
||||
|
||||
use rtiow::{
|
||||
use renderer::{
|
||||
hitable::Hit, material::Lambertian, ray::Ray, sphere::Sphere, texture::ConstantTexture,
|
||||
vec3::Vec3,
|
||||
};
|
||||
@ -20,14 +17,15 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
// Miss
|
||||
Ray::new([0., 0., -2.], [0., 0., -1.], 0.),
|
||||
];
|
||||
c.bench(
|
||||
"sphere",
|
||||
ParameterizedBenchmark::new(
|
||||
"Sphere",
|
||||
move |b, r| b.iter(|| sphere.hit(*r, 0., 1.)),
|
||||
rays,
|
||||
),
|
||||
);
|
||||
let mut group = c.benchmark_group("sphere");
|
||||
group.throughput(Throughput::Elements(1));
|
||||
group.bench_with_input(BenchmarkId::new("Sphere", "hit"), &rays[0], |b, r| {
|
||||
b.iter(|| sphere.hit(*r, 0., 1.))
|
||||
});
|
||||
group.bench_with_input(BenchmarkId::new("Sphere", "miss"), &rays[1], |b, r| {
|
||||
b.iter(|| sphere.hit(*r, 0., 1.))
|
||||
});
|
||||
group.finish()
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
|
||||
@ -2,7 +2,7 @@ use std::fmt;
|
||||
|
||||
use crate::{ray::Ray, vec3::Vec3};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq)]
|
||||
pub struct AABB {
|
||||
bounds: [Vec3; 2],
|
||||
}
|
||||
@ -30,7 +30,12 @@ fn max(x: f32, y: f32) -> f32 {
|
||||
}
|
||||
|
||||
impl AABB {
|
||||
pub fn new(min: Vec3, max: Vec3) -> AABB {
|
||||
pub fn new<V: Into<Vec3>>(min: V, max: V) -> 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] }
|
||||
}
|
||||
|
||||
@ -61,10 +66,33 @@ impl AABB {
|
||||
pub fn min(&self) -> Vec3 {
|
||||
self.bounds[0]
|
||||
}
|
||||
|
||||
pub fn max(&self) -> Vec3 {
|
||||
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 {
|
||||
let mut t_min = t_min;
|
||||
let mut t_max = t_max;
|
||||
@ -119,21 +147,26 @@ impl AABB {
|
||||
t_min < t1 && t_max > t0
|
||||
}
|
||||
|
||||
pub fn hit(&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;
|
||||
}
|
||||
pub fn hit_simd(&self, r: Ray, t_min: f32, t_max: f32) -> bool {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
unsafe {
|
||||
use std::arch::x86_64::*;
|
||||
let o4 = _mm_set_ps(0., r.origin.z, r.origin.y, r.origin.x);
|
||||
let d4 = _mm_set_ps(0., r.direction.z, r.direction.y, r.direction.x);
|
||||
let bmin4 = _mm_set_ps(0., self.min().z, self.min().y, self.min().x);
|
||||
let bmax4 = _mm_set_ps(0., self.max().z, self.max().y, self.max().x);
|
||||
let mask4 = _mm_cmpeq_ps(_mm_setzero_ps(), _mm_set_ps(1., 0., 0., 0.));
|
||||
let t1 = _mm_mul_ps(_mm_sub_ps(_mm_and_ps(bmin4, mask4), o4), d4);
|
||||
let t2 = _mm_mul_ps(_mm_sub_ps(_mm_and_ps(bmax4, mask4), o4), d4);
|
||||
let vmax4 = _mm_max_ps(t1, t2);
|
||||
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 {
|
||||
@ -169,3 +202,48 @@ pub fn surrounding_box(box0: &AABB, box1: &AABB) -> AABB {
|
||||
);
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
289
rtiow/renderer/src/bvh_triangles.rs
Normal file
289
rtiow/renderer/src/bvh_triangles.rs
Normal file
@ -0,0 +1,289 @@
|
||||
/// 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,5 +1,6 @@
|
||||
pub mod aabb;
|
||||
pub mod bvh;
|
||||
pub mod bvh_triangles;
|
||||
pub mod camera;
|
||||
pub mod constant_medium;
|
||||
pub mod cuboid;
|
||||
@ -17,8 +18,10 @@ pub mod ray;
|
||||
pub mod rect;
|
||||
pub mod renderer;
|
||||
pub mod rotate;
|
||||
pub mod scale;
|
||||
pub mod scenes;
|
||||
pub mod sphere;
|
||||
pub mod texture;
|
||||
pub mod translate;
|
||||
pub mod triangles;
|
||||
pub mod vec3;
|
||||
|
||||
@ -7,7 +7,8 @@ use std::{
|
||||
mpsc::{sync_channel, Receiver, SyncSender},
|
||||
Arc, Mutex,
|
||||
},
|
||||
thread, time,
|
||||
thread,
|
||||
time::{self, Instant},
|
||||
};
|
||||
|
||||
use core_affinity;
|
||||
@ -41,6 +42,7 @@ pub enum Model {
|
||||
Mandelbrot,
|
||||
PerlinDebug,
|
||||
Spheramid,
|
||||
Stltest,
|
||||
Test,
|
||||
Tron,
|
||||
Tutorial,
|
||||
@ -58,6 +60,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 +90,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 +111,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(),
|
||||
@ -394,6 +399,7 @@ impl AddAssign for RenderStats {
|
||||
}
|
||||
|
||||
fn progress(
|
||||
start_time: Instant,
|
||||
last_stat: &RenderStats,
|
||||
current_stat: &RenderStats,
|
||||
time_diff: time::Duration,
|
||||
@ -402,13 +408,18 @@ fn progress(
|
||||
let human = human::Formatter::new();
|
||||
let pixel_diff = current_stat.pixels - last_stat.pixels;
|
||||
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!(
|
||||
"{:7} / {:7}pixels ({:2}%) {:7}pixels/s {:7}rays/s",
|
||||
"{:7} / {:7}pixels ({:2.0}%) {:7}pixels/s {:7}rays/s eta {:.0}s",
|
||||
human.format(current_stat.pixels as f64),
|
||||
human.format(pixel_total as f64),
|
||||
100 * current_stat.pixels / pixel_total,
|
||||
percent,
|
||||
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
|
||||
)
|
||||
}
|
||||
|
||||
@ -576,10 +587,9 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
|
||||
drop(pixel_req_rx);
|
||||
drop(pixel_resp_tx);
|
||||
|
||||
let start_time = time::Instant::now();
|
||||
let (w, h) = (scene.width, scene.height);
|
||||
handles.push(thread::spawn(move || {
|
||||
let batch_line_requests = true;
|
||||
let batch_line_requests = false;
|
||||
if batch_line_requests {
|
||||
for y in 0..h {
|
||||
pixel_req_tx
|
||||
@ -604,6 +614,7 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
|
||||
let mut last_time = time::Instant::now();
|
||||
let mut last_stat: RenderStats = Default::default();
|
||||
let mut current_stat: RenderStats = Default::default();
|
||||
let start_time = time::Instant::now();
|
||||
for resp in pixel_resp_rx {
|
||||
match resp {
|
||||
Response::Pixel { x, y, pixel, rs } => {
|
||||
@ -621,9 +632,15 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
|
||||
let now = time::Instant::now();
|
||||
let time_diff = now - last_time;
|
||||
if time_diff > time::Duration::from_secs(1) {
|
||||
info!(
|
||||
println!(
|
||||
"{}",
|
||||
progress(&last_stat, ¤t_stat, time_diff, pixel_total)
|
||||
progress(
|
||||
start_time,
|
||||
&last_stat,
|
||||
¤t_stat,
|
||||
time_diff,
|
||||
pixel_total
|
||||
)
|
||||
);
|
||||
last_stat = current_stat;
|
||||
last_time = now;
|
||||
@ -633,10 +650,16 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
|
||||
thr.join().expect("thread join");
|
||||
}
|
||||
let time_diff = time::Instant::now() - start_time;
|
||||
info!(
|
||||
println!(
|
||||
"Runtime {} seconds {}",
|
||||
time_diff.as_secs_f32(),
|
||||
progress(&Default::default(), ¤t_stat, time_diff, pixel_total)
|
||||
progress(
|
||||
start_time,
|
||||
&Default::default(),
|
||||
¤t_stat,
|
||||
time_diff,
|
||||
pixel_total
|
||||
)
|
||||
);
|
||||
|
||||
output::write_images(&scene, time_diff, output_dir)
|
||||
|
||||
53
rtiow/renderer/src/scale.rs
Normal file
53
rtiow/renderer/src/scale.rs
Normal file
@ -0,0 +1,53 @@
|
||||
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,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;
|
||||
|
||||
126
rtiow/renderer/src/scenes/stltest.rs
Normal file
126
rtiow/renderer/src/scenes/stltest.rs
Normal file
@ -0,0 +1,126 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
168
rtiow/renderer/src/triangles.rs
Normal file
168
rtiow/renderer/src/triangles.rs
Normal file
@ -0,0 +1,168 @@
|
||||
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,274 +1 @@
|
||||
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.));
|
||||
}
|
||||
}
|
||||
pub use vec3::*;
|
||||
|
||||
BIN
rtiow/renderer/stls/20mm cube.stl
Normal file
BIN
rtiow/renderer/stls/20mm cube.stl
Normal file
Binary file not shown.
@ -1,2 +0,0 @@
|
||||
imports_granularity = "Crate"
|
||||
format_code_in_doc_comments = true
|
||||
@ -2,12 +2,12 @@
|
||||
name = "tracer"
|
||||
version = "0.1.0"
|
||||
authors = ["Bill Thiede <git@xinu.tv>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.5"
|
||||
log = "0.4.17"
|
||||
renderer = { path = "../renderer" }
|
||||
stderrlog = "0.4.1"
|
||||
structopt = "0.2.10"
|
||||
stderrlog = "0.4.3"
|
||||
structopt = "0.2.18"
|
||||
|
||||
10
rtiow/vec3/Cargo.toml
Normal file
10
rtiow/vec3/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
||||
[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"
|
||||
2
rtiow/vec3/src/lib.rs
Normal file
2
rtiow/vec3/src/lib.rs
Normal file
@ -0,0 +1,2 @@
|
||||
mod vec3;
|
||||
pub use vec3::*;
|
||||
309
rtiow/vec3/src/vec3.rs
Normal file
309
rtiow/vec3/src/vec3.rs
Normal file
@ -0,0 +1,309 @@
|
||||
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.));
|
||||
}
|
||||
}
|
||||
56
zigrtiow/test/threads.zig
Normal file
56
zigrtiow/test/threads.zig
Normal file
@ -0,0 +1,56 @@
|
||||
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