2022-05-05 13:19:56 +02:00
|
|
|
extern crate image;
|
|
|
|
|
2022-05-11 18:38:30 +02:00
|
|
|
mod camera;
|
2022-04-20 18:58:58 +02:00
|
|
|
mod color;
|
2022-04-27 18:27:07 +02:00
|
|
|
mod hittable;
|
2022-05-04 18:21:48 +02:00
|
|
|
mod hittable_list;
|
2022-06-29 16:17:28 +02:00
|
|
|
mod material;
|
2022-04-20 18:58:58 +02:00
|
|
|
mod ray;
|
2022-05-04 18:21:48 +02:00
|
|
|
mod utility;
|
2022-05-11 18:38:30 +02:00
|
|
|
mod vec3;
|
|
|
|
|
|
|
|
use camera::Camera;
|
2022-07-12 17:55:16 +02:00
|
|
|
use hittable::{HitRecord, Hittable, Sphere, Triangle};
|
2022-05-04 18:21:48 +02:00
|
|
|
use hittable_list::HittableList;
|
2022-05-11 18:38:30 +02:00
|
|
|
use image::{Rgb, RgbImage};
|
2022-08-02 17:07:49 +02:00
|
|
|
use indicatif::ProgressBar;
|
|
|
|
use material::{Dielectric, Lambertian, Material, Metal, Rainbow};
|
2022-05-05 13:19:56 +02:00
|
|
|
use ray::Ray;
|
2022-06-01 17:59:25 +02:00
|
|
|
use rayon::prelude::*;
|
2022-06-01 20:13:55 +02:00
|
|
|
use std::env;
|
|
|
|
use std::sync::Arc;
|
2022-07-15 00:08:51 +02:00
|
|
|
use tobj;
|
2022-06-01 20:13:55 +02:00
|
|
|
use vec3::{Color, Point3, Vec3};
|
2022-04-20 18:58:58 +02:00
|
|
|
|
2022-08-06 15:23:45 +02:00
|
|
|
/* Gets the pixel color for the passed ray */
|
2022-05-18 17:49:46 +02:00
|
|
|
fn ray_color(r: &Ray, world: &HittableList, depth: u32) -> Color {
|
2022-05-04 18:21:48 +02:00
|
|
|
let mut rec = HitRecord::empty();
|
2022-04-20 18:59:29 +02:00
|
|
|
|
2022-05-18 17:49:46 +02:00
|
|
|
if depth <= 0 {
|
2022-06-08 18:28:45 +02:00
|
|
|
return Color::null();
|
2022-05-18 17:49:46 +02:00
|
|
|
}
|
|
|
|
|
2022-06-08 18:28:45 +02:00
|
|
|
if world.hit(r, 0.001, f64::INFINITY, &mut rec) {
|
|
|
|
let mut scattered = Ray::new(Point3::null(), Vec3::null());
|
|
|
|
let mut attenuation = Color::null();
|
2022-06-29 16:17:28 +02:00
|
|
|
if rec
|
|
|
|
.mat_ptr
|
|
|
|
.scatter(r, &rec, &mut attenuation, &mut scattered)
|
|
|
|
{
|
|
|
|
return attenuation * ray_color(&scattered, world, depth - 1);
|
2022-06-08 18:28:45 +02:00
|
|
|
}
|
|
|
|
return Color::null();
|
2022-05-04 18:21:48 +02:00
|
|
|
}
|
2022-07-26 19:50:09 +02:00
|
|
|
|
2022-04-20 18:58:58 +02:00
|
|
|
let unit_direction = r.direction();
|
2022-04-20 18:59:29 +02:00
|
|
|
let t = 0.5 * (unit_direction.y() + 1.0);
|
|
|
|
return (1.0 - t) * Color::new(1.0, 1.0, 1.0) + t * Color::new(0.5, 0.7, 1.0);
|
2022-04-20 18:58:58 +02:00
|
|
|
}
|
2022-04-12 19:14:11 +02:00
|
|
|
|
2022-08-06 15:23:45 +02:00
|
|
|
// Current world view:
|
|
|
|
//
|
|
|
|
// I y
|
|
|
|
// I
|
|
|
|
// I
|
|
|
|
// I
|
|
|
|
// / \
|
|
|
|
// / \
|
|
|
|
// / \
|
|
|
|
// / z \ x
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Main function that builds everything and runs the raytracing
|
2022-07-12 17:55:16 +02:00
|
|
|
*/
|
2022-06-28 18:23:22 +02:00
|
|
|
fn main() {
|
|
|
|
// File
|
|
|
|
let mut default_file = "image.ppm";
|
|
|
|
|
|
|
|
// Image
|
2022-08-02 18:30:39 +02:00
|
|
|
let aspect_ratio = 10.0 / 7.5; //16.0 / 9.0;
|
2022-09-20 17:47:34 +02:00
|
|
|
let image_width = 300;
|
2022-06-28 18:23:22 +02:00
|
|
|
let image_height = (image_width as f64 / aspect_ratio) as u32;
|
2022-09-20 17:47:34 +02:00
|
|
|
let samples_per_pixel = 1_u32;
|
2022-07-26 18:16:08 +02:00
|
|
|
let max_depth = 50;
|
2022-08-09 17:33:10 +02:00
|
|
|
let antialiasing_threshold = 0.2; // at what diff between two colors will a pixel be antialiased
|
2022-06-28 18:23:22 +02:00
|
|
|
|
2022-09-13 23:30:17 +02:00
|
|
|
let vfov = 43.0;
|
|
|
|
let lookfrom = Point3::new(2.0, 1.0, 1.0);
|
|
|
|
let lookat = Point3::new(0.0, 0.2, 0.0);
|
2022-06-28 18:23:22 +02:00
|
|
|
let vup = Vec3::new(0.0, 1.0, 0.0);
|
2022-09-13 23:30:17 +02:00
|
|
|
let dist_to_focus = 1.0;
|
2022-08-02 18:30:39 +02:00
|
|
|
let aperture = 0.0; // disable depth of field
|
2022-06-28 18:23:22 +02:00
|
|
|
|
2022-07-17 02:22:09 +02:00
|
|
|
// limit rayon multithreading thread count
|
2022-08-09 17:33:10 +02:00
|
|
|
let thread_count = 0; // if 0, for each logical cpu core a thread wil be created
|
2022-07-17 02:22:09 +02:00
|
|
|
if thread_count > 0 {
|
2022-07-19 16:30:14 +02:00
|
|
|
env::set_var("RAYON_NUM_THREADS", thread_count.to_string());
|
2022-07-17 02:22:09 +02:00
|
|
|
}
|
|
|
|
|
2022-06-28 18:23:22 +02:00
|
|
|
// World
|
2022-08-02 18:30:39 +02:00
|
|
|
eprintln!("[1/4] Loading meshes from file...");
|
2022-09-13 23:30:17 +02:00
|
|
|
let world = from_obj("obj/viking_room.obj");
|
2022-07-17 02:00:32 +02:00
|
|
|
// let world = random_world();
|
2022-06-28 18:23:22 +02:00
|
|
|
|
2022-04-20 18:58:58 +02:00
|
|
|
// Camera
|
2022-06-29 16:17:28 +02:00
|
|
|
let cam = Camera::new(
|
|
|
|
lookfrom,
|
|
|
|
lookat,
|
|
|
|
vup,
|
|
|
|
vfov,
|
|
|
|
aspect_ratio,
|
|
|
|
aperture,
|
|
|
|
dist_to_focus,
|
|
|
|
);
|
2022-04-20 18:59:29 +02:00
|
|
|
|
2022-04-20 18:58:58 +02:00
|
|
|
// Render
|
|
|
|
let args: Vec<String> = env::args().collect();
|
|
|
|
if args.len() > 1 && args[1] != "" {
|
|
|
|
default_file = &args[1];
|
|
|
|
}
|
2022-05-05 13:19:56 +02:00
|
|
|
|
2022-08-02 18:30:39 +02:00
|
|
|
eprintln!("[2/4] Generating image...");
|
2022-08-02 17:07:49 +02:00
|
|
|
let bar = ProgressBar::new(image_height as u64);
|
|
|
|
|
2022-05-05 13:33:06 +02:00
|
|
|
let mut image = RgbImage::new(image_width, image_height);
|
2022-05-05 13:19:56 +02:00
|
|
|
|
2022-06-01 20:13:55 +02:00
|
|
|
let color_lines: Vec<_> = (0..image_height)
|
2022-07-12 17:55:16 +02:00
|
|
|
.into_par_iter() // threadded/parallel variant
|
2022-07-05 16:13:22 +02:00
|
|
|
//.into_iter() // iterative variant
|
2022-06-01 20:13:55 +02:00
|
|
|
.rev()
|
|
|
|
.map(|j| {
|
2022-08-02 17:07:49 +02:00
|
|
|
bar.inc(1);
|
2022-06-01 20:13:55 +02:00
|
|
|
|
|
|
|
let mut colors = Vec::new();
|
|
|
|
for i in 0..image_width {
|
2022-08-02 18:30:39 +02:00
|
|
|
let pixel_color = ray_color(
|
2022-07-17 21:09:39 +02:00
|
|
|
&cam.get_ray(
|
|
|
|
i as f64 / (image_width - 1) as f64,
|
|
|
|
j as f64 / (image_height - 1) as f64,
|
|
|
|
),
|
|
|
|
&world,
|
|
|
|
max_depth,
|
2022-08-02 18:30:39 +02:00
|
|
|
);
|
2022-06-01 20:13:55 +02:00
|
|
|
colors.push(pixel_color);
|
2022-05-11 18:38:30 +02:00
|
|
|
}
|
2022-06-01 20:13:55 +02:00
|
|
|
return colors;
|
|
|
|
})
|
|
|
|
.collect();
|
2022-08-02 17:07:49 +02:00
|
|
|
bar.finish_and_clear();
|
2022-04-07 23:04:50 +02:00
|
|
|
|
2022-09-13 23:30:17 +02:00
|
|
|
// no antialiasing
|
|
|
|
if samples_per_pixel == 1_u32 {
|
|
|
|
eprintln!("[4/4] Exporting image to disk...");
|
|
|
|
(0..image_height).into_iter().rev().for_each(|j| {
|
|
|
|
(0..image_width).into_iter().for_each(|i| {
|
|
|
|
color::put_color(
|
|
|
|
&mut image,
|
|
|
|
&color_lines[(image_height - j - 1) as usize][i as usize],
|
|
|
|
i,
|
|
|
|
image_height - j - 1,
|
|
|
|
1,
|
|
|
|
);
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
image.save(default_file).unwrap();
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-08-02 18:30:39 +02:00
|
|
|
eprintln!("[3/4] Antialiasing image...");
|
|
|
|
let mut antialiasing: Vec<Vec<bool>> = Vec::new();
|
|
|
|
let mut antialiasing_counter = 0;
|
|
|
|
let mut antialiasing_col_counter = 0;
|
|
|
|
let mut antialiases_per_line: Vec<u64> = Vec::new();
|
|
|
|
(0..image_height).into_iter().for_each(|j| {
|
|
|
|
antialiasing.push(Vec::new());
|
|
|
|
(0..image_width).into_iter().for_each(|i| {
|
|
|
|
if j != 0
|
|
|
|
&& Color::diff(
|
|
|
|
&color_lines[(j - 1) as usize][i as usize],
|
|
|
|
&color_lines[j as usize][i as usize],
|
|
|
|
) >= antialiasing_threshold
|
|
|
|
{
|
|
|
|
antialiasing[j as usize].push(true);
|
|
|
|
antialiasing_col_counter += 1;
|
|
|
|
} else if j != image_height - 1
|
|
|
|
&& Color::diff(
|
|
|
|
&color_lines[(j + 1) as usize][i as usize],
|
|
|
|
&color_lines[j as usize][i as usize],
|
|
|
|
) >= antialiasing_threshold
|
|
|
|
{
|
|
|
|
antialiasing[j as usize].push(true);
|
|
|
|
antialiasing_col_counter += 1;
|
|
|
|
} else if i != 0
|
|
|
|
&& Color::diff(
|
|
|
|
&color_lines[j as usize][(i - 1) as usize],
|
|
|
|
&color_lines[j as usize][i as usize],
|
|
|
|
) >= antialiasing_threshold
|
|
|
|
{
|
|
|
|
antialiasing[j as usize].push(true);
|
|
|
|
antialiasing_col_counter += 1;
|
|
|
|
} else if i != image_width - 1
|
|
|
|
&& Color::diff(
|
|
|
|
&color_lines[j as usize][(i + 1) as usize],
|
|
|
|
&color_lines[j as usize][i as usize],
|
|
|
|
) >= antialiasing_threshold
|
|
|
|
{
|
|
|
|
antialiasing[j as usize].push(true);
|
|
|
|
antialiasing_col_counter += 1;
|
|
|
|
}
|
|
|
|
if antialiasing[j as usize].len() < (i + 1) as usize {
|
|
|
|
antialiasing[j as usize].push(false);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
antialiases_per_line.push(antialiasing_col_counter);
|
|
|
|
antialiasing_counter += antialiasing_col_counter;
|
|
|
|
antialiasing_col_counter = 0;
|
|
|
|
});
|
|
|
|
|
|
|
|
let bar = ProgressBar::new(antialiasing_counter as u64);
|
|
|
|
let color_lines_antialiased: Vec<_> = (0..image_height)
|
|
|
|
.into_par_iter() // threadded/parallel variant
|
|
|
|
//.into_iter() // iterative variant
|
|
|
|
.map(|j| {
|
|
|
|
let mut colors = Vec::new();
|
|
|
|
for i in 0..image_width {
|
|
|
|
if samples_per_pixel > 0 && antialiasing[j as usize][i as usize] {
|
|
|
|
let mut pixel_color = Color::null();
|
2022-08-09 17:33:10 +02:00
|
|
|
|
2022-08-02 18:30:39 +02:00
|
|
|
for _ in 0..samples_per_pixel {
|
|
|
|
let u = (i as f64 + utility::random_f64()) / (image_width - 1) as f64;
|
2022-08-09 17:33:10 +02:00
|
|
|
let v = (image_height as f64- 1.0 -(j as f64 + utility::random_f64())) / (image_height - 1) as f64;
|
|
|
|
let new_pixel_color = ray_color(&cam.get_ray(u, v), &world, max_depth);
|
|
|
|
pixel_color += new_pixel_color;
|
2022-08-02 18:30:39 +02:00
|
|
|
}
|
|
|
|
// Correct antialiasing gamma
|
2022-08-09 17:33:10 +02:00
|
|
|
let fin_color = (1.0/samples_per_pixel as f64) * pixel_color;
|
|
|
|
//println!("x: {}, y: {}, z: {}", pixel_color.x(), pixel_color.y(), pixel_color.z());
|
|
|
|
|
|
|
|
colors.push(fin_color);
|
2022-08-02 18:30:39 +02:00
|
|
|
} else {
|
|
|
|
colors.push(color_lines[j as usize][i as usize]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bar.inc(antialiases_per_line[j as usize]);
|
|
|
|
return colors;
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
bar.finish_and_clear();
|
|
|
|
|
|
|
|
eprintln!("[4/4] Exporting image to disk...");
|
2022-06-01 17:59:25 +02:00
|
|
|
(0..image_height).into_iter().rev().for_each(|j| {
|
|
|
|
(0..image_width).into_iter().for_each(|i| {
|
2022-05-11 18:38:30 +02:00
|
|
|
color::put_color(
|
|
|
|
&mut image,
|
2022-08-02 18:30:39 +02:00
|
|
|
&color_lines_antialiased[(image_height - j - 1) as usize][i as usize],
|
2022-05-11 18:38:30 +02:00
|
|
|
i,
|
|
|
|
image_height - j - 1,
|
2022-08-02 18:30:39 +02:00
|
|
|
1,
|
2022-05-11 18:38:30 +02:00
|
|
|
);
|
2022-06-01 17:59:25 +02:00
|
|
|
})
|
|
|
|
});
|
|
|
|
|
2022-05-18 16:58:08 +02:00
|
|
|
image.save(default_file).unwrap();
|
|
|
|
}
|
2022-08-06 15:23:45 +02:00
|
|
|
|
|
|
|
|
|
|
|
/********************
|
|
|
|
* WORLD GENERATION *
|
2022-09-20 16:15:51 +02:00
|
|
|
********************/
|
2022-08-06 15:23:45 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
2022-08-06 15:36:34 +02:00
|
|
|
* Generates world based on .obj mesh file passed by path.
|
|
|
|
*
|
|
|
|
* Currently only works for .obj files which contain face normals.
|
2022-08-06 15:23:45 +02:00
|
|
|
*/
|
|
|
|
fn from_obj(path: &str) -> HittableList {
|
|
|
|
let mut world = HittableList::new();
|
|
|
|
|
|
|
|
/*
|
|
|
|
let material_ground = Arc::new(Lambertian::new(&Color::new(
|
|
|
|
29.0 / 255.0,
|
|
|
|
71.0 / 255.0,
|
|
|
|
14.0 / 255.0,
|
|
|
|
)));
|
|
|
|
world.add(Box::<Sphere>::new(Sphere::new(
|
2022-08-09 17:33:10 +02:00
|
|
|
Point3::new(-500.0, -5005.0, -500.0),
|
2022-08-06 15:23:45 +02:00
|
|
|
5000.0,
|
|
|
|
material_ground.clone(),
|
|
|
|
)));
|
|
|
|
*/
|
|
|
|
|
2022-09-13 23:30:17 +02:00
|
|
|
//let material = Arc::new(Lambertian::new(&Color::new(
|
|
|
|
// 77.0 / 255.0,
|
|
|
|
// 77.0 / 255.0,
|
|
|
|
// 118.0 / 255.0,
|
|
|
|
//)));
|
2022-08-06 15:23:45 +02:00
|
|
|
//let material = Arc::new(Dielectric::new(2.0));
|
2022-09-20 17:47:34 +02:00
|
|
|
let material = Arc::new(Metal::new(&Color::new(0.7, 0.6, 0.5), 0.0));
|
2022-08-09 17:33:10 +02:00
|
|
|
//let material = Arc::new(Rainbow::new());
|
2022-08-06 15:23:45 +02:00
|
|
|
|
|
|
|
let cornell_box = tobj::load_obj(path, &tobj::OFFLINE_RENDERING_LOAD_OPTIONS);
|
|
|
|
let (models, materials) = cornell_box.expect("Failed to load OBJ file");
|
|
|
|
let materials = materials.expect("Failed to load MTL file");
|
|
|
|
|
|
|
|
for (i, m) in models.iter().enumerate() {
|
|
|
|
let mesh = &m.mesh;
|
|
|
|
|
|
|
|
/* Mesh vector lengths
|
|
|
|
println!("positions: {}", mesh.positions.len());
|
|
|
|
println!("vertex_color: {}", mesh.vertex_color.len());
|
|
|
|
println!("normals: {}", mesh.normals.len());
|
|
|
|
println!("texcoords: {}", mesh.texcoords.len());
|
|
|
|
println!("indices: {}", mesh.indices.len());
|
|
|
|
println!("face_arities: {}", mesh.face_arities.len());
|
|
|
|
println!("texcoord_indices: {}", mesh.texcoord_indices.len());
|
|
|
|
println!("normal_indices: {}", mesh.normal_indices.len());
|
|
|
|
*/
|
|
|
|
|
|
|
|
let mut next_face = 0;
|
|
|
|
for f in 0..mesh.face_arities.len() {
|
|
|
|
let end = next_face + mesh.face_arities[f] as usize;
|
|
|
|
let face_indices: Vec<_> = mesh.indices[next_face..end].iter().collect();
|
|
|
|
println!(" face[{}] = {:?}", f, face_indices);
|
|
|
|
next_face = end;
|
|
|
|
}
|
|
|
|
|
|
|
|
for v in 0..mesh.indices.len() / 3 {
|
|
|
|
let index_a = mesh.indices[3 * v] as usize;
|
2022-09-20 17:47:34 +02:00
|
|
|
let index_b = mesh.indices[3 * v + 2] as usize;
|
|
|
|
let index_c = mesh.indices[3 * v + 1] as usize;
|
2022-08-06 15:23:45 +02:00
|
|
|
let index_normal_a = mesh.normal_indices[3 * v] as usize;
|
|
|
|
let index_normal_b = mesh.normal_indices[3 * v + 1] as usize;
|
|
|
|
let index_normal_c = mesh.normal_indices[3 * v + 2] as usize;
|
|
|
|
|
|
|
|
let normal_avg = Vec3::unit_vector(
|
|
|
|
Vec3::new(
|
|
|
|
mesh.positions[3 * index_normal_a] as f64,
|
|
|
|
mesh.positions[3 * index_normal_a + 2] as f64,
|
2022-09-20 17:47:34 +02:00
|
|
|
mesh.positions[3 * index_normal_a + 1] as f64,
|
2022-08-06 15:23:45 +02:00
|
|
|
) + Vec3::new(
|
|
|
|
mesh.positions[3 * index_normal_b] as f64,
|
|
|
|
mesh.positions[3 * index_normal_b + 2] as f64,
|
2022-09-20 17:47:34 +02:00
|
|
|
mesh.positions[3 * index_normal_b + 1] as f64,
|
2022-08-06 15:23:45 +02:00
|
|
|
) + Vec3::new(
|
|
|
|
mesh.positions[3 * index_normal_c] as f64,
|
|
|
|
mesh.positions[3 * index_normal_c + 2] as f64,
|
2022-09-20 17:47:34 +02:00
|
|
|
mesh.positions[3 * index_normal_c + 1] as f64,
|
2022-08-06 15:23:45 +02:00
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
/*
|
|
|
|
println!("a:{},{},{}; b:{},{},{}; c:{},{},{}",
|
|
|
|
mesh.normals[3*index_normal_a],
|
|
|
|
mesh.normals[3*index_normal_a+1],
|
|
|
|
mesh.normals[3*index_normal_a+2],
|
|
|
|
mesh.normals[3*index_normal_b],
|
|
|
|
mesh.normals[3*index_normal_b+1],
|
|
|
|
mesh.normals[3*index_normal_b+2],
|
|
|
|
mesh.normals[3*index_normal_c],
|
|
|
|
mesh.normals[3*index_normal_c+1],
|
|
|
|
mesh.normals[3*index_normal_c+2]);
|
|
|
|
*/
|
|
|
|
|
|
|
|
world.add(Box::<Triangle>::new(Triangle::new(
|
|
|
|
Point3::new(
|
|
|
|
mesh.positions[3 * index_a] as f64,
|
|
|
|
mesh.positions[3 * index_a + 2] as f64,
|
2022-09-13 23:30:17 +02:00
|
|
|
mesh.positions[3 * index_a + 1] as f64,
|
2022-08-06 15:23:45 +02:00
|
|
|
),
|
|
|
|
Point3::new(
|
|
|
|
mesh.positions[3 * index_b] as f64,
|
|
|
|
mesh.positions[3 * index_b + 2] as f64,
|
2022-09-13 23:30:17 +02:00
|
|
|
mesh.positions[3 * index_b + 1] as f64,
|
2022-08-06 15:23:45 +02:00
|
|
|
),
|
|
|
|
Point3::new(
|
|
|
|
mesh.positions[3 * index_c] as f64,
|
|
|
|
mesh.positions[3 * index_c + 2] as f64,
|
2022-09-13 23:30:17 +02:00
|
|
|
mesh.positions[3 * index_c + 1] as f64,
|
2022-08-06 15:23:45 +02:00
|
|
|
),
|
|
|
|
normal_avg,
|
|
|
|
material.clone(),
|
|
|
|
)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return world;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generates a world with a bunch of spheres
|
|
|
|
*/
|
|
|
|
fn random_world() -> HittableList {
|
|
|
|
let mut world = HittableList::new();
|
|
|
|
|
|
|
|
let material_ground = Arc::new(Lambertian::new(&Color::new(0.05, 0.05, 0.05)));
|
|
|
|
world.add(Box::<Sphere>::new(Sphere::new(
|
|
|
|
Point3::new(0.0, -50000.0, 0.0),
|
|
|
|
50000.0,
|
|
|
|
material_ground.clone(),
|
|
|
|
)));
|
|
|
|
|
|
|
|
(-6..5).into_iter().for_each(|a| {
|
|
|
|
(-6..5).into_iter().for_each(|b| {
|
|
|
|
let choose_mat = utility::random_f64();
|
|
|
|
let rad = utility::random_rng(0.1, 0.5);
|
|
|
|
let center = Point3::new(
|
|
|
|
1.5 * a as f64 + 1.3 * utility::random_f64(),
|
|
|
|
rad,
|
|
|
|
1.5 * b as f64 + 1.3 * utility::random_f64(),
|
|
|
|
);
|
|
|
|
if (center - Point3::new(4.0, rad, 0.0)).length() > 0.9 {
|
|
|
|
if choose_mat < 0.8 {
|
|
|
|
// diffuse
|
|
|
|
let sphere_material = Arc::new(Lambertian::new(
|
|
|
|
&(Color::random_f64() * Color::random_f64()),
|
|
|
|
));
|
|
|
|
world.add(Box::<Sphere>::new(Sphere::new(
|
|
|
|
center,
|
|
|
|
rad,
|
|
|
|
sphere_material.clone(),
|
|
|
|
)));
|
|
|
|
} else if choose_mat < 0.95 {
|
|
|
|
// metal
|
|
|
|
let sphere_material = Arc::new(Metal::new(
|
|
|
|
&Color::random_rng(0.5, 1.0),
|
|
|
|
utility::random_rng(0.0, 0.5),
|
|
|
|
));
|
|
|
|
world.add(Box::<Sphere>::new(Sphere::new(
|
|
|
|
center,
|
|
|
|
0.2,
|
|
|
|
sphere_material.clone(),
|
|
|
|
)));
|
|
|
|
} else {
|
|
|
|
// glass
|
|
|
|
let sphere_material = Arc::new(Dielectric::new(1.5));
|
|
|
|
world.add(Box::<Sphere>::new(Sphere::new(
|
|
|
|
center,
|
|
|
|
0.2,
|
|
|
|
sphere_material.clone(),
|
|
|
|
)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
let material1 = Arc::new(Dielectric::new(1.5));
|
|
|
|
let material2 = Arc::new(Lambertian::new(&Color::new(0.4, 0.2, 0.1)));
|
|
|
|
let material3 = Arc::new(Metal::new(&Color::new(0.7, 0.6, 0.5), 0.0));
|
|
|
|
let material4 = Arc::new(Dielectric::new(2.0));
|
|
|
|
let material5 = Arc::new(Metal::new(&Color::new(0.9, 0.9, 0.7), 0.0));
|
|
|
|
|
|
|
|
world.add(Box::<Sphere>::new(Sphere::new(
|
|
|
|
Point3::new(0.0, 1.0, 0.0),
|
|
|
|
1.0,
|
|
|
|
material2.clone(),
|
|
|
|
)));
|
|
|
|
|
|
|
|
world.add(Box::<Sphere>::new(Sphere::new(
|
|
|
|
Point3::new(-4.0, 1.0, 0.0),
|
|
|
|
1.0,
|
|
|
|
material1.clone(),
|
|
|
|
)));
|
|
|
|
|
|
|
|
world.add(Box::<Sphere>::new(Sphere::new(
|
|
|
|
Point3::new(4.0, 1.0, 0.0),
|
|
|
|
1.0,
|
|
|
|
material3.clone(),
|
|
|
|
)));
|
|
|
|
|
|
|
|
world.add(Box::<Sphere>::new(Sphere::new(
|
|
|
|
Point3::new(-2.0, 2.0, -5.0),
|
|
|
|
2.0,
|
|
|
|
material4.clone(),
|
|
|
|
)));
|
|
|
|
|
|
|
|
world.add(Box::<Sphere>::new(Sphere::new(
|
|
|
|
Point3::new(-3.6, 2.0, -2.0),
|
|
|
|
0.6,
|
|
|
|
material5.clone(),
|
|
|
|
)));
|
|
|
|
|
|
|
|
/*
|
|
|
|
world.add(Box::<Triangle>::new(Triangle::new(
|
|
|
|
Point3::new(0.0, 1.0, 5.0),
|
|
|
|
Point3::new(3.0, 2.0, 0.0),
|
|
|
|
Point3::new(0.0, 4.0, 0.0),
|
|
|
|
material2.clone(),
|
|
|
|
)));
|
|
|
|
|
|
|
|
world.add(Box::<Triangle>::new(Triangle::new(
|
|
|
|
Point3::new(5.0, 1.0, -6.0),
|
|
|
|
Point3::new(1.0, 3.0, -5.0),
|
|
|
|
Point3::new(6.0, 4.0, -6.0),
|
|
|
|
material5.clone(),
|
|
|
|
)));
|
|
|
|
world.add(Box::<Triangle>::new(Triangle::new(
|
|
|
|
Point3::new(5.0, 1.0, -6.0),
|
|
|
|
Point3::new(8.0, 1.0, -7.0),
|
|
|
|
Point3::new(6.0, 4.0, -6.0),
|
|
|
|
material5.clone(),
|
|
|
|
)));
|
|
|
|
world.add(Box::<Triangle>::new(Triangle::new(
|
|
|
|
Point3::new(8.0, 4.0, -5.0),
|
|
|
|
Point3::new(8.0, 1.0, -7.0),
|
|
|
|
Point3::new(6.0, 4.0, -6.0),
|
|
|
|
material5.clone(),
|
|
|
|
)));
|
|
|
|
*/
|
|
|
|
|
|
|
|
return world;
|
|
|
|
}
|