From 6d363aecd0b3d1775d21b53cb41d7f1fd871307c Mon Sep 17 00:00:00 2001 From: hal8174 Date: Wed, 20 Aug 2025 23:20:41 +0200 Subject: [PATCH] Rendering something parsed from a pbrt file --- Cargo.lock | 1 + ray-tracing-core/src/affine_transform.rs | 8 ++ ray-tracing-pbrt-scene/src/lib.rs | 74 ++-------- ray-tracing-pbrt-scene/src/scene.rs | 40 ++++++ ray-tracing-pbrt-scene/src/shape.rs | 154 +++++++++++++++++++++ ray-tracing-tev/Cargo.toml | 1 + ray-tracing-tev/src/main.rs | 165 ++++++++++++++--------- 7 files changed, 316 insertions(+), 127 deletions(-) create mode 100644 ray-tracing-pbrt-scene/src/scene.rs create mode 100644 ray-tracing-pbrt-scene/src/shape.rs diff --git a/Cargo.lock b/Cargo.lock index d67629a..4386486 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2345,6 +2345,7 @@ dependencies = [ "indicatif", "rand", "ray-tracing-core", + "ray-tracing-pbrt-scene", "ray-tracing-renderer", "ray-tracing-scene", "rayon", diff --git a/ray-tracing-core/src/affine_transform.rs b/ray-tracing-core/src/affine_transform.rs index 00f754c..c437dc4 100644 --- a/ray-tracing-core/src/affine_transform.rs +++ b/ray-tracing-core/src/affine_transform.rs @@ -174,6 +174,14 @@ impl AffineTransform { self.inv[2][0] * pos.x() + self.inv[2][1] * pos.y() + self.inv[2][2] * pos.z(), ) } + + pub fn transform_ray(&self, ray: Ray) -> Ray { + Ray::new( + self.transform_pos(ray.start()), + self.transform_dir(ray.dir()), + ray.time(), + ) + } } impl Mul for AffineTransform { diff --git a/ray-tracing-pbrt-scene/src/lib.rs b/ray-tracing-pbrt-scene/src/lib.rs index 9991ea7..d2d9c66 100644 --- a/ray-tracing-pbrt-scene/src/lib.rs +++ b/ray-tracing-pbrt-scene/src/lib.rs @@ -1,4 +1,9 @@ -use crate::{texture::Pbrt_2d_float_texture, tokenizer::Tokenizer}; +use crate::{ + scene::PbrtScene, + shape::{Shape, ShapeAlpha, ShapeType}, + texture::Pbrt_2d_float_texture, + tokenizer::Tokenizer, +}; use error::SourceFile; use material::PbrtMaterial; use miette::{Diagnostic, IntoDiagnostic, Result, SourceSpan, bail, miette}; @@ -21,6 +26,8 @@ mod tokenizer; mod either; mod error; mod material; +pub mod scene; +mod shape; mod texture; struct Lexer { @@ -595,64 +602,10 @@ impl Parser { } } -#[derive(Debug)] -enum ShapeType { - Sphere { - radius: Float, - zmin: Float, - zmax: Float, - phimax: Float, - }, - TriangleMesh { - indices: Vec, - p: Vec, - n: Vec, - s: Vec, - uv: Vec<[Float; 2]>, - }, - BilinearMesh { - indices: Vec, - p: Vec, - n: Vec, - uv: Vec<[Float; 2]>, - }, - LoopSubDiv { - levels: u32, - indices: Vec, - p: Vec, - }, - Disk { - height: Float, - radius: Float, - innerradius: Float, - phimax: Float, - }, - PlyMesh { - filename: String, - displacement: Option, - edgelength: Float, - }, -} - -#[derive(Debug)] -enum ShapeAlpha { - None, - Value(Float), - Texture(String), -} - -#[derive(Debug)] -struct Shape { - ctm: AffineTransform, - material: Arc, - obj: ShapeType, - alpha: ShapeAlpha, -} - #[derive(Debug)] pub struct Pbrt { - settings: PbrtWorldSettings, - scene: PbrtScene, + pub settings: PbrtWorldSettings, + pub scene: PbrtScene, } impl Pbrt { @@ -670,11 +623,6 @@ struct PbrtWorldSettings { camera_ctm: AffineTransform, } -#[derive(Debug)] -struct PbrtScene { - shapes: Vec, -} - #[derive(Debug)] pub struct PbrtContext { ctm: Vec, @@ -861,7 +809,7 @@ fn inner_parse_pbrt(path: impl AsRef + std::fmt::Debug) -> Result { } } - dbg!(context); + // dbg!(context); Ok(pbrt) } diff --git a/ray-tracing-pbrt-scene/src/scene.rs b/ray-tracing-pbrt-scene/src/scene.rs new file mode 100644 index 0000000..01c1143 --- /dev/null +++ b/ray-tracing-pbrt-scene/src/scene.rs @@ -0,0 +1,40 @@ +use ray_tracing_core::{prelude::Rng, scene::Scene}; + +use crate::shape::Shape; + +#[derive(Debug)] +pub struct PbrtScene { + pub(crate) shapes: Vec, +} + +impl Scene for PbrtScene { + fn intersect( + &self, + ray: ray_tracing_core::prelude::Ray, + min: ray_tracing_core::prelude::Float, + max: ray_tracing_core::prelude::Float, + ) -> Option> { + let mut i = None; + for s in &self.shapes { + if let Some(new_i) = s.intersect::(ray, min, max) + && i.as_ref() + .is_none_or(|i: &ray_tracing_core::scene::Intersection<'_, R>| { + i.t() > new_i.t() + }) + { + i = Some(new_i); + } + } + + i + } + + fn sample_light( + &self, + w_in: ray_tracing_core::prelude::Dir3, + intersection: &ray_tracing_core::scene::Intersection<'_, R>, + rng: &mut R, + ) -> Option> { + None + } +} diff --git a/ray-tracing-pbrt-scene/src/shape.rs b/ray-tracing-pbrt-scene/src/shape.rs new file mode 100644 index 0000000..9c93721 --- /dev/null +++ b/ray-tracing-pbrt-scene/src/shape.rs @@ -0,0 +1,154 @@ +use std::sync::Arc; + +use ray_tracing_core::{affine_transform::AffineTransform, prelude::*, scene::Intersection}; + +use crate::material::PbrtMaterial; + +#[derive(Debug)] +pub(crate) struct Shape { + pub(crate) ctm: AffineTransform, + pub(crate) material: Arc, + pub(crate) obj: ShapeType, + pub(crate) alpha: ShapeAlpha, +} + +#[derive(Debug)] +pub(crate) enum ShapeAlpha { + None, + Value(Float), + Texture(String), +} + +#[derive(Debug)] +pub(crate) enum ShapeType { + Sphere { + radius: Float, + zmin: Float, + zmax: Float, + phimax: Float, + }, + TriangleMesh { + indices: Vec, + p: Vec, + n: Vec, + s: Vec, + uv: Vec<[Float; 2]>, + }, + BilinearMesh { + indices: Vec, + p: Vec, + n: Vec, + uv: Vec<[Float; 2]>, + }, + LoopSubDiv { + levels: u32, + indices: Vec, + p: Vec, + }, + Disk { + height: Float, + radius: Float, + innerradius: Float, + phimax: Float, + }, + PlyMesh { + filename: String, + displacement: Option, + edgelength: Float, + }, +} + +impl Shape { + pub(crate) fn intersect( + &self, + ray: Ray, + min: Float, + max: Float, + ) -> Option> { + let ray = self.ctm.transform_ray(ray); + + match &self.obj { + &ShapeType::Sphere { + radius, + zmin, + zmax, + phimax, + } => { + let a = ray.dir().length_squared(); + let b = 2.0 * Dir3::dot(ray.dir(), ray.start() - Pos3::zero()); + let c = Dir3::dot(ray.start() - Pos3::zero(), ray.start() - Pos3::zero()) + - radius * radius; + + let dir = ray.dir().normalize(); + let v = ray.start() - (b / (2.0 * a) * dir); + let length = (v - Pos3::zero()).length(); + let discrim = 4.0 * a * (radius + length) * (radius - length); + + if discrim > 0.0 { + let root_discrim = Float::sqrt(discrim); + + let q = if b < 0.0 { + -0.5 * (b - root_discrim) + } else { + -0.5 * (b + root_discrim) + }; + + let mut t0 = q / a; + let mut t1 = c / q; + + if t1 < t0 { + std::mem::swap(&mut t1, &mut t0); + } + + if let Some(t) = [t0, t1] + .into_iter() + .filter(|&t| { + if min <= t && t <= max { + let p = ray.at(t0); + let mut phi = Float::atan2(p.y(), p.x()); + if phi < 0.0 { + phi += 2.0 * FloatConsts::PI; + } + if zmin <= p.z() && p.z() <= zmax && phi <= phimax { + return true; + } + } + false + }) + .next() + { + return Some(Intersection::new( + t, + (ray.at(t) - Pos3::zero()).normalize(), + None, + None, + 0.0, + )); + } + } + } + ShapeType::TriangleMesh { + indices, + p, + n, + s, + uv, + } => (), + ShapeType::BilinearMesh { indices, p, n, uv } => (), + ShapeType::LoopSubDiv { levels, indices, p } => todo!(), + ShapeType::Disk { + height, + radius, + innerradius, + phimax, + } => (), + ShapeType::PlyMesh { + filename, + displacement, + edgelength, + } => (), + } + + None + } +} diff --git a/ray-tracing-tev/Cargo.toml b/ray-tracing-tev/Cargo.toml index 693e014..8b6d035 100644 --- a/ray-tracing-tev/Cargo.toml +++ b/ray-tracing-tev/Cargo.toml @@ -8,6 +8,7 @@ tev_client = "0.5.2" ray-tracing-core = { path = "../ray-tracing-core" } ray-tracing-scene = { path = "../ray-tracing-scene" } ray-tracing-renderer = { path = "../ray-tracing-renderer" } +ray-tracing-pbrt-scene = { path = "../ray-tracing-pbrt-scene" } rayon = "1.10.0" rand = { version = "0.8.5", features = ["small_rng"] } clap = { version = "4.5.19", features = ["derive"] } diff --git a/ray-tracing-tev/src/main.rs b/ray-tracing-tev/src/main.rs index 1d1c515..aa96c11 100644 --- a/ray-tracing-tev/src/main.rs +++ b/ray-tracing-tev/src/main.rs @@ -7,6 +7,7 @@ use ray_tracing_core::{ renderer::ClassicalRenderer, scene::Scene, }; +use ray_tracing_pbrt_scene::parse_pbrt_v4; use ray_tracing_renderer::{ depth_renderer::DepthRenderer, mis::MIS, next_event_estimation::NextEventEstimation, path_tracer::PathTracer, path_tracer_importance::PathTracerImportance, @@ -30,6 +31,8 @@ struct Args { height: u32, #[arg(long, default_value_t = 1024)] samples_per_pixel: usize, + #[arg(long)] + pbrt_filename: Option, } fn render_image< @@ -105,17 +108,91 @@ fn render_image< Ok(()) } +fn choose_renderer(args: &Args, scene: &str, s: &S, c: &C, tev: &mut TevClient) +where + S: Scene + Sync, + C: Camera + Sync, +{ + if args.renderers.is_empty() || args.renderers.contains(&String::from("depth")) { + let r = DepthRenderer::new(args.width, args.height); + render_image( + format!("{scene} - depth renderer"), + &r, + s, + c, + args.samples_per_pixel, + tev, + ) + .unwrap(); + } + + if args.renderers.is_empty() || args.renderers.contains(&String::from("path")) { + let r = PathTracer::new(args.width, args.height); + render_image( + format!("{scene} - path tracer"), + &r, + s, + c, + args.samples_per_pixel, + tev, + ) + .unwrap(); + } + + if args.renderers.is_empty() || args.renderers.contains(&String::from("importance")) { + let r = PathTracerImportance::new(args.width, args.height); + render_image( + format!("{scene} - path tracer importance"), + &r, + s, + c, + args.samples_per_pixel, + tev, + ) + .unwrap(); + } + + if args.renderers.is_empty() || args.renderers.contains(&String::from("nee")) { + let r = NextEventEstimation::new(args.width, args.height); + render_image( + format!("{scene} - next event estimation"), + &r, + s, + c, + args.samples_per_pixel, + tev, + ) + .unwrap(); + } + + if args.renderers.is_empty() || args.renderers.contains(&String::from("mis")) { + let r = MIS::new(args.width, args.height); + render_image( + format!("{scene} - mis"), + &r, + s, + c, + args.samples_per_pixel, + tev, + ) + .unwrap(); + } +} fn main() { let args = Args::parse(); - let mut client = TevClient::wrap(TcpStream::connect(args.tev).unwrap()); + let mut client = TevClient::wrap(TcpStream::connect(&args.tev).unwrap()); let map = example_scenes(); - let scenes: Vec<&str> = if args.scenes.is_empty() { - map.keys().copied().collect() + let scenes: Vec<&str> = if args.pbrt_filename.is_none() { + if args.scenes.is_empty() { + map.keys().copied().collect() + } else { + args.scenes.iter().map(|s| s.as_str()).collect() + } } else { - args.scenes.iter().map(|s| s.as_str()).collect() + Vec::new() }; for scene in scenes { @@ -132,69 +209,29 @@ fn main() { f.get_horizontal_fov(), ); - if args.renderers.is_empty() || args.renderers.contains(&String::from("depth")) { - let r = DepthRenderer::new(args.width, args.height); - render_image( - format!("{scene} - depth renderer"), - &r, - &s, - &c, - args.samples_per_pixel, - &mut client, - ) - .unwrap(); - } + choose_renderer(&args, scene, &s, &c, &mut client); + } - if args.renderers.is_empty() || args.renderers.contains(&String::from("path")) { - let r = PathTracer::new(args.width, args.height); - render_image( - format!("{scene} - path tracer"), - &r, - &s, - &c, - args.samples_per_pixel, - &mut client, - ) - .unwrap(); - } + if let Some(pbrt_filename) = &args.pbrt_filename { + let pbrt = parse_pbrt_v4(pbrt_filename).unwrap(); - if args.renderers.is_empty() || args.renderers.contains(&String::from("importance")) { - let r = PathTracerImportance::new(args.width, args.height); - render_image( - format!("{scene} - path tracer importance"), - &r, - &s, - &c, - args.samples_per_pixel, - &mut client, - ) - .unwrap(); - } + let c = BasicCamera::from_look_at( + args.width, + args.height, + Pos3::new(3.0, 4.0, 1.5), + Pos3::new(0.5, 0.5, 0.0), + Dir3::new(0.0, 0.0, 1.0), + Float::to_radians(45.0), + ); - if args.renderers.is_empty() || args.renderers.contains(&String::from("nee")) { - let r = NextEventEstimation::new(args.width, args.height); - render_image( - format!("{scene} - next event estimation"), - &r, - &s, - &c, - args.samples_per_pixel, - &mut client, - ) - .unwrap(); - } + let s = &pbrt.scene; - if args.renderers.is_empty() || args.renderers.contains(&String::from("mis")) { - let r = MIS::new(args.width, args.height); - render_image( - format!("{scene} - mis"), - &r, - &s, - &c, - args.samples_per_pixel, - &mut client, - ) - .unwrap(); - } + choose_renderer( + &args, + &format!("pbrt \"{pbrt_filename:?}\""), + s, + &c, + &mut client, + ); } }