Rendering something parsed from a pbrt file
This commit is contained in:
parent
c8ff77a0a9
commit
6d363aecd0
7 changed files with 316 additions and 127 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -2345,6 +2345,7 @@ dependencies = [
|
||||||
"indicatif",
|
"indicatif",
|
||||||
"rand",
|
"rand",
|
||||||
"ray-tracing-core",
|
"ray-tracing-core",
|
||||||
|
"ray-tracing-pbrt-scene",
|
||||||
"ray-tracing-renderer",
|
"ray-tracing-renderer",
|
||||||
"ray-tracing-scene",
|
"ray-tracing-scene",
|
||||||
"rayon",
|
"rayon",
|
||||||
|
|
|
||||||
|
|
@ -174,6 +174,14 @@ impl AffineTransform {
|
||||||
self.inv[2][0] * pos.x() + self.inv[2][1] * pos.y() + self.inv[2][2] * pos.z(),
|
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 {
|
impl Mul<AffineTransform> for AffineTransform {
|
||||||
|
|
|
||||||
|
|
@ -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 error::SourceFile;
|
||||||
use material::PbrtMaterial;
|
use material::PbrtMaterial;
|
||||||
use miette::{Diagnostic, IntoDiagnostic, Result, SourceSpan, bail, miette};
|
use miette::{Diagnostic, IntoDiagnostic, Result, SourceSpan, bail, miette};
|
||||||
|
|
@ -21,6 +26,8 @@ mod tokenizer;
|
||||||
mod either;
|
mod either;
|
||||||
mod error;
|
mod error;
|
||||||
mod material;
|
mod material;
|
||||||
|
pub mod scene;
|
||||||
|
mod shape;
|
||||||
mod texture;
|
mod texture;
|
||||||
|
|
||||||
struct Lexer {
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct Pbrt {
|
pub struct Pbrt {
|
||||||
settings: PbrtWorldSettings,
|
pub settings: PbrtWorldSettings,
|
||||||
scene: PbrtScene,
|
pub scene: PbrtScene,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pbrt {
|
impl Pbrt {
|
||||||
|
|
@ -670,11 +623,6 @@ struct PbrtWorldSettings {
|
||||||
camera_ctm: AffineTransform,
|
camera_ctm: AffineTransform,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct PbrtScene {
|
|
||||||
shapes: Vec<Shape>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PbrtContext {
|
pub struct PbrtContext {
|
||||||
ctm: Vec<AffineTransform>,
|
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)
|
Ok(pbrt)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
40
ray-tracing-pbrt-scene/src/scene.rs
Normal file
40
ray-tracing-pbrt-scene/src/scene.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
154
ray-tracing-pbrt-scene/src/shape.rs
Normal file
154
ray-tracing-pbrt-scene/src/shape.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ tev_client = "0.5.2"
|
||||||
ray-tracing-core = { path = "../ray-tracing-core" }
|
ray-tracing-core = { path = "../ray-tracing-core" }
|
||||||
ray-tracing-scene = { path = "../ray-tracing-scene" }
|
ray-tracing-scene = { path = "../ray-tracing-scene" }
|
||||||
ray-tracing-renderer = { path = "../ray-tracing-renderer" }
|
ray-tracing-renderer = { path = "../ray-tracing-renderer" }
|
||||||
|
ray-tracing-pbrt-scene = { path = "../ray-tracing-pbrt-scene" }
|
||||||
rayon = "1.10.0"
|
rayon = "1.10.0"
|
||||||
rand = { version = "0.8.5", features = ["small_rng"] }
|
rand = { version = "0.8.5", features = ["small_rng"] }
|
||||||
clap = { version = "4.5.19", features = ["derive"] }
|
clap = { version = "4.5.19", features = ["derive"] }
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use ray_tracing_core::{
|
||||||
renderer::ClassicalRenderer,
|
renderer::ClassicalRenderer,
|
||||||
scene::Scene,
|
scene::Scene,
|
||||||
};
|
};
|
||||||
|
use ray_tracing_pbrt_scene::parse_pbrt_v4;
|
||||||
use ray_tracing_renderer::{
|
use ray_tracing_renderer::{
|
||||||
depth_renderer::DepthRenderer, mis::MIS, next_event_estimation::NextEventEstimation,
|
depth_renderer::DepthRenderer, mis::MIS, next_event_estimation::NextEventEstimation,
|
||||||
path_tracer::PathTracer, path_tracer_importance::PathTracerImportance,
|
path_tracer::PathTracer, path_tracer_importance::PathTracerImportance,
|
||||||
|
|
@ -30,6 +31,8 @@ struct Args {
|
||||||
height: u32,
|
height: u32,
|
||||||
#[arg(long, default_value_t = 1024)]
|
#[arg(long, default_value_t = 1024)]
|
||||||
samples_per_pixel: usize,
|
samples_per_pixel: usize,
|
||||||
|
#[arg(long)]
|
||||||
|
pbrt_filename: Option<std::path::PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_image<
|
fn render_image<
|
||||||
|
|
@ -105,17 +108,91 @@ fn render_image<
|
||||||
Ok(())
|
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() {
|
fn main() {
|
||||||
let args = Args::parse();
|
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 map = example_scenes();
|
||||||
|
|
||||||
let scenes: Vec<&str> = if args.scenes.is_empty() {
|
let scenes: Vec<&str> = if args.pbrt_filename.is_none() {
|
||||||
map.keys().copied().collect()
|
if args.scenes.is_empty() {
|
||||||
|
map.keys().copied().collect()
|
||||||
|
} else {
|
||||||
|
args.scenes.iter().map(|s| s.as_str()).collect()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
args.scenes.iter().map(|s| s.as_str()).collect()
|
Vec::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
for scene in scenes {
|
for scene in scenes {
|
||||||
|
|
@ -132,69 +209,29 @@ fn main() {
|
||||||
f.get_horizontal_fov(),
|
f.get_horizontal_fov(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if args.renderers.is_empty() || args.renderers.contains(&String::from("depth")) {
|
choose_renderer(&args, scene, &s, &c, &mut client);
|
||||||
let r = DepthRenderer::new(args.width, args.height);
|
}
|
||||||
render_image(
|
|
||||||
format!("{scene} - depth renderer"),
|
|
||||||
&r,
|
|
||||||
&s,
|
|
||||||
&c,
|
|
||||||
args.samples_per_pixel,
|
|
||||||
&mut client,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.renderers.is_empty() || args.renderers.contains(&String::from("path")) {
|
if let Some(pbrt_filename) = &args.pbrt_filename {
|
||||||
let r = PathTracer::new(args.width, args.height);
|
let pbrt = parse_pbrt_v4(pbrt_filename).unwrap();
|
||||||
render_image(
|
|
||||||
format!("{scene} - path tracer"),
|
|
||||||
&r,
|
|
||||||
&s,
|
|
||||||
&c,
|
|
||||||
args.samples_per_pixel,
|
|
||||||
&mut client,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.renderers.is_empty() || args.renderers.contains(&String::from("importance")) {
|
let c = BasicCamera::from_look_at(
|
||||||
let r = PathTracerImportance::new(args.width, args.height);
|
args.width,
|
||||||
render_image(
|
args.height,
|
||||||
format!("{scene} - path tracer importance"),
|
Pos3::new(3.0, 4.0, 1.5),
|
||||||
&r,
|
Pos3::new(0.5, 0.5, 0.0),
|
||||||
&s,
|
Dir3::new(0.0, 0.0, 1.0),
|
||||||
&c,
|
Float::to_radians(45.0),
|
||||||
args.samples_per_pixel,
|
);
|
||||||
&mut client,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.renderers.is_empty() || args.renderers.contains(&String::from("nee")) {
|
let s = &pbrt.scene;
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.renderers.is_empty() || args.renderers.contains(&String::from("mis")) {
|
choose_renderer(
|
||||||
let r = MIS::new(args.width, args.height);
|
&args,
|
||||||
render_image(
|
&format!("pbrt \"{pbrt_filename:?}\""),
|
||||||
format!("{scene} - mis"),
|
s,
|
||||||
&r,
|
&c,
|
||||||
&s,
|
&mut client,
|
||||||
&c,
|
);
|
||||||
args.samples_per_pixel,
|
|
||||||
&mut client,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue