Move Material to new module && TriangleBVH intersect && Seperate light and material

This commit is contained in:
hal8174 2024-09-30 21:13:50 +02:00
parent 50d3874467
commit b3fdef8837
18 changed files with 180 additions and 90 deletions

9
Cargo.lock generated
View file

@ -731,12 +731,21 @@ dependencies = [
"rayon", "rayon",
] ]
[[package]]
name = "ray-tracing-material"
version = "0.1.0"
dependencies = [
"rand",
"ray-tracing-core",
]
[[package]] [[package]]
name = "ray-tracing-scene" name = "ray-tracing-scene"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"rand", "rand",
"ray-tracing-core", "ray-tracing-core",
"ray-tracing-material",
] ]
[[package]] [[package]]

View file

@ -1,4 +1,4 @@
[workspace] [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" resolver = "2"

View file

@ -43,7 +43,7 @@ impl AABB {
&& pos.z <= self.max.z && pos.z <= self.max.z
} }
pub fn intersect_ray(self, ray: Ray) -> Option<Float> { pub fn intersect_ray(self, ray: Ray, min: Float, max: Float) -> Option<Float> {
todo!() todo!()
} }
} }

View file

@ -1,6 +1,7 @@
pub mod aabb; pub mod aabb;
pub mod camera; pub mod camera;
pub mod color; pub mod color;
pub mod light;
pub mod material; pub mod material;
pub mod math; pub mod math;
pub mod ray; pub mod ray;
@ -11,6 +12,7 @@ pub mod prelude {
pub type Float = f32; pub type Float = f32;
pub use crate::aabb::AABB; pub use crate::aabb::AABB;
pub use crate::color::Color; pub use crate::color::Color;
pub use crate::light::Light;
pub use crate::material::Material; pub use crate::material::Material;
pub use crate::math::*; pub use crate::math::*;
pub use crate::ray::Ray; pub use crate::ray::Ray;

View file

@ -0,0 +1,26 @@
use crate::prelude::*;
use rand::Rng;
pub trait Light<R: Rng>: 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<R: Rng> Light<R> for AreaLight {
fn emit(&self, w_in: Dir3, _rng: &mut R) -> Color {
if w_in.y() > 0.0 {
self.color
} else {
Color::black()
}
}
}

View file

@ -3,52 +3,5 @@ use rand::Rng;
/// All calculations for the material are done a tangent space of the intersection. /// All calculations for the material are done a tangent space of the intersection.
pub trait Material<R: Rng>: Sync { pub trait Material<R: Rng>: Sync {
fn eval(&self, w_in: Dir3, w_out: Dir3, rng: &mut R) -> Option<Color> { fn eval(&self, w_in: Dir3, w_out: Dir3, rng: &mut R) -> Color;
None
}
fn emit(&self, w_in: Dir3, rng: &mut R) -> Option<Color> {
None
}
}
pub struct DefaultMaterial {}
impl<R: Rng> Material<R> for DefaultMaterial {
// evaluates the bsdf
fn eval(&self, _w_in: Dir3, _w_out: Dir3, _rng: &mut R) -> Option<Color> {
None
}
}
pub struct DiffuseMaterial {
color: Color,
}
impl DiffuseMaterial {
pub fn new(color: Color) -> Self {
Self { color }
}
}
impl<R: Rng> Material<R> for DiffuseMaterial {
fn eval(&self, _w_in: Dir3, _w_out: Dir3, _rng: &mut R) -> Option<Color> {
Some(self.color)
}
}
pub struct AreaLight {
color: Color,
}
impl AreaLight {
pub fn new(color: Color) -> Self {
Self { color }
}
}
impl<R: Rng> Material<R> for AreaLight {
fn emit(&self, _w_in: Dir3, _rng: &mut R) -> Option<Color> {
Some(self.color)
}
} }

View file

@ -39,7 +39,9 @@ mod test {
dbg!(f); 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!() panic!()
} }

View file

