csci5607/assignment-1d/src/main.rs

131 lines
3.4 KiB
Rust
Raw Normal View History

2023-03-20 21:10:01 +00:00
#[macro_use]
extern crate tracing;
use std::fs::File;
use std::path::PathBuf;
use anyhow::Result;
use assignment_1d::{image::Image, ray::Ray, scene::Scene};
2023-04-03 07:01:51 +00:00
use clap::{ArgAction, Parser};
2023-03-20 21:10:01 +00:00
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
2023-04-03 07:01:51 +00:00
use tracing::metadata::LevelFilter;
use tracing_subscriber::{
fmt::Layer, prelude::__tracing_subscriber_SubscriberExt,
util::SubscriberInitExt,
};
2023-03-20 21:10:01 +00:00
/// Simple raytracer with Blinn-Phong illumination and shadowing.
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
struct Opt {
/// Path to the input file to use.
#[clap()]
input_path: PathBuf,
/// Path to the output (defaults to the same file name as the input except
/// with an extension of .ppm)
#[clap(short = 'o', long = "output")]
output_path: Option<PathBuf>,
/// Force parallel projection to be used
#[clap(long = "parallel")]
force_parallel: bool,
/// Override distance from eye
#[clap(long = "distance", default_value = "1.0")]
distance: f64,
2023-04-03 07:01:51 +00:00
/// Verbosity
#[clap(short, long, action = ArgAction::Count)]
verbosity: u8,
2023-03-20 21:10:01 +00:00
}
fn main() -> Result<()> {
let opt = Opt::parse();
2023-04-03 07:01:51 +00:00
let level_filter = match opt.verbosity {
0 => LevelFilter::ERROR,
1 => LevelFilter::WARN,
2 => LevelFilter::INFO,
3 => LevelFilter::DEBUG,
_ => LevelFilter::TRACE,
};
2023-03-20 21:10:01 +00:00
// Set up logging
2023-04-03 07:01:51 +00:00
let layer = Layer::default()
.json()
2023-03-20 21:10:01 +00:00
.with_target(false)
.with_timer(tracing_subscriber::fmt::time::uptime())
2023-04-03 07:01:51 +00:00
.with_level(true);
tracing_subscriber::registry()
.with(layer)
.with(level_filter)
2023-03-20 21:10:01 +00:00
.init();
// Rename the output file if it's not provided
let out_file = opt
.output_path
.unwrap_or_else(|| opt.input_path.with_extension("ppm"));
let mut scene = Scene::from_input_file(&opt.input_path)?;
let distance = opt.distance;
// Force-override parallel projection
if opt.force_parallel {
scene.parallel_projection = true;
}
// Translate image pixels to real-world 3d coords
let translate_pixel = scene.pixel_translation_function(distance);
// Generate a parallel iterator for pixels
// The iterator preserves order and uses row-major order
let pixels_iter = (0..scene.image_height)
.into_par_iter()
.flat_map(|y| (0..scene.image_width).into_par_iter().map(move |x| (x, y)));
// Loop through every single pixel of the output file
let pixels = pixels_iter
.map(|(px, py)| {
2023-04-03 07:01:51 +00:00
let span = trace_span!("main_loop", px = px, py = py);
let _enter = span.enter();
2023-03-20 21:10:01 +00:00
let pixel_in_space = translate_pixel(px, py);
let ray_start = if scene.parallel_projection {
// For a parallel projection, we'll just take the view direction and
// subtract it from the target point. This means every single
// ray will be viewed from a point at infinity, rather than a single eye
// position.
let n = scene.view_dir.normalize();
let view_dir = n * distance;
pixel_in_space - view_dir
} else {
scene.eye_pos
};
let ray = Ray::from_endpoints(ray_start, pixel_in_space);
2023-04-03 07:01:51 +00:00
// let res= rayon::spawn(|| scene.trace_single_ray(ray, 0));
2023-03-25 05:55:56 +00:00
scene.trace_single_ray(ray, 0)
2023-03-20 21:10:01 +00:00
})
.collect::<Result<Vec<_>>>()?;
// Construct and emit image
let image = Image {
width: scene.image_width,
height: scene.image_height,
data: pixels,
};
{
let file = File::create(out_file)?;
image.write(file)?;
}
Ok(())
}