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",
]
[[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]]

View file

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

View file

@ -43,7 +43,7 @@ impl AABB {
&& 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!()
}
}

View file

@ -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;

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.
pub trait Material<R: Rng>: Sync {
fn eval(&self, w_in: Dir3, w_out: Dir3, rng: &mut R) -> Option<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)
}
fn eval(&self, w_in: Dir3, w_out: Dir3, rng: &mut R) -> Color;
}

View file

@ -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!()
}

View file

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

View file

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

View file

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

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]
rand = "0.8.5"
ray-tracing-core = { path = "../ray-tracing-core" }
ray-tracing-material = { path = "../ray-tracing-material" }

View file

@ -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<R: Rng> Scene<R> 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() {

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 triangle_bvh;

View file

@ -9,10 +9,15 @@ type Index = u32;
pub struct TriangleBVH<R: Rng> {
vertices: Vec<Pos3>,
triangles: Vec<Triangle>,
materials: Vec<Box<dyn Material<R>>>,
materials: Vec<BVHMaterial<R>>,
bvh: Vec<Node>,
}
struct BVHMaterial<R: Rng> {
material: Option<Box<dyn Material<R>>>,
light: Option<Box<dyn Light<R>>>,
}
#[derive(Debug, Clone, Copy)]
enum Node {
Inner {
@ -53,7 +58,7 @@ fn triangle_intersection(ray: Ray, v: [Pos3; 3]) -> Option<Float> {
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<R: Rng> TriangleBVH<R> {
fn new(
vertices: Vec<Pos3>,
triangles: Vec<Triangle>,
materials: Vec<Box<dyn Material<R>>>,
) -> Self {
fn new(vertices: Vec<Pos3>, triangles: Vec<Triangle>, materials: Vec<BVHMaterial<R>>) -> Self {
Self {
vertices,
bvh: vec![Node::Leaf {
@ -120,7 +121,40 @@ impl<R: Rng> TriangleBVH<R> {
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<R: Rng> Scene<R> for TriangleBVH<R> {
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<R: Rng>() -> TriangleBVH<R> {
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))),
},
],
)
}