diff options
Diffstat (limited to 'racer-tracer')
| -rw-r--r-- | racer-tracer/Cargo.toml | 2 | ||||
| -rw-r--r-- | racer-tracer/config.yml | 16 | ||||
| -rw-r--r-- | racer-tracer/src/config.rs | 54 | ||||
| -rw-r--r-- | racer-tracer/src/error.rs | 4 | ||||
| -rw-r--r-- | racer-tracer/src/image.rs | 14 | ||||
| -rw-r--r-- | racer-tracer/src/main.rs | 42 | ||||
| -rw-r--r-- | racer-tracer/src/render.rs | 68 |
7 files changed, 118 insertions, 82 deletions
diff --git a/racer-tracer/Cargo.toml b/racer-tracer/Cargo.toml index 9bd6b6a..0e80de3 100644 --- a/racer-tracer/Cargo.toml +++ b/racer-tracer/Cargo.toml @@ -13,3 +13,5 @@ rayon = "1.6.1" structopt = "0.3" num_cpus = "1.15.0" synchronoise = "1.0.1" +config = "0.13.3" +serde = { version = "1", features = ["derive"] } diff --git a/racer-tracer/config.yml b/racer-tracer/config.yml new file mode 100644 index 0000000..5719ee3 --- /dev/null +++ b/racer-tracer/config.yml @@ -0,0 +1,16 @@ +preview: + samples: 2 + max_depth: 4 + recurse_depth: 4 + scale: 4 + +render: + samples: 1000 + max_depth: 40 + recurse_depth: 4 + scale: 1 + +screen: + width: 1280 + height: 720 + diff --git a/racer-tracer/src/config.rs b/racer-tracer/src/config.rs new file mode 100644 index 0000000..109528a --- /dev/null +++ b/racer-tracer/src/config.rs @@ -0,0 +1,54 @@ +use config::File; +use serde::Deserialize; +use structopt::StructOpt; + +use crate::error::TracerError; + +#[derive(Default, Debug, Deserialize)] +pub struct Screen { + pub height: usize, + pub width: usize, +} + +#[derive(Default, Debug, Deserialize)] +pub struct RenderData { + pub samples: usize, + pub max_depth: usize, + pub recurse_depth: usize, + pub scale: usize, +} + +#[derive(StructOpt, Debug)] +#[structopt(name = "racer-tracer")] +pub struct Args { + #[structopt( + short = "c", + long = "config", + default_value = "./config.yml", + env = "CONFIG" + )] + pub config: String, +} + +#[derive(Default, Debug, Deserialize)] +pub struct Config { + #[serde(default)] + pub preview: RenderData, + + #[serde(default)] + pub render: RenderData, + + #[serde(default)] + pub screen: Screen, +} + +impl Config { + pub fn from_file(file: String) -> Result<Self, TracerError> { + config::Config::builder() + .add_source(File::from(file.as_ref())) + .build() + .map_err(|e| TracerError::Configuration(file.clone(), e.to_string()))? + .try_deserialize() + .map_err(|e| TracerError::Configuration(file, e.to_string())) + } +} diff --git a/racer-tracer/src/error.rs b/racer-tracer/src/error.rs index 9254715..70a9ab2 100644 --- a/racer-tracer/src/error.rs +++ b/racer-tracer/src/error.rs @@ -13,6 +13,9 @@ pub enum TracerError { #[error("Resolution is not power of two.")] ResolutionIsNotPowerOfTwo(), + + #[error("Config Error ({0}): {1}")] + Configuration(String, String), } impl From<TracerError> for i32 { @@ -25,6 +28,7 @@ impl From<TracerError> for i32 { TracerError::FailedToCreateWindow(_) => 2, TracerError::FailedToUpdateWindow(_) => 3, TracerError::ResolutionIsNotPowerOfTwo() => 4, + TracerError::Configuration(_, _) => 5, } } } diff --git a/racer-tracer/src/image.rs b/racer-tracer/src/image.rs index 502a15a..1df4cc5 100644 --- a/racer-tracer/src/image.rs +++ b/racer-tracer/src/image.rs @@ -2,16 +2,14 @@ pub struct Image { pub aspect_ratio: f64, pub width: usize, pub height: usize, - pub samples_per_pixel: usize, } impl Image { - pub fn new(aspect_ratio: f64, width: usize, samples_per_pixel: usize) -> Image { + pub fn new(width: usize, height: usize) -> Image { Image { - aspect_ratio, + aspect_ratio: width as f64 / height as f64, width, - height: (width as f64 / aspect_ratio) as usize, - samples_per_pixel, + height, } } } @@ -24,7 +22,6 @@ impl From<&Image> for SubImage { y: 0, width: image.width, height: image.height, - samples: image.samples_per_pixel, screen_width: image.width, screen_height: image.height, } @@ -38,7 +35,6 @@ pub struct SubImage { pub screen_height: usize, pub width: usize, pub height: usize, - pub samples: usize, } pub trait QuadSplit { @@ -57,7 +53,6 @@ impl QuadSplit for SubImage { y: self.y, width: half_w, height: half_h, - samples: self.samples, screen_width: self.screen_width, screen_height: self.screen_height, }, @@ -67,7 +62,6 @@ impl QuadSplit for SubImage { y: self.y, width: half_w, height: half_h, - samples: self.samples, screen_width: self.screen_width, screen_height: self.screen_height, }, @@ -77,7 +71,6 @@ impl QuadSplit for SubImage { y: self.y + half_h, width: half_w, height: half_h, - samples: self.samples, screen_width: self.screen_width, screen_height: self.screen_height, }, @@ -87,7 +80,6 @@ impl QuadSplit for SubImage { y: self.y + half_h, width: half_w, height: half_h, - samples: self.samples, screen_width: self.screen_width, screen_height: self.screen_height, }, diff --git a/racer-tracer/src/main.rs b/racer-tracer/src/main.rs index 5331fd1..83d2408 100644 --- a/racer-tracer/src/main.rs +++ b/racer-tracer/src/main.rs @@ -1,6 +1,7 @@ #[macro_use] mod error; mod camera; +mod config; mod geometry; mod image; mod material; @@ -22,6 +23,7 @@ use synchronoise::SignalEvent; use crate::{ camera::Camera, + config::{Args, Config}, error::TracerError, geometry::sphere::Sphere, geometry::Hittable, @@ -75,14 +77,11 @@ fn create_scene() -> Scene { scene } -fn run( - aspect_ratio: f64, - screen_width: usize, - samples: usize, - max_depth: usize, - recurse_depth: usize, -) -> Result<(), TracerError> { - let image = image::Image::new(aspect_ratio, screen_width, samples); +fn run(config: Config) -> Result<(), TracerError> { + let preview_render_data = Arc::new(config.preview); + let recurse_depth = 4; + let render_data = Arc::new(config.render); + let image = image::Image::new(config.screen.width, config.screen.height); let camera = Arc::new(RwLock::new(Camera::new(&image, 2.0, 1.0))); let scene: Arc<Box<dyn Hittable>> = Arc::new(Box::new(create_scene())); let screen_buffer: Arc<RwLock<Vec<u32>>> = @@ -103,12 +102,6 @@ fn run( rayon::scope(|s| { s.spawn(|_| { - // TODO: Make configurable - let preview_scale = 4; - let preview_samples = 2; - let preview_max_depth = 4; - let preview_recurse_depth = 4; - loop { if exit.wait_timeout(Duration::from_secs(0)) { return; @@ -122,9 +115,7 @@ fn run( Arc::clone(&camera), &sub_image, Arc::clone(&scene), - samples, - 1, - max_depth, + Arc::clone(&render_data), recurse_depth, Some(cancel_render_event), ); @@ -140,11 +131,8 @@ fn run( Arc::clone(&camera), &sub_image, Arc::clone(&scene), - preview_samples, - preview_scale, - preview_max_depth, - // TODO: Could create a function to create the optimal value - preview_recurse_depth, //recursive thread depth + Arc::clone(&preview_render_data), + recurse_depth, None, ); } @@ -218,13 +206,11 @@ fn run( let res = (window_res.lock().expect("Failed to get result lock.")).clone(); res } - +use structopt::StructOpt; fn main() { - // TODO: Read configuration and args - let samples = 1000; // Samples per pixel - let max_depth = 50; // Max ray trace depth - let recurse_depth = 4; // How many times the screen with split itself into sub images each time splitting it into 4 new smaller ones. - if let Err(e) = run(16.0 / 9.0, 1280, samples, max_depth, recurse_depth) { + let args = Args::from_args(); + + if let Err(e) = Config::from_file(args.config).and_then(run) { eprintln!("{}", e); std::process::exit(e.into()) } diff --git a/racer-tracer/src/render.rs b/racer-tracer/src/render.rs index c5013cf..ea5c17d 100644 --- a/racer-tracer/src/render.rs +++ b/racer-tracer/src/render.rs @@ -9,6 +9,7 @@ use synchronoise::SignalEvent; use crate::{ camera::Camera, + config::RenderData, geometry::Hittable, image::{QuadSplit, SubImage}, ray::Ray, @@ -26,18 +27,14 @@ fn ray_color(scene: &dyn Hittable, ray: &Ray, depth: usize) -> Vec3 { return attenuation * ray_color(scene, &scattered, depth - 1); } return Color::default(); - //let target = rec.point + random_in_hemisphere(&rec.normal); - //let target = rec.point + rec.normal + random_unit_vector(); - //return 0.5 * ray_color(scene, &Ray::new(rec.point, target - rec.point), depth - 1); - //return hit_record.color; - //return 0.5 * (hit_record.normal + Vec3::new(1.0, 1.0, 1.0)); } - // TODO: make sky part of scene. // Sky + let first_color = Vec3::new(1.0, 1.0, 1.0); + let second_color = Vec3::new(0.5, 0.7, 1.0); let unit_direction = ray.direction().unit_vector(); let t = 0.5 * (unit_direction.y() + 1.0); - (1.0 - t) * Vec3::new(1.0, 1.0, 1.0) + t * Vec3::new(0.5, 0.7, 1.0) + (1.0 - t) * first_color + t * second_color } pub fn raytrace( @@ -46,16 +43,14 @@ pub fn raytrace( scene: &dyn Hittable, camera: &Camera, image: &SubImage, - samples: usize, - scale: usize, - max_depth: usize, + data: &RenderData, ) { // TODO: This scale shit doesn't work. // Just force power of two or other solutions to avoid this. // Can be ok for preview but the actual render could use a different function. - let mut scaled_width = image.width / scale; - let mut scaled_height = image.height / scale; + let mut scaled_width = image.width / data.scale; + let mut scaled_height = image.height / data.scale; // In the case where we get an odd one out we patch the widht and // height with the esception of the edges of the screen. Without // this everything has to be power of 2 which isn't a crazy @@ -64,32 +59,32 @@ pub fn raytrace( // Biggest problem is that the width and height we get here is // depending on resolution and how many times the image is split // up between threads. - if scaled_width * scale != image.width - && (image.x + scaled_width * scale + 1 < image.screen_width) + if scaled_width * data.scale != image.width + && (image.x + scaled_width * data.scale + 1 < image.screen_width) { scaled_width += 1; } - if scaled_height * scale != image.height - && (image.y + scaled_height * scale + 1 < image.screen_height) + if scaled_height * data.scale != image.height + && (image.y + scaled_height * data.scale + 1 < image.screen_height) { scaled_height += 1; } - let scaled_screen_width = image.screen_width / scale; - let scaled_screen_height = image.screen_height / scale; + let scaled_screen_width = image.screen_width / data.scale; + let scaled_screen_height = image.screen_height / data.scale; let mut colors: Vec<Vec3> = vec![Vec3::default(); scaled_height * scaled_width as usize]; for row in 0..scaled_height { for column in 0..scaled_width { - let u: f64 = ((image.x / scale + column) as f64 + random_double()) + let u: f64 = ((image.x / data.scale + column) as f64 + random_double()) / (scaled_screen_width - 1) as f64; - for _ in 0..samples { - let v: f64 = ((image.y / scale + row) as f64 + random_double()) + for _ in 0..data.samples { + let v: f64 = ((image.y / data.scale + row) as f64 + random_double()) / (scaled_screen_height - 1) as f64; colors[row * scaled_width + column].add(ray_color( scene, &camera.get_ray(u, v), - max_depth, + data.max_depth, )); } } @@ -111,14 +106,14 @@ pub fn raytrace( for half_row in 0..scaled_height { for half_col in 0..scaled_width { let color = colors[half_row * scaled_width + half_col] - .scale_sqrt(samples) + .scale_sqrt(data.samples) .as_color(); - let row = half_row * scale; - let col = half_col * scale; + let row = half_row * data.scale; + let col = half_col * data.scale; - for scale_x in 0..scale { - for scale_y in 0..scale { + for scale_x in 0..data.scale { + for scale_y in 0..data.scale { buf[offset + (row + scale_x) * image.screen_width + col + scale_y] = color; } } @@ -138,9 +133,7 @@ pub fn render( camera: Arc<RwLock<Camera>>, image: &SubImage, scene: Arc<Box<dyn Hittable>>, - samples: usize, - scale: usize, - max_depth: usize, + data: Arc<RenderData>, split_depth: usize, cancel_event: Option<Arc<SignalEvent>>, ) { @@ -151,16 +144,7 @@ pub fn render( if split_depth == 0 { let scene: &(dyn Hittable) = (*scene).borrow(); let camera = { camera.read().expect("TODO").clone() }; - raytrace( - &buffer, - cancel_event, - scene, - &camera, - image, - samples, - scale, - max_depth, - ); + raytrace(&buffer, cancel_event, scene, &camera, image, &data); } else { // Split into more quads let quads = image.quad_split(); @@ -170,9 +154,7 @@ pub fn render( Arc::clone(&camera), &image, Arc::clone(&scene), - samples, - scale, - max_depth, + Arc::clone(&data), split_depth - 1, cancel_event.clone(), ); |