@ -50,27 +50,25 @@ where
let mut count = 0; let mut count = 0;
while let Some(i) = self.scene.intersect(r, 0.001, Float::INFINITY) { while let Some(i) = self.scene.intersect(r, 0.001, Float::INFINITY) {
if count > 4 {
break;
}
let frame = i.tangent_frame(); let frame = i.tangent_frame();
let w_in = frame.to_frame(-r.dir()); let w_in = frame.to_frame(-r.dir());
let w_out = Dir3::generate_uniform_hemisphere(rng); let w_out = Dir3::generate_uniform_hemisphere(rng);
let mat = i.material(); if let Some(light) = i.light() {
sum += alpha * light.emit(w_in, rng) * w_out.y();
if let Some(c) = mat.emit(w_in, rng) {
sum += alpha * c * w_out.y();
} }
if let Some(c) = mat.eval(w_in, w_out, rng) { if let Some(material) = i.material() {
alpha *= c; alpha *= material.eval(w_in, w_out, rng) * w_out.y();
} else { } else {
return sum; return sum;
} }
r = Ray::new(r.at(i.t()), frame.to_world(w_out), r.time()); r = Ray::new(r.at(i.t()), frame.to_world(w_out), r.time());
count += 1; count += 1;
if count > 4 {
break;
}
} }
sum sum

View file

@ -8,15 +8,22 @@ pub trait Scene<R: Rng> {
pub struct Intersection<'sc, R: Rng> { pub struct Intersection<'sc, R: Rng> {
t: Float, t: Float,
normal: Dir3, normal: Dir3,
material: &'sc dyn Material<R>, material: Option<&'sc dyn Material<R>>,
light: Option<&'sc dyn Light<R>>,
} }
impl<'sc, R: Rng> Intersection<'sc, R> { impl<'sc, R: Rng> Intersection<'sc, R> {
pub fn new(t: Float, normal: Dir3, material: &'sc dyn Material<R>) -> Self { pub fn new(
t: Float,
normal: Dir3,
material: Option<&'sc dyn Material<R>>,
light: Option<&'sc dyn Light<R>>,
) -> Self {
Self { Self {
t, t,
normal, normal,
material, material,
light,
} }
} }
@ -28,10 +35,14 @@ impl<'sc, R: Rng> Intersection<'sc, R> {
self.normal self.normal
} }
pub fn material(&self) -> &'sc dyn Material<R> { pub fn material(&self) -> Option<&'sc dyn Material<R>> {
self.material self.material
} }
pub fn light(&self) -> Option<&'sc dyn Light<R>> {
self.light
}
pub fn tangent_frame(&self) -> Frame { pub fn tangent_frame(&self) -> Frame {
Frame::from_normal(self.normal) Frame::from_normal(self.normal)
} }

View file

