Move tracing logic to renderer module and add CLI flags.
This commit is contained in:
@@ -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<Box<Hit>> {
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut objects: Vec<Box<Hit>> = vec![Box::new(Sphere::new(
|
||||
@@ -106,15 +90,7 @@ fn random_scene() -> Vec<Box<Hit>> {
|
||||
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::<f32>(0., 1.) + x as f32) / scene.width as f32;
|
||||
let v = (rng.gen_range::<f32>(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::<Vec<_>>();
|
||||
|
||||
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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
96
rtiow/src/renderer.rs
Normal file
96
rtiow/src/renderer.rs
Normal file
@@ -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::<f32>(0., 1.) + x as f32) / scene.width as f32;
|
||||
let v = (rng.gen_range::<f32>(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::<Vec<_>>();
|
||||
|
||||
pixels
|
||||
.iter()
|
||||
.for_each(|(x, y, p)| img.put_pixel(*x, *y, *p));
|
||||
img
|
||||
}
|
||||
Reference in New Issue
Block a user