Compare commits
1 Commits
master
...
ultraviole
Author | SHA1 | Date | |
---|---|---|---|
7353855119 |
25
Cargo.lock
generated
25
Cargo.lock
generated
|
@ -12,6 +12,12 @@ version = "1.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytemuck"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37fa13df2292ecb479ec23aa06f4507928bef07839be9ef15281411076629431"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "c2-chacha"
|
name = "c2-chacha"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
|
@ -207,6 +213,7 @@ dependencies = [
|
||||||
"rand",
|
"rand",
|
||||||
"rayon",
|
"rayon",
|
||||||
"sdl2",
|
"sdl2",
|
||||||
|
"ultraviolet",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -261,8 +268,26 @@ version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ultraviolet"
|
||||||
|
version = "0.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae513d44058e41a4669dc336b13a017ea96ad6984bac3797a3d49744e3ffa0ba"
|
||||||
|
dependencies = [
|
||||||
|
"wide",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.9.0+wasi-snapshot-preview1"
|
version = "0.9.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 = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wide"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "528f4316973b7d116d4d87634a4360c97de5d8664352104a130403c32e4b48e8"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
]
|
||||||
|
|
|
@ -8,3 +8,4 @@ edition = "2018"
|
||||||
sdl2 = "0.33.0"
|
sdl2 = "0.33.0"
|
||||||
rand = "0.7.3"
|
rand = "0.7.3"
|
||||||
rayon = "1.3.0"
|
rayon = "1.3.0"
|
||||||
|
ultraviolet = "0.4.5"
|
||||||
|
|
|
@ -39,7 +39,7 @@ fn render(&self, buf: &mut vec<u8>, width: usize, height: usize, samples: u8) {
|
||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
.for_each(move |i| {
|
.for_each(move |i| {
|
||||||
let world = self.world();
|
let world = self.world();
|
||||||
let camera = self.camera(nx as f64 / ny as f64);
|
let camera = self.camera(nx as f32 / ny as f32);
|
||||||
|
|
||||||
let start_y = j * ny;
|
let start_y = j * ny;
|
||||||
let start_x = i * nx;
|
let start_x = i * nx;
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
use {
|
use {crate::types::Ray, rand::Rng, ultraviolet::vec::Vec3};
|
||||||
crate::types::{Ray, Vec3},
|
|
||||||
rand::Rng,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Camera {
|
pub struct Camera {
|
||||||
origin: Vec3,
|
origin: Vec3,
|
||||||
horizontal: Vec3,
|
horizontal: Vec3,
|
||||||
vertical: Vec3,
|
vertical: Vec3,
|
||||||
lower_left_corner: Vec3,
|
lower_left_corner: Vec3,
|
||||||
lens_radius: f64,
|
lens_radius: f32,
|
||||||
|
|
||||||
// position vectors
|
// position vectors
|
||||||
u: Vec3,
|
u: Vec3,
|
||||||
|
@ -26,20 +23,20 @@ impl Camera {
|
||||||
look_from: Vec3,
|
look_from: Vec3,
|
||||||
look_at: Vec3,
|
look_at: Vec3,
|
||||||
v_up: Vec3,
|
v_up: Vec3,
|
||||||
vertical_fov: f64,
|
vertical_fov: f32,
|
||||||
aspect: f64,
|
aspect: f32,
|
||||||
aperture: f64,
|
aperture: f32,
|
||||||
focus_distance: f64,
|
focus_distance: f32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// convert degree to radian
|
// convert degree to radian
|
||||||
let angle = vertical_fov * std::f64::consts::PI / 180.0;
|
let angle = vertical_fov * std::f32::consts::PI / 180.0;
|
||||||
let half_height = (angle / 2.0).tan();
|
let half_height = (angle / 2.0).tan();
|
||||||
let half_width = aspect * half_height;
|
let half_width = aspect * half_height;
|
||||||
|
|
||||||
let origin = look_from;
|
let origin = look_from;
|
||||||
let w = (look_from - look_at).unit_vector();
|
let w = (look_from - look_at).normalized();
|
||||||
let u = v_up.cross(&w).unit_vector();
|
let u = v_up.cross(w).normalized();
|
||||||
let v = w.cross(&u);
|
let v = w.cross(u);
|
||||||
|
|
||||||
let lower_left_corner = origin
|
let lower_left_corner = origin
|
||||||
- u * focus_distance * half_width
|
- u * focus_distance * half_width
|
||||||
|
@ -61,10 +58,10 @@ impl Camera {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_ray(&self, u: f64, v: f64) -> Ray {
|
pub fn get_ray(&self, u: f32, v: f32) -> Ray {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let rd = random_in_unit_disk(&mut rng) * self.lens_radius;
|
let rd = random_in_unit_disk(&mut rng) * self.lens_radius;
|
||||||
let offset = self.u * rd.x() + self.v * rd.y();
|
let offset = self.u * rd.x + self.v * rd.y;
|
||||||
Ray::new(
|
Ray::new(
|
||||||
self.origin + offset,
|
self.origin + offset,
|
||||||
self.lower_left_corner + self.horizontal * u + self.vertical * v - self.origin - offset,
|
self.lower_left_corner + self.horizontal * u + self.vertical * v - self.origin - offset,
|
||||||
|
@ -73,10 +70,10 @@ impl Camera {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_in_unit_disk(rng: &mut rand::rngs::ThreadRng) -> Vec3 {
|
fn random_in_unit_disk(rng: &mut rand::rngs::ThreadRng) -> Vec3 {
|
||||||
let mut p = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), 0.0) * 2.0 - Vec3::new(1.0, 1.0, 0.0);
|
let mut p = Vec3::new(rng.gen::<f32>(), rng.gen::<f32>(), 0.0) * 2.0 - Vec3::new(1.0, 1.0, 0.0);
|
||||||
|
|
||||||
while p.dot(&p) >= 1.0 {
|
while p.dot(p) >= 1.0 {
|
||||||
p = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), 0.0) * 2.0 - Vec3::new(1.0, 0.0, 0.0);
|
p = Vec3::new(rng.gen::<f32>(), rng.gen::<f32>(), 0.0) * 2.0 - Vec3::new(1.0, 0.0, 0.0);
|
||||||
}
|
}
|
||||||
p
|
p
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,12 @@ use {
|
||||||
demos::{Chunk, Demo},
|
demos::{Chunk, Demo},
|
||||||
types::{
|
types::{
|
||||||
material::{Dielectric, Lambertian, Metal},
|
material::{Dielectric, Lambertian, Metal},
|
||||||
Hitable, HitableList, Ray, Sphere, Vec3,
|
Hitable, HitableList, Ray, Sphere,
|
||||||
},
|
},
|
||||||
Camera,
|
Camera,
|
||||||
},
|
},
|
||||||
rand::Rng,
|
rand::Rng,
|
||||||
|
ultraviolet::vec::Vec3,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct DefocusBlur;
|
pub struct DefocusBlur;
|
||||||
|
@ -18,7 +19,7 @@ impl Demo for DefocusBlur {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn world(&self) -> Option<HitableList> {
|
fn world(&self) -> Option<HitableList> {
|
||||||
let radius = (std::f64::consts::PI / 4.0).cos();
|
let radius = (std::f32::consts::PI / 4.0).cos();
|
||||||
Some(HitableList {
|
Some(HitableList {
|
||||||
list: vec![
|
list: vec![
|
||||||
Box::new(Sphere::with_material(
|
Box::new(Sphere::with_material(
|
||||||
|
@ -45,11 +46,11 @@ impl Demo for DefocusBlur {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn camera(&self, aspect_ratio: f64) -> Option<Camera> {
|
fn camera(&self, aspect_ratio: f32) -> Option<Camera> {
|
||||||
let lookfrom = Vec3::new(3.0, 3.0, 3.0);
|
let lookfrom = Vec3::new(3.0, 3.0, 3.0);
|
||||||
let lookat = Vec3::new(0.0, 0.0, -1.0);
|
let lookat = Vec3::new(0.0, 0.0, -1.0);
|
||||||
let aperture = 2.0;
|
let aperture = 2.0;
|
||||||
let distance_to_focus = (lookfrom - lookat).length();
|
let distance_to_focus = (lookfrom - lookat).mag();
|
||||||
let camera = Camera::new(
|
let camera = Camera::new(
|
||||||
lookfrom,
|
lookfrom,
|
||||||
lookat,
|
lookat,
|
||||||
|
@ -88,19 +89,19 @@ impl Demo for DefocusBlur {
|
||||||
for i in start_x..start_x + nx {
|
for i in start_x..start_x + nx {
|
||||||
let mut color = Vec3::new(0.0, 0.0, 0.0);
|
let mut color = Vec3::new(0.0, 0.0, 0.0);
|
||||||
for _s in 0..samples {
|
for _s in 0..samples {
|
||||||
let u = (i as f64 + rng.gen::<f64>()) / x as f64;
|
let u = (i as f32 + rng.gen::<f32>()) / x as f32;
|
||||||
let v = (j as f64 + rng.gen::<f64>()) / y as f64;
|
let v = (j as f32 + rng.gen::<f32>()) / y as f32;
|
||||||
|
|
||||||
let ray = camera.get_ray(u, v);
|
let ray = camera.get_ray(u, v);
|
||||||
color += calc_color(ray, &world, 0);
|
color += calc_color(ray, &world, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
color /= samples as f64;
|
color /= samples as f32;
|
||||||
|
|
||||||
// gamma 2 corrected
|
// gamma 2 corrected
|
||||||
buffer[offset] = (255.99 * color.r().sqrt()) as u8;
|
buffer[offset] = (255.99 * color.x.sqrt()) as u8;
|
||||||
buffer[offset + 1] = (255.99 * color.g().sqrt()) as u8;
|
buffer[offset + 1] = (255.99 * color.y.sqrt()) as u8;
|
||||||
buffer[offset + 2] = (255.99 * color.b().sqrt()) as u8;
|
buffer[offset + 2] = (255.99 * color.z.sqrt()) as u8;
|
||||||
offset += 4;
|
offset += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,7 +109,7 @@ impl Demo for DefocusBlur {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 {
|
fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 {
|
||||||
if let Some(hit_rec) = world.hit(&ray, 0.001, std::f64::MAX) {
|
if let Some(hit_rec) = world.hit(&ray, 0.001, std::f32::MAX) {
|
||||||
if depth >= 50 {
|
if depth >= 50 {
|
||||||
Vec3::new(0.0, 0.0, 0.0)
|
Vec3::new(0.0, 0.0, 0.0)
|
||||||
} else {
|
} else {
|
||||||
|
@ -120,8 +121,8 @@ fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let unit_direction = ray.direction().unit_vector();
|
let unit_direction = ray.direction().normalized();
|
||||||
let t = 0.5 * (unit_direction.y() + 1.0);
|
let t = 0.5 * (unit_direction.y + 1.0);
|
||||||
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,12 @@ use {
|
||||||
demos::{Chunk, Demo},
|
demos::{Chunk, Demo},
|
||||||
types::{
|
types::{
|
||||||
material::{Dielectric, Lambertian, Metal},
|
material::{Dielectric, Lambertian, Metal},
|
||||||
Hitable, HitableList, Ray, Sphere, Vec3,
|
Hitable, HitableList, Ray, Sphere,
|
||||||
},
|
},
|
||||||
Camera,
|
Camera,
|
||||||
},
|
},
|
||||||
rand::Rng,
|
rand::Rng,
|
||||||
|
ultraviolet::vec::Vec3,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct DielectricMaterial;
|
pub struct DielectricMaterial;
|
||||||
|
@ -75,19 +76,19 @@ impl Demo for DielectricMaterial {
|
||||||
for i in start_x..start_x + nx {
|
for i in start_x..start_x + nx {
|
||||||
let mut color = Vec3::new(0.0, 0.0, 0.0);
|
let mut color = Vec3::new(0.0, 0.0, 0.0);
|
||||||
for _s in 0..samples {
|
for _s in 0..samples {
|
||||||
let u = (i as f64 + rng.gen::<f64>()) / x as f64;
|
let u = (i as f32 + rng.gen::<f32>()) / x as f32;
|
||||||
let v = (j as f64 + rng.gen::<f64>()) / y as f64;
|
let v = (j as f32 + rng.gen::<f32>()) / y as f32;
|
||||||
|
|
||||||
let ray = camera.get_ray(u, v);
|
let ray = camera.get_ray(u, v);
|
||||||
color += calc_color(ray, &world, 0);
|
color += calc_color(ray, &world, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
color /= samples as f64;
|
color /= samples as f32;
|
||||||
|
|
||||||
// gamma 2 corrected
|
// gamma 2 corrected
|
||||||
buffer[offset] = (255.99 * color.r().sqrt()) as u8;
|
buffer[offset] = (255.99 * color.x.sqrt()) as u8;
|
||||||
buffer[offset + 1] = (255.99 * color.g().sqrt()) as u8;
|
buffer[offset + 1] = (255.99 * color.y.sqrt()) as u8;
|
||||||
buffer[offset + 2] = (255.99 * color.b().sqrt()) as u8;
|
buffer[offset + 2] = (255.99 * color.z.sqrt()) as u8;
|
||||||
offset += 4;
|
offset += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,7 +96,7 @@ impl Demo for DielectricMaterial {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 {
|
fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 {
|
||||||
if let Some(hit_rec) = world.hit(&ray, 0.001, std::f64::MAX) {
|
if let Some(hit_rec) = world.hit(&ray, 0.001, std::f32::MAX) {
|
||||||
if depth >= 50 {
|
if depth >= 50 {
|
||||||
Vec3::new(0.0, 0.0, 0.0)
|
Vec3::new(0.0, 0.0, 0.0)
|
||||||
} else {
|
} else {
|
||||||
|
@ -107,8 +108,8 @@ fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let unit_direction = ray.direction().unit_vector();
|
let unit_direction = ray.direction().normalized();
|
||||||
let t = 0.5 * (unit_direction.y() + 1.0);
|
let t = 0.5 * (unit_direction.y + 1.0);
|
||||||
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
demos::{Chunk, Demo},
|
demos::{Chunk, Demo},
|
||||||
types::{Hitable, HitableList, Ray, Sphere, Vec3},
|
types::{Hitable, HitableList, Ray, Sphere},
|
||||||
Camera,
|
Camera,
|
||||||
},
|
},
|
||||||
rand::Rng,
|
rand::Rng,
|
||||||
|
ultraviolet::vec::Vec3,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct DiffuseMaterials;
|
pub struct DiffuseMaterials;
|
||||||
|
@ -49,13 +50,13 @@ impl Demo for DiffuseMaterials {
|
||||||
let mut color = Vec3::new(0.0, 0.0, 0.0);
|
let mut color = Vec3::new(0.0, 0.0, 0.0);
|
||||||
|
|
||||||
for _s in 0..samples {
|
for _s in 0..samples {
|
||||||
let u = (i as f64 + rng.gen::<f64>()) / x as f64;
|
let u = (i as f32 + rng.gen::<f32>()) / x as f32;
|
||||||
let v = (j as f64 + rng.gen::<f64>()) / y as f64;
|
let v = (j as f32 + rng.gen::<f32>()) / y as f32;
|
||||||
let r = camera.get_ray(u, v);
|
let r = camera.get_ray(u, v);
|
||||||
color += calc_color(r, &world, &mut rng);
|
color += calc_color(r, &world, &mut rng);
|
||||||
}
|
}
|
||||||
|
|
||||||
color /= samples as f64;
|
color /= samples as f32;
|
||||||
|
|
||||||
// Without taking square root of each color, we get a picture that
|
// Without taking square root of each color, we get a picture that
|
||||||
// is quite dark
|
// is quite dark
|
||||||
|
@ -63,9 +64,9 @@ impl Demo for DiffuseMaterials {
|
||||||
// So, IRL, It *should* look a bit lighter in color
|
// So, IRL, It *should* look a bit lighter in color
|
||||||
// To do that, We apply gamma correction by a factor of 2
|
// To do that, We apply gamma correction by a factor of 2
|
||||||
// which means multiple rgb values by 1/gamma aka 1/2
|
// which means multiple rgb values by 1/gamma aka 1/2
|
||||||
buffer[offset] = (255.99 * color.r().sqrt()) as u8;
|
buffer[offset] = (255.99 * color.x.sqrt()) as u8;
|
||||||
buffer[offset + 1] = (255.99 * color.g().sqrt()) as u8;
|
buffer[offset + 1] = (255.99 * color.y.sqrt()) as u8;
|
||||||
buffer[offset + 2] = (255.99 * color.b().sqrt()) as u8;
|
buffer[offset + 2] = (255.99 * color.z.sqrt()) as u8;
|
||||||
offset += 4;
|
offset += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,25 +74,25 @@ impl Demo for DiffuseMaterials {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_color(ray: Ray, world: &HitableList, rng: &mut rand::rngs::ThreadRng) -> Vec3 {
|
fn calc_color(ray: Ray, world: &HitableList, rng: &mut rand::rngs::ThreadRng) -> Vec3 {
|
||||||
// The value of t_min here could've been 0.0 but since f32/f64 can only be
|
// The value of t_min here could've been 0.0 but since f32/f32 can only be
|
||||||
// partially compared, It may cause shadow acne effect.
|
// partially compared, It may cause shadow acne effect.
|
||||||
// To combat this problem, We set a bias
|
// To combat this problem, We set a bias
|
||||||
// More information here, https://www.opengl-tutorial.org/intermediate-tutorials/tutorial-16-shadow-mapping/#shadow-acne
|
// More information here, https://www.opengl-tutorial.org/intermediate-tutorials/tutorial-16-shadow-mapping/#shadow-acne
|
||||||
if let Some(hit_rec) = world.hit(&ray, 0.001, std::f64::MAX) {
|
if let Some(hit_rec) = world.hit(&ray, 0.001, std::f32::MAX) {
|
||||||
let target = hit_rec.p + hit_rec.normal + random_point_in_unit_sphere(rng);
|
let target = hit_rec.p + hit_rec.normal + random_point_in_unit_sphere(rng);
|
||||||
calc_color(Ray::new(hit_rec.p, target - hit_rec.p), &world, rng) * 0.5
|
calc_color(Ray::new(hit_rec.p, target - hit_rec.p), &world, rng) * 0.5
|
||||||
} else {
|
} else {
|
||||||
let unit_direction = ray.direction().unit_vector();
|
let unit_direction = ray.direction().normalized();
|
||||||
let t = 0.5 * (unit_direction.y() + 1.0);
|
let t = 0.5 * (unit_direction.y + 1.0);
|
||||||
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_point_in_unit_sphere(rng: &mut rand::rngs::ThreadRng) -> Vec3 {
|
fn random_point_in_unit_sphere(rng: &mut rand::rngs::ThreadRng) -> Vec3 {
|
||||||
let mut point = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), rng.gen::<f64>()) * 2.0
|
let mut point = Vec3::new(rng.gen::<f32>(), rng.gen::<f32>(), rng.gen::<f32>()) * 2.0
|
||||||
- Vec3::new(1.0, 1.0, 1.0);
|
- Vec3::new(1.0, 1.0, 1.0);
|
||||||
while point.sq_len() >= 1.0 {
|
while point.mag_sq() >= 1.0 {
|
||||||
point = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), rng.gen::<f64>()) * 2.0
|
point = Vec3::new(rng.gen::<f32>(), rng.gen::<f32>(), rng.gen::<f32>()) * 2.0
|
||||||
- Vec3::new(1.0, 1.0, 1.0);
|
- Vec3::new(1.0, 1.0, 1.0);
|
||||||
}
|
}
|
||||||
point
|
point
|
||||||
|
|
|
@ -3,11 +3,12 @@ use {
|
||||||
demos::{Chunk, Demo},
|
demos::{Chunk, Demo},
|
||||||
types::{
|
types::{
|
||||||
material::{Dielectric, Lambertian, Metal},
|
material::{Dielectric, Lambertian, Metal},
|
||||||
Hitable, HitableList, Ray, Sphere, Vec3,
|
Hitable, HitableList, Ray, Sphere,
|
||||||
},
|
},
|
||||||
Camera,
|
Camera,
|
||||||
},
|
},
|
||||||
rand::Rng,
|
rand::Rng,
|
||||||
|
ultraviolet::vec::Vec3,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct FinalScene;
|
pub struct FinalScene;
|
||||||
|
@ -33,22 +34,22 @@ impl Demo for FinalScene {
|
||||||
let l = Vec3::new(4.0, 0.2, 0.0);
|
let l = Vec3::new(4.0, 0.2, 0.0);
|
||||||
|
|
||||||
for a in -11..11 {
|
for a in -11..11 {
|
||||||
let a = a as f64;
|
let a = a as f32;
|
||||||
for b in -11..11 {
|
for b in -11..11 {
|
||||||
let b = b as f64;
|
let b = b as f32;
|
||||||
let choose_material_probability = rng.gen::<f64>();
|
let choose_material_probability = rng.gen::<f32>();
|
||||||
let center = Vec3::new(a + 0.9 * rng.gen::<f64>(), 0.2, b + 0.9 * rng.gen::<f64>());
|
let center = Vec3::new(a + 0.9 * rng.gen::<f32>(), 0.2, b + 0.9 * rng.gen::<f32>());
|
||||||
|
|
||||||
if (center - l).length() > 0.9 {
|
if (center - l).mag() > 0.9 {
|
||||||
if choose_material_probability < 0.8 {
|
if choose_material_probability < 0.8 {
|
||||||
// diffuse material
|
// diffuse material
|
||||||
world.push(Box::new(Sphere::with_material(
|
world.push(Box::new(Sphere::with_material(
|
||||||
center,
|
center,
|
||||||
radius,
|
radius,
|
||||||
Box::new(Lambertian::new(Vec3::new(
|
Box::new(Lambertian::new(Vec3::new(
|
||||||
rng.gen::<f64>() * rng.gen::<f64>(),
|
rng.gen::<f32>() * rng.gen::<f32>(),
|
||||||
rng.gen::<f64>() * rng.gen::<f64>(),
|
rng.gen::<f32>() * rng.gen::<f32>(),
|
||||||
rng.gen::<f64>() * rng.gen::<f64>(),
|
rng.gen::<f32>() * rng.gen::<f32>(),
|
||||||
))),
|
))),
|
||||||
)));
|
)));
|
||||||
} else if choose_material_probability < 0.95 {
|
} else if choose_material_probability < 0.95 {
|
||||||
|
@ -58,11 +59,11 @@ impl Demo for FinalScene {
|
||||||
radius,
|
radius,
|
||||||
Box::new(Metal::with_fuzz(
|
Box::new(Metal::with_fuzz(
|
||||||
Vec3::new(
|
Vec3::new(
|
||||||
(1.0 + rng.gen::<f64>()) * 0.5,
|
(1.0 + rng.gen::<f32>()) * 0.5,
|
||||||
(1.0 + rng.gen::<f64>()) * 0.5,
|
(1.0 + rng.gen::<f32>()) * 0.5,
|
||||||
(1.0 + rng.gen::<f64>()) * 0.5,
|
(1.0 + rng.gen::<f32>()) * 0.5,
|
||||||
),
|
),
|
||||||
0.5 * rng.gen::<f64>(),
|
0.5 * rng.gen::<f32>(),
|
||||||
)),
|
)),
|
||||||
)));
|
)));
|
||||||
} else {
|
} else {
|
||||||
|
@ -96,7 +97,7 @@ impl Demo for FinalScene {
|
||||||
Some(world)
|
Some(world)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn camera(&self, aspect_ratio: f64) -> Option<Camera> {
|
fn camera(&self, aspect_ratio: f32) -> Option<Camera> {
|
||||||
let lookfrom = Vec3::new(13.0, 2.0, 3.0);
|
let lookfrom = Vec3::new(13.0, 2.0, 3.0);
|
||||||
let lookat = Vec3::new(0.0, 0.0, 0.0);
|
let lookat = Vec3::new(0.0, 0.0, 0.0);
|
||||||
let camera = Camera::new(
|
let camera = Camera::new(
|
||||||
|
@ -137,19 +138,19 @@ impl Demo for FinalScene {
|
||||||
for i in start_x..start_x + nx {
|
for i in start_x..start_x + nx {
|
||||||
let mut color = Vec3::new(0.0, 0.0, 0.0);
|
let mut color = Vec3::new(0.0, 0.0, 0.0);
|
||||||
for _s in 0..samples {
|
for _s in 0..samples {
|
||||||
let u = (i as f64 + rng.gen::<f64>()) / x as f64;
|
let u = (i as f32 + rng.gen::<f32>()) / x as f32;
|
||||||
let v = (j as f64 + rng.gen::<f64>()) / y as f64;
|
let v = (j as f32 + rng.gen::<f32>()) / y as f32;
|
||||||
|
|
||||||
let ray = camera.get_ray(u, v);
|
let ray = camera.get_ray(u, v);
|
||||||
color += calc_color(ray, &world, 0);
|
color += calc_color(ray, &world, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
color /= samples as f64;
|
color /= samples as f32;
|
||||||
|
|
||||||
// gamma 2 corrected
|
// gamma 2 corrected
|
||||||
buffer[offset] = (255.99 * color.r().sqrt()) as u8;
|
buffer[offset] = (255.99 * color.x.sqrt()) as u8;
|
||||||
buffer[offset + 1] = (255.99 * color.g().sqrt()) as u8;
|
buffer[offset + 1] = (255.99 * color.y.sqrt()) as u8;
|
||||||
buffer[offset + 2] = (255.99 * color.b().sqrt()) as u8;
|
buffer[offset + 2] = (255.99 * color.z.sqrt()) as u8;
|
||||||
offset += 4;
|
offset += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -157,7 +158,7 @@ impl Demo for FinalScene {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 {
|
fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 {
|
||||||
if let Some(hit_rec) = world.hit(&ray, 0.001, std::f64::MAX) {
|
if let Some(hit_rec) = world.hit(&ray, 0.001, std::f32::MAX) {
|
||||||
if depth >= 50 {
|
if depth >= 50 {
|
||||||
Vec3::new(0.0, 0.0, 0.0)
|
Vec3::new(0.0, 0.0, 0.0)
|
||||||
} else {
|
} else {
|
||||||
|
@ -169,8 +170,8 @@ fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let unit_direction = ray.direction().unit_vector();
|
let unit_direction = ray.direction().normalized();
|
||||||
let t = 0.5 * (unit_direction.y() + 1.0);
|
let t = 0.5 * (unit_direction.y + 1.0);
|
||||||
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
use crate::{
|
use {
|
||||||
demos::{Chunk, Demo},
|
crate::{
|
||||||
types::{Hitable, HitableList, Ray, Sphere, Vec3},
|
demos::{Chunk, Demo},
|
||||||
Camera,
|
types::{Hitable, HitableList, Ray, Sphere},
|
||||||
|
Camera,
|
||||||
|
},
|
||||||
|
ultraviolet::vec::Vec3,
|
||||||
};
|
};
|
||||||
pub struct HitableSphere;
|
pub struct HitableSphere;
|
||||||
|
|
||||||
|
@ -45,14 +48,14 @@ impl Demo for HitableSphere {
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
for j in start_y..start_y + ny {
|
for j in start_y..start_y + ny {
|
||||||
for i in start_x..start_x + nx {
|
for i in start_x..start_x + nx {
|
||||||
let u = i as f64 / x as f64;
|
let u = i as f32 / x as f32;
|
||||||
let v = j as f64 / y as f64;
|
let v = j as f32 / y as f32;
|
||||||
let ray = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v);
|
let ray = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v);
|
||||||
let color = calc_color(ray, &world);
|
let color = calc_color(ray, &world);
|
||||||
|
|
||||||
buffer[offset] = (255.99 * color.r()) as u8;
|
buffer[offset] = (255.99 * color.x) as u8;
|
||||||
buffer[offset + 1] = (255.99 * color.g()) as u8;
|
buffer[offset + 1] = (255.99 * color.y) as u8;
|
||||||
buffer[offset + 2] = (255.99 * color.b()) as u8;
|
buffer[offset + 2] = (255.99 * color.z) as u8;
|
||||||
offset += 4;
|
offset += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,19 +63,19 @@ impl Demo for HitableSphere {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_color(ray: Ray, world: &HitableList) -> Vec3 {
|
fn calc_color(ray: Ray, world: &HitableList) -> Vec3 {
|
||||||
if let Some(hit_rec) = world.hit(&ray, 0.0, std::f64::MAX) {
|
if let Some(hit_rec) = world.hit(&ray, 0.0, std::f32::MAX) {
|
||||||
// It's easier to visualise normals as unit vectors
|
// It's easier to visualise normals as unit vectors
|
||||||
// So, This trick of adding 1 to each dimension and then halving
|
// So, This trick of adding 1 to each dimension and then halving
|
||||||
// the resulting value shifts the normals from -1<->1 range to
|
// the resulting value shifts the normals from -1<->1 range to
|
||||||
// 0<->1 range
|
// 0<->1 range
|
||||||
Vec3::new(
|
Vec3::new(
|
||||||
hit_rec.normal.x() + 1.0,
|
hit_rec.normal.x + 1.0,
|
||||||
hit_rec.normal.y() + 1.0,
|
hit_rec.normal.y + 1.0,
|
||||||
hit_rec.normal.z() + 1.0,
|
hit_rec.normal.z + 1.0,
|
||||||
) * 0.5
|
) * 0.5
|
||||||
} else {
|
} else {
|
||||||
let unit_direction = ray.direction().unit_vector();
|
let unit_direction = ray.direction().normalized();
|
||||||
let t = unit_direction.y() * 0.5 + 1.0;
|
let t = unit_direction.y * 0.5 + 1.0;
|
||||||
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
pub struct LinearGradientRectangle;
|
pub struct LinearGradientRectangle;
|
||||||
|
|
||||||
use crate::{
|
use {
|
||||||
types::{HitableList, Ray, Vec3},
|
crate::{
|
||||||
Camera,
|
types::{HitableList, Ray},
|
||||||
{demos::Chunk, Demo},
|
Camera,
|
||||||
|
{demos::Chunk, Demo},
|
||||||
|
},
|
||||||
|
ultraviolet::vec::Vec3,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl Demo for LinearGradientRectangle {
|
impl Demo for LinearGradientRectangle {
|
||||||
|
@ -38,14 +41,14 @@ impl Demo for LinearGradientRectangle {
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
for j in start_y..start_y + ny {
|
for j in start_y..start_y + ny {
|
||||||
for i in start_x..start_x + nx {
|
for i in start_x..start_x + nx {
|
||||||
let u = i as f64 / x as f64;
|
let u = i as f32 / x as f32;
|
||||||
let v = j as f64 / y as f64;
|
let v = j as f32 / y as f32;
|
||||||
let ray = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v);
|
let ray = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v);
|
||||||
|
|
||||||
let c = color(ray);
|
let c = color(ray);
|
||||||
buffer[offset] = (255.99 * c.r()) as u8;
|
buffer[offset] = (255.99 * c.x) as u8;
|
||||||
buffer[offset + 1] = (255.99 * c.g()) as u8;
|
buffer[offset + 1] = (255.99 * c.y) as u8;
|
||||||
buffer[offset + 2] = (255.99 * c.b()) as u8;
|
buffer[offset + 2] = (255.99 * c.z) as u8;
|
||||||
offset += 4;
|
offset += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,7 +56,7 @@ impl Demo for LinearGradientRectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn color(ray: Ray) -> Vec3 {
|
fn color(ray: Ray) -> Vec3 {
|
||||||
let unit_direction = ray.direction().unit_vector();
|
let unit_direction = ray.direction().normalized();
|
||||||
let t = 0.5 * unit_direction.y() + 1.0;
|
let t = 0.5 * unit_direction.y + 1.0;
|
||||||
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,12 @@ use {
|
||||||
demos::{Chunk, Demo},
|
demos::{Chunk, Demo},
|
||||||
types::{
|
types::{
|
||||||
material::{Lambertian, Metal},
|
material::{Lambertian, Metal},
|
||||||
Hitable, HitableList, Ray, Sphere, Vec3,
|
Hitable, HitableList, Ray, Sphere,
|
||||||
},
|
},
|
||||||
Camera,
|
Camera,
|
||||||
},
|
},
|
||||||
rand::Rng,
|
rand::Rng,
|
||||||
|
ultraviolet::vec::Vec3,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Materials;
|
pub struct Materials;
|
||||||
|
@ -69,19 +70,19 @@ impl Demo for Materials {
|
||||||
for i in start_x..start_x + nx {
|
for i in start_x..start_x + nx {
|
||||||
let mut color = Vec3::new(0.0, 0.0, 0.0);
|
let mut color = Vec3::new(0.0, 0.0, 0.0);
|
||||||
for _s in 0..samples {
|
for _s in 0..samples {
|
||||||
let u = (i as f64 + rng.gen::<f64>()) / x as f64;
|
let u = (i as f32 + rng.gen::<f32>()) / x as f32;
|
||||||
let v = (j as f64 + rng.gen::<f64>()) / y as f64;
|
let v = (j as f32 + rng.gen::<f32>()) / y as f32;
|
||||||
|
|
||||||
let ray = camera.get_ray(u, v);
|
let ray = camera.get_ray(u, v);
|
||||||
color += calc_color(ray, world.unwrap(), 0);
|
color += calc_color(ray, world.unwrap(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
color /= samples as f64;
|
color /= samples as f32;
|
||||||
|
|
||||||
// gamma 2 corrected
|
// gamma 2 corrected
|
||||||
buffer[offset] = (255.99 * color.r().sqrt()) as u8;
|
buffer[offset] = (255.99 * color.x.sqrt()) as u8;
|
||||||
buffer[offset + 1] = (255.99 * color.g().sqrt()) as u8;
|
buffer[offset + 1] = (255.99 * color.y.sqrt()) as u8;
|
||||||
buffer[offset + 2] = (255.99 * color.b().sqrt()) as u8;
|
buffer[offset + 2] = (255.99 * color.z.sqrt()) as u8;
|
||||||
offset += 4;
|
offset += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,7 +90,7 @@ impl Demo for Materials {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 {
|
fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 {
|
||||||
if let Some(hit_rec) = world.hit(&ray, 0.001, std::f64::MAX) {
|
if let Some(hit_rec) = world.hit(&ray, 0.001, std::f32::MAX) {
|
||||||
if depth >= 50 {
|
if depth >= 50 {
|
||||||
Vec3::new(0.0, 0.0, 0.0)
|
Vec3::new(0.0, 0.0, 0.0)
|
||||||
} else {
|
} else {
|
||||||
|
@ -101,8 +102,8 @@ fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let unit_direction = ray.direction().unit_vector();
|
let unit_direction = ray.direction().normalized();
|
||||||
let t = 0.5 * (unit_direction.y() + 1.0);
|
let t = 0.5 * (unit_direction.y + 1.0);
|
||||||
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,16 +25,14 @@ pub use simple_sphere::SimpleSphere;
|
||||||
pub use surface_normal_sphere::SurfaceNormalSphere;
|
pub use surface_normal_sphere::SurfaceNormalSphere;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{types::HitableList, Camera, HORIZONTAL_PARTITION, VERTICAL_PARTITION},
|
||||||
types::{HitableList, Vec3},
|
|
||||||
Camera, HORIZONTAL_PARTITION, VERTICAL_PARTITION,
|
|
||||||
},
|
|
||||||
rayon::prelude::*,
|
rayon::prelude::*,
|
||||||
std::{
|
std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
io::Write,
|
io::Write,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
},
|
},
|
||||||
|
ultraviolet::vec::Vec3,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
|
@ -52,7 +50,7 @@ pub trait Demo: std::marker::Sync {
|
||||||
let nx = width / VERTICAL_PARTITION;
|
let nx = width / VERTICAL_PARTITION;
|
||||||
let ny = height / HORIZONTAL_PARTITION;
|
let ny = height / HORIZONTAL_PARTITION;
|
||||||
let world = self.world();
|
let world = self.world();
|
||||||
let camera = self.camera(nx as f64 / ny as f64);
|
let camera = self.camera(nx as f32 / ny as f32);
|
||||||
|
|
||||||
let buf = Arc::new(Mutex::new(buf));
|
let buf = Arc::new(Mutex::new(buf));
|
||||||
|
|
||||||
|
@ -93,7 +91,7 @@ pub trait Demo: std::marker::Sync {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn camera(&self, aspect_ratio: f64) -> Option<Camera> {
|
fn camera(&self, aspect_ratio: f32) -> Option<Camera> {
|
||||||
let lookfrom = Vec3::new(0.0, 0.0, 0.0);
|
let lookfrom = Vec3::new(0.0, 0.0, 0.0);
|
||||||
let lookat = Vec3::new(0.0, 0.0, -1.0);
|
let lookat = Vec3::new(0.0, 0.0, -1.0);
|
||||||
Some(Camera::new(
|
Some(Camera::new(
|
||||||
|
|
|
@ -3,11 +3,12 @@ use {
|
||||||
demos::{Chunk, Demo},
|
demos::{Chunk, Demo},
|
||||||
types::{
|
types::{
|
||||||
material::{Dielectric, Lambertian, Metal},
|
material::{Dielectric, Lambertian, Metal},
|
||||||
Hitable, HitableList, Ray, Sphere, Vec3,
|
Hitable, HitableList, Ray, Sphere,
|
||||||
},
|
},
|
||||||
Camera,
|
Camera,
|
||||||
},
|
},
|
||||||
rand::Rng,
|
rand::Rng,
|
||||||
|
ultraviolet::vec::Vec3,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct PositionableCamera;
|
pub struct PositionableCamera;
|
||||||
|
@ -18,7 +19,7 @@ impl Demo for PositionableCamera {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn world(&self) -> Option<HitableList> {
|
fn world(&self) -> Option<HitableList> {
|
||||||
let radius = (std::f64::consts::PI / 4.0).cos();
|
let radius = (std::f32::consts::PI / 4.0).cos();
|
||||||
Some(HitableList {
|
Some(HitableList {
|
||||||
list: vec![
|
list: vec![
|
||||||
Box::new(Sphere::with_material(
|
Box::new(Sphere::with_material(
|
||||||
|
@ -45,7 +46,7 @@ impl Demo for PositionableCamera {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn camera(&self, aspect_ratio: f64) -> Option<Camera> {
|
fn camera(&self, aspect_ratio: f32) -> Option<Camera> {
|
||||||
let lookfrom = Vec3::new(-2.0, 2.0, 1.0);
|
let lookfrom = Vec3::new(-2.0, 2.0, 1.0);
|
||||||
let lookat = Vec3::new(0.0, 0.0, -1.0);
|
let lookat = Vec3::new(0.0, 0.0, -1.0);
|
||||||
Some(Camera::new(
|
Some(Camera::new(
|
||||||
|
@ -84,17 +85,17 @@ impl Demo for PositionableCamera {
|
||||||
for i in start_x..start_x + nx {
|
for i in start_x..start_x + nx {
|
||||||
let mut color = Vec3::new(0.0, 0.0, 0.0);
|
let mut color = Vec3::new(0.0, 0.0, 0.0);
|
||||||
for _s in 0..samples {
|
for _s in 0..samples {
|
||||||
let u = (i as f64 + rng.gen::<f64>()) / x as f64;
|
let u = (i as f32 + rng.gen::<f32>()) / x as f32;
|
||||||
let v = (j as f64 + rng.gen::<f64>()) / y as f64;
|
let v = (j as f32 + rng.gen::<f32>()) / y as f32;
|
||||||
|
|
||||||
let ray = camera.get_ray(u, v);
|
let ray = camera.get_ray(u, v);
|
||||||
color += calc_color(ray, &world, 0);
|
color += calc_color(ray, &world, 0);
|
||||||
}
|
}
|
||||||
color /= samples as f64;
|
color /= samples as f32;
|
||||||
// gamma 2 corrected
|
// gamma 2 corrected
|
||||||
buffer[offset] = (255.99 * color.r().sqrt()) as u8;
|
buffer[offset] = (255.99 * color.x.sqrt()) as u8;
|
||||||
buffer[offset + 1] = (255.99 * color.g().sqrt()) as u8;
|
buffer[offset + 1] = (255.99 * color.y.sqrt()) as u8;
|
||||||
buffer[offset + 2] = (255.99 * color.b().sqrt()) as u8;
|
buffer[offset + 2] = (255.99 * color.z.sqrt()) as u8;
|
||||||
offset += 4;
|
offset += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,7 +103,7 @@ impl Demo for PositionableCamera {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 {
|
fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 {
|
||||||
if let Some(hit_rec) = world.hit(&ray, 0.001, std::f64::MAX) {
|
if let Some(hit_rec) = world.hit(&ray, 0.001, std::f32::MAX) {
|
||||||
if depth >= 50 {
|
if depth >= 50 {
|
||||||
Vec3::new(0.0, 0.0, 0.0)
|
Vec3::new(0.0, 0.0, 0.0)
|
||||||
} else {
|
} else {
|
||||||
|
@ -114,8 +115,8 @@ fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let unit_direction = ray.direction().unit_vector();
|
let unit_direction = ray.direction().normalized();
|
||||||
let t = 0.5 * (unit_direction.y() + 1.0);
|
let t = 0.5 * (unit_direction.y + 1.0);
|
||||||
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
demos::{Chunk, Demo},
|
demos::{Chunk, Demo},
|
||||||
types::{Hitable, HitableList, Ray, Sphere, Vec3},
|
types::{Hitable, HitableList, Ray, Sphere},
|
||||||
Camera,
|
Camera,
|
||||||
},
|
},
|
||||||
rand::Rng,
|
rand::Rng,
|
||||||
|
ultraviolet::vec::Vec3,
|
||||||
};
|
};
|
||||||
pub struct SimpleAntialiasing;
|
pub struct SimpleAntialiasing;
|
||||||
|
|
||||||
|
@ -47,16 +48,16 @@ impl Demo for SimpleAntialiasing {
|
||||||
for i in start_x..start_x + nx {
|
for i in start_x..start_x + nx {
|
||||||
let mut color = Vec3::new(0.0, 0.0, 0.0);
|
let mut color = Vec3::new(0.0, 0.0, 0.0);
|
||||||
for _s in 0..samples {
|
for _s in 0..samples {
|
||||||
let u = (i as f64 + rng.gen::<f64>()) / x as f64;
|
let u = (i as f32 + rng.gen::<f32>()) / x as f32;
|
||||||
let v = (j as f64 + rng.gen::<f64>()) / y as f64;
|
let v = (j as f32 + rng.gen::<f32>()) / y as f32;
|
||||||
|
|
||||||
let r = camera.get_ray(u, v);
|
let r = camera.get_ray(u, v);
|
||||||
color += calc_color(r, world.unwrap());
|
color += calc_color(r, world.unwrap());
|
||||||
}
|
}
|
||||||
color /= samples as f64;
|
color /= samples as f32;
|
||||||
buffer[offset] = (255.99 * color.r()) as u8;
|
buffer[offset] = (255.99 * color.x) as u8;
|
||||||
buffer[offset + 1] = (255.99 * color.g()) as u8;
|
buffer[offset + 1] = (255.99 * color.y) as u8;
|
||||||
buffer[offset + 2] = (255.99 * color.b()) as u8;
|
buffer[offset + 2] = (255.99 * color.z) as u8;
|
||||||
offset += 4;
|
offset += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,19 +65,19 @@ impl Demo for SimpleAntialiasing {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_color(ray: Ray, world: &HitableList) -> Vec3 {
|
fn calc_color(ray: Ray, world: &HitableList) -> Vec3 {
|
||||||
if let Some(hit_rec) = world.hit(&ray, 0.0, std::f64::MAX) {
|
if let Some(hit_rec) = world.hit(&ray, 0.0, std::f32::MAX) {
|
||||||
// It's easier to visualise normals as unit vectors
|
// It's easier to visualise normals as unit vectors
|
||||||
// So, This trick of adding 1 to each dimension and then halving
|
// So, This trick of adding 1 to each dimension and then halving
|
||||||
// the resulting value shifts the normals from -1<->1 range to
|
// the resulting value shifts the normals from -1<->1 range to
|
||||||
// 0<->1 range
|
// 0<->1 range
|
||||||
Vec3::new(
|
Vec3::new(
|
||||||
hit_rec.normal.x() + 1.0,
|
hit_rec.normal.x + 1.0,
|
||||||
hit_rec.normal.y() + 1.0,
|
hit_rec.normal.y + 1.0,
|
||||||
hit_rec.normal.z() + 1.0,
|
hit_rec.normal.z + 1.0,
|
||||||
) * 0.5
|
) * 0.5
|
||||||
} else {
|
} else {
|
||||||
let unit_direction = ray.direction().unit_vector();
|
let unit_direction = ray.direction().normalized();
|
||||||
let t = unit_direction.y() * 0.5 + 1.0;
|
let t = unit_direction.y * 0.5 + 1.0;
|
||||||
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ impl Demo for SimpleRectangle {
|
||||||
|
|
||||||
for j in start_y..start_y + ny {
|
for j in start_y..start_y + ny {
|
||||||
for i in start_x..start_x + nx {
|
for i in start_x..start_x + nx {
|
||||||
let color = [i as f64 / x as f64, j as f64 / y as f64, 0.2];
|
let color = [i as f32 / x as f32, j as f32 / y as f32, 0.2];
|
||||||
buffer[offset] = (255.99 * color[0]) as u8;
|
buffer[offset] = (255.99 * color[0]) as u8;
|
||||||
buffer[offset + 1] = (255.99 * color[1]) as u8;
|
buffer[offset + 1] = (255.99 * color[1]) as u8;
|
||||||
buffer[offset + 2] = (255.99 * color[2]) as u8;
|
buffer[offset + 2] = (255.99 * color[2]) as u8;
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
use crate::{
|
use {
|
||||||
demos::{Chunk, Demo},
|
crate::{
|
||||||
types::{HitableList, Ray, Vec3},
|
demos::{Chunk, Demo},
|
||||||
Camera,
|
types::{HitableList, Ray},
|
||||||
|
Camera,
|
||||||
|
},
|
||||||
|
ultraviolet::vec::Vec3,
|
||||||
};
|
};
|
||||||
|
|
||||||
const RADIUS: f64 = 0.5;
|
const RADIUS: f32 = 0.5;
|
||||||
|
|
||||||
pub struct SimpleSphere;
|
pub struct SimpleSphere;
|
||||||
|
|
||||||
|
@ -51,21 +54,21 @@ impl Demo for SimpleSphere {
|
||||||
for i in start_x..start_x + nx {
|
for i in start_x..start_x + nx {
|
||||||
// relative offsets
|
// relative offsets
|
||||||
// current position to total width/length
|
// current position to total width/length
|
||||||
let u = i as f64 / x as f64;
|
let u = i as f32 / x as f32;
|
||||||
let v = j as f64 / y as f64;
|
let v = j as f32 / y as f32;
|
||||||
|
|
||||||
let ray = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v);
|
let ray = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v);
|
||||||
let color = calc_color(ray);
|
let color = calc_color(ray);
|
||||||
buffer[offset] = (255.99 * color.r()) as u8;
|
buffer[offset] = (255.99 * color.x) as u8;
|
||||||
buffer[offset + 1] = (255.99 * color.g()) as u8;
|
buffer[offset + 1] = (255.99 * color.y) as u8;
|
||||||
buffer[offset + 2] = (255.99 * color.b()) as u8;
|
buffer[offset + 2] = (255.99 * color.z) as u8;
|
||||||
offset += 4;
|
offset += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ray_hit_sphere(center: Vec3, radius: f64, ray: &Ray) -> bool {
|
fn ray_hit_sphere(center: Vec3, radius: f32, ray: &Ray) -> bool {
|
||||||
// For a point to lie on a circle,
|
// For a point to lie on a circle,
|
||||||
// (x-cx)^2 + (y-cy)^2 + (z-cz)^2 = R * R
|
// (x-cx)^2 + (y-cy)^2 + (z-cz)^2 = R * R
|
||||||
// should hold true. This equation can be rewritten as,
|
// should hold true. This equation can be rewritten as,
|
||||||
|
@ -81,9 +84,9 @@ fn ray_hit_sphere(center: Vec3, radius: f64, ray: &Ray) -> bool {
|
||||||
// Vector from circle center to point
|
// Vector from circle center to point
|
||||||
let ac = ray.origin() - center;
|
let ac = ray.origin() - center;
|
||||||
|
|
||||||
let a = ray.direction().dot(&ray.direction());
|
let a = ray.direction().dot(ray.direction());
|
||||||
let b = 2.0 * ray.direction().dot(&ac);
|
let b = 2.0 * ray.direction().dot(ac);
|
||||||
let c = ac.dot(&ac) - radius * radius;
|
let c = ac.dot(ac) - radius * radius;
|
||||||
let discriminant = b * b - 4.0 * a * c;
|
let discriminant = b * b - 4.0 * a * c;
|
||||||
|
|
||||||
discriminant > 0.0
|
discriminant > 0.0
|
||||||
|
@ -100,10 +103,10 @@ fn calc_color(ray: Ray) -> Vec3 {
|
||||||
// This will result in a sphere that is red in color
|
// This will result in a sphere that is red in color
|
||||||
return Vec3::new(1.0, 0.0, 0.0);
|
return Vec3::new(1.0, 0.0, 0.0);
|
||||||
}
|
}
|
||||||
let unit_direction = ray.direction().unit_vector();
|
let unit_direction = ray.direction().normalized();
|
||||||
// For rays that don't hit sphere, It'll paint the gradient as the background
|
// For rays that don't hit sphere, It'll paint the gradient as the background
|
||||||
// Linear gradient depends on y
|
// Linear gradient depends on y
|
||||||
let t = 0.5 * unit_direction.y() + 1.0;
|
let t = 0.5 * unit_direction.y + 1.0;
|
||||||
|
|
||||||
// start color + end color
|
// start color + end color
|
||||||
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
use crate::{
|
use {
|
||||||
demos::{Chunk, Demo},
|
crate::{
|
||||||
types::{HitableList, Ray, Vec3},
|
demos::{Chunk, Demo},
|
||||||
Camera,
|
types::{HitableList, Ray},
|
||||||
|
Camera,
|
||||||
|
},
|
||||||
|
ultraviolet::vec::Vec3,
|
||||||
};
|
};
|
||||||
|
|
||||||
const RADIUS: f64 = 0.5;
|
const RADIUS: f32 = 0.5;
|
||||||
pub struct SurfaceNormalSphere;
|
pub struct SurfaceNormalSphere;
|
||||||
|
|
||||||
impl Demo for SurfaceNormalSphere {
|
impl Demo for SurfaceNormalSphere {
|
||||||
|
@ -48,14 +51,14 @@ impl Demo for SurfaceNormalSphere {
|
||||||
|
|
||||||
for j in start_y..start_y + ny {
|
for j in start_y..start_y + ny {
|
||||||
for i in start_x..start_x + nx {
|
for i in start_x..start_x + nx {
|
||||||
let u = i as f64 / x as f64;
|
let u = i as f32 / x as f32;
|
||||||
let v = j as f64 / y as f64;
|
let v = j as f32 / y as f32;
|
||||||
|
|
||||||
let ray = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v);
|
let ray = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v);
|
||||||
let color = calculate_color(ray);
|
let color = calculate_color(ray);
|
||||||
let ir = (255.99 * color.r()) as u8;
|
let ir = (255.99 * color.x) as u8;
|
||||||
let ig = (255.99 * color.g()) as u8;
|
let ig = (255.99 * color.y) as u8;
|
||||||
let ib = (255.99 * color.b()) as u8;
|
let ib = (255.99 * color.z) as u8;
|
||||||
|
|
||||||
buffer[offset] = ir;
|
buffer[offset] = ir;
|
||||||
buffer[offset + 1] = ig;
|
buffer[offset + 1] = ig;
|
||||||
|
@ -69,23 +72,23 @@ impl Demo for SurfaceNormalSphere {
|
||||||
fn calculate_color(ray: Ray) -> Vec3 {
|
fn calculate_color(ray: Ray) -> Vec3 {
|
||||||
let t = ray_hit_sphere(Vec3::new(0.0, 0.0, -1.0), RADIUS, &ray);
|
let t = ray_hit_sphere(Vec3::new(0.0, 0.0, -1.0), RADIUS, &ray);
|
||||||
if t > 0.0 {
|
if t > 0.0 {
|
||||||
let n = (ray.point_at_parameter(t) - Vec3::new(0.0, 0.0, -1.0)).unit_vector();
|
let n = (ray.point_at_parameter(t) - Vec3::new(0.0, 0.0, -1.0)).normalized();
|
||||||
return Vec3::new(n.x() + 1.0, n.y() + 1.0, n.z() + 1.0) * 0.5;
|
return Vec3::new(n.x + 1.0, n.y + 1.0, n.z + 1.0) * 0.5;
|
||||||
}
|
}
|
||||||
let unit_direction = ray.direction().unit_vector();
|
let unit_direction = ray.direction().normalized();
|
||||||
// For rays that don't hit sphere, It'll paint the gradient as the background
|
// For rays that don't hit sphere, It'll paint the gradient as the background
|
||||||
// Linear gradient depends on y
|
// Linear gradient depends on y
|
||||||
let t = 0.5 * unit_direction.y() + 1.0;
|
let t = 0.5 * unit_direction.y + 1.0;
|
||||||
|
|
||||||
// start color + end color
|
// start color + end color
|
||||||
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ray_hit_sphere(center: Vec3, radius: f64, ray: &Ray) -> f64 {
|
fn ray_hit_sphere(center: Vec3, radius: f32, ray: &Ray) -> f32 {
|
||||||
let pc = ray.origin() - center;
|
let pc = ray.origin() - center;
|
||||||
let a = ray.direction().dot(&ray.direction());
|
let a = ray.direction().dot(ray.direction());
|
||||||
let b = 2.0 * pc.dot(&ray.direction());
|
let b = 2.0 * pc.dot(ray.direction());
|
||||||
let c = pc.dot(&pc) - radius * radius;
|
let c = pc.dot(pc) - radius * radius;
|
||||||
let discriminant = b * b - 4.0 * a * c;
|
let discriminant = b * b - 4.0 * a * c;
|
||||||
|
|
||||||
if discriminant >= 0.0 {
|
if discriminant >= 0.0 {
|
||||||
|
|
|
@ -143,7 +143,7 @@ fn main() -> Result<(), String> {
|
||||||
println!(
|
println!(
|
||||||
"Demo {} Time Taken(s) = {}",
|
"Demo {} Time Taken(s) = {}",
|
||||||
active_demo.name(),
|
active_demo.name(),
|
||||||
now.elapsed().as_secs_f64()
|
now.elapsed().as_secs_f32()
|
||||||
);
|
);
|
||||||
|
|
||||||
texture.update(None, &buffer, width * 4).unwrap();
|
texture.update(None, &buffer, width * 4).unwrap();
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use crate::types::{Material, Ray, Vec3};
|
use {
|
||||||
|
crate::types::{Material, Ray},
|
||||||
|
ultraviolet::vec::Vec3,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct HitRecord<'a> {
|
pub struct HitRecord<'a> {
|
||||||
/// Rays are represented by A + t * B
|
/// Rays are represented by A + t * B
|
||||||
|
@ -8,7 +11,7 @@ pub struct HitRecord<'a> {
|
||||||
/// t is the point at which a ray intersected another object.
|
/// t is the point at which a ray intersected another object.
|
||||||
/// As in, If we put this value of t in A + t * B equation, We'll get the exact
|
/// As in, If we put this value of t in A + t * B equation, We'll get the exact
|
||||||
/// point at which a ray intersects some other object
|
/// point at which a ray intersects some other object
|
||||||
pub t: f64,
|
pub t: f32,
|
||||||
/// Ray object otherwise is represented by the Source/Destination points
|
/// Ray object otherwise is represented by the Source/Destination points
|
||||||
/// p is what we get when we perform the operation, A + t * B
|
/// p is what we get when we perform the operation, A + t * B
|
||||||
/// i.e. A vector from Ray source to the point t
|
/// i.e. A vector from Ray source to the point t
|
||||||
|
@ -22,7 +25,7 @@ pub struct HitRecord<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Hitable: Send + Sync {
|
pub trait Hitable: Send + Sync {
|
||||||
fn hit(&self, _ray: &Ray, _t_min: f64, _t_max: f64) -> Option<HitRecord> {
|
fn hit(&self, _ray: &Ray, _t_min: f32, _t_max: f32) -> Option<HitRecord> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ pub struct HitableList {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hitable for HitableList {
|
impl Hitable for HitableList {
|
||||||
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
fn hit(&self, ray: &Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
|
||||||
let mut closest_so_far = t_max;
|
let mut closest_so_far = t_max;
|
||||||
let mut hit_rec: Option<HitRecord> = None;
|
let mut hit_rec: Option<HitRecord> = None;
|
||||||
for obj in &self.list {
|
for obj in &self.list {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::types::{HitRecord, Ray, Vec3},
|
crate::types::{HitRecord, Ray},
|
||||||
rand::Rng,
|
rand::Rng,
|
||||||
|
ultraviolet::vec::Vec3,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait Material: Send + Sync {
|
pub trait Material: Send + Sync {
|
||||||
|
@ -29,14 +30,14 @@ impl Material for Lambertian {
|
||||||
|
|
||||||
pub struct Metal {
|
pub struct Metal {
|
||||||
albedo: Vec3,
|
albedo: Vec3,
|
||||||
fuzz: f64,
|
fuzz: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Metal {
|
impl Metal {
|
||||||
pub fn new(albedo: Vec3) -> Self {
|
pub fn new(albedo: Vec3) -> Self {
|
||||||
Self { albedo, fuzz: 0.0 }
|
Self { albedo, fuzz: 0.0 }
|
||||||
}
|
}
|
||||||
pub fn with_fuzz(albedo: Vec3, fuzz: f64) -> Self {
|
pub fn with_fuzz(albedo: Vec3, fuzz: f32) -> Self {
|
||||||
Self { albedo, fuzz }
|
Self { albedo, fuzz }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,13 +46,13 @@ impl Material for Metal {
|
||||||
fn scatter(&self, ray_in: &Ray, hit_rec: &HitRecord) -> (Vec3, Option<Ray>) {
|
fn scatter(&self, ray_in: &Ray, hit_rec: &HitRecord) -> (Vec3, Option<Ray>) {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
let reflected_ray = reflect(ray_in.direction().unit_vector(), hit_rec.normal);
|
let reflected_ray = reflect(ray_in.direction().normalized(), hit_rec.normal);
|
||||||
let scattered_ray = Ray::new(
|
let scattered_ray = Ray::new(
|
||||||
hit_rec.p,
|
hit_rec.p,
|
||||||
reflected_ray + random_point_in_unit_sphere(&mut rng) * self.fuzz,
|
reflected_ray + random_point_in_unit_sphere(&mut rng) * self.fuzz,
|
||||||
);
|
);
|
||||||
|
|
||||||
if scattered_ray.direction().dot(&hit_rec.normal) > 0.0 {
|
if scattered_ray.direction().dot(hit_rec.normal) > 0.0 {
|
||||||
(self.albedo, Some(scattered_ray))
|
(self.albedo, Some(scattered_ray))
|
||||||
} else {
|
} else {
|
||||||
(self.albedo, None)
|
(self.albedo, None)
|
||||||
|
@ -60,11 +61,11 @@ impl Material for Metal {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Dielectric {
|
pub struct Dielectric {
|
||||||
reflection_index: f64,
|
reflection_index: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dielectric {
|
impl Dielectric {
|
||||||
pub fn new(reflection_index: f64) -> Self {
|
pub fn new(reflection_index: f32) -> Self {
|
||||||
Self { reflection_index }
|
Self { reflection_index }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,26 +77,25 @@ impl Material for Dielectric {
|
||||||
let attenuation = Vec3::new(1.0, 1.0, 1.0);
|
let attenuation = Vec3::new(1.0, 1.0, 1.0);
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
let (outward_normal, ni_over_nt, cosine) = if ray_in.direction().dot(&hit_rec.normal) > 0.0
|
let (outward_normal, ni_over_nt, cosine) = if ray_in.direction().dot(hit_rec.normal) > 0.0 {
|
||||||
{
|
|
||||||
(
|
(
|
||||||
-hit_rec.normal,
|
-hit_rec.normal,
|
||||||
self.reflection_index,
|
self.reflection_index,
|
||||||
(ray_in.direction().dot(&hit_rec.normal) * self.reflection_index)
|
(ray_in.direction().dot(hit_rec.normal) * self.reflection_index)
|
||||||
/ ray_in.direction().length(),
|
/ ray_in.direction().mag(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
hit_rec.normal,
|
hit_rec.normal,
|
||||||
1.0 / self.reflection_index,
|
1.0 / self.reflection_index,
|
||||||
(-ray_in.direction().dot(&hit_rec.normal)) / ray_in.direction().length(),
|
(-ray_in.direction().dot(hit_rec.normal)) / ray_in.direction().mag(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(refracted_ray) = refract(ray_in.direction(), outward_normal, ni_over_nt) {
|
if let Some(refracted_ray) = refract(ray_in.direction(), outward_normal, ni_over_nt) {
|
||||||
let reflect_prob = schlick(cosine, self.reflection_index);
|
let reflect_prob = schlick(cosine, self.reflection_index);
|
||||||
|
|
||||||
if rng.gen::<f64>() < reflect_prob {
|
if rng.gen::<f32>() < reflect_prob {
|
||||||
(attenuation, Some(Ray::new(hit_rec.p, reflected_ray)))
|
(attenuation, Some(Ray::new(hit_rec.p, reflected_ray)))
|
||||||
} else {
|
} else {
|
||||||
(attenuation, Some(Ray::new(hit_rec.p, refracted_ray)))
|
(attenuation, Some(Ray::new(hit_rec.p, refracted_ray)))
|
||||||
|
@ -108,20 +108,20 @@ impl Material for Dielectric {
|
||||||
|
|
||||||
// Christophe Schlick's Polynomial approximation to figure out reflectivity as the angle changes
|
// Christophe Schlick's Polynomial approximation to figure out reflectivity as the angle changes
|
||||||
// See Fresnel Equations, https://en.wikipedia.org/wiki/Fresnel_equations
|
// See Fresnel Equations, https://en.wikipedia.org/wiki/Fresnel_equations
|
||||||
fn schlick(cosine: f64, reflection_index: f64) -> f64 {
|
fn schlick(cosine: f32, reflection_index: f32) -> f32 {
|
||||||
let mut r0 = (1.0 - reflection_index) / (1.0 + reflection_index);
|
let mut r0 = (1.0 - reflection_index) / (1.0 + reflection_index);
|
||||||
r0 = r0 * r0;
|
r0 = r0 * r0;
|
||||||
r0 + (1.0 - r0) * (1.0 - cosine).powf(5.0)
|
r0 + (1.0 - r0) * (1.0 - cosine).powf(5.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reflect(incident: Vec3, normal: Vec3) -> Vec3 {
|
fn reflect(incident: Vec3, normal: Vec3) -> Vec3 {
|
||||||
incident - normal * incident.dot(&normal) * 2.0
|
incident - normal * incident.dot(normal) * 2.0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Snell's Law
|
// Snell's Law
|
||||||
fn refract(incident: Vec3, normal: Vec3, ni_over_nt: f64) -> Option<Vec3> {
|
fn refract(incident: Vec3, normal: Vec3, ni_over_nt: f32) -> Option<Vec3> {
|
||||||
let uv = incident.unit_vector();
|
let uv = incident.normalized();
|
||||||
let dt = uv.dot(&normal);
|
let dt = uv.dot(normal);
|
||||||
let discriminant = 1.0 - ni_over_nt * ni_over_nt * (1.0 - dt * dt);
|
let discriminant = 1.0 - ni_over_nt * ni_over_nt * (1.0 - dt * dt);
|
||||||
if discriminant > 0.0 {
|
if discriminant > 0.0 {
|
||||||
Some((uv - normal * dt) * ni_over_nt - normal * discriminant.sqrt())
|
Some((uv - normal * dt) * ni_over_nt - normal * discriminant.sqrt())
|
||||||
|
@ -131,10 +131,10 @@ fn refract(incident: Vec3, normal: Vec3, ni_over_nt: f64) -> Option<Vec3> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_point_in_unit_sphere(rng: &mut rand::rngs::ThreadRng) -> Vec3 {
|
fn random_point_in_unit_sphere(rng: &mut rand::rngs::ThreadRng) -> Vec3 {
|
||||||
let mut point = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), rng.gen::<f64>()) * 2.0
|
let mut point = Vec3::new(rng.gen::<f32>(), rng.gen::<f32>(), rng.gen::<f32>()) * 2.0
|
||||||
- Vec3::new(1.0, 1.0, 1.0);
|
- Vec3::new(1.0, 1.0, 1.0);
|
||||||
while point.sq_len() >= 1.0 {
|
while point.mag_sq() >= 1.0 {
|
||||||
point = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), rng.gen::<f64>()) * 2.0
|
point = Vec3::new(rng.gen::<f32>(), rng.gen::<f32>(), rng.gen::<f32>()) * 2.0
|
||||||
- Vec3::new(1.0, 1.0, 1.0);
|
- Vec3::new(1.0, 1.0, 1.0);
|
||||||
}
|
}
|
||||||
point
|
point
|
||||||
|
|
|
@ -3,11 +3,9 @@ mod hitable_list;
|
||||||
pub mod material;
|
pub mod material;
|
||||||
mod ray;
|
mod ray;
|
||||||
mod sphere;
|
mod sphere;
|
||||||
mod vec3;
|
|
||||||
|
|
||||||
pub use hitable::{HitRecord, Hitable};
|
pub use hitable::{HitRecord, Hitable};
|
||||||
pub use hitable_list::HitableList;
|
pub use hitable_list::HitableList;
|
||||||
pub use material::Material;
|
pub use material::Material;
|
||||||
pub use ray::Ray;
|
pub use ray::Ray;
|
||||||
pub use sphere::Sphere;
|
pub use sphere::Sphere;
|
||||||
pub use vec3::Vec3;
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::types::Vec3;
|
use ultraviolet::vec::Vec3;
|
||||||
|
|
||||||
pub struct Ray {
|
pub struct Ray {
|
||||||
a: Vec3,
|
a: Vec3,
|
||||||
|
@ -18,7 +18,7 @@ impl Ray {
|
||||||
self.b
|
self.b
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn point_at_parameter(&self, t: f64) -> Vec3 {
|
pub fn point_at_parameter(&self, t: f32) -> Vec3 {
|
||||||
self.a + self.b * t
|
self.a + self.b * t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,23 @@
|
||||||
use crate::types::{HitRecord, Hitable, Material, Ray, Vec3};
|
use {
|
||||||
|
crate::types::{HitRecord, Hitable, Material, Ray},
|
||||||
|
ultraviolet::vec::Vec3,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct Sphere {
|
pub struct Sphere {
|
||||||
center: Vec3,
|
center: Vec3,
|
||||||
radius: f64,
|
radius: f32,
|
||||||
material: Option<Box<dyn Material>>,
|
material: Option<Box<dyn Material>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sphere {
|
impl Sphere {
|
||||||
pub fn new(center: Vec3, radius: f64) -> Self {
|
pub fn new(center: Vec3, radius: f32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
center,
|
center,
|
||||||
radius,
|
radius,
|
||||||
material: None,
|
material: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn with_material(center: Vec3, radius: f64, material: Box<dyn Material>) -> Self {
|
pub fn with_material(center: Vec3, radius: f32, material: Box<dyn Material>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
center,
|
center,
|
||||||
radius,
|
radius,
|
||||||
|
@ -24,11 +27,11 @@ impl Sphere {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hitable for Sphere {
|
impl Hitable for Sphere {
|
||||||
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
fn hit(&self, ray: &Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
|
||||||
let oc = ray.origin() - self.center;
|
let oc = ray.origin() - self.center;
|
||||||
let a = ray.direction().dot(&ray.direction());
|
let a = ray.direction().dot(ray.direction());
|
||||||
let b = oc.dot(&ray.direction());
|
let b = oc.dot(ray.direction());
|
||||||
let c = oc.dot(&oc) - self.radius * self.radius;
|
let c = oc.dot(oc) - self.radius * self.radius;
|
||||||
|
|
||||||
// The discriminant is calculated using b^2 - 4 * a * c
|
// The discriminant is calculated using b^2 - 4 * a * c
|
||||||
// but in this specific case, If we put the equation in the
|
// but in this specific case, If we put the equation in the
|
||||||
|
|
|
@ -1,192 +0,0 @@
|
||||||
use std::{
|
|
||||||
fmt::{Display, Formatter, Result as FmtResult},
|
|
||||||
ops::{Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Vec3([f64; 3]);
|
|
||||||
|
|
||||||
impl Vec3 {
|
|
||||||
#[inline]
|
|
||||||
pub const fn new(a: f64, b: f64, c: f64) -> Vec3 {
|
|
||||||
Vec3([a, b, c])
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn x(&self) -> f64 {
|
|
||||||
self[0]
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn y(&self) -> f64 {
|
|
||||||
self[1]
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn z(&self) -> f64 {
|
|
||||||
self[2]
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn r(&self) -> f64 {
|
|
||||||
self[0]
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn g(&self) -> f64 {
|
|
||||||
self[1]
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn b(&self) -> f64 {
|
|
||||||
self[2]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn length(&self) -> f64 {
|
|
||||||
self.sq_len().sqrt()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn sq_len(&self) -> f64 {
|
|
||||||
self[0] * self[0] + self[1] * self[1] + self[2] * self[2]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn dot(&self, v: &Vec3) -> f64 {
|
|
||||||
self[0] * v[0] + self[1] * v[1] + self[2] * v[2]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn cross(&self, v: &Vec3) -> Vec3 {
|
|
||||||
Vec3([
|
|
||||||
self[1] * v[2] - self[2] * v[1],
|
|
||||||
self[2] * v[0] - self[0] * v[2],
|
|
||||||
self[0] * v[1] - self[1] * v[0],
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn make_unit_vector(&mut self) {
|
|
||||||
let k = 1.0f64 / (self[0] * self[0] + self[1] * self[1] + self[2] * self[2]);
|
|
||||||
self[0] *= k;
|
|
||||||
self[1] *= k;
|
|
||||||
self[2] *= k;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn unit_vector(&self) -> Vec3 {
|
|
||||||
let length = self.length();
|
|
||||||
Vec3([self[0] / length, self[1] / length, self[2] / length])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Add for Vec3 {
|
|
||||||
type Output = Vec3;
|
|
||||||
|
|
||||||
fn add(self, o: Vec3) -> Vec3 {
|
|
||||||
Vec3([self[0] + o[0], self[1] + o[1], self[2] + o[2]])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AddAssign for Vec3 {
|
|
||||||
fn add_assign(&mut self, o: Vec3) {
|
|
||||||
self.0[0] += o.0[0];
|
|
||||||
self.0[1] += o.0[1];
|
|
||||||
self.0[2] += o.0[2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sub for Vec3 {
|
|
||||||
type Output = Vec3;
|
|
||||||
|
|
||||||
fn sub(self, o: Vec3) -> Vec3 {
|
|
||||||
Vec3([self[0] - o[0], self[1] - o[1], self[2] - o[2]])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SubAssign for Vec3 {
|
|
||||||
fn sub_assign(&mut self, o: Vec3) {
|
|
||||||
self[0] -= o[0];
|
|
||||||
self[1] -= o[1];
|
|
||||||
self[2] -= o[2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Neg for Vec3 {
|
|
||||||
type Output = Vec3;
|
|
||||||
|
|
||||||
fn neg(self) -> Vec3 {
|
|
||||||
Vec3([-self[0], -self[1], -self[2]])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MulAssign<Vec3> for Vec3 {
|
|
||||||
fn mul_assign(&mut self, o: Vec3) {
|
|
||||||
self[0] *= o[0];
|
|
||||||
self[1] *= o[1];
|
|
||||||
self[2] *= o[2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MulAssign<f64> for Vec3 {
|
|
||||||
fn mul_assign(&mut self, o: f64) {
|
|
||||||
self[0] *= o;
|
|
||||||
self[1] *= o;
|
|
||||||
self[2] *= o;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<f64> for Vec3 {
|
|
||||||
type Output = Vec3;
|
|
||||||
fn mul(self, o: f64) -> Vec3 {
|
|
||||||
Vec3([self[0] * o, self[1] * o, self[2] * o])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<Vec3> for Vec3 {
|
|
||||||
type Output = Vec3;
|
|
||||||
fn mul(self, o: Vec3) -> Vec3 {
|
|
||||||
Vec3([self[0] * o[0], self[1] * o[1], self[2] * o[2]])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Div<Vec3> for Vec3 {
|
|
||||||
type Output = Vec3;
|
|
||||||
|
|
||||||
fn div(self, o: Vec3) -> Vec3 {
|
|
||||||
Vec3([self[0] / o[0], self[1] / o[1], self[2] / o[2]])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Div<f64> for Vec3 {
|
|
||||||
type Output = Vec3;
|
|
||||||
|
|
||||||
fn div(self, o: f64) -> Vec3 {
|
|
||||||
let o = 1.0 / o;
|
|
||||||
Vec3([self[0] * o, self[1] * o, self[2] * o])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DivAssign<f64> for Vec3 {
|
|
||||||
fn div_assign(&mut self, o: f64) {
|
|
||||||
let o = 1.0 / o;
|
|
||||||
self.0[0] *= o;
|
|
||||||
self.0[1] *= o;
|
|
||||||
self.0[2] *= o;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Index<usize> for Vec3 {
|
|
||||||
type Output = f64;
|
|
||||||
|
|
||||||
fn index(&self, q: usize) -> &f64 {
|
|
||||||
&self.0[q]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IndexMut<usize> for Vec3 {
|
|
||||||
fn index_mut(&mut self, q: usize) -> &mut f64 {
|
|
||||||
&mut self.0[q]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Vec3 {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> FmtResult {
|
|
||||||
f.write_fmt(format_args!("{} {} {}", self[0], self[1], self[2]))
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user