891 lines
23 KiB
Rust
891 lines
23 KiB
Rust
use std::sync::{Arc, Mutex};
|
|
use std::time::Instant;
|
|
use crate::camera::Camera;
|
|
use crate::hittable::{Sphere, HittableList, HittableObject};
|
|
use crate::output::{Output, PNG};
|
|
use crate::ray::Ray;
|
|
use crate::vec3::{Color, Point3, Vec3};
|
|
use hittable::MovableSphere;
|
|
use rand::Rng;
|
|
use rand::distributions::{Distribution, Uniform};
|
|
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
|
use crate::material::{Dielectric, DiffuseLight, Lambertian, Material, Metal};
|
|
use crate::aabb::Aabb;
|
|
use crate::bvh::BVH;
|
|
use crate::constant_medium::ConstantMedium;
|
|
use crate::cuboid::Cuboid;
|
|
use crate::image_texture::ImageTexture;
|
|
use crate::noise::NoiseTexture;
|
|
use crate::obj::obj_to_hitable;
|
|
use crate::perlin::Perlin;
|
|
use crate::texture::CheckerTexture;
|
|
use crate::rect::{Plane, Rect2D};
|
|
use crate::rotate_y::RotateY;
|
|
use crate::translate::Translate;
|
|
use crate::triangle::Triangle;
|
|
|
|
mod vec3;
|
|
mod ray;
|
|
mod hittable;
|
|
mod material;
|
|
mod camera;
|
|
mod output;
|
|
mod aabb;
|
|
mod bvh;
|
|
mod texture;
|
|
mod perlin;
|
|
mod noise;
|
|
mod image_texture;
|
|
mod rect;
|
|
mod cuboid;
|
|
mod translate;
|
|
mod rotate_y;
|
|
mod constant_medium;
|
|
mod isotropic;
|
|
mod obj;
|
|
mod triangle;
|
|
|
|
// Image
|
|
const DEFAULT_ASPECT_RATIO: f64 = 3.0 / 2.0;
|
|
const IMAGE_WIDTH: usize = 300;
|
|
const SAMPLES_PER_PIXEL: i32 = 100;
|
|
const MAX_DEPTH: i32 = 50;
|
|
|
|
struct Scene {
|
|
pub world: HittableList,
|
|
pub cam: Camera,
|
|
pub background: Color
|
|
}
|
|
|
|
fn obj(path: &str) -> Scene {
|
|
let object = obj_to_hitable(&std::path::Path::new(path));
|
|
print!("Object hitbox: {:#?}", object.bounding_box(0.0, 1.0));
|
|
|
|
let difflight = Arc::new(Material::white_light(4.0));
|
|
|
|
let world: HittableList = vec![
|
|
object,
|
|
Arc::new(Rect2D::new(
|
|
Plane::XZ,
|
|
-2.0,
|
|
2.0,
|
|
-2.0,
|
|
2.0,
|
|
10.0,
|
|
difflight.clone()
|
|
)),
|
|
Arc::new(Sphere::new(
|
|
Point3::new(-5.0, 5.0, 0.0),
|
|
1.0,
|
|
difflight.clone()
|
|
)),
|
|
Arc::new(Sphere::new(
|
|
Point3::new(0.6, 3.5, -0.6),
|
|
0.4,
|
|
Arc::new(Material::Metal(Metal::new(Color::new(1.0, 0.0, 0.0), 0.2)))
|
|
)),
|
|
Arc::new(Sphere::new(
|
|
Point3::new(1.7, 0.4, 1.7),
|
|
0.4,
|
|
Arc::new(Material::solid(0.2, 0.2, 0.8))
|
|
)),
|
|
Arc::new(Sphere::new(
|
|
Point3::new(0.0, 300.0, 0.0),
|
|
300.0,
|
|
Arc::new(Material::Lambertian(Lambertian::textured(Arc::new(NoiseTexture { noise: Perlin::new(), scale: 0.1 }))))
|
|
))];
|
|
|
|
let look_from = Point3::new(8.0, 10.0, 8.0);
|
|
let look_at = Point3::new(0.0, 1.0, 0.0);
|
|
let focus_dist = 2.0;
|
|
|
|
let cam = Camera::new(
|
|
look_from,
|
|
look_at,
|
|
Vec3::new(0.0, 1.0, 0.0),
|
|
1.0,
|
|
30.0,
|
|
0.0,
|
|
focus_dist,
|
|
0.0,
|
|
1.0);
|
|
|
|
Scene {
|
|
world,
|
|
cam,
|
|
background: Color::default()
|
|
}
|
|
}
|
|
|
|
fn cornell_box() -> Scene {
|
|
let mut world:HittableList = Vec::new();
|
|
let red = Material::Lambertian(
|
|
Lambertian::from(Color::new(0.65, 0.05, 0.05)));
|
|
let white = Arc::new(Material::Lambertian(
|
|
Lambertian::from(Color::new(0.73, 0.73, 0.73))));
|
|
let green = Material::Lambertian(
|
|
Lambertian::from(Color::new(0.12, 0.45, 0.15)));
|
|
let light = Material::DiffuseLight(
|
|
DiffuseLight::from(Color::new(15.0, 15.0, 15.0)));
|
|
|
|
// Walls
|
|
world.push(Arc::new(Rect2D::new(
|
|
Plane::YZ,
|
|
0.0,
|
|
555.0,
|
|
0.0,
|
|
555.0,
|
|
555.0,
|
|
Arc::new(green)
|
|
)));
|
|
world.push(Arc::new(Rect2D::new(
|
|
Plane::YZ,
|
|
0.0,
|
|
555.0,
|
|
0.0,
|
|
555.0,
|
|
0.0,
|
|
Arc::new(red)
|
|
)));
|
|
world.push(Arc::new(Rect2D::new(
|
|
Plane::XZ,
|
|
213.0,
|
|
343.0,
|
|
227.0,
|
|
332.0,
|
|
554.0,
|
|
Arc::new(light)
|
|
)));
|
|
world.push(Arc::new(Rect2D::new(
|
|
Plane::XZ,
|
|
0.0,
|
|
555.0,
|
|
0.0,
|
|
555.0,
|
|
0.0,
|
|
white.clone()
|
|
)));
|
|
world.push(Arc::new(Rect2D::new(
|
|
Plane::XZ,
|
|
0.0,
|
|
555.0,
|
|
0.0,
|
|
555.0,
|
|
555.0,
|
|
white.clone()
|
|
)));
|
|
world.push(Arc::new(Rect2D::new(
|
|
Plane::XY,
|
|
0.0,
|
|
555.0,
|
|
0.0,
|
|
555.0,
|
|
555.0,
|
|
white.clone()
|
|
)));
|
|
|
|
// Boxes
|
|
|
|
let rotated1 = RotateY::new(
|
|
Cuboid::new(
|
|
Point3::new(0.0, 0.0, 0.0),
|
|
Point3::new(165.0, 330.0, 165.0),
|
|
white.clone()
|
|
),
|
|
15.0
|
|
);
|
|
let box1 = Translate::new(
|
|
rotated1,
|
|
Vec3::new(265.0, 0.0, 295.0)
|
|
);
|
|
world.push(Arc::new(box1));
|
|
let rotated2 = RotateY::new(
|
|
Cuboid::new(
|
|
Point3::new(0.0, 0.0, 0.0),
|
|
Point3::new(165.0, 165.0, 165.0),
|
|
white.clone()
|
|
),
|
|
-18.0
|
|
);
|
|
let box2 = Translate::new(
|
|
rotated2,
|
|
Vec3::new(130.0,0.0,65.0)
|
|
);
|
|
world.push(Arc::new(box2));
|
|
|
|
let look_from = Point3::new(278.0, 278.0, -800.0);
|
|
let look_at = Point3::new(278.0, 278.0, 0.0);
|
|
let focus_dist = 2.0;
|
|
|
|
let cam = Camera::new(
|
|
look_from,
|
|
look_at,
|
|
Vec3::new(0.0, 1.0, 0.0),
|
|
1.0,
|
|
40.0,
|
|
0.0,
|
|
focus_dist,
|
|
0.0,
|
|
1.0);
|
|
|
|
Scene {
|
|
world,
|
|
cam,
|
|
background: Color::default()
|
|
}
|
|
}
|
|
|
|
fn cornell_box_smoke() -> Scene {
|
|
let mut world:HittableList = Vec::new();
|
|
let red = Material::Lambertian(
|
|
Lambertian::from(Color::new(0.65, 0.05, 0.05)));
|
|
let white = Arc::new(Material::Lambertian(
|
|
Lambertian::from(Color::new(0.73, 0.73, 0.73))));
|
|
let green = Material::Lambertian(
|
|
Lambertian::from(Color::new(0.12, 0.45, 0.15)));
|
|
let light = Material::DiffuseLight(
|
|
DiffuseLight::from(Color::new(7.0, 7.0, 7.0)));
|
|
|
|
// Walls
|
|
world.push(Arc::new(Rect2D::new(
|
|
Plane::YZ,
|
|
0.0,
|
|
555.0,
|
|
0.0,
|
|
555.0,
|
|
555.0,
|
|
Arc::new(green)
|
|
)));
|
|
world.push(Arc::new(Rect2D::new(
|
|
Plane::YZ,
|
|
0.0,
|
|
555.0,
|
|
0.0,
|
|
555.0,
|
|
0.0,
|
|
Arc::new(red)
|
|
)));
|
|
world.push(Arc::new(Rect2D::new(
|
|
Plane::XZ,
|
|
113.0,
|
|
443.0,
|
|
127.0,
|
|
432.0,
|
|
554.0,
|
|
Arc::new(light)
|
|
)));
|
|
world.push(Arc::new(Rect2D::new(
|
|
Plane::XZ,
|
|
0.0,
|
|
555.0,
|
|
0.0,
|
|
555.0,
|
|
0.0,
|
|
white.clone()
|
|
)));
|
|
world.push(Arc::new(Rect2D::new(
|
|
Plane::XZ,
|
|
0.0,
|
|
555.0,
|
|
0.0,
|
|
555.0,
|
|
555.0,
|
|
white.clone()
|
|
)));
|
|
world.push(Arc::new(Rect2D::new(
|
|
Plane::XY,
|
|
0.0,
|
|
555.0,
|
|
0.0,
|
|
555.0,
|
|
555.0,
|
|
white.clone()
|
|
)));
|
|
|
|
// Boxes
|
|
|
|
let rotated1 = RotateY::new(
|
|
Cuboid::new(
|
|
Point3::new(0.0, 0.0, 0.0),
|
|
Point3::new(165.0, 330.0, 165.0),
|
|
white.clone()
|
|
),
|
|
15.0
|
|
);
|
|
let box1 = Translate::new(
|
|
rotated1,
|
|
Vec3::new(265.0, 0.0, 295.0)
|
|
);
|
|
let medium1 = ConstantMedium::colored(
|
|
box1,
|
|
Color::new(0.0, 0.0, 0.0),
|
|
0.01);
|
|
world.push(Arc::new(medium1));
|
|
let rotated2 = RotateY::new(
|
|
Cuboid::new(
|
|
Point3::new(0.0, 0.0, 0.0),
|
|
Point3::new(165.0, 165.0, 165.0),
|
|
white.clone()
|
|
),
|
|
-18.0
|
|
);
|
|
let box2 = Translate::new(
|
|
rotated2,
|
|
Vec3::new(130.0,0.0,65.0)
|
|
);
|
|
let medium2 = ConstantMedium::colored(
|
|
box2,
|
|
Color::new(1.0, 1.0, 1.0),
|
|
0.01);
|
|
world.push(Arc::new(medium2));
|
|
|
|
let look_from = Point3::new(278.0, 278.0, -800.0);
|
|
let look_at = Point3::new(278.0, 278.0, 0.0);
|
|
let focus_dist = 2.0;
|
|
|
|
let cam = Camera::new(
|
|
look_from,
|
|
look_at,
|
|
Vec3::new(0.0, 1.0, 0.0),
|
|
1.0,
|
|
40.0,
|
|
0.0,
|
|
focus_dist,
|
|
0.0,
|
|
1.0);
|
|
|
|
Scene {
|
|
world,
|
|
cam,
|
|
background: Color::default()
|
|
}
|
|
}
|
|
|
|
fn simple_light() -> Scene {
|
|
let mut world:HittableList = Vec::new();
|
|
|
|
let noise = NoiseTexture { noise: Perlin::new(), scale: 4.0 };
|
|
let noise_material = Arc::new(Material::Lambertian(Lambertian::textured(Arc::new(noise))));
|
|
world.push(Arc::new(Sphere {
|
|
center: Point3::new(0.0, -1000.0, 0.0),
|
|
radius: 1000.0,
|
|
material: Arc::clone(&noise_material)
|
|
}));
|
|
world.push(Arc::new(Sphere {
|
|
center: Point3::new(0.0, 2.0, 0.0),
|
|
radius: 2.0,
|
|
material: noise_material
|
|
}));
|
|
|
|
|
|
let difflight = Material::DiffuseLight(
|
|
// Brighter than 1.0/1.0/1.0 so it can light things
|
|
DiffuseLight::from(Color::new(4.0, 4.0, 4.0)));
|
|
world.push(Arc::new(Rect2D::new(
|
|
Plane::XY,
|
|
3.0,
|
|
5.0,
|
|
1.0,
|
|
3.0,
|
|
-2.0,
|
|
Arc::new(difflight)
|
|
)));
|
|
|
|
let look_from = Point3::new(26.0, 3.0, 6.0);
|
|
let look_at = Point3::new(0.0, 2.0, 0.0);
|
|
let focus_dist = 2.0;
|
|
|
|
let cam = Camera::new(
|
|
look_from,
|
|
look_at,
|
|
Vec3::new(0.0, 1.0, 0.0),
|
|
DEFAULT_ASPECT_RATIO,
|
|
20.0,
|
|
0.0,
|
|
focus_dist,
|
|
0.0,
|
|
1.0);
|
|
|
|
Scene {
|
|
world,
|
|
cam,
|
|
background: Color::default()
|
|
}
|
|
}
|
|
|
|
fn sun() -> Scene {
|
|
let mut world:HittableList = Vec::new();
|
|
let earth_material = Arc::new(
|
|
Material::DiffuseLight(DiffuseLight::from(Color::new(1.0, 1.0, 0.0))));
|
|
world.push(Arc::new(Sphere {
|
|
center: Point3::new(0.0, 0.0, 0.0),
|
|
radius: 2.0,
|
|
material: earth_material
|
|
}));
|
|
|
|
let look_from = Point3::new(13.0, 2.0, 3.0);
|
|
let look_at = Point3::new(0.0, 0.0, 0.0);
|
|
let focus_dist = 2.0;
|
|
|
|
let cam = Camera::new(
|
|
look_from,
|
|
look_at,
|
|
Vec3::new(0.0, 1.0, 0.0),
|
|
DEFAULT_ASPECT_RATIO,
|
|
20.0,
|
|
0.0,
|
|
focus_dist,
|
|
0.0,
|
|
1.0);
|
|
|
|
Scene {
|
|
world,
|
|
cam,
|
|
background: Color::default()
|
|
}
|
|
}
|
|
|
|
fn earth() -> Scene {
|
|
let mut world:HittableList = Vec::new();
|
|
let earth_texture = ImageTexture::new("textures/earthmap.jpg");
|
|
let earth_material = Arc::new(
|
|
Material::Lambertian(Lambertian::textured(Arc::new(earth_texture))));
|
|
world.push(Arc::new(Sphere {
|
|
center: Point3::new(0.0, 2.0, 1.0),
|
|
radius: 2.0,
|
|
material: earth_material
|
|
}));
|
|
|
|
let glass = Arc::new(
|
|
Material::Dielectric(Dielectric::new(1.5)));
|
|
world.push(Arc::new(Sphere {
|
|
center: Point3::new(-0.5,1.0, -1.5),
|
|
radius: 1.0,
|
|
material: glass
|
|
}));
|
|
|
|
let noise = NoiseTexture { noise: Perlin::new(), scale: 4.0 };
|
|
let noise_material = Arc::new(Material::Lambertian(Lambertian::textured(Arc::new(noise))));
|
|
world.push(Arc::new(Sphere {
|
|
center: Point3::new(0.0, -1000.0, 0.0),
|
|
radius: 1000.0,
|
|
material: noise_material
|
|
}));
|
|
|
|
let look_from = Point3::new(10.0, 6.0, 3.0);
|
|
let look_at = Point3::new(0.0, 0.0, 0.0);
|
|
let focus_dist = 2.0;
|
|
|
|
let cam = Camera::new(
|
|
look_from,
|
|
look_at,
|
|
Vec3::new(0.0, 1.0, 0.0),
|
|
DEFAULT_ASPECT_RATIO,
|
|
40.0,
|
|
0.0,
|
|
focus_dist,
|
|
0.0,
|
|
1.0);
|
|
|
|
Scene {
|
|
world,
|
|
cam,
|
|
background: Color::new(0.70, 0.80, 1.00)
|
|
}
|
|
}
|
|
|
|
fn two_spheres() -> Scene {
|
|
let mut world:HittableList = Vec::new();
|
|
let checker = CheckerTexture::colored(
|
|
Color::new(0.2, 0.3, 0.1),
|
|
Color::new(0.9, 0.9, 0.9));
|
|
let checker_material = Arc::new(Material::Lambertian(Lambertian::textured(Arc::new(checker))));
|
|
world.push(Arc::new(Sphere {
|
|
center: Point3::new(0.0, -10.0, 0.0),
|
|
radius: 10.0,
|
|
material: Arc::clone(&checker_material)
|
|
}));
|
|
world.push(Arc::new(Sphere {
|
|
center: Point3::new(0.0, 10.0, 0.0),
|
|
radius: 10.0,
|
|
material: checker_material
|
|
}));
|
|
|
|
let look_from = Point3::new(13.0, 2.0, 3.0);
|
|
let look_at = Point3::new(0.0, 0.0, 0.0);
|
|
let focus_dist = 2.0;
|
|
|
|
let cam = Camera::new(
|
|
look_from,
|
|
look_at,
|
|
Vec3::new(0.0, 1.0, 0.0),
|
|
DEFAULT_ASPECT_RATIO,
|
|
20.0,
|
|
0.0,
|
|
focus_dist,
|
|
0.0,
|
|
1.0);
|
|
|
|
Scene {
|
|
world,
|
|
cam,
|
|
background: Color::new(0.70, 0.80, 1.00)
|
|
}
|
|
}
|
|
fn two_perlin_spheres() -> Scene {
|
|
let mut world:HittableList = Vec::new();
|
|
let noise = NoiseTexture { noise: Perlin::new(), scale: 4.0 };
|
|
let noise_material = Arc::new(Material::Lambertian(Lambertian::textured(Arc::new(noise))));
|
|
world.push(Arc::new(Sphere {
|
|
center: Point3::new(0.0, -1000.0, 0.0),
|
|
radius: 1000.0,
|
|
material: Arc::clone(&noise_material)
|
|
}));
|
|
world.push(Arc::new(Sphere {
|
|
center: Point3::new(1.0, 2.0, 1.0),
|
|
radius: 2.0,
|
|
material: noise_material
|
|
}));
|
|
|
|
let look_from = Point3::new(13.0, 2.0, 3.0);
|
|
let look_at = Point3::new(0.0, 0.0, 0.0);
|
|
let focus_dist = 2.0;
|
|
|
|
let cam = Camera::new(
|
|
look_from,
|
|
look_at,
|
|
Vec3::new(0.0, 1.0, 0.0),
|
|
DEFAULT_ASPECT_RATIO,
|
|
20.0,
|
|
0.0,
|
|
focus_dist,
|
|
0.0,
|
|
1.0);
|
|
|
|
Scene {
|
|
world,
|
|
cam,
|
|
background: Color::new(0.70, 0.80, 1.00)
|
|
}
|
|
}
|
|
|
|
fn random_scene() -> Scene {
|
|
let mut world: HittableList = Vec::new();
|
|
|
|
let checker = CheckerTexture::colored(
|
|
Color::new(0.2, 0.3, 0.1),
|
|
Color::new(0.9, 0.9, 0.9));
|
|
let material_ground = Arc::new(Material::Lambertian(Lambertian::textured(Arc::new(checker))));
|
|
let ground = Sphere {
|
|
center: Point3::new(0.0, -1000.0, 0.0),
|
|
radius: 1000.0,
|
|
material: material_ground
|
|
};
|
|
world.push(Arc::new(ground));
|
|
|
|
let unit_range = Uniform::from(0.0..1.0);
|
|
let fuzz_range = Uniform::from(0.0..0.5);
|
|
let mut rng = rand::thread_rng();
|
|
let p = Point3::new(4.0, 0.2, 0.0);
|
|
for a in -1..11 {
|
|
for b in -11..11 {
|
|
let choose_material = unit_range.sample(&mut rng);
|
|
let center = Point3::new(
|
|
(a as f64) + 0.9*unit_range.sample(&mut rng),
|
|
0.2,
|
|
(b as f64) + 0.9*unit_range.sample(&mut rng));
|
|
if (center - p).length() < 0.9 {
|
|
continue;
|
|
}
|
|
let material = match choose_material {
|
|
_ if choose_material < 0.8 => Arc::new(Material::Lambertian(Lambertian::from(
|
|
Color::random(0.0, 1.0) * Color::random(0.0, 1.0)))),
|
|
_ if choose_material < 0.95 => Arc::new(Material::Metal(Metal::new(
|
|
Color::random(0.5, 1.0),
|
|
fuzz_range.sample(&mut rng)))),
|
|
_ => Arc::new(Material::Dielectric(Dielectric::new(1.5))),
|
|
};
|
|
let sphere: HittableObject = match rng.gen_bool(1.0 / 3.0) {
|
|
true => {
|
|
let center1 = center + Vec3::new(0.0, fuzz_range.sample(&mut rng) / 2.0, 0.0);
|
|
Arc::new(MovableSphere {
|
|
center0: center,
|
|
center1,
|
|
radius: 0.2,
|
|
material,
|
|
time0: 0.0,
|
|
time1: 1.0
|
|
})
|
|
}
|
|
false => Arc::new(Sphere {
|
|
center,
|
|
radius: 0.2,
|
|
material
|
|
})
|
|
};
|
|
world.push(sphere);
|
|
}
|
|
}
|
|
|
|
let material1 = Arc::new(Material::Dielectric(Dielectric::new(1.5)));
|
|
world.push(Arc::new(Sphere {
|
|
center: Point3::new(0.0, 1.0, 0.0),
|
|
radius: 1.0,
|
|
material: material1
|
|
}));
|
|
let material2 = Arc::new(Material::Lambertian(Lambertian::from(Color::new(0.4, 0.2, 0.1))));
|
|
world.push(Arc::new(Sphere {
|
|
center: Point3::new(-4.0, 1.0, 0.0),
|
|
radius: 1.0,
|
|
material: material2
|
|
}));
|
|
let material3 = Arc::new(Material::Metal(Metal::new(Color::new(0.7, 0.6, 0.5), 0.0)));
|
|
world.push(Arc::new(Sphere {
|
|
center: Point3::new(4.0, 1.0, 0.0),
|
|
radius: 1.0,
|
|
material: material3
|
|
}));
|
|
|
|
let look_from = Point3::new(13.0, 2.0, 3.0);
|
|
let look_at = Point3::new(0.0, 0.0, 0.0);
|
|
let focus_dist = 2.0;
|
|
|
|
let cam = Camera::new(
|
|
look_from,
|
|
look_at,
|
|
Vec3::new(0.0, 1.0, 0.0),
|
|
DEFAULT_ASPECT_RATIO,
|
|
20.0,
|
|
0.0,
|
|
focus_dist,
|
|
0.0,
|
|
1.0);
|
|
|
|
Scene {
|
|
world,
|
|
cam,
|
|
background: Color::new(0.70, 0.80, 1.00)
|
|
}
|
|
}
|
|
|
|
fn next_week_final() -> Scene {
|
|
let ground = Arc::new(
|
|
Material::Lambertian(Lambertian::from(Color::new(0.48, 0.84, 0.53))));
|
|
let boxes_per_side = 20;
|
|
let mut rng = rand::thread_rng();
|
|
let box_range = Uniform::from(1.0..101.0);
|
|
let mut boxes1: HittableList = Vec::with_capacity(boxes_per_side*boxes_per_side);
|
|
for i in 0..boxes_per_side {
|
|
for j in 0..boxes_per_side {
|
|
let w = 100.0;
|
|
let x0 = -1000.0 + (i as f64)*w;
|
|
let z0 = -1000.0 + (j as f64)*w;
|
|
let y0 = 0.0;
|
|
let x1 = x0 + w;
|
|
let y1 = box_range.sample(&mut rng);
|
|
let z1 = z0 + w;
|
|
boxes1.push(Arc::new(Cuboid::new(
|
|
Vec3::new(x0, y0, z0),
|
|
Vec3::new(x1, y1, z1),
|
|
ground.clone()
|
|
)));
|
|
}
|
|
}
|
|
|
|
let mut world: HittableList = Vec::new();
|
|
world.push(Arc::new(BVH::new(boxes1, 0.0, 1.0)));
|
|
|
|
let light = Arc::new(
|
|
Material::DiffuseLight(DiffuseLight::from(Color::new(7.0, 7.0, 7.0))));
|
|
world.push(Arc::new(Rect2D::new(
|
|
Plane::XZ,
|
|
123.0,
|
|
423.0,
|
|
147.0,
|
|
412.0,
|
|
554.0,
|
|
light
|
|
)));
|
|
|
|
// Moving Sphere
|
|
let moving_sphere_material = Arc::new(
|
|
Material::Lambertian(Lambertian::from(Color::new(0.7, 0.3, 0.1))));
|
|
let center1 = Point3::new(400.0, 400.0, 200.0);
|
|
world.push(Arc::new(MovableSphere::new(
|
|
center1,
|
|
center1 + Vec3::new(30.0, 0.0, 0.0),
|
|
50.0,
|
|
moving_sphere_material,
|
|
0.0,
|
|
1.0
|
|
)));
|
|
|
|
let glass = Arc::new(Material::Dielectric(Dielectric::new(1.5)));
|
|
|
|
// Glass
|
|
world.push(Arc::new(Sphere::new(
|
|
Point3::new(260.0, 150.0, 45.0),
|
|
50.0,
|
|
glass.clone()
|
|
)));
|
|
|
|
// Metal sphere
|
|
world.push(Arc::new(Sphere::new(
|
|
Point3::new(0.0, 150.0, 145.0),
|
|
50.0,
|
|
Arc::new(
|
|
Material::Metal(Metal::new(Color::new(0.8, 0.8, 0.9), 1.0)))
|
|
)));
|
|
let boundary = Sphere::new(
|
|
Point3::new(360.0, 150.0, 145.0),
|
|
70.0,
|
|
glass.clone()
|
|
);
|
|
let boundary_clone = Sphere::new(
|
|
Point3::new(360.0, 150.0, 145.0),
|
|
70.0,
|
|
glass.clone()
|
|
);
|
|
world.push(Arc::new(boundary_clone));
|
|
world.push(Arc::new(
|
|
ConstantMedium::colored(
|
|
boundary,
|
|
Color::new(0.2, 0.4, 0.9),
|
|
0.2)));
|
|
let boundary = Sphere::new(
|
|
Point3::new(0.0, 0.0, 0.0),
|
|
5000.0,
|
|
glass.clone()
|
|
);
|
|
world.push(Arc::new(
|
|
ConstantMedium::colored(
|
|
boundary,
|
|
Color::new(1.0, 1.0, 1.0),
|
|
0.0001)));
|
|
|
|
// Earth
|
|
let earth_material = Arc::new(Material::Lambertian(
|
|
Lambertian::textured(
|
|
Arc::new(ImageTexture::new("textures/earthmap.jpg")))));
|
|
world.push(Arc::new(Sphere::new(
|
|
Point3::new(400.0, 200.0, 400.0),
|
|
100.0,
|
|
earth_material
|
|
)));
|
|
|
|
// Gray sphere
|
|
let pertext = Arc::new(Material::Lambertian(Lambertian::textured(
|
|
Arc::new(NoiseTexture {
|
|
noise: Perlin::new(),
|
|
scale: 0.1
|
|
}))));
|
|
world.push(Arc::new(Sphere::new(
|
|
Point3::new(220.0, 280.0, 300.0),
|
|
80.0,
|
|
pertext
|
|
)));
|
|
|
|
let ns = 1000;
|
|
let mut boxes2: HittableList = Vec::with_capacity(ns);
|
|
let white = Arc::new(Material::Lambertian(
|
|
Lambertian::from(Color::new(0.73, 0.73, 0.73))));
|
|
|
|
|
|
for _i in 0..ns {
|
|
let sphere = Sphere::new(
|
|
Point3::random(0.0, 165.0),
|
|
10.0,
|
|
white.clone()
|
|
);
|
|
boxes2.push(Arc::new(sphere));
|
|
}
|
|
world.push(
|
|
Arc::new(
|
|
Translate::new(
|
|
RotateY::new(
|
|
BVH::new(boxes2, 0.0, 1.0),
|
|
15.0
|
|
),
|
|
Vec3::new(-100.0, 270.0, 395.0)
|
|
)
|
|
)
|
|
);
|
|
|
|
|
|
let look_from = Point3::new(478.0, 278.0, -600.0);
|
|
let look_at = Point3::new(278.0, 278.0, 0.0);
|
|
let focus_dist = 2.0;
|
|
|
|
let cam = Camera::new(
|
|
look_from,
|
|
look_at,
|
|
Vec3::new(0.0, 1.0, 0.0),
|
|
1.0,
|
|
40.0,
|
|
0.0,
|
|
focus_dist,
|
|
0.0,
|
|
1.0);
|
|
|
|
Scene {
|
|
world,
|
|
cam,
|
|
background: Color::default()
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
rayon::ThreadPoolBuilder::new().num_threads(1).build_global().unwrap(); // Enable, to reduce load
|
|
// World
|
|
let scene: u8 = 8;
|
|
let scene_setup = match scene {
|
|
0 => two_spheres(),
|
|
1 => two_perlin_spheres(),
|
|
2 => earth(),
|
|
3 => sun(),
|
|
4 => simple_light(),
|
|
5 => cornell_box(),
|
|
6 => cornell_box_smoke(),
|
|
7 => next_week_final(),
|
|
8 => obj("teapot.obj"),
|
|
_ => random_scene(),
|
|
};
|
|
|
|
let between = Uniform::from(0.0..1.0);
|
|
|
|
let start = Instant::now();
|
|
let image_height: usize = (IMAGE_WIDTH as f64 / scene_setup.cam.aspect_ratio()) as usize;
|
|
let mut pixels = vec![0; IMAGE_WIDTH * image_height * 3];
|
|
let bands: Vec<(usize, &mut [u8])> = pixels.chunks_mut(3).enumerate().collect();
|
|
|
|
let count = Mutex::new(0);
|
|
bands.into_par_iter().for_each(|(i, pixel)| {
|
|
let row = image_height - (i / IMAGE_WIDTH) - 1;
|
|
let col = i % IMAGE_WIDTH;
|
|
let mut rng = rand::thread_rng();
|
|
let mut color = Color::default();
|
|
(0..SAMPLES_PER_PIXEL).for_each(|_s| {
|
|
let random_number = between.sample(&mut rng);
|
|
let u = (col as f64 + random_number) / (IMAGE_WIDTH - 1) as f64;
|
|
let v = (row as f64 + random_number) / (image_height - 1) as f64;
|
|
let ray = scene_setup.cam.get_ray(u, v);
|
|
color += ray.pixel_color(scene_setup.background, &scene_setup.world, MAX_DEPTH);
|
|
});
|
|
let bytes = color.into_bytes(SAMPLES_PER_PIXEL);
|
|
pixel[0] = bytes[0];
|
|
pixel[1] = bytes[1];
|
|
pixel[2] = bytes[2];
|
|
if i % 100 == 0 {
|
|
let mut rem = count.lock().unwrap();
|
|
let percent_done_before = 100 * *rem / (IMAGE_WIDTH * image_height);
|
|
*rem += 100;
|
|
let percent_done_after = 100 * *rem / (IMAGE_WIDTH * image_height);
|
|
if percent_done_before != percent_done_after {
|
|
eprint!("\rProgress: {}% ", percent_done_after);
|
|
}
|
|
}
|
|
});
|
|
PNG::write("imc.png", &pixels, IMAGE_WIDTH, image_height).expect("Error writing image: {}");
|
|
eprintln!("\nDone. Time: {}ms", start.elapsed().as_millis());
|
|
}
|