120 lines
3.7 KiB
Rust

use std::time::Instant;
use anyhow::Result;
use structopt::StructOpt;
use rtchallenge::{
camera::{Camera, RenderStrategy},
float::consts::PI,
lights::PointLightBuilder,
materials::MaterialBuilder,
matrices::Matrix4x4,
shapes::{Geometry, ShapeBuilder},
transformations::view_transform,
tuples::Tuple,
world::WorldBuilder,
Float, WHITE,
};
/// Experimenting with balls.
#[derive(StructOpt, Debug)]
#[structopt(name = "balls")]
struct Opt {
#[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,
}
fn main() -> Result<()> {
let start = Instant::now();
let opt = Opt::from_args();
let width = 2560;
let height = 1440;
let light1 = PointLightBuilder::default()
.position(Tuple::point(-5., 5., -5.))
.intensity(WHITE)
.build()?;
let light2 = PointLightBuilder::default()
.position(Tuple::point(5., 5., -5.))
.intensity([0.2, 0.2, 0.6])
.build()?;
let light3 = PointLightBuilder::default()
.position(Tuple::point(0., 2., -5.))
.intensity([0.2, 0.2, 0.1])
.build()?;
let ball_size = 0.5;
let num_per_axis = 3;
let center_to_center = 4. * ball_size;
let from = Tuple::point(
-5. * center_to_center,
5. * center_to_center,
-4. * center_to_center,
);
let to = Tuple::point(
num_per_axis as Float * center_to_center / 2.,
0., //num_per_axis as Float * center_to_center / 2.,
num_per_axis as Float * center_to_center / 2.,
);
let up = Tuple::point(0., 1., 0.);
let mut camera = Camera::new(width, height, PI / 6.);
camera.set_transform(view_transform(from, to, up));
camera.render_strategy = opt.render_strategy;
camera.samples_per_pixel = opt.samples;
let floor = ShapeBuilder::default()
.geometry(Geometry::Plane)
.transform(Matrix4x4::translation(0., -ball_size, 0.))
.material(
MaterialBuilder::default()
.color([0.2, 0.2, 0.2])
.specular(0.)
.build()?,
)
.build()?;
let mut objects = vec![floor];
for z in 0..num_per_axis {
for y in 0..num_per_axis {
for x in 0..num_per_axis {
objects.push(
ShapeBuilder::default()
.transform(
Matrix4x4::translation(
x as Float * center_to_center,
y as Float * center_to_center,
z as Float * center_to_center,
) * Matrix4x4::scaling(ball_size, ball_size, ball_size),
)
.material(
MaterialBuilder::default()
.color([0.1, 1., 0.5])
.ambient(y as Float / 4.)
.diffuse(x as Float / 4.)
.specular(z as Float / 4.)
.build()?,
)
.build()?,
);
}
}
}
let world = WorldBuilder::default()
.lights(vec![light1, light2, light3])
.objects(objects)
.build()?;
let image = camera.render(&world);
let path = "/tmp/balls.png";
println!("saving output to {}", path);
image.write_to_file(path)?;
println!("Render time {:.3} seconds", start.elapsed().as_secs_f32());
Ok(())
}