Renderer/src/main.rs

316 lines
9.2 KiB
Rust
Raw Normal View History

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-06-29 16:17:28 +02:00
use material::{Dielectric, Lambertian, Material, Metal};
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::atomic::{AtomicU32, Ordering};
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-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-06-29 16:17:28 +02:00
2022-06-08 18:28:45 +02:00
//let target = rec.p + rec.normal + Vec3::random_unit_vector(); // rec.p + rec.normal.random_in_hemisphere();
//return 0.5 * ray_color(&Ray::new(rec.p, target - rec.p), world, depth - 1);
2022-05-04 18:21:48 +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-06-28 18:23:22 +02:00
fn random_world() -> HittableList {
2022-05-04 18:21:48 +02:00
let mut world = HittableList::new();
2022-06-08 18:28:45 +02:00
2022-07-12 17:55:16 +02:00
let material_ground = Arc::new(Lambertian::new(&Color::new(0.05, 0.05, 0.05)));
world.add(Box::<Sphere>::new(Sphere::new(
2022-06-29 16:17:28 +02:00
Point3::new(0.0, -50000.0, 0.0),
50000.0,
2022-06-08 18:28:45 +02:00
material_ground.clone(),
2022-05-11 18:38:30 +02:00
)));
2022-06-28 18:23:22 +02:00
(-6..5).into_iter().for_each(|a| {
(-6..5).into_iter().for_each(|b| {
let choose_mat = utility::random_f64();
2022-07-05 18:02:28 +02:00
let rad = utility::random_rng(0.1, 0.5);
2022-06-29 16:17:28 +02:00
let center = Point3::new(
1.5 * a as f64 + 1.3 * utility::random_f64(),
2022-07-05 18:02:28 +02:00
rad,
2022-06-29 16:17:28 +02:00
1.5 * b as f64 + 1.3 * utility::random_f64(),
);
2022-07-05 18:02:28 +02:00
if (center - Point3::new(4.0, rad, 0.0)).length() > 0.9 {
2022-06-29 16:17:28 +02:00
if choose_mat < 0.8 {
// diffuse
let sphere_material = Arc::new(Lambertian::new(
&(Color::random_f64() * Color::random_f64()),
));
2022-06-28 18:23:22 +02:00
world.add(Box::<Sphere>::new(Sphere::new(
center,
2022-07-05 18:02:28 +02:00
rad,
2022-06-29 16:17:28 +02:00
sphere_material.clone(),
2022-06-28 18:23:22 +02:00
)));
2022-06-29 16:17:28 +02:00
} 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),
));
2022-06-28 18:23:22 +02:00
world.add(Box::<Sphere>::new(Sphere::new(
center,
0.2,
2022-06-29 16:17:28 +02:00
sphere_material.clone(),
2022-06-28 18:23:22 +02:00
)));
2022-06-29 16:17:28 +02:00
} else {
// glass
2022-06-28 18:23:22 +02:00
let sphere_material = Arc::new(Dielectric::new(1.5));
world.add(Box::<Sphere>::new(Sphere::new(
center,
0.2,
2022-06-29 16:17:28 +02:00
sphere_material.clone(),
2022-06-28 18:23:22 +02:00
)));
}
}
});
});
let material1 = Arc::new(Dielectric::new(1.5));
let material2 = Arc::new(Lambertian::new(&Color::new(0.4, 0.2, 0.1)));
2022-06-29 16:17:28 +02:00
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(
2022-06-28 18:23:22 +02:00
Point3::new(0.0, 1.0, 0.0),
1.0,
material2.clone(),
2022-05-11 18:38:30 +02:00
)));
2022-06-28 18:23:22 +02:00
world.add(Box::<Sphere>::new(Sphere::new(
2022-06-28 18:23:22 +02:00
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(
2022-06-29 16:17:28 +02:00
Point3::new(-2.0, 2.0, -5.0),
2.0,
material4.clone(),
2022-05-11 18:38:30 +02:00
)));
2022-06-29 16:17:28 +02:00
2022-06-23 12:11:00 +02:00
world.add(Box::<Sphere>::new(Sphere::new(
2022-06-29 16:17:28 +02:00
Point3::new(-3.6, 2.0, -2.0),
0.6,
material5.clone(),
2022-06-23 12:11:00 +02:00
)));
2022-05-04 18:21:48 +02:00
2022-07-12 17:55:16 +02:00
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(),
)));
2022-06-28 18:23:22 +02:00
return world;
}
2022-07-15 00:08:51 +02:00
fn from_obj(path: &str) -> 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(),
)));
dbg!(path);
let cornell_box = tobj::load_obj(path, &tobj::GPU_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;
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;
}
todo!("find out how to get triangular faces and build world") // https://docs.rs/tobj/3.2.3/tobj/struct.Mesh.html
for i in mesh.indices.iter() {
}
for v in 0..mesh.positions.len() / 3 {
println!(
" v[{}] = ({}, {}, {})",
v,
mesh.positions[3 * v],
mesh.positions[3 * v + 1],
mesh.positions[3 * v + 2]
);
}
}
let material = Arc::new(Lambertian::new(&Color::new(0.4, 0.2, 0.1)));
return world;
}
2022-07-12 17:55:16 +02:00
/*
Current world view:
I y
I
I
I
/ \
/ \
/ \
/ z \ x
*/
2022-06-28 18:23:22 +02:00
fn main() {
// File
let mut default_file = "image.ppm";
// Image
let aspect_ratio = 16.0 / 9.0;
2022-07-15 00:08:51 +02:00
let image_width = 200;
2022-06-28 18:23:22 +02:00
let image_height = (image_width as f64 / aspect_ratio) as u32;
2022-07-12 17:55:16 +02:00
let samples_per_pixel = 100_u32;
2022-06-28 18:23:22 +02:00
let max_depth = 50;
2022-07-05 18:02:28 +02:00
let vfov = 40.0;
2022-06-29 16:17:28 +02:00
let lookfrom = Point3::new(10.0, 4.0, 13.0);
2022-06-28 18:23:22 +02:00
let lookat = Point3::new(0.0, 0.0, 0.0);
let vup = Vec3::new(0.0, 1.0, 0.0);
2022-06-29 16:17:28 +02:00
let dist_to_focus = 17.0;
2022-06-28 18:23:22 +02:00
let aperture = 0.1;
2022-07-15 00:08:51 +02:00
let world = from_obj("obj/Lowpoly_tree_sample.obj");
2022-06-28 18:23:22 +02:00
// World
let world = random_world();
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];
}
let mut image = RgbImage::new(image_width, image_height);
2022-06-01 20:13:55 +02:00
let atomic_counter = Arc::new(AtomicU32::new(0));
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| {
let v = atomic_counter.fetch_add(1, Ordering::Relaxed);
eprint!("\rScanlines remaining: {:5}", image_height - v);
let mut colors = Vec::new();
for i in 0..image_width {
let mut pixel_color = Color::new(0.0, 0.0, 0.0);
for _ in 0..samples_per_pixel {
let u = (i as f64 + utility::random_f64()) / (image_width - 1) as f64;
let v = (j as f64 + utility::random_f64()) / (image_height - 1) as f64;
let r = cam.get_ray(u, v);
pixel_color += ray_color(&r, &world, max_depth);
}
colors.push(pixel_color);
2022-05-11 18:38:30 +02:00
}
2022-06-01 20:13:55 +02:00
return colors;
})
.collect();
eprint!("\rScanlines remaining: {:5}", 0);
2022-04-07 23:04:50 +02:00
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-06-01 17:59:25 +02:00
&color_lines[(image_height - j - 1) as usize][i as usize],
2022-05-11 18:38:30 +02:00
i,
image_height - j - 1,
samples_per_pixel,
);
2022-06-01 17:59:25 +02:00
})
});
image.save(default_file).unwrap();
eprintln!("\nDone!");
}