use std::{error::Error, path::Path};
use lumiere::{bvh::BVHNode, camera, image, material, object, scene::Scene, Colour, Point3};
use rand::{rngs, Rng};
fn main() -> Result<(), Box<dyn Error>> {
let mut rng = rand::thread_rng();
const ASPECT_RATIO: f64 = 16. / 9.;
const IMAGE_WIDTH: usize = 3840;
const IMAGE_HEIGHT: usize = (IMAGE_WIDTH as f64 / ASPECT_RATIO) as usize;
let samples_per_pixel: usize = 500;
let max_depth = 50;
const BUFFER_LENGTH: usize = 3 * IMAGE_WIDTH * IMAGE_HEIGHT;
let mut pixels = vec![0_u8; BUFFER_LENGTH];
let (camera, world) = example_complex_scene(&mut rng);
let mut bvh_root = object::HittableList::new();
bvh_root.add(Box::new(BVHNode::new(world, &mut rng)));
let scene = Scene::new(
bvh_root,
camera,
max_depth,
samples_per_pixel,
IMAGE_WIDTH,
IMAGE_HEIGHT,
);
scene.render(&mut pixels, &mut rng)?;
image::png::write_image::<&Path, IMAGE_WIDTH, IMAGE_HEIGHT>(&pixels, Path::new("image.png"))?;
eprintln!("Saved image");
Ok(())
}
pub fn example_basic_scene(_rng: &mut rngs::ThreadRng) -> (camera::Camera, object::HittableList) {
let camera_look_dir = Point3::new(-13., -2., -3.);
let camera = camera::CameraBuilder::new()
.origin(Point3::new(13., 2., 3.))
.look_dir(camera_look_dir)
.fov(20.)
.aperture(0.1)
.focus_dist(10.)
.build();
let mut world = object::HittableList::new();
let material_ground = Box::new(material::Lambertian::new(Colour::new(0.5, 0.5, 0.5)));
world.add(Box::new(object::Sphere::new(
"ground".to_string(),
Point3::new(0., -1000., 0.),
1000.,
material_ground,
)));
let material_3 = Box::new(material::Metal::new(Colour::new(0.7, 0.6, 0.5), 0.0));
world.add(Box::new(object::Sphere::new(
"obj3".to_string(),
Point3::new(4., 1., 0.),
1.,
material_3,
)));
let material_4 = Box::new(material::Lambertian::new(Colour::new(0.7, 0.1, 0.5)));
world.add(Box::new(object::Sphere::new(
"obj4".to_string(),
Point3::new(7., 1., 0.),
0.4,
material_4,
)));
(camera, world)
}
pub fn example_complex_scene(rng: &mut rngs::ThreadRng) -> (camera::Camera, object::HittableList) {
let camera_look_dir = Point3::new(-13., -2., -3.);
let camera = camera::CameraBuilder::new()
.origin(Point3::new(13., 2., 3.))
.look_dir(camera_look_dir)
.fov(20.)
.aperture(0.1)
.focus_dist(10.)
.build();
let mut world = object::HittableList::new();
let material_ground = Box::new(material::Lambertian::new(Colour::new(0.5, 0.5, 0.5)));
world.add(Box::new(object::Sphere::new(
"ground".to_string(),
Point3::new(0., -1000., 0.),
1000.,
material_ground,
)));
for a in -11..11 {
for b in -11..11 {
let choose_mat: f64 = rng.gen();
let centre = Point3::new(
a as f64 + 0.9 * choose_mat,
0.2,
b as f64 + 0.9 * choose_mat,
);
if (centre - Point3::new(4., 0.2, 0.)).length() > 0.9 {
match choose_mat {
a if a < 0.8 => {
let albedo = Colour::random_in_range_inclusive(rng, 0.0..=1.0);
let sphere_material = material::Lambertian::new(albedo);
let centre_1 = centre; world.add(Box::new(object::MovingSphere::new(
"".to_string(),
centre,
centre_1,
0.2,
Box::new(sphere_material),
)));
}
a if a < 0.95 => {
let albedo = Colour::random_in_range_inclusive(rng, 0.5..=1.0);
let fuzziness: f64 = rng.gen_range(0.0..0.5);
let sphere_material = material::Metal::new(albedo, fuzziness);
world.add(Box::new(object::Sphere::new(
"".to_string(),
centre,
0.2,
Box::new(sphere_material),
)));
}
_ => {
let sphere_material = material::Dielectric::new(1.5);
world.add(Box::new(object::Sphere::new(
"".to_string(),
centre,
0.2,
Box::new(sphere_material),
)));
}
}
}
}
}
let material_1 = Box::new(material::Dielectric::new(1.5));
world.add(Box::new(object::Sphere::new(
"obj1".to_string(),
Point3::new(0., 1., 0.),
1.,
material_1,
)));
let material_2 = Box::new(material::Lambertian::new(Colour::new(0.4, 0.2, 0.1)));
world.add(Box::new(object::Sphere::new(
"obj2".to_string(),
Point3::new(-4., 1., 0.),
1.,
material_2,
)));
let material_3 = Box::new(material::Metal::new(Colour::new(0.7, 0.6, 0.5), 0.0));
world.add(Box::new(object::Sphere::new(
"obj3".to_string(),
Point3::new(4., 1., 0.),
1.,
material_3,
)));
(camera, world)
}