Rendering something parsed from a pbrt file

This commit is contained in:
hal8174 2025-08-20 23:20:41 +02:00
parent c8ff77a0a9
commit 6d363aecd0
Signed by: hal8174
SSH key fingerprint: SHA256:NN98ZYwnrreQLSOV/g+amY7C3yL/mS1heD7bi5t6PPw
7 changed files with 316 additions and 127 deletions

1
Cargo.lock generated
View file

@ -2345,6 +2345,7 @@ dependencies = [
"indicatif",
"rand",
"ray-tracing-core",
"ray-tracing-pbrt-scene",
"ray-tracing-renderer",
"ray-tracing-scene",
"rayon",

View file

@ -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<AffineTransform> for AffineTransform {

View file

@ -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<usize>,
p: Vec<Pos3>,
n: Vec<Dir3>,
s: Vec<Dir3>,
uv: Vec<[Float; 2]>,
},
BilinearMesh {
indices: Vec<usize>,
p: Vec<Pos3>,
n: Vec<Dir3>,
uv: Vec<[Float; 2]>,
},
LoopSubDiv {
levels: u32,
indices: Vec<usize>,
p: Vec<Pos3>,
},
Disk {
height: Float,
radius: Float,
innerradius: Float,
phimax: Float,
},
PlyMesh {
filename: String,
displacement: Option<String>,
edgelength: Float,
},
}
#[derive(Debug)]
enum ShapeAlpha {
None,
Value(Float),
Texture(String),
}
#[derive(Debug)]
struct Shape {
ctm: AffineTransform,
material: Arc<dyn PbrtMaterial>,
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<Shape>,
}
#[derive(Debug)]
pub struct PbrtContext {
ctm: Vec<AffineTransform>,
@ -861,7 +809,7 @@ fn inner_parse_pbrt(path: impl AsRef<Path> + std::fmt::Debug) -> Result<Pbrt> {
}
}
dbg!(context);
// dbg!(context);
Ok(pbrt)
}

View file

@ -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<Shape>,
}
impl<R: Rng> Scene<R> for PbrtScene {
fn intersect(
&self,
ray: ray_tracing_core::prelude::Ray,
min: ray_tracing_core::prelude::Float,
max: ray_tracing_core::prelude::Float,
) -> Option<ray_tracing_core::scene::Intersection<'_, R>> {
let mut i = None;
for s in &self.shapes {
if let Some(new_i) = s.intersect::<R>(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<ray_tracing_core::scene::LightSample<'_, R>> {
None
}
}

View file

@ -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<dyn PbrtMaterial>,
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<usize>,
p: Vec<Pos3>,
n: Vec<Dir3>,
s: Vec<Dir3>,
uv: Vec<[Float; 2]>,
},
BilinearMesh {
indices: Vec<usize>,
p: Vec<Pos3>,
n: Vec<Dir3>,
uv: Vec<[Float; 2]>,
},
LoopSubDiv {
levels: u32,
indices: Vec<usize>,
p: Vec<Pos3>,
},
Disk {
height: Float,
radius: Float,
innerradius: Float,
phimax: Float,
},
PlyMesh {
filename: String,
displacement: Option<String>,
edgelength: Float,
},
}
impl Shape {
pub(crate) fn intersect<R: Rng>(
&self,
ray: Ray,
min: Float,
max: Float,
) -> Option<Intersection<R>> {
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
}
}

View file

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

View file

@ -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<std::path::PathBuf>,
}
fn render_image<
@ -105,17 +108,91 @@ fn render_image<
Ok(())
}
fn choose_renderer<S, C>(args: &Args, scene: &str, s: &S, c: &C, tev: &mut TevClient)
where
S: Scene<SmallRng> + Sync,
C: Camera<SmallRng> + 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() {
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 {
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,
choose_renderer(
&args,
&format!("pbrt \"{pbrt_filename:?}\""),
s,
&c,
args.samples_per_pixel,
&mut client,
)
.unwrap();
}
);
}
}