Compare commits

...

20 commits

Author SHA1 Message Date
Jonathan Flueren
8468b8f80a Merge branch 'main' of git.flueren.eu:JonOfUs/Renderer 2022-10-13 17:04:53 +02:00
Jonathan Flueren
a3644701b5 More tries to fix Metal material 2022-10-13 16:59:35 +02:00
Jonathan Flueren
6292881294 More tries to fix Metal material 2022-10-05 16:18:51 +02:00
Jonathan Flueren
fd9cbea2f6 Begin implementing obj rotation 2022-09-27 17:19:21 +02:00
Jonathan Flueren
637abb06ad Some tries to fix normal directions 2022-09-20 17:47:34 +02:00
Jonathan Flueren
e3cb37817c Merge 2022-09-20 16:18:50 +02:00
Jonathan Flueren
bbc31ca172 Cargo Update 2022-09-20 16:15:51 +02:00
Jonathan Flueren
3f225bde7f Viking room first tries 2022-09-13 23:30:17 +02:00
Jonathan Flueren
64f46fb251 Fixed selective antialiasing 2022-08-09 17:33:10 +02:00
a830e47ae9 Update 'README.md' 2022-08-06 15:59:51 +02:00
25bb6dd53a Update 'README.md' 2022-08-06 15:59:37 +02:00
cfdf4c30b1 Update 'README.md' 2022-08-06 15:59:07 +02:00
Jonathan Flueren
477f95d762 Added link to cg-ag in readme 2022-08-06 15:45:03 +02:00
Jonathan Flueren
491886b687 Add comment about that only obj with face normals work currentlY 2022-08-06 15:36:34 +02:00
e0a2fa25fa Added another image to README 2022-08-06 15:29:50 +02:00
Jonathan Flueren
1c6d05946b clean up method order and add some doc 2022-08-06 15:23:45 +02:00
34a7708c0d Update Readme.md 2022-08-02 19:21:31 +02:00
012ad35d85 Update 'README.md' 2022-08-02 18:32:14 +02:00
Jonathan Flueren
bfa58c6962 Add images 2022-08-02 18:30:39 +02:00
Jonathan Flueren
c787724541 Add Rainbow material based on normal 2022-08-02 17:07:49 +02:00
18 changed files with 522 additions and 263 deletions

6
.gitignore vendored
View file

