diff --git a/Cargo.lock b/Cargo.lock index 28fe388..801bfeb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -731,12 +731,21 @@ dependencies = [ "rayon", ] +[[package]] +name = "ray-tracing-material" +version = "0.1.0" +dependencies = [ + "rand", + "ray-tracing-core", +] + [[package]] name = "ray-tracing-scene" version = "0.1.0" dependencies = [ "rand", "ray-tracing-core", + "ray-tracing-material", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 0c703a5..3b77f21 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,4 @@ [workspace] -members = [ "ray-tracing-core", "ray-tracing-image", "ray-tracing-scene"] +members = [ "ray-tracing-core", "ray-tracing-image", "ray-tracing-material", "ray-tracing-scene"] resolver = "2" diff --git a/ray-tracing-core/src/aabb.rs b/ray-tracing-core/src/aabb.rs index 603b290..ff2b41b 100644 --- a/ray-tracing-core/src/aabb.rs +++ b/ray-tracing-core/src/aabb.rs @@ -43,7 +43,7 @@ impl AABB { && pos.z <= self.max.z } - pub fn intersect_ray(self, ray: Ray) -> Option { + pub fn intersect_ray(self, ray: Ray, min: Float, max: Float) -> Option { todo!() } } diff --git a/ray-tracing-core/src/lib.rs b/ray-tracing-core/src/lib.rs index 4a51959..cd512f1 100644 --- a/ray-tracing-core/src/lib.rs +++ b/ray-tracing-core/src/lib.rs @@ -1,6 +1,7 @@ pub mod aabb; pub mod camera; pub mod color; +pub mod light; pub mod material; pub mod math; pub mod ray; @@ -11,6 +12,7 @@ pub mod prelude { pub type Float = f32; pub use crate::aabb::AABB; pub use crate::color::Color; + pub use crate::light::Light; pub use crate::material::Material; pub use crate::math::*; pub use crate::ray::Ray; diff --git a/ray-tracing-core/src/light.rs b/ray-tracing-core/src/light.rs new file mode 100644 index 0000000..300ad7e --- /dev/null +++ b/ray-tracing-core/src/light.rs @@ -0,0 +1,26 @@ +use crate::prelude::*; +use rand::Rng; + +pub trait Light: Sync { + fn emit(&self, w_in: Dir3, rng: &mut R) -> Color; +} + +pub struct AreaLight { + pub(crate) color: Color, +} + +impl AreaLight { + pub fn new(color: Color) -> Self { + Self { color } + } +} + +impl Light for AreaLight { + fn emit(&self, w_in: Dir3, _rng: &mut R) -> Color { + if w_in.y() > 0.0 { + self.color + } else { + Color::black() + } + } +} diff --git a/ray-tracing-core/src/material.rs b/ray-tracing-core/src/material.rs index ff22f87..cb55d74 100644 --- a/ray-tracing-core/src/material.rs +++ b/ray-tracing-core/src/material.rs @@ -3,52 +3,5 @@ use rand::Rng; /// All calculations for the material are done a tangent space of the intersection. pub trait Material: Sync { - fn eval(&self, w_in: Dir3, w_out: Dir3, rng: &mut R) -> Option { - None - } - - fn emit(&self, w_in: Dir3, rng: &mut R) -> Option { - None - } -} - -pub struct DefaultMaterial {} - -impl Material for DefaultMaterial { - // evaluates the bsdf - fn eval(&self, _w_in: Dir3, _w_out: Dir3, _rng: &mut R) -> Option { - None - } -} - -pub struct DiffuseMaterial { - color: Color, -} - -impl DiffuseMaterial { - pub fn new(color: Color) -> Self { - Self { color } - } -} - -impl Material for DiffuseMaterial { - fn eval(&self, _w_in: Dir3, _w_out: Dir3, _rng: &mut R) -> Option { - Some(self.color) - } -} - -pub struct AreaLight { - color: Color, -} - -impl AreaLight { - pub fn new(color: Color) -> Self { - Self { color } - } -} - -impl Material for AreaLight { - fn emit(&self, _w_in: Dir3, _rng: &mut R) -> Option { - Some(self.color) - } + fn eval(&self, w_in: Dir3, w_out: Dir3, rng: &mut R) -> Color; } diff --git a/ray-tracing-core/src/math/frame.rs b/ray-tracing-core/src/math/frame.rs index 159eec4..c474165 100644 --- a/ray-tracing-core/src/math/frame.rs +++ b/ray-tracing-core/src/math/frame.rs @@ -39,7 +39,9 @@ mod test { dbg!(f); - dbg!(f.to_world(Dir3::new(0.0, 1.0, 0.0))); + let t = dbg!(f.to_world(Dir3::new(1.0, 1.0, 1.0))); + + dbg!(f.to_frame(t)); panic!() } diff --git a/ray-tracing-core/src/renderer.rs b/ray-tracing-core/src/renderer.rs index d128386..3ba7a73 100644 --- a/ray-tracing-core/src/renderer.rs +++ b/ray-tracing-core/src/renderer.rs @@ -50,27 +50,25 @@ where let mut count = 0; while let Some(i) = self.scene.intersect(r, 0.001, Float::INFINITY) { - if count > 4 { - break; - } let frame = i.tangent_frame(); let w_in = frame.to_frame(-r.dir()); let w_out = Dir3::generate_uniform_hemisphere(rng); - let mat = i.material(); - - if let Some(c) = mat.emit(w_in, rng) { - sum += alpha * c * w_out.y(); + if let Some(light) = i.light() { + sum += alpha * light.emit(w_in, rng) * w_out.y(); } - if let Some(c) = mat.eval(w_in, w_out, rng) { - alpha *= c; + if let Some(material) = i.material() { + alpha *= material.eval(w_in, w_out, rng) * w_out.y(); } else { return sum; } r = Ray::new(r.at(i.t()), frame.to_world(w_out), r.time()); count += 1; + if count > 4 { + break; + } } sum diff --git a/ray-tracing-core/src/scene.rs b/ray-tracing-core/src/scene.rs index 524e2c9..8c07fb1 100644 --- a/ray-tracing-core/src/scene.rs +++ b/ray-tracing-core/src/scene.rs @@ -8,15 +8,22 @@ pub trait Scene { pub struct Intersection<'sc, R: Rng> { t: Float, normal: Dir3, - material: &'sc dyn Material, + material: Option<&'sc dyn Material>, + light: Option<&'sc dyn Light>, } impl<'sc, R: Rng> Intersection<'sc, R> { - pub fn new(t: Float, normal: Dir3, material: &'sc dyn Material) -> Self { + pub fn new( + t: Float, + normal: Dir3, + material: Option<&'sc dyn Material>, + light: Option<&'sc dyn Light>, + ) -> Self { Self { t, normal, material, + light, } } @@ -28,10 +35,14 @@ impl<'sc, R: Rng> Intersection<'sc, R> { self.normal } - pub fn material(&self) -> &'sc dyn Material { + pub fn material(&self) -> Option<&'sc dyn Material> { self.material } + pub fn light(&self) -> Option<&'sc dyn Light> { + self.light + } + pub fn tangent_frame(&self) -> Frame { Frame::from_normal(self.normal) } diff --git a/ray-tracing-image/src/main.rs b/ray-tracing-image/src/main.rs index 9bf2eff..da9515e 100644 --- a/ray-tracing-image/src/main.rs +++ b/ray-tracing-image/src/main.rs @@ -39,9 +39,9 @@ fn main() -> ImageResult<()> { let s = cornel(); let c = BasicCamera::new( - 640, 400, - Pos3::new(-10.0, 0.0, 0.0), + 400, + Pos3::new(-6.0, 0.0, 0.0), Dir3::new(1.0, 0.0, 0.0), Dir3::up(), Float::to_radians(90.0), @@ -49,5 +49,5 @@ fn main() -> ImageResult<()> { let r = PathTracer::new(s, c); - render_image(r, "test.exr", 1024) + render_image(r, "test.exr", 1048) } diff --git a/ray-tracing-material/Cargo.toml b/ray-tracing-material/Cargo.toml new file mode 100644 index 0000000..0e4572f --- /dev/null +++ b/ray-tracing-material/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "ray-tracing-material" +version = "0.1.0" +edition = "2021" + +[dependencies] +rand = "0.8.5" +ray-tracing-core = { path = "../ray-tracing-core" } diff --git a/ray-tracing-material/src/default.rs b/ray-tracing-material/src/default.rs new file mode 100644 index 0000000..c8ae5a4 --- /dev/null +++ b/ray-tracing-material/src/default.rs @@ -0,0 +1,11 @@ +use rand::Rng; +use ray_tracing_core::prelude::*; + +pub struct DefaultMaterial {} + +impl Material for DefaultMaterial { + // evaluates the bsdf + fn eval(&self, _w_in: Dir3, _w_out: Dir3, _rng: &mut R) -> Color { + Color::black() + } +} diff --git a/ray-tracing-material/src/diffuse.rs b/ray-tracing-material/src/diffuse.rs new file mode 100644 index 0000000..9cdba40 --- /dev/null +++ b/ray-tracing-material/src/diffuse.rs @@ -0,0 +1,20 @@ +use rand::Rng; +use ray_tracing_core::prelude::*; + +pub struct DiffuseMaterial { + pub(crate) albedo: Color, +} + +impl DiffuseMaterial { + pub fn new(color: Color) -> Self { + Self { + albedo: color * FloatConsts::FRAC_1_PI, + } + } +} + +impl Material for DiffuseMaterial { + fn eval(&self, _w_in: Dir3, _w_out: Dir3, _rng: &mut R) -> Color { + self.albedo + } +} diff --git a/ray-tracing-material/src/lib.rs b/ray-tracing-material/src/lib.rs new file mode 100644 index 0000000..991b6b1 --- /dev/null +++ b/ray-tracing-material/src/lib.rs @@ -0,0 +1,2 @@ +pub mod default; +pub mod diffuse; diff --git a/ray-tracing-scene/Cargo.toml b/ray-tracing-scene/Cargo.toml index 09f2662..ce8f09c 100644 --- a/ray-tracing-scene/Cargo.toml +++ b/ray-tracing-scene/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" [dependencies] rand = "0.8.5" ray-tracing-core = { path = "../ray-tracing-core" } +ray-tracing-material = { path = "../ray-tracing-material" } diff --git a/ray-tracing-scene/src/basic_scene.rs b/ray-tracing-scene/src/basic_scene.rs index 7b315df..0e68498 100644 --- a/ray-tracing-scene/src/basic_scene.rs +++ b/ray-tracing-scene/src/basic_scene.rs @@ -1,7 +1,7 @@ use rand::Rng; -use ray_tracing_core::material::DefaultMaterial; use ray_tracing_core::prelude::*; use ray_tracing_core::scene::{Intersection, Scene}; +use ray_tracing_material::default::DefaultMaterial; pub struct BasicScene { pub(crate) spheres: Vec<(Pos3, Float)>, @@ -30,7 +30,8 @@ impl Scene for BasicScene { let int = Intersection::new( d, ((ray.start() + d * ray.dir()) - c).normalize(), - &DefaultMaterial {}, + Some(&DefaultMaterial {}), + None, ); if d >= min && d <= max { if let Some(i) = intersection.as_ref() { diff --git a/ray-tracing-scene/src/lib.rs b/ray-tracing-scene/src/lib.rs index db5f491..d56f56c 100644 --- a/ray-tracing-scene/src/lib.rs +++ b/ray-tracing-scene/src/lib.rs @@ -1,7 +1,2 @@ -use rand::Rng; -use ray_tracing_core::material::DefaultMaterial; -use ray_tracing_core::prelude::*; - pub mod basic_scene; - pub mod triangle_bvh; diff --git a/ray-tracing-scene/src/triangle_bvh.rs b/ray-tracing-scene/src/triangle_bvh.rs index a5fae51..b6a3409 100644 --- a/ray-tracing-scene/src/triangle_bvh.rs +++ b/ray-tracing-scene/src/triangle_bvh.rs @@ -9,10 +9,15 @@ type Index = u32; pub struct TriangleBVH { vertices: Vec, triangles: Vec, - materials: Vec>>, + materials: Vec>, bvh: Vec, } +struct BVHMaterial { + material: Option>>, + light: Option>>, +} + #[derive(Debug, Clone, Copy)] enum Node { Inner { @@ -53,7 +58,7 @@ fn triangle_intersection(ray: Ray, v: [Pos3; 3]) -> Option { let inv_det = 1.0 / det; let s = ray.start() - v[0]; let u = inv_det * s.dot(ray_cross_e2); - if u < 0.0 || u > 1.0 { + if !(0.0..=1.0).contains(&u) { return None; } @@ -82,11 +87,7 @@ fn triangle_normal(v: [Pos3; 3]) -> Dir3 { } impl TriangleBVH { - fn new( - vertices: Vec, - triangles: Vec, - materials: Vec>>, - ) -> Self { + fn new(vertices: Vec, triangles: Vec, materials: Vec>) -> Self { Self { vertices, bvh: vec![Node::Leaf { @@ -120,7 +121,40 @@ impl TriangleBVH { left_aabb, right, right_aabb, - } => todo!(), + } => { + let left_intersect = left_aabb.intersect_ray(ray, min, max); + let right_intersect = right_aabb.intersect_ray(ray, min, max); + + match (left_intersect, right_intersect) { + (None, None) => None, + (None, Some(_)) => self.intersect_bvh(right, ray, min, max), + (Some(_), None) => self.intersect_bvh(left, ray, min, max), + (Some(l), Some(r)) => { + let close; + let far; + if l < r { + close = left; + far = right; + } else { + close = right; + far = left; + } + + 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)) + .filter(|far_intersect| far_intersect.1 < close_intersect.1) + { + Some(far_intersect) + } else { + Some(close_intersect) + } + } else { + self.intersect_bvh(far, ray, min, max) + } + } + } + } Node::Leaf { start, count } => { let mut intersection = None; for i in start..(start + count) { @@ -148,19 +182,24 @@ impl Scene for TriangleBVH { let (i, t) = self.intersect_bvh(0, ray, min, max)?; let triangle = self.triangles[i as usize]; - let material = self.materials[triangle.material as usize].as_ref(); + let material = &self.materials[triangle.material as usize]; let n = triangle_normal(self.get_vertices(i)).normalize(); - Some(Intersection::new(t, n, material)) + Some(Intersection::new( + t, + n, + material.material.as_deref(), + material.light.as_deref(), + )) } } pub mod examples { - use super::{Triangle, TriangleBVH}; + use super::{BVHMaterial, Triangle, TriangleBVH}; use rand::Rng; - use ray_tracing_core::material::{AreaLight, DiffuseMaterial}; - use ray_tracing_core::prelude::*; + use ray_tracing_core::{light::AreaLight, prelude::*}; + use ray_tracing_material::diffuse::DiffuseMaterial; pub fn cornel() -> TriangleBVH { let side_length = 1.5; @@ -196,10 +235,22 @@ pub mod examples { Triangle::new([11, 9, 10], 3), ], vec![ - Box::new(DiffuseMaterial::new(Color::new(0.8, 0.8, 0.8))), - Box::new(DiffuseMaterial::new(Color::new(0.9, 0.0, 0.0))), - Box::new(DiffuseMaterial::new(Color::new(0.0, 0.9, 0.0))), - Box::new(AreaLight::new(Color::new(5.0, 5.0, 5.0))), + BVHMaterial { + material: Some(Box::new(DiffuseMaterial::new(Color::new(0.8, 0.8, 0.8)))), + light: None, + }, + BVHMaterial { + material: Some(Box::new(DiffuseMaterial::new(Color::new(0.9, 0.0, 0.0)))), + light: None, + }, + BVHMaterial { + material: Some(Box::new(DiffuseMaterial::new(Color::new(0.0, 0.9, 0.0)))), + light: None, + }, + BVHMaterial { + material: None, + light: Some(Box::new(AreaLight::new(Color::white() * 30.0))), + }, ], ) }