@ -39,9 +39,9 @@ fn main() -> ImageResult<()> {
let s = cornel(); let s = cornel();
let c = BasicCamera::new( let c = BasicCamera::new(
640,
400, 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::new(1.0, 0.0, 0.0),
Dir3::up(), Dir3::up(),
Float::to_radians(90.0), Float::to_radians(90.0),
@ -49,5 +49,5 @@ fn main() -> ImageResult<()> {
let r = PathTracer::new(s, c); let r = PathTracer::new(s, c);
render_image(r, "test.exr", 1024) render_image(r, "test.exr", 1048)
} }

View file

@ -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" }

View file

@ -0,0 +1,11 @@
use rand::Rng;
use ray_tracing_core::prelude::*;
pub struct DefaultMaterial {}
impl<R: Rng> Material<R> for DefaultMaterial {
// evaluates the bsdf
fn eval(&self, _w_in: Dir3, _w_out: Dir3, _rng: &mut R) -> Color {
Color::black()
}
}

View file

@ -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<R: Rng> Material<R> for DiffuseMaterial {
fn eval(&self, _w_in: Dir3, _w_out: Dir3, _rng: &mut R) -> Color {
self.albedo
}
}

View file

@ -0,0 +1,2 @@
pub mod default;
pub mod diffuse;

View file

@ -6,3 +6,4 @@ edition = "2021"
[dependencies] [dependencies]
rand = "0.8.5" rand = "0.8.5"
ray-tracing-core = { path = "../ray-tracing-core" } ray-tracing-core = { path = "../ray-tracing-core" }
ray-tracing-material = { path = "../ray-tracing-material" }

View file

@ -1,7 +1,7 @@
use rand::Rng; use rand::Rng;
use ray_tracing_core::material::DefaultMaterial;
use ray_tracing_core::prelude::*; use ray_tracing_core::prelude::*;
use ray_tracing_core::scene::{Intersection, Scene}; use ray_tracing_core::scene::{Intersection, Scene};
use ray_tracing_material::default::DefaultMaterial;
pub struct BasicScene { pub struct BasicScene {
pub(crate) spheres: Vec<(Pos3, Float)>, pub(crate) spheres: Vec<(Pos3, Float)>,
@ -30,7 +30,8 @@ impl<R: Rng> Scene<R> for BasicScene {
let int = Intersection::new( let int = Intersection::new(
d, d,
((ray.start() + d * ray.dir()) - c).normalize(), ((ray.start() + d * ray.dir()) - c).normalize(),
&DefaultMaterial {}, Some(&DefaultMaterial {}),
None,
); );
if d >= min && d <= max { if d >= min && d <= max {
if let Some(i) = intersection.as_ref() { if let Some(i) = intersection.as_ref() {

View file

@ -1,7 +1,2 @@
use rand::Rng;
use ray_tracing_core::material::DefaultMaterial;
use ray_tracing_core::prelude::*;
pub mod basic_scene; pub mod basic_scene;
pub mod triangle_bvh; pub mod triangle_bvh;

View file

@ -9,10 +9,15 @@ type Index = u32;
pub struct TriangleBVH<R: Rng> { pub struct TriangleBVH<R: Rng> {
vertices: Vec<Pos3>, vertices: Vec<Pos3>,
triangles: Vec<Triangle>, triangles: Vec<Triangle>,
materials: Vec<Box<dyn Material<R>>>, materials: Vec<BVHMaterial<R>>,
bvh: Vec<Node>, bvh: Vec<Node>,
} }
struct BVHMaterial<R: Rng> {
material: Option<Box<dyn Material<R>>>,
light: Option<Box<dyn Light<R>>>,
}
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
enum Node { enum Node {
Inner { Inner {
@ -53,7 +58,7 @@ fn triangle_intersection(ray: Ray, v: [Pos3; 3]) -> Option<Float> {
let inv_det = 1.0 / det; let inv_det = 1.0 / det;
let s = ray.start() - v[0]; let s = ray.start() - v[0];
let u = inv_det * s.dot(ray_cross_e2); 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; return None;
} }
@ -82,11 +87,7 @@ fn triangle_normal(v: [Pos3; 3]) -> Dir3 {
} }
impl<R: Rng> TriangleBVH<R> { impl<R: Rng> TriangleBVH<R> {
fn new( fn new(vertices: Vec<Pos3>, triangles: Vec<Triangle>, materials: Vec<BVHMaterial<R>>) -> Self {
vertices: Vec<Pos3>,
triangles: Vec<Triangle>,
materials: Vec<Box<dyn Material<R>>>,
) -> Self {
Self { Self {
vertices, vertices,
bvh: vec![Node::Leaf { bvh: vec![Node::Leaf {
@ -120,7 +121,40 @@ impl<R: Rng> TriangleBVH<R> {
left_aabb, left_aabb,
right, right,
right_aabb, 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 } => { Node::Leaf { start, count } => {
let mut intersection = None; let mut intersection = None;
for i in start..(start + count) { for i in start..(start + count) {
@ -148,19 +182,24 @@ impl<R: Rng> Scene<R> for TriangleBVH<R> {
let (i, t) = self.intersect_bvh(0, ray, min, max)?; let (i, t) = self.intersect_bvh(0, ray, min, max)?;
let triangle = self.triangles[i as usize]; 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(); 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 { pub mod examples {
use super::{Triangle, TriangleBVH}; use super::{BVHMaterial, Triangle, TriangleBVH};
use rand::Rng; use rand::Rng;
use ray_tracing_core::material::{AreaLight, DiffuseMaterial}; use ray_tracing_core::{light::AreaLight, prelude::*};
use ray_tracing_core::prelude::*; use ray_tracing_material::diffuse::DiffuseMaterial;
pub fn cornel<R: Rng>() -> TriangleBVH<R> { pub fn cornel<R: Rng>() -> TriangleBVH<R> {
let side_length = 1.5; let side_length = 1.5;
@ -196,10 +235,22 @@ pub mod examples {
Triangle::new([11, 9, 10], 3), Triangle::new([11, 9, 10], 3),
], ],
vec![ vec![
Box::new(DiffuseMaterial::new(Color::new(0.8, 0.8, 0.8))), BVHMaterial {
Box::new(DiffuseMaterial::new(Color::new(0.9, 0.0, 0.0))), material: Some(Box::new(DiffuseMaterial::new(Color::new(0.8, 0.8, 0.8)))),
Box::new(DiffuseMaterial::new(Color::new(0.0, 0.9, 0.0))), light: None,
Box::new(AreaLight::new(Color::new(5.0, 5.0, 5.0))), },
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))),
},
], ],
) )
} }