@ -1,6 +1,6 @@
/target /target
*.ppm /*.ppm
*.jpg /*.jpg
*.png /*.png
flamegraph.svg flamegraph.svg
perf.data* perf.data*

129
Cargo.lock generated
View file

@ -8,12 +8,6 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "adler32"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
[[package]] [[package]]
name = "ahash" name = "ahash"
version = "0.7.6" version = "0.7.6"
@ -39,9 +33,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "bytemuck" name = "bytemuck"
version = "1.11.0" version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5377c8865e74a160d21f29c2d40669f53286db6eab59b88540cbb12ffc8b835" checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
@ -61,6 +55,20 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "console"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89eab4d20ce20cea182308bca13088fecea9c05f6776cf287205d41a0ed3c847"
dependencies = [
"encode_unicode",
"libc",
"once_cell",
"terminal_size",
"unicode-width",
"winapi",
]
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.3.2" version = "1.3.2"
@ -116,19 +124,26 @@ dependencies = [
] ]
[[package]] [[package]]
name = "deflate" name = "either"
version = "1.0.0" version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f" checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
dependencies = [
"adler32",
]
[[package]] [[package]]
name = "either" name = "encode_unicode"
version = "1.7.0" version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "flate2"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
@ -165,6 +180,17 @@ dependencies = [
"png", "png",
] ]
[[package]]
name = "indicatif"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfddc9561e8baf264e0e45e197fd7696320026eb10a8180340debc27b18f535b"
dependencies = [
"console",
"number_prefix",
"unicode-width",
]
[[package]] [[package]]
name = "jpeg-decoder" name = "jpeg-decoder"
version = "0.2.6" version = "0.2.6"
@ -173,9 +199,9 @@ checksum = "9478aa10f73e7528198d75109c8be5cd7d15fb530238040148d5f9a22d4c5b3b"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.126" version = "0.2.133"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966"
[[package]] [[package]]
name = "memoffset" name = "memoffset"
@ -188,9 +214,9 @@ dependencies = [
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.5.3" version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"
dependencies = [ dependencies = [
"adler", "adler",
] ]
@ -236,20 +262,26 @@ dependencies = [
] ]
[[package]] [[package]]
name = "once_cell" name = "number_prefix"
version = "1.13.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]]
name = "once_cell"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0"
[[package]] [[package]]
name = "png" name = "png"
version = "0.17.5" version = "0.17.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" checksum = "8f0e7f4c94ec26ff209cee506314212639d6c91b80afb82984819fafce9df01c"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"crc32fast", "crc32fast",
"deflate", "flate2",
"miniz_oxide", "miniz_oxide",
] ]
@ -282,9 +314,9 @@ dependencies = [
[[package]] [[package]]
name = "rand_core" name = "rand_core"
version = "0.6.3" version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [ dependencies = [
"getrandom", "getrandom",
] ]
@ -318,6 +350,7 @@ name = "renderer"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"image", "image",
"indicatif",
"rand", "rand",
"rayon", "rayon",
"tobj", "tobj",
@ -329,6 +362,16 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "terminal_size"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
dependencies = [
"libc",
"winapi",
]
[[package]] [[package]]
name = "tobj" name = "tobj"
version = "3.2.3" version = "3.2.3"
@ -338,6 +381,12 @@ dependencies = [
"ahash", "ahash",
] ]
[[package]]
name = "unicode-width"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.4" version = "0.9.4"
@ -349,3 +398,25 @@ name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View file

@ -7,6 +7,7 @@ edition = "2021"
[dependencies] [dependencies]
image = { version = "0.24.2", default-features = false, features = ["jpeg", "png", "pnm"] } image = { version = "0.24.2", default-features = false, features = ["jpeg", "png", "pnm"] }
indicatif = "0.17.0"
rand = { version = "0.8.5", features = ["small_rng"] } rand = { version = "0.8.5", features = ["small_rng"] }
rayon = "1.5.3" rayon = "1.5.3"
tobj = "3.2.3" tobj = "3.2.3"

View file

@ -1,5 +1,20 @@
# Renderer - Grafik-AG # Renderer - Grafik-AG
Programmed in the [Computergrafik-AG](https://fsinfo.cs.tu-dortmund.de/ags/cg-ag/start).
Following [Raytracing In One Weekend](https://raytracing.github.io/books/RayTracingInOneWeekend.html).
Some rendered images are in `images/`.
### Image after adding metal material for spheres
![RaytracingInOneWeekend](images/RayTracingInOneWeekend1.jpg)
### One of the final images from the guide
![RaytracingInOneWeekend](images/RayTracingInOneWeekend2.png)
### Upgraded with triangles and mesh importing
![Suzanne Rainbow](images/suzanne_rainbow.png)
*** ***
### Performance analysis with Flamegraph ### Performance analysis with Flamegraph
https://github.com/flamegraph-rs/flamegraph https://github.com/flamegraph-rs/flamegraph

Binary file not shown.

After

Width:  |  Height:  |  Size: 738 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 KiB

BIN
images/cat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 KiB

BIN
images/suzanne_rainbow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 KiB

BIN
images/suzanne_rainbow2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 KiB

View file

@ -1,7 +1,4 @@
# Blender v2.82 (sub 7) OBJ File: ''
# www.blender.org
mtllib viking_room.mtl
o mesh_all1_Texture1_0
v -0.573651 0.001530 0.713748 v -0.573651 0.001530 0.713748
v -0.573651 0.151382 -0.000154 v -0.573651 0.151382 -0.000154
v -0.573651 0.164474 0.619081 v -0.573651 0.164474 0.619081

View file

@ -5,7 +5,7 @@ pub fn put_color(img: &mut RgbImage, pixel_color: &Color, x: u32, y: u32, sample
let mut g = pixel_color.y(); let mut g = pixel_color.y();
let mut b = pixel_color.z(); let mut b = pixel_color.z();
let scale = 1.0 / samples_per_pixel as f64; let scale = 1.0;// / samples_per_pixel as f64;
r = (scale * r).sqrt(); r = (scale * r).sqrt();
g = (scale * g).sqrt(); g = (scale * g).sqrt();
b = (scale * b).sqrt(); b = (scale * b).sqrt();

View file

@ -29,7 +29,8 @@ impl HittableList {
for obj in &self.objects { for obj in &self.objects {
let mut temp_rec = HitRecord::empty(); let mut temp_rec = HitRecord::empty();
if obj.hit(&r, t_min, closest_so_far, &mut temp_rec) { if obj.hit(&r, t_min, closest_so_far, &mut temp_rec)
&& closest_so_far > temp_rec.t {
hit_anything = true; hit_anything = true;
closest_so_far = temp_rec.t; closest_so_far = temp_rec.t;
*rec = temp_rec; *rec = temp_rec;

View file

@ -13,15 +13,16 @@ use camera::Camera;
use hittable::{HitRecord, Hittable, Sphere, Triangle}; use hittable::{HitRecord, Hittable, Sphere, Triangle};
use hittable_list::HittableList; use hittable_list::HittableList;
use image::{Rgb, RgbImage}; use image::{Rgb, RgbImage};
use material::{Dielectric, Lambertian, Material, Metal}; use indicatif::ProgressBar;
use material::{Dielectric, Lambertian, Material, Metal, Rainbow};
use ray::Ray; use ray::Ray;
use rayon::prelude::*; use rayon::prelude::*;
use std::env; use std::env;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc; use std::sync::Arc;
use tobj; use tobj;
use vec3::{Color, Point3, Vec3}; use vec3::{Color, Point3, Vec3};
/* Gets the pixel color for the passed ray */
fn ray_color(r: &Ray, world: &HittableList, depth: u32) -> Color { fn ray_color(r: &Ray, world: &HittableList, depth: u32) -> Color {
let mut rec = HitRecord::empty(); let mut rec = HitRecord::empty();
@ -38,7 +39,7 @@ fn ray_color(r: &Ray, world: &HittableList, depth: u32) -> Color {
{ {
return attenuation * ray_color(&scattered, world, depth - 1); return attenuation * ray_color(&scattered, world, depth - 1);
} }
return Color::null(); return Color::new(255.0, 0.0, 0.0);//Color::null();
} }
let unit_direction = r.direction(); let unit_direction = r.direction();
@ -46,6 +47,353 @@ fn ray_color(r: &Ray, world: &HittableList, depth: u32) -> Color {
return (1.0 - t) * Color::new(1.0, 1.0, 1.0) + t * Color::new(0.5, 0.7, 1.0); return (1.0 - t) * Color::new(1.0, 1.0, 1.0) + t * Color::new(0.5, 0.7, 1.0);
} }
// Current world view:
//
// I y
// I
// I
// I
// / \
// / \
// / \
// / z \ x
/*
* Main function that builds everything and runs the raytracing
*/
fn main() {
// File
let mut default_file = "image.ppm";
// Image
let aspect_ratio = 10.0 / 7.5; //16.0 / 9.0;
let image_width = 1200;
let image_height = (image_width as f64 / aspect_ratio) as u32;
let samples_per_pixel = 1_u32;
let max_depth = 50;
let antialiasing_threshold = 0.2; // at what diff between two colors will a pixel be antialiased
let vfov = 43.0;
let lookfrom = Point3::new(2.0, 1.0, 1.0);
let lookat = Point3::new(0.0, 0.2, 0.0);
let vup = Vec3::new(0.0, 1.0, 0.0);
let dist_to_focus = 1.0;
let aperture = 0.0; // disable depth of field
// limit rayon multithreading thread count
let thread_count = 0; // if 0, for each logical cpu core a thread wil be created
if thread_count > 0 {
env::set_var("RAYON_NUM_THREADS", thread_count.to_string());
}
// World
eprintln!("[1/4] Loading meshes from file...");
let world = from_obj("obj/viking_room.obj");
// let world = random_world();
// Camera
let cam = Camera::new(
lookfrom,
lookat,
vup,
vfov,
aspect_ratio,
aperture,
dist_to_focus,
);
// Render
let args: Vec<String> = env::args().collect();
if args.len() > 1 && args[1] != "" {
default_file = &args[1];
}
eprintln!("[2/4] Generating image...");
let bar = ProgressBar::new(image_height as u64);
let mut image = RgbImage::new(image_width, image_height);
let color_lines: Vec<_> = (0..image_height)
.into_par_iter() // threadded/parallel variant
//.into_iter() // iterative variant
.rev()
.map(|j| {
bar.inc(1);
let mut colors = Vec::new();
for i in 0..image_width {
let pixel_color = ray_color(
&cam.get_ray(
i as f64 / (image_width - 1) as f64,
j as f64 / (image_height - 1) as f64,
),
&world,
max_depth,
);
colors.push(pixel_color);
}
return colors;
})
.collect();
bar.finish_and_clear();
// 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;
}
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();
for _ in 0..samples_per_pixel {
let u = (i as f64 + utility::random_f64()) / (image_width - 1) as f64;
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;
}
// Correct antialiasing gamma
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);
} 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...");
(0..image_height).into_iter().rev().for_each(|j| {
(0..image_width).into_iter().for_each(|i| {
color::put_color(
&mut image,
&color_lines_antialiased[(image_height - j - 1) as usize][i as usize],
i,
image_height - j - 1,
1,
);
})
});
image.save(default_file).unwrap();
}
/********************
* WORLD GENERATION *
********************/
/*
* Generates world based on .obj mesh file passed by path.
*
* Currently only works for .obj files which contain face normals.
*/
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(
Point3::new(-500.0, -5005.0, -500.0),
5000.0,
material_ground.clone(),
)));
*/
//let material = Arc::new(Lambertian::new(&Color::new(
// 77.0 / 255.0,
// 77.0 / 255.0,
// 118.0 / 255.0,
//)));
//let material = Arc::new(Dielectric::new(2.0));
let material = Arc::new(Metal::new(&Color::new(0.5, 0.55, 0.7), 0.0));
//let material = Arc::new(Rainbow::new());
let rotate_obj = 1; // rotate clockwise, 0: 0, 1: 90, 2: 180, 3: 270
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");
let mut new_x = 0;
let mut new_y = 1;
let mut new_z = 2;
match rotate_obj {
1 => {
new_x = 0;
new_y = 2;
new_z = 1;
},
2 => {
new_x = 0;
new_y = 2;
new_z = 1;
},
3 => {
new_x = 0;
new_y = 2;
new_z = 1;
},
_ => {}
}
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;
}
for v in 0..mesh.indices.len() / 3 {
let index_a = mesh.indices[3 * v] as usize;
let index_b = mesh.indices[3 * v + 1] as usize;
let index_c = mesh.indices[3 * v + 2] as usize;
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 + new_x] as f64,
mesh.positions[3 * index_normal_a + new_y] as f64,
mesh.positions[3 * index_normal_a + new_z] as f64,
) + Vec3::new(
mesh.positions[3 * index_normal_b + new_x] as f64,
mesh.positions[3 * index_normal_b + new_y] as f64,
mesh.positions[3 * index_normal_b + new_z] as f64,
) + Vec3::new(
mesh.positions[3 * index_normal_c + new_x] as f64,
mesh.positions[3 * index_normal_c + new_y] as f64,
mesh.positions[3 * index_normal_c + new_z] as f64,
),
);
/*
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 + new_x] as f64,
mesh.positions[3 * index_a + new_y] as f64,
mesh.positions[3 * index_a + new_z] as f64,
),
Point3::new(
mesh.positions[3 * index_b + new_x] as f64,
mesh.positions[3 * index_b + new_y] as f64,
mesh.positions[3 * index_b + new_z] as f64,
),
Point3::new(
mesh.positions[3 * index_c + new_x] as f64,
mesh.positions[3 * index_c + new_y] as f64,
mesh.positions[3 * index_c + new_z] as f64,
),
normal_avg,
material.clone(),
)));
}
}
return world;
}
/*
* Generates a world with a bunch of spheres
*/
fn random_world() -> HittableList { fn random_world() -> HittableList {
let mut world = HittableList::new(); let mut world = HittableList::new();
@ -166,221 +514,3 @@ fn random_world() -> HittableList {
return world; return world;
} }
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(
Point3::new(0.0, -5005.0, 0.0),
5000.0,
material_ground.clone(),
)));
let material = Arc::new(Lambertian::new(&Color::new(
26.0 / 255.0,
82.0 / 255.0,
118.0 / 255.0,
)));
//let material = Arc::new(Dielectric::new(2.0));
//let material = Arc::new(Metal::new(&Color::new(0.9, 0.9, 0.7), 1.0));
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;
let index_b = mesh.indices[3 * v + 1] as usize;
let index_c = mesh.indices[3 * v + 2] as usize;
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 + 1] as f64,
mesh.positions[3 * index_normal_a + 2] as f64,
) + Vec3::new(
mesh.positions[3 * index_normal_b] as f64,
mesh.positions[3 * index_normal_b + 1] as f64,
mesh.positions[3 * index_normal_b + 2] as f64,
) + Vec3::new(
mesh.positions[3 * index_normal_c] as f64,
mesh.positions[3 * index_normal_c + 1] as f64,
mesh.positions[3 * index_normal_c + 2] as f64,
),
);
/*
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 + 1] as f64,
mesh.positions[3 * index_a + 2] as f64,
),
Point3::new(
mesh.positions[3 * index_b] as f64,
mesh.positions[3 * index_b + 1] as f64,
mesh.positions[3 * index_b + 2] as f64,
),
Point3::new(
mesh.positions[3 * index_c] as f64,
mesh.positions[3 * index_c + 1] as f64,
mesh.positions[3 * index_c + 2] as f64,
),
normal_avg,
material.clone(),
)));
}
}
return world;
}
/*
Current world view:
I y
I
I
I
/ \
/ \
/ \
/ z \ x
*/
fn main() {
// File
let mut default_file = "image.ppm";
// Image
let aspect_ratio = 16.0 / 9.0;
let image_width = 1200;
let image_height = (image_width as f64 / aspect_ratio) as u32;
let samples_per_pixel = 50_u32;
let max_depth = 50;
let vfov = 40.0;
let lookfrom = Point3::new(2.0, 0.8, 3.0);
let lookat = Point3::new(0.0, 0.0, 0.0);
let vup = Vec3::new(0.0, 1.0, 0.0);
let dist_to_focus = 3.0;
let aperture = 0.1;
// limit rayon multithreading thread count
let thread_count = 6; // if 0, for each logical cpu core a thread wil be created
if thread_count > 0 {
env::set_var("RAYON_NUM_THREADS", thread_count.to_string());
}
// World
let world = from_obj("obj/suzanne.obj");
// let world = random_world();
// Camera
let cam = Camera::new(
lookfrom,
lookat,
vup,
vfov,
aspect_ratio,
aperture,
dist_to_focus,
);
// 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);
let atomic_counter = Arc::new(AtomicU32::new(0));
let color_lines: Vec<_> = (0..image_height)
.into_par_iter() // threadded/parallel variant
//.into_iter() // iterative variant
.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 = ray_color(
&cam.get_ray(
i as f64 / (image_width - 1) as f64,
j as f64 / (image_height - 1) as f64,
),
&world,
max_depth,
); // first ray not antialiased to disable antialiasing on value 1
for _ in 1..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);
}
return colors;
})
.collect();
eprint!("\rScanlines remaining: {:5}", 0);
(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,
samples_per_pixel,
);
})
});
image.save(default_file).unwrap();
eprintln!("\nDone!");
}

