use std::time::Instant; use anyhow::Result; use structopt::StructOpt; use rtchallenge::prelude::*; use rtchallenge::{ camera::RenderStrategy, float::consts::PI, patterns::{test_pattern, BLACK_PAT, WHITE_PAT}, WHITE, }; /// End of chapter 14 challenge. #[derive(StructOpt, Debug)] #[structopt(name = "eoc14")] struct Opt { /// Strategy for casting rays into image. #[structopt(long, default_value = "rayon")] render_strategy: RenderStrategy, /// Number of samples per pixel. 0 renders from the center of the pixel, 1 or more samples N /// times randomly across the pixel. #[structopt(short, long, default_value = "0")] samples: usize, /// Rendered image width in pixels. #[structopt(short, long, default_value = "2560")] width: usize, /// Rendered image height in pixels. #[structopt(short, long, default_value = "1440")] height: usize, } fn main() -> Result<()> { let start = Instant::now(); let opt = Opt::from_args(); let light1 = PointLightBuilder::default() .position(point(-5., 5., -5.)) .intensity(WHITE) .build()?; let light2 = PointLightBuilder::default() .position(point(5., 5., -5.)) .intensity([0.2, 0.2, 0.6]) .build()?; let light3 = PointLightBuilder::default() .position(point(0., 2., -5.)) .intensity([0.2, 0.2, 0.1]) .build()?; let from = point(2., 8., -10.); let to = point(2., 2., -1.); let up = point(0., 1., 0.); let camera = CameraBuilder::default() .hsize(opt.width) .vsize(opt.height) .field_of_view(PI / 12.) .transform(view_transform(from, to, up)) .render_strategy(opt.render_strategy) .samples_per_pixel(opt.samples) .build()?; let floor = plane() .material( MaterialBuilder::default() .color( checkers_pattern( ring_pattern([0.8, 0.8, 0.2].into(), [0.8, 0.2, 0.8].into()) .transform(scaling(1. / 8., 1. / 8., 1. / 8.)) .build()?, ring_pattern([0.2, 0.8, 0.2].into(), [0.8, 0.2, 0.2].into()) .transform(scaling(1. / 4., 1. / 4., 1. / 4.)) .build()?, ) .transform(translation(0., 1., 0.) * scaling(2., 2., 2.)) .build()?, ) .specular(0.) .reflective(0.5) .build()?, ) .build()?; let sphere_size = scaling(0.5, 0.5, 0.5); let x1y1 = sphere() .transform(translation(1., 1., 0.) * sphere_size) .material( MaterialBuilder::default() .color( gradient_pattern([0., 0., 1.].into(), [1., 1., 0.].into()) .transform(scaling(2., 1., 1.) * translation(-0.5, 0., 0.)) .build()?, ) .diffuse(0.7) .specular(0.3) .reflective(0.5) .build()?, ) .build()?; let x2y1 = sphere() .transform(translation(2., 1., 0.) * sphere_size) .material( MaterialBuilder::default() .color(stripe_pattern(WHITE_PAT, BLACK_PAT).build()?) .diffuse(0.7) .specular(0.3) .build()?, ) .build()?; let x3y1 = sphere() .transform(translation(3., 1., 0.) * sphere_size) .material( MaterialBuilder::default() .color( stripe_pattern(WHITE_PAT, BLACK_PAT) .transform(scaling(0.2, 1., 1.)) .build()?, ) .diffuse(0.7) .specular(0.0) .build()?, ) .build()?; let x1y2 = sphere() .transform(translation(1., 2., 0.) * sphere_size) .material( MaterialBuilder::default() .color(test_pattern().build()?) .diffuse(0.7) .specular(0.3) .build()?, ) .build()?; let x2y2 = sphere() .transform(translation(2., 2., 0.) * sphere_size) .material( MaterialBuilder::default() .color( ring_pattern(WHITE_PAT, BLACK_PAT) .transform(scaling(0.2, 0.2, 0.2)) .build()?, ) .diffuse(0.7) .specular(0.3) .build()?, ) .build()?; let x3y2 = sphere() .transform(translation(3., 2., 0.) * sphere_size) .material( MaterialBuilder::default() .color( checkers_pattern(WHITE_PAT, BLACK_PAT) .transform(scaling(0.5, 0.5, 0.5)) .build()?, ) .diffuse(0.7) .specular(0.3) .build()?, ) .build()?; let x1y2z1 = sphere() .transform(translation(1., 2., -1.) * sphere_size) .material( MaterialBuilder::default() .color([0.8, 0.2, 0.2]) .diffuse(0.7) .specular(0.3) .transparency(0.5) .build()?, ) .build()?; let x2y2z1 = sphere() .transform(translation(2., 2., -1.) * sphere_size) .material( MaterialBuilder::default() .color([0.8, 0.2, 0.2]) .diffuse(0.7) .specular(0.3) .transparency(0.9) .refractive_index(1.5) .build()?, ) .build()?; let x3y2z1 = cube() .transform( translation(3., 2., -1.) * (sphere_size * scaling(0.5, 0.5, 0.5)) * rotation_y(PI / 4.), ) .material( MaterialBuilder::default() .color([0.8, 0.8, 0.2]) .diffuse(0.7) .specular(0.3) .reflective(0.5) .build()?, ) .build()?; let x2y4z1 = sphere() .transform(translation(2., 4., 0.) * sphere_size) .material( MaterialBuilder::default() .color([0.2, 0.2, 0.8]) .diffuse(0.7) .specular(0.3) .build()?, ) .build()?; let world = WorldBuilder::default() .lights(vec![light1, light2, light3]) .objects(vec![ floor, x1y1, x2y1, x3y1, x1y2, x2y2, x3y2, x1y2z1, x2y2z1, x2y4z1, x3y2z1, ]) .build()?; let image = camera.render(&world); let path = "/tmp/eoc14.png"; println!("saving output to {}", path); image.write_to_file(path)?; println!("Render time {:.3} seconds", start.elapsed().as_secs_f32()); Ok(()) }