From 7d122d44b3e4cc4ffcfb6ac6c16073d0680437e1 Mon Sep 17 00:00:00 2001 From: hal8174 Date: Thu, 3 Oct 2024 22:32:37 +0200 Subject: [PATCH] Complete BVH building --- ray-tracing-core/src/aabb.rs | 74 +++++++++++++++++- ray-tracing-core/src/light.rs | 4 +- ray-tracing-core/src/math/dir3.rs | 15 +++- ray-tracing-core/src/math/pos3.rs | 24 ++++-- ray-tracing-scene/src/triangle_bvh.rs | 107 +++++++++++++++++++++++--- 5 files changed, 207 insertions(+), 17 deletions(-) diff --git a/ray-tracing-core/src/aabb.rs b/ray-tracing-core/src/aabb.rs index ff2b41b..65b36e9 100644 --- a/ray-tracing-core/src/aabb.rs +++ b/ray-tracing-core/src/aabb.rs @@ -26,6 +26,21 @@ impl AABB { } } + pub fn extend(self, other: Pos3) -> Self { + Self { + min: Pos3::new( + Float::min(self.min.x, other.x), + Float::min(self.min.y, other.y), + Float::min(self.min.z, other.z), + ), + max: Pos3::new( + Float::max(self.max.x, other.x), + Float::max(self.max.y, other.y), + Float::max(self.max.z, other.z), + ), + } + } + pub fn min(self) -> Pos3 { self.min } @@ -43,7 +58,64 @@ impl AABB { && pos.z <= self.max.z } + pub fn size(self) -> Dir3 { + Dir3::new( + self.max.x - self.min.x, + self.max.y - self.min.y, + self.max.z - self.min.z, + ) + } + pub fn intersect_ray(self, ray: Ray, min: Float, max: Float) -> Option { - todo!() + let t_min_x = (self.min.x() - ray.start().x()) / ray.dir().x(); + let t_min_y = (self.min.y() - ray.start().y()) / ray.dir().y(); + let t_min_z = (self.min.z() - ray.start().z()) / ray.dir().z(); + + let t_max_x = (self.max.x() - ray.start().x()) / ray.dir().x(); + let t_max_y = (self.max.y() - ray.start().y()) / ray.dir().y(); + let t_max_z = (self.max.z() - ray.start().z()) / ray.dir().z(); + + let t_min = Float::max( + Float::max(Float::min(t_min_x, t_max_x), Float::min(t_min_y, t_max_y)), + Float::max(Float::min(t_min_z, t_max_z), min), + ); + let t_max = Float::min( + Float::min(Float::max(t_min_x, t_max_x), Float::max(t_min_y, t_max_y)), + Float::min(Float::max(t_min_z, t_max_z), max), + ); + + if t_min < t_max { + Some(t_min) + } else { + None + } + } +} + +#[cfg(test)] +mod test { + + use super::AABB; + use crate::prelude::*; + + #[test] + fn intersection() { + let aabb = AABB::new(Pos3::new(-1.0, -1.0, -1.0), Pos3::new(1.0, 1.0, 1.0)); + + let r = Ray::new(Pos3::new(-2.0, 0.0, 0.0), Dir3::new(1.0, 0.0, 0.0), 0.0); + + assert_eq!(aabb.intersect_ray(r, 0.0, 10.0), Some(1.0)); + + let r = Ray::new( + Pos3::new(-2.0, -2.0, 0.0), + Dir3::new(1.0, 1.0, 0.0).normalize(), + 0.0, + ); + + assert_eq!(aabb.intersect_ray(r, 0.0, 10.0), Some(FloatConsts::SQRT_2)); + + let r = Ray::new(Pos3::new(0.0, 0.0, 0.0), Dir3::new(1.0, 0.0, 0.0), 0.0); + + assert_eq!(aabb.intersect_ray(r, 0.0, 10.0), Some(0.0)); } } diff --git a/ray-tracing-core/src/light.rs b/ray-tracing-core/src/light.rs index 3829865..6f3e2f6 100644 --- a/ray-tracing-core/src/light.rs +++ b/ray-tracing-core/src/light.rs @@ -1,9 +1,11 @@ use crate::prelude::*; +use std::fmt::Debug; -pub trait Light: Sync { +pub trait Light: Sync + Debug { fn emit(&self, w_in: Dir3, rng: &mut R) -> Color; } +#[derive(Debug)] pub struct AreaLight { pub(crate) color: Color, } diff --git a/ray-tracing-core/src/math/dir3.rs b/ray-tracing-core/src/math/dir3.rs index 047ac42..f38ecd8 100644 --- a/ray-tracing-core/src/math/dir3.rs +++ b/ray-tracing-core/src/math/dir3.rs @@ -1,5 +1,5 @@ use crate::prelude::*; -use std::ops::{Add, Div, Mul, Neg, Sub}; +use std::ops::{Add, Div, Index, Mul, Neg, Sub}; #[derive(Debug, Clone, Copy)] pub struct Dir3 { @@ -180,3 +180,16 @@ impl Neg for Dir3 { } } } + +impl Index for Dir3 { + type Output = Float; + + fn index(&self, index: usize) -> &Self::Output { + match index { + 0 => &self.x, + 1 => &self.y, + 2 => &self.z, + _ => panic!("Index out of bound {}", index), + } + } +} diff --git a/ray-tracing-core/src/math/pos3.rs b/ray-tracing-core/src/math/pos3.rs index 2ed49fa..87b729f 100644 --- a/ray-tracing-core/src/math/pos3.rs +++ b/ray-tracing-core/src/math/pos3.rs @@ -1,5 +1,6 @@ use crate::prelude::*; -use std::ops::{Add, Sub}; +use core::panic; +use std::ops::{Add, Index, Sub}; #[derive(Debug, Clone, Copy)] pub struct Pos3 { @@ -32,7 +33,7 @@ impl Pos3 { } } -impl Sub for pos3::Pos3 { +impl Sub for Pos3 { type Output = Dir3; fn sub(self, rhs: Self) -> Self::Output { @@ -44,14 +45,27 @@ impl Sub for pos3::Pos3 { } } -impl Add for pos3::Pos3 { - type Output = pos3::Pos3; +impl Add for Pos3 { + type Output = Pos3; fn add(self, rhs: Dir3) -> Self::Output { - pos3::Pos3 { + Pos3 { x: self.x + rhs.x, y: self.y + rhs.y, z: self.z + rhs.z, } } } + +impl Index for Pos3 { + type Output = Float; + + fn index(&self, index: usize) -> &Self::Output { + match index { + 0 => &self.x, + 1 => &self.y, + 2 => &self.z, + _ => panic!("Index out of bound {}", index), + } + } +} diff --git a/ray-tracing-scene/src/triangle_bvh.rs b/ray-tracing-scene/src/triangle_bvh.rs index 25034f7..4858f4d 100644 --- a/ray-tracing-scene/src/triangle_bvh.rs +++ b/ray-tracing-scene/src/triangle_bvh.rs @@ -5,6 +5,7 @@ use ray_tracing_core::{ type Index = u32; +#[derive(Debug)] pub struct TriangleBVH { vertices: Vec, triangles: Vec, @@ -12,6 +13,7 @@ pub struct TriangleBVH { bvh: Vec, } +#[derive(Debug)] struct BVHMaterial { material: Option>>, light: Option>>, @@ -85,14 +87,100 @@ fn triangle_normal(v: [Pos3; 3]) -> Dir3 { Dir3::cross(e1, e2) } +fn calculate_aabb(vertices: &[Pos3], triangles: &[Triangle]) -> AABB { + let mut aabb = AABB::new( + vertices[triangles[0].vertices[0] as usize], + vertices[triangles[0].vertices[1] as usize], + ); + aabb = aabb.extend(vertices[triangles[0].vertices[2] as usize]); + + for t in triangles.iter().skip(1) { + for v in t.vertices { + aabb = aabb.extend(vertices[v as usize]); + } + } + + aabb +} + +fn build_bvh( + vertices: &[Pos3], + triangles: &mut [Triangle], + bvh: &mut Vec, + node: usize, + aabb: AABB, +) { + let (start, count) = if let Node::Leaf { start, count } = bvh[node] { + (start, count) + } else { + unreachable!() + }; + + if count < 8 { + return; + } + + let size = aabb.size(); + + let dim = if size.x() > Float::max(size.y(), size.z()) { + 0 + } else if size.y() > Float::max(size.x(), size.z()) { + 1 + } else { + 2 + }; + + let get_key = |t: &Triangle| { + t.vertices + .iter() + .map(|&v| vertices[v as usize][dim]) + .sum::() + / 3.0 + }; + + triangles.sort_by(|a, b| get_key(a).partial_cmp(&get_key(b)).unwrap()); + + let (left_range, right_range) = triangles.split_at_mut((count / 2) as usize); + + let left_aabb = calculate_aabb(vertices, left_range); + let right_aabb = calculate_aabb(vertices, right_range); + + let i = bvh.len() as Index; + bvh[node] = Node::Inner { + left: i, + left_aabb, + right: i + 1, + right_aabb, + }; + + bvh.push(Node::Leaf { + start, + count: left_range.len() as Index, + }); + bvh.push(Node::Leaf { + start: start + left_range.len() as Index, + count: right_range.len() as Index, + }); + + build_bvh(vertices, left_range, bvh, i as usize, left_aabb); + build_bvh(vertices, right_range, bvh, i as usize + 1, right_aabb); +} + impl TriangleBVH { - fn new(vertices: Vec, triangles: Vec, materials: Vec>) -> Self { + fn new( + vertices: Vec, + mut triangles: Vec, + materials: Vec>, + ) -> Self { + let mut bvh = vec![Node::Leaf { + start: 0, + count: triangles.len() as Index, + }]; + let aabb = calculate_aabb(&vertices, &triangles); + build_bvh(&vertices, &mut triangles, &mut bvh, 0, aabb); Self { vertices, - bvh: vec![Node::Leaf { - start: 0, - count: triangles.len() as Index, - }], + bvh, triangles, materials, } @@ -141,7 +229,7 @@ impl TriangleBVH { if let Some(close_intersect) = self.intersect_bvh(close, ray, min, max) { if let Some(far_intersect) = self - .intersect_bvh(far, ray, min, Float::min(min, close_intersect.1)) + .intersect_bvh(far, ray, min, Float::min(max, close_intersect.1)) .filter(|far_intersect| far_intersect.1 < close_intersect.1) { Some(far_intersect) @@ -198,13 +286,14 @@ pub mod examples { use super::{BVHMaterial, Triangle, TriangleBVH}; use ray_tracing_core::{light::AreaLight, prelude::*}; use ray_tracing_material::{diffuse::DiffuseMaterial, mirror::Mirror}; + use std::fmt::Debug; - pub fn cornel() -> TriangleBVH { + pub fn cornel() -> TriangleBVH { let side_length = 1.5; let light_size = 0.5; let light_offset = 0.01; let box_size = 0.3; - TriangleBVH::new( + dbg!(TriangleBVH::new( vec![ Pos3::new(side_length, side_length, side_length), Pos3::new(side_length, side_length, -side_length), @@ -264,6 +353,6 @@ pub mod examples { light: None, }, ], - ) + )) } }