View file

@ -17,6 +17,8 @@ pub struct Dielectric {
ir: f64, ir: f64,
} }
pub struct Rainbow {}
pub trait Material: Sync + Send { pub trait Material: Sync + Send {
fn scatter( fn scatter(
&self, &self,
@ -75,7 +77,13 @@ impl Material for Metal {
*scattered = Ray::new(rec.p, reflected + self.fuzz * Vec3::random_in_unit_sphere()); *scattered = Ray::new(rec.p, reflected + self.fuzz * Vec3::random_in_unit_sphere());
*attenuation = self.albedo.clone(); *attenuation = self.albedo.clone();
return Vec3::dot(scattered.direction(), rec.normal) > 0.0; let reflect = Vec3::dot(r_in.direction(), rec.normal);
//dbg!(reflect);
/*if reflect > 0.0 {
*attenuation = Color::new(100.0, 0.0, 0.0);
}*/
return true;//reflect > 0.0;
} }
} }
@ -95,7 +103,7 @@ impl Material for Mirror {
) -> bool { ) -> bool {
if utility::random_f64() > 0.8 { if utility::random_f64() > 0.8 {
// Reflektiert // Reflektiert
let reflected = Vec3::reflect(&Vec3::unit_vector(r_in.direction()), &rec.normal); let reflected = Vec3::reflect(&r_in.direction(), &rec.normal);
*scattered = Ray::new(rec.p, reflected); *scattered = Ray::new(rec.p, reflected);
*attenuation = self.albedo.clone(); *attenuation = self.albedo.clone();
@ -157,3 +165,35 @@ impl Material for Dielectric {
return true; return true;
} }
} }
impl Rainbow {
pub fn new() -> Self {
Rainbow {}
}
}
impl Material for Rainbow {
fn scatter(
&self,
r_in: &Ray,
rec: &HitRecord,
attenuation: &mut Color,
scattered: &mut Ray,
) -> bool {
let mut scatter_direction = rec.normal + Vec3::random_unit_vector();
if scatter_direction.near_zero() {
scatter_direction = rec.normal;
}
*scattered = Ray::new(rec.p, scatter_direction);
let color = 0.5
* Color::new(
rec.normal.x() + 1.0,
rec.normal.y() + 1.0,
rec.normal.z() + 1.0,
);
*attenuation = color;
return true;
}
}

View file

@ -30,6 +30,10 @@ impl Vec3 {
self.e[0] * self.e[0] + self.e[1] * self.e[1] + self.e[2] * self.e[2] self.e[0] * self.e[0] + self.e[1] * self.e[1] + self.e[2] * self.e[2]
} }
pub fn diff(a: &Self, b: &Self) -> f64 {
(a.x() - b.x()).abs() + (a.y() - b.y()).abs() + (a.z() - b.z()).abs()
}
pub fn cross(a: Vec3, b: Vec3) -> Vec3 { pub fn cross(a: Vec3, b: Vec3) -> Vec3 {
Vec3::new( Vec3::new(
a.y() * b.z() - a.z() * b.y(), a.y() * b.z() - a.z() * b.y(),