Compare commits
20 commits
selective-
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
8468b8f80a | ||
|
a3644701b5 | ||
|
6292881294 | ||
|
fd9cbea2f6 | ||
|
637abb06ad | ||
|
e3cb37817c | ||
|
bbc31ca172 | ||
|
3f225bde7f | ||
|
64f46fb251 | ||
a830e47ae9 | |||
25bb6dd53a | |||
cfdf4c30b1 | |||
|
477f95d762 | ||
|
491886b687 | ||
e0a2fa25fa | |||
|
1c6d05946b | ||
34a7708c0d | |||
012ad35d85 | |||
|
bfa58c6962 | ||
|
c787724541 |
6
.gitignore
vendored
|
@ -1,6 +1,6 @@
|
||||||
/target
|
/target
|
||||||
*.ppm
|
/*.ppm
|
||||||
*.jpg
|
/*.jpg
|
||||||
*.png
|
/*.png
|
||||||
flamegraph.svg
|
flamegraph.svg
|
||||||
perf.data*
|
perf.data*
|
||||||
|
|
129
Cargo.lock
generated
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
15
README.md
|
@ -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
|
||||||
|
|
BIN
images/Baum_without_Antialiasing.png
Normal file
After Width: | Height: | Size: 738 KiB |
BIN
images/RayTracingInOneWeekend1.jpg
Normal file
After Width: | Height: | Size: 256 KiB |
BIN
images/RayTracingInOneWeekend2.png
Normal file
After Width: | Height: | Size: 4 MiB |
BIN
images/RayTracingInOneWeekend3.png
Normal file
After Width: | Height: | Size: 2.1 MiB |
BIN
images/RayTracingInOneWeekend4.png
Normal file
After Width: | Height: | Size: 578 KiB |
BIN
images/cat.png
Normal file
After Width: | Height: | Size: 674 KiB |
BIN
images/suzanne_rainbow.png
Normal file
After Width: | Height: | Size: 454 KiB |
BIN
images/suzanne_rainbow2.png
Normal file
After Width: | Height: | Size: 520 KiB |
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
572
src/main.rs
|
@ -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!");
|
|
||||||
}
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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(),
|
||||||
|
|