From 23058d126888935a171f979bba09a89426de97b1 Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Wed, 12 Sep 2018 16:15:10 -0700 Subject: [PATCH] Move tracing logic to renderer module and add CLI flags. --- rtiow/Cargo.lock | 110 ++++++++++++++++++++++++++++++++++++++++ rtiow/Cargo.toml | 1 + rtiow/src/bin/tracer.rs | 92 +++++---------------------------- rtiow/src/lib.rs | 7 +++ rtiow/src/renderer.rs | 96 +++++++++++++++++++++++++++++++++++ 5 files changed, 228 insertions(+), 78 deletions(-) create mode 100644 rtiow/src/renderer.rs diff --git a/rtiow/Cargo.lock b/rtiow/Cargo.lock index 81f5689..72e2973 100644 --- a/rtiow/Cargo.lock +++ b/rtiow/Cargo.lock @@ -3,6 +3,14 @@ name = "adler32" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "arrayvec" version = "0.4.7" @@ -11,6 +19,16 @@ dependencies = [ "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "atty" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "bitflags" version = "1.0.4" @@ -26,6 +44,20 @@ name = "cfg-if" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "clap" +version = "2.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cloudabi" version = "0.0.3" @@ -352,6 +384,19 @@ dependencies = [ "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "redox_syscall" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_termios" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rtiow" version = "0.1.0" @@ -361,6 +406,7 @@ dependencies = [ "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -407,6 +453,30 @@ name = "stable_deref_trait" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "strsim" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "structopt" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt-derive 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "structopt-derive" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syn" version = "0.14.9" @@ -417,6 +487,29 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "termion" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "textwrap" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-width" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-xid" version = "0.1.0" @@ -430,6 +523,11 @@ dependencies = [ "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "vec_map" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "version_check" version = "0.1.4" @@ -461,10 +559,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" +"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" +"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "90492c5858dd7d2e78691cfb89f90d273a2800fc11d98f60786e5d87e2f83781" "checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3" +"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum color_quant 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0dbbb57365263e881e805dc77d94697c9118fd94d8da011240555aa7b23445bd" "checksum crossbeam-channel 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6c0a94250b0278d7fc5a894c3d276b11ea164edc8bf8feb10ca1ea517b44a649" @@ -503,6 +604,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2" "checksum rayon 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "df7a791f788cb4c516f0e091301a29c2b71ef680db5e644a7d68835c8ae6dbfa" "checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356" +"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" +"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" @@ -510,9 +613,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "153ffa32fd170e9944f7e0838edf824a754ec4c1fc64746fcc9fe1f8fa602e5d" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" +"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" +"checksum structopt 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8e9ad6a11096cbecdcca0cc6aa403fdfdbaeda2fb3323a39c98e6a166a1e45a" +"checksum structopt-derive 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4cbce8ccdc62166bd594c14396a3242bf94c337a51dbfa9be1076dd74b3db2af" "checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741" +"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" +"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" +"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7716c242968ee87e5542f8021178248f267f295a5c4803beae8b8b7fd9bc6051" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "773ef9dcc5f24b7d850d0ff101e542ff24c3b090a9768e03ff889fdef41f00fd" diff --git a/rtiow/Cargo.toml b/rtiow/Cargo.toml index 0619904..b122701 100644 --- a/rtiow/Cargo.toml +++ b/rtiow/Cargo.toml @@ -9,3 +9,4 @@ image = "0.19.0" crossbeam-channel = "0.2.4" num_cpus = "1.8.0" rayon = "1.0.2" +structopt = "0.2.10" diff --git a/rtiow/src/bin/tracer.rs b/rtiow/src/bin/tracer.rs index cece355..4d46874 100644 --- a/rtiow/src/bin/tracer.rs +++ b/rtiow/src/bin/tracer.rs @@ -1,13 +1,11 @@ -extern crate image; extern crate rand; -extern crate rayon; extern crate rtiow; +extern crate structopt; use std::time::Instant; -use image::RgbImage; use rand::Rng; -use rayon::prelude::*; +use structopt::StructOpt; use rtiow::camera::Camera; use rtiow::hitable::Hit; @@ -15,28 +13,14 @@ use rtiow::hitable_list::HitableList; use rtiow::material::Dielectric; use rtiow::material::Lambertian; use rtiow::material::Metal; -use rtiow::ray::Ray; +use rtiow::renderer::render; +use rtiow::renderer::Opt; +use rtiow::renderer::Scene; use rtiow::sphere::Sphere; use rtiow::vec3::Vec3; const BOOK_COVER: bool = true; -fn color(r: Ray, world: &Hit, depth: usize) -> Vec3 { - if let Some(rec) = world.hit(r, 0.001, std::f32::MAX) { - let scatter_response = rec.material.scatter(&r, &rec); - if depth < 50 && scatter_response.reflected { - return scatter_response.attenutation - * color(scatter_response.scattered, world, depth + 1); - } - return Default::default(); - } - - // No hit, choose color from background. - let unit_direction = r.direction().unit_vector(); - let t = 0.5 * (unit_direction.y + 1.); - Vec3::new(1., 1., 1.) * (1. - t) + Vec3::new(0.5, 0.7, 1.) * t -} - fn random_scene() -> Vec> { let mut rng = rand::thread_rng(); let mut objects: Vec> = vec![Box::new(Sphere::new( @@ -106,15 +90,7 @@ fn random_scene() -> Vec> { objects } -struct Scene { - world: HitableList, - camera: Camera, - subsamples: usize, - width: usize, - height: usize, -} - -fn build_scene(width: usize, height: usize, subsamples: usize) -> Scene { +fn build_scene(opt: &Opt) -> Scene { let (camera, world) = if BOOK_COVER { let lookfrom = Vec3::new(13., 2., 3.); let lookat = Vec3::new(0., 0., 0.); @@ -125,7 +101,7 @@ fn build_scene(width: usize, height: usize, subsamples: usize) -> Scene { lookat, Vec3::new(0., 1., 0.), 20., - width as f32 / height as f32, + opt.width as f32 / opt.height as f32, aperture, dist_to_focus, ); @@ -141,7 +117,7 @@ fn build_scene(width: usize, height: usize, subsamples: usize) -> Scene { lookat, Vec3::new(0., 1., 0.), 20., - width as f32 / height as f32, + opt.width as f32 / opt.height as f32, aperture, dist_to_focus, ); @@ -177,57 +153,17 @@ fn build_scene(width: usize, height: usize, subsamples: usize) -> Scene { Scene { camera, world, - subsamples, - width, - height, + subsamples: opt.subsamples, + width: opt.width, + height: opt.height, } } -fn trace_pixel(x: usize, y: usize, scene: &Scene) -> [u8; 3] { - let mut rng = rand::thread_rng(); - let mut col: Vec3 = Default::default(); - for _ in 0..scene.subsamples { - let u = (rng.gen_range::(0., 1.) + x as f32) / scene.width as f32; - let v = (rng.gen_range::(0., 1.) + y as f32) / scene.height as f32; - let ray = scene.camera.get_ray(u, v); - col = col + color(ray, &scene.world, 0); - } - col = col / scene.subsamples as f32; - // Gamma correct, use gamma 2 correction, which is 1/gamma where gamma=2 which is 1/2 - // or sqrt. - col = Vec3::new(col[0].sqrt(), col[1].sqrt(), col[2].sqrt()); - let ir = (255.99 * col[0]) as u8; - let ig = (255.99 * col[1]) as u8; - let ib = (255.99 * col[2]) as u8; - [ir, ig, ib] -} - fn main() -> Result<(), std::io::Error> { let start = Instant::now(); - // Set to 1 to do full res. - let dev_factor = 1; - let width = 1200 / dev_factor; - let height = 800 / dev_factor; - let subsamples = 1000 / dev_factor; - - let scene = build_scene(width, height, subsamples); - let mut img = RgbImage::new(width as u32, height as u32); - let coords: Vec<_> = (0..height) - .flat_map(|j| (0..width).map(move |i| (i, j))) - .collect(); - - let pixels = coords - .par_iter() - .map(|(i, j)| { - let p = trace_pixel(*i, *j, &scene); - // height-j is to flip y-axis. - (*i as u32, (height - *j - 1) as u32, image::Rgb(p)) - }) - .collect::>(); - - pixels - .iter() - .for_each(|(x, y, p)| img.put_pixel(*x, *y, *p)); + let opt = Opt::from_args(); + let scene = build_scene(&opt); + let img = render(&scene); let runtime = start.elapsed(); eprintln!( "Render time {}.{} seconds", diff --git a/rtiow/src/lib.rs b/rtiow/src/lib.rs index 01ecb3e..7745230 100644 --- a/rtiow/src/lib.rs +++ b/rtiow/src/lib.rs @@ -3,5 +3,12 @@ pub mod hitable; pub mod hitable_list; pub mod material; pub mod ray; +pub mod renderer; pub mod sphere; pub mod vec3; + +extern crate image; +extern crate rand; +extern crate rayon; +#[macro_use] +extern crate structopt; diff --git a/rtiow/src/renderer.rs b/rtiow/src/renderer.rs new file mode 100644 index 0000000..3b8daa6 --- /dev/null +++ b/rtiow/src/renderer.rs @@ -0,0 +1,96 @@ +use std; +use std::path::PathBuf; + +use image; +use image::RgbImage; +use rand; +use rand::Rng; +use rayon::prelude::*; + +use camera::Camera; +use hitable::Hit; +use hitable_list::HitableList; +use ray::Ray; +use vec3::Vec3; + +#[derive(Debug, StructOpt)] +#[structopt(name = "tracert", about = "An experimental ray tracer.")] +pub struct Opt { + /// Image width + #[structopt(short = "w", long = "width", default_value = "1280")] + pub width: usize, + /// Image height + #[structopt(short = "h", long = "height", default_value = "720")] + pub height: usize, + /// Sub-samples per pixel + #[structopt(short = "s", long = "subsample", default_value = "10")] + pub subsamples: usize, + + /// Output file + #[structopt(parse(from_os_str))] + pub output: PathBuf, +} + +pub struct Scene { + pub world: HitableList, + pub camera: Camera, + pub subsamples: usize, + pub width: usize, + pub height: usize, +} + +fn color(r: Ray, world: &Hit, depth: usize) -> Vec3 { + if let Some(rec) = world.hit(r, 0.001, std::f32::MAX) { + let scatter_response = rec.material.scatter(&r, &rec); + if depth < 50 && scatter_response.reflected { + return scatter_response.attenutation + * color(scatter_response.scattered, world, depth + 1); + } + return Default::default(); + } + + // No hit, choose color from background. + let unit_direction = r.direction().unit_vector(); + let t = 0.5 * (unit_direction.y + 1.); + Vec3::new(1., 1., 1.) * (1. - t) + Vec3::new(0.5, 0.7, 1.) * t +} + +fn trace_pixel(x: usize, y: usize, scene: &Scene) -> [u8; 3] { + let mut rng = rand::thread_rng(); + let mut col: Vec3 = Default::default(); + for _ in 0..scene.subsamples { + let u = (rng.gen_range::(0., 1.) + x as f32) / scene.width as f32; + let v = (rng.gen_range::(0., 1.) + y as f32) / scene.height as f32; + let ray = scene.camera.get_ray(u, v); + col = col + color(ray, &scene.world, 0); + } + col = col / scene.subsamples as f32; + // Gamma correct, use gamma 2 correction, which is 1/gamma where gamma=2 which is 1/2 + // or sqrt. + col = Vec3::new(col[0].sqrt(), col[1].sqrt(), col[2].sqrt()); + let ir = (255.99 * col[0]) as u8; + let ig = (255.99 * col[1]) as u8; + let ib = (255.99 * col[2]) as u8; + [ir, ig, ib] +} + +pub fn render(scene: &Scene) -> image::RgbImage { + let mut img = RgbImage::new(scene.width as u32, scene.height as u32); + let coords: Vec<_> = (0..scene.height) + .flat_map(|j| (0..scene.width).map(move |i| (i, j))) + .collect(); + + let pixels = coords + .par_iter() + .map(|(i, j)| { + let p = trace_pixel(*i, *j, &scene); + // height-j is to flip y-axis. + (*i as u32, (scene.height - *j - 1) as u32, image::Rgb(p)) + }) + .collect::>(); + + pixels + .iter() + .for_each(|(x, y, p)| img.put_pixel(*x, *y, *p)); + img +}