From ae732ba80335b6c10f7450aa676c882b4dea1a13 Mon Sep 17 00:00:00 2001 From: Daniel Oom Date: Fri, 19 Jul 2024 20:46:31 +0200 Subject: [PATCH] Use indices instead of Arc for materials in Scene --- kdtree-tester/src/ray_bouncer.rs | 9 +-- scene/src/lib.rs | 115 ++++++++++++++++--------------- tracing/src/pathtracer.rs | 14 ++-- tracing/src/raytracer.rs | 14 ++-- 4 files changed, 83 insertions(+), 69 deletions(-) diff --git a/kdtree-tester/src/ray_bouncer.rs b/kdtree-tester/src/ray_bouncer.rs index 1ffd9ace..fcf38dc8 100644 --- a/kdtree-tester/src/ray_bouncer.rs +++ b/kdtree-tester/src/ray_bouncer.rs @@ -60,14 +60,12 @@ impl RayBouncer { return Some(intersection); }; let intersection = intersection.reference?; - let triangle = &self.scene.triangle_data[intersection.index as usize]; + let triangle = &self.scene.triangles[intersection.index as usize]; let intersection = intersection.intersection; let wi = -ray.direction; let n = triangle.normals.lerp(intersection.u, intersection.v); let uv = triangle.texcoords.lerp(intersection.u, intersection.v); - let material = triangle.material.as_ref(); - // TODO: How to chose offset? let offset = 0.00001 * n; let point = ray.param(intersection.t); @@ -88,7 +86,10 @@ impl RayBouncer { return Some(checked.clone()); } - let sample = material.sample(&IncomingRay { wi, n, uv }, &mut rng); + let sample = self + .scene + .get_material(triangle) + .sample(&IncomingRay { wi, n, uv }, &mut rng); let next_ray = Ray::new( if sample.wo.dot(n) >= 0.0 { point_above diff --git a/scene/src/lib.rs b/scene/src/lib.rs index ea455dec..29818c42 100644 --- a/scene/src/lib.rs +++ b/scene/src/lib.rs @@ -1,6 +1,6 @@ use geometry::{geometry::Geometry, triangle::Triangle}; use glam::{Vec2, Vec3}; -use std::{collections::BTreeMap, fs::File, io::BufReader, path::Path, sync::Arc}; +use std::{collections::BTreeMap, fs::File, io::BufReader, path::Path}; use wavefront::{mtl, obj}; pub mod camera; @@ -42,63 +42,22 @@ impl TriangleTexcoords { } } -pub struct TriangleData { +pub struct SceneTriangle { pub triangle: Triangle, pub normals: TriangleNormals, pub texcoords: TriangleTexcoords, - pub material: Arc, + pub material_index: usize, } pub struct Scene { - pub triangle_data: Vec, + pub triangles: Vec, + pub materials: Vec, pub cameras: Vec, pub lights: Vec, pub environment: Vec3, } -fn collect_triangle_data( - image_directory: &Path, - obj: &obj::Obj, - mtl: &mtl::Mtl, -) -> Vec { - let materials = materials_from_mtl(image_directory, mtl); - obj.chunks - .iter() - .flat_map(|chunk| { - chunk.faces.iter().map(|face| { - if face.points.len() != 3 { - panic!( - "Only tringular faces supported but found {} vertices.", - face.points.len() - ); - } - let triangle = Triangle { - v0: obj.index_vertex(&face.points[0]).into(), - v1: obj.index_vertex(&face.points[1]).into(), - v2: obj.index_vertex(&face.points[2]).into(), - }; - let normals = TriangleNormals { - n0: obj.index_normal(&face.points[0]).into(), - n1: obj.index_normal(&face.points[1]).into(), - n2: obj.index_normal(&face.points[2]).into(), - }; - let texcoords = TriangleTexcoords { - uv0: obj.index_texcoord(&face.points[0]).into(), - uv1: obj.index_texcoord(&face.points[1]).into(), - uv2: obj.index_texcoord(&face.points[2]).into(), - }; - TriangleData { - triangle, - normals, - texcoords, - material: materials[chunk.material.as_str()].clone(), - } - }) - }) - .collect::>() -} - -fn blend_from_mtl(image_directory: &Path, material: &mtl::Material) -> Arc { +fn blend_from_mtl(image_directory: &Path, material: &mtl::Material) -> MaterialModel { let texture = (!material.diffuse_map.is_empty()).then(|| { image::open(image_directory.join(&material.diffuse_map)) .unwrap() @@ -129,13 +88,13 @@ fn blend_from_mtl(image_directory: &Path, material: &mtl::Material) -> Arc( image_directory: &Path, mtl: &'m mtl::Mtl, -) -> BTreeMap<&'m str, Arc> { +) -> BTreeMap<&'m str, MaterialModel> { mtl.materials .iter() .map(|m| (m.name.as_str(), blend_from_mtl(image_directory, m))) @@ -172,8 +131,47 @@ fn collect_lights(mtl: &mtl::Mtl) -> Vec { impl Scene { pub fn from_wavefront(image_directory: &Path, obj: &obj::Obj, mtl: &mtl::Mtl) -> Scene { + let materials = materials_from_mtl(image_directory, mtl); + let triangles = obj.chunks.iter().flat_map(|chunk| { + chunk.faces.iter().map(|face| { + if face.points.len() != 3 { + panic!( + "Only tringular faces supported but found {} vertices.", + face.points.len() + ); + } + let triangle = Triangle { + v0: obj.index_vertex(&face.points[0]).into(), + v1: obj.index_vertex(&face.points[1]).into(), + v2: obj.index_vertex(&face.points[2]).into(), + }; + let normals = TriangleNormals { + n0: obj.index_normal(&face.points[0]).into(), + n1: obj.index_normal(&face.points[1]).into(), + n2: obj.index_normal(&face.points[2]).into(), + }; + let texcoords = TriangleTexcoords { + uv0: obj.index_texcoord(&face.points[0]).into(), + uv1: obj.index_texcoord(&face.points[1]).into(), + uv2: obj.index_texcoord(&face.points[2]).into(), + }; + let material_index = materials + .iter() + .enumerate() + .find(|m| m.1 .0 == &chunk.material) + .unwrap() + .0; + SceneTriangle { + triangle, + normals, + texcoords, + material_index, + } + }) + }); Scene { - triangle_data: collect_triangle_data(image_directory, obj, mtl), + triangles: triangles.collect(), + materials: materials.into_iter().map(|m| m.1).collect(), cameras: collect_cameras(mtl), lights: collect_lights(mtl), environment: Vec3::new(0.8, 0.8, 0.8), @@ -197,14 +195,21 @@ impl Scene { println!("Collecting scene..."); let scene = Scene::from_wavefront(mtl_path.parent().unwrap(), &obj, &mtl); - println!(" Triangles: {}", scene.triangle_data.len()); + println!(" Triangles: {}", scene.triangles.len()); scene } + #[inline] + pub fn get_triangle(&self, index: u32) -> &SceneTriangle { + &self.triangles[index as usize] + } + + #[inline] + pub fn get_material(&self, triangle: &SceneTriangle) -> &MaterialModel { + &self.materials[triangle.material_index] + } + pub fn collect_geometries_as_vec(&self) -> Vec { - self.triangle_data - .iter() - .map(|t| t.triangle.into()) - .collect() + self.triangles.iter().map(|t| t.triangle.into()).collect() } } diff --git a/tracing/src/pathtracer.rs b/tracing/src/pathtracer.rs index 34a61110..c49e0b2d 100644 --- a/tracing/src/pathtracer.rs +++ b/tracing/src/pathtracer.rs @@ -46,7 +46,7 @@ impl Pathtracer { return accumulated_radiance + accumulated_transport * self.scene.environment; } let intersection = intersection.unwrap(); - let triangle = &self.scene.triangle_data[intersection.index as usize]; + let triangle = self.scene.get_triangle(intersection.index); let intersection = intersection.intersection; ray_logger @@ -56,7 +56,6 @@ impl Pathtracer { let wi = -ray.direction; let n = triangle.normals.lerp(intersection.u, intersection.v); let uv = triangle.texcoords.lerp(intersection.u, intersection.v); - let material = triangle.material.as_ref(); // TODO: How to chose offset? let offset = 0.00001 * n; @@ -75,13 +74,20 @@ impl Pathtracer { } let wo = shadow_ray.direction.normalize(); let radiance = light.emitted(point); - material.brdf(&OutgoingRay { wi, n, wo, uv }) * radiance * wo.dot(n).abs() + let brdf = self + .scene + .get_material(triangle) + .brdf(&OutgoingRay { wi, n, wo, uv }); + brdf * radiance * wo.dot(n).abs() }) .sum(); let accumulated_radiance = accumulated_radiance + accumulated_transport * incoming_radiance; - let sample = material.sample(&IncomingRay { wi, n, uv }, rng); + let sample = self + .scene + .get_material(triangle) + .sample(&IncomingRay { wi, n, uv }, rng); let next_ray = Ray::new( if sample.wo.dot(n) >= 0.0 { point_above diff --git a/tracing/src/raytracer.rs b/tracing/src/raytracer.rs index 9c04f6be..56a621d9 100644 --- a/tracing/src/raytracer.rs +++ b/tracing/src/raytracer.rs @@ -7,7 +7,7 @@ use crate::{ use geometry::ray::Ray; use glam::{UVec2, Vec2, Vec3}; use kdtree::{intersection::KdIntersection, KdTree}; -use scene::{camera::Pinhole, material::MaterialModel, Scene}; +use scene::{camera::Pinhole, Scene}; pub struct Raytracer { pub scene: Scene, @@ -30,7 +30,7 @@ impl Raytracer { return self.scene.environment; } let intersection = intersection.unwrap(); - let triangle = &self.scene.triangle_data[intersection.index as usize]; + let triangle = self.scene.get_triangle(intersection.index); let intersection = intersection.intersection; let wi = -ray.direction; @@ -45,16 +45,18 @@ impl Raytracer { .lights .iter() .map(|light| { - let this = &self; - let material: &MaterialModel = &triangle.material; let direction = light.center - point; let shadow_ray = Ray::new(offset_point, direction); - if this.intersect_any(&shadow_ray, 0.0..=1.0) { + if self.intersect_any(&shadow_ray, 0.0..=1.0) { return Vec3::ZERO; } let wo = direction.normalize(); let radiance = light.emitted(point); - material.brdf(&OutgoingRay { wi, n, wo, uv }) * radiance * wo.dot(n).abs() + let brdf = self + .scene + .get_material(triangle) + .brdf(&OutgoingRay { wi, n, wo, uv }); + brdf * radiance * wo.dot(n).abs() }) .sum() }