Compare commits

..

No commits in common. "ultraviolet-vec3" and "master" have entirely different histories.

25 changed files with 411 additions and 263 deletions

25
Cargo.lock generated
View File

@ -12,12 +12,6 @@ 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"
@ -213,7 +207,6 @@ dependencies = [
"rand", "rand",
"rayon", "rayon",
"sdl2", "sdl2",
"ultraviolet",
] ]
[[package]] [[package]]
@ -268,26 +261,8 @@ 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",
]

View File

@ -8,4 +8,3 @@ 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"

View File

@ -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 f32 / ny as f32); let camera = self.camera(nx as f64 / ny as f64);
let start_y = j * ny; let start_y = j * ny;
let start_x = i * nx; let start_x = i * nx;

View File

@ -1,11 +1,14 @@
use {crate::types::Ray, rand::Rng, ultraviolet::vec::Vec3}; use {
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: f32, lens_radius: f64,
// position vectors // position vectors
u: Vec3, u: Vec3,
@ -23,20 +26,20 @@ impl Camera {
look_from: Vec3, look_from: Vec3,
look_at: Vec3, look_at: Vec3,
v_up: Vec3, v_up: Vec3,
vertical_fov: f32, vertical_fov: f64,
aspect: f32, aspect: f64,
aperture: f32, aperture: f64,
focus_distance: f32, focus_distance: f64,
) -> Self { ) -> Self {
// convert degree to radian // convert degree to radian
let angle = vertical_fov * std::f32::consts::PI / 180.0; let angle = vertical_fov * std::f64::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).normalized(); let w = (look_from - look_at).unit_vector();
let u = v_up.cross(w).normalized(); let u = v_up.cross(&w).unit_vector();
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
@ -58,10 +61,10 @@ impl Camera {
} }
} }
pub fn get_ray(&self, u: f32, v: f32) -> Ray { pub fn get_ray(&self, u: f64, v: f64) -> 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,
@ -70,10 +73,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::<f32>(), rng.gen::<f32>(), 0.0) * 2.0 - Vec3::new(1.0, 1.0, 0.0); let mut p = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), 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::<f32>(), rng.gen::<f32>(), 0.0) * 2.0 - Vec3::new(1.0, 0.0, 0.0); p = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), 0.0) * 2.0 - Vec3::new(1.0, 0.0, 0.0);
} }
p p
} }

View File

@ -3,12 +3,11 @@ use {
demos::{Chunk, Demo}, demos::{Chunk, Demo},
types::{ types::{
material::{Dielectric, Lambertian, Metal}, material::{Dielectric, Lambertian, Metal},
Hitable, HitableList, Ray, Sphere, Hitable, HitableList, Ray, Sphere, Vec3,
}, },
Camera, Camera,
}, },
rand::Rng, rand::Rng,
ultraviolet::vec::Vec3,
}; };
pub struct DefocusBlur; pub struct DefocusBlur;
@ -19,7 +18,7 @@ impl Demo for DefocusBlur {
} }
fn world(&self) -> Option<HitableList> { fn world(&self) -> Option<HitableList> {
let radius = (std::f32::consts::PI / 4.0).cos(); let radius = (std::f64::consts::PI / 4.0).cos();
Some(HitableList { Some(HitableList {
list: vec![ list: vec![
Box::new(Sphere::with_material( Box::new(Sphere::with_material(
@ -46,11 +45,11 @@ impl Demo for DefocusBlur {
}) })
} }
fn camera(&self, aspect_ratio: f32) -> Option<Camera> { fn camera(&self, aspect_ratio: f64) -> 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).mag(); let distance_to_focus = (lookfrom - lookat).length();
let camera = Camera::new( let camera = Camera::new(
lookfrom, lookfrom,
lookat, lookat,
@ -89,19 +88,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 f32 + rng.gen::<f32>()) / x as f32; let u = (i as f64 + rng.gen::<f64>()) / x as f64;
let v = (j as f32 + rng.gen::<f32>()) / y as f32; let v = (j as f64 + rng.gen::<f64>()) / y as f64;
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 f32; color /= samples as f64;
// gamma 2 corrected // gamma 2 corrected
buffer[offset] = (255.99 * color.x.sqrt()) as u8; buffer[offset] = (255.99 * color.r().sqrt()) as u8;
buffer[offset + 1] = (255.99 * color.y.sqrt()) as u8; buffer[offset + 1] = (255.99 * color.g().sqrt()) as u8;
buffer[offset + 2] = (255.99 * color.z.sqrt()) as u8; buffer[offset + 2] = (255.99 * color.b().sqrt()) as u8;
offset += 4; offset += 4;
} }
} }
@ -109,7 +108,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::f32::MAX) { if let Some(hit_rec) = world.hit(&ray, 0.001, std::f64::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 {
@ -121,8 +120,8 @@ fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 {
} }
} }
} else { } else {
let unit_direction = ray.direction().normalized(); let unit_direction = ray.direction().unit_vector();
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
} }
} }

View File

@ -3,12 +3,11 @@ use {
demos::{Chunk, Demo}, demos::{Chunk, Demo},
types::{ types::{
material::{Dielectric, Lambertian, Metal}, material::{Dielectric, Lambertian, Metal},
Hitable, HitableList, Ray, Sphere, Hitable, HitableList, Ray, Sphere, Vec3,
}, },
Camera, Camera,
}, },
rand::Rng, rand::Rng,
ultraviolet::vec::Vec3,
}; };
pub struct DielectricMaterial; pub struct DielectricMaterial;
@ -76,19 +75,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 f32 + rng.gen::<f32>()) / x as f32; let u = (i as f64 + rng.gen::<f64>()) / x as f64;
let v = (j as f32 + rng.gen::<f32>()) / y as f32; let v = (j as f64 + rng.gen::<f64>()) / y as f64;
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 f32; color /= samples as f64;
// gamma 2 corrected // gamma 2 corrected
buffer[offset] = (255.99 * color.x.sqrt()) as u8; buffer[offset] = (255.99 * color.r().sqrt()) as u8;
buffer[offset + 1] = (255.99 * color.y.sqrt()) as u8; buffer[offset + 1] = (255.99 * color.g().sqrt()) as u8;
buffer[offset + 2] = (255.99 * color.z.sqrt()) as u8; buffer[offset + 2] = (255.99 * color.b().sqrt()) as u8;
offset += 4; offset += 4;
} }
} }
@ -96,7 +95,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::f32::MAX) { if let Some(hit_rec) = world.hit(&ray, 0.001, std::f64::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 {
@ -108,8 +107,8 @@ fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 {
} }
} }
} else { } else {
let unit_direction = ray.direction().normalized(); let unit_direction = ray.direction().unit_vector();
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
} }
} }

View File

@ -1,11 +1,10 @@
use { use {
crate::{ crate::{
demos::{Chunk, Demo}, demos::{Chunk, Demo},
types::{Hitable, HitableList, Ray, Sphere}, types::{Hitable, HitableList, Ray, Sphere, Vec3},
Camera, Camera,
}, },
rand::Rng, rand::Rng,
ultraviolet::vec::Vec3,
}; };
pub struct DiffuseMaterials; pub struct DiffuseMaterials;
@ -50,13 +49,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 f32 + rng.gen::<f32>()) / x as f32; let u = (i as f64 + rng.gen::<f64>()) / x as f64;
let v = (j as f32 + rng.gen::<f32>()) / y as f32; let v = (j as f64 + rng.gen::<f64>()) / y as f64;
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 f32; color /= samples as f64;
// 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
@ -64,9 +63,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.x.sqrt()) as u8; buffer[offset] = (255.99 * color.r().sqrt()) as u8;
buffer[offset + 1] = (255.99 * color.y.sqrt()) as u8; buffer[offset + 1] = (255.99 * color.g().sqrt()) as u8;
buffer[offset + 2] = (255.99 * color.z.sqrt()) as u8; buffer[offset + 2] = (255.99 * color.b().sqrt()) as u8;
offset += 4; offset += 4;
} }
} }
@ -74,25 +73,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/f32 can only be // The value of t_min here could've been 0.0 but since f32/f64 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::f32::MAX) { if let Some(hit_rec) = world.hit(&ray, 0.001, std::f64::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().normalized(); let unit_direction = ray.direction().unit_vector();
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::<f32>(), rng.gen::<f32>(), rng.gen::<f32>()) * 2.0 let mut point = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), rng.gen::<f64>()) * 2.0
- Vec3::new(1.0, 1.0, 1.0); - Vec3::new(1.0, 1.0, 1.0);
while point.mag_sq() >= 1.0 { while point.sq_len() >= 1.0 {
point = Vec3::new(rng.gen::<f32>(), rng.gen::<f32>(), rng.gen::<f32>()) * 2.0 point = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), rng.gen::<f64>()) * 2.0
- Vec3::new(1.0, 1.0, 1.0); - Vec3::new(1.0, 1.0, 1.0);
} }
point point

View File

@ -3,12 +3,11 @@ use {
demos::{Chunk, Demo}, demos::{Chunk, Demo},
types::{ types::{
material::{Dielectric, Lambertian, Metal}, material::{Dielectric, Lambertian, Metal},
Hitable, HitableList, Ray, Sphere, Hitable, HitableList, Ray, Sphere, Vec3,
}, },
Camera, Camera,
}, },
rand::Rng, rand::Rng,
ultraviolet::vec::Vec3,
}; };
pub struct FinalScene; pub struct FinalScene;
@ -34,22 +33,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 f32; let a = a as f64;
for b in -11..11 { for b in -11..11 {
let b = b as f32; let b = b as f64;
let choose_material_probability = rng.gen::<f32>(); let choose_material_probability = rng.gen::<f64>();
let center = Vec3::new(a + 0.9 * rng.gen::<f32>(), 0.2, b + 0.9 * rng.gen::<f32>()); let center = Vec3::new(a + 0.9 * rng.gen::<f64>(), 0.2, b + 0.9 * rng.gen::<f64>());
if (center - l).mag() > 0.9 { if (center - l).length() > 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::<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>(), rng.gen::<f64>() * rng.gen::<f64>(),
))), ))),
))); )));
} else if choose_material_probability < 0.95 { } else if choose_material_probability < 0.95 {
@ -59,11 +58,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::<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, (1.0 + rng.gen::<f64>()) * 0.5,
), ),
0.5 * rng.gen::<f32>(), 0.5 * rng.gen::<f64>(),
)), )),
))); )));
} else { } else {
@ -97,7 +96,7 @@ impl Demo for FinalScene {
Some(world) Some(world)
} }
fn camera(&self, aspect_ratio: f32) -> Option<Camera> { fn camera(&self, aspect_ratio: f64) -> 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(
@ -138,19 +137,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 f32 + rng.gen::<f32>()) / x as f32; let u = (i as f64 + rng.gen::<f64>()) / x as f64;
let v = (j as f32 + rng.gen::<f32>()) / y as f32; let v = (j as f64 + rng.gen::<f64>()) / y as f64;
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 f32; color /= samples as f64;
// gamma 2 corrected // gamma 2 corrected
buffer[offset] = (255.99 * color.x.sqrt()) as u8; buffer[offset] = (255.99 * color.r().sqrt()) as u8;
buffer[offset + 1] = (255.99 * color.y.sqrt()) as u8; buffer[offset + 1] = (255.99 * color.g().sqrt()) as u8;
buffer[offset + 2] = (255.99 * color.z.sqrt()) as u8; buffer[offset + 2] = (255.99 * color.b().sqrt()) as u8;
offset += 4; offset += 4;
} }
} }
@ -158,7 +157,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::f32::MAX) { if let Some(hit_rec) = world.hit(&ray, 0.001, std::f64::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 {
@ -170,8 +169,8 @@ fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 {
} }
} }
} else { } else {
let unit_direction = ray.direction().normalized(); let unit_direction = ray.direction().unit_vector();
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
} }
} }

View File

@ -1,10 +1,7 @@
use { use crate::{
crate::{ demos::{Chunk, Demo},
demos::{Chunk, Demo}, types::{Hitable, HitableList, Ray, Sphere, Vec3},
types::{Hitable, HitableList, Ray, Sphere}, Camera,
Camera,
},
ultraviolet::vec::Vec3,
}; };
pub struct HitableSphere; pub struct HitableSphere;
@ -48,14 +45,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 f32 / x as f32; let u = i as f64 / x as f64;
let v = j as f32 / y as f32; let v = j as f64 / y as f64;
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.x) as u8; buffer[offset] = (255.99 * color.r()) as u8;
buffer[offset + 1] = (255.99 * color.y) as u8; buffer[offset + 1] = (255.99 * color.g()) as u8;
buffer[offset + 2] = (255.99 * color.z) as u8; buffer[offset + 2] = (255.99 * color.b()) as u8;
offset += 4; offset += 4;
} }
} }
@ -63,19 +60,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::f32::MAX) { if let Some(hit_rec) = world.hit(&ray, 0.0, std::f64::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().normalized(); let unit_direction = ray.direction().unit_vector();
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
} }
} }

View File

@ -1,12 +1,9 @@
pub struct LinearGradientRectangle; pub struct LinearGradientRectangle;
use { use crate::{
crate::{ types::{HitableList, Ray, Vec3},
types::{HitableList, Ray}, Camera,
Camera, {demos::Chunk, Demo},
{demos::Chunk, Demo},
},
ultraviolet::vec::Vec3,
}; };
impl Demo for LinearGradientRectangle { impl Demo for LinearGradientRectangle {
@ -41,14 +38,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 f32 / x as f32; let u = i as f64 / x as f64;
let v = j as f32 / y as f32; let v = j as f64 / y as f64;
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.x) as u8; buffer[offset] = (255.99 * c.r()) as u8;
buffer[offset + 1] = (255.99 * c.y) as u8; buffer[offset + 1] = (255.99 * c.g()) as u8;
buffer[offset + 2] = (255.99 * c.z) as u8; buffer[offset + 2] = (255.99 * c.b()) as u8;
offset += 4; offset += 4;
} }
} }
@ -56,7 +53,7 @@ impl Demo for LinearGradientRectangle {
} }
fn color(ray: Ray) -> Vec3 { fn color(ray: Ray) -> Vec3 {
let unit_direction = ray.direction().normalized(); let unit_direction = ray.direction().unit_vector();
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
} }

View File

@ -3,12 +3,11 @@ use {
demos::{Chunk, Demo}, demos::{Chunk, Demo},
types::{ types::{
material::{Lambertian, Metal}, material::{Lambertian, Metal},
Hitable, HitableList, Ray, Sphere, Hitable, HitableList, Ray, Sphere, Vec3,
}, },
Camera, Camera,
}, },
rand::Rng, rand::Rng,
ultraviolet::vec::Vec3,
}; };
pub struct Materials; pub struct Materials;
@ -70,19 +69,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 f32 + rng.gen::<f32>()) / x as f32; let u = (i as f64 + rng.gen::<f64>()) / x as f64;
let v = (j as f32 + rng.gen::<f32>()) / y as f32; let v = (j as f64 + rng.gen::<f64>()) / y as f64;
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 f32; color /= samples as f64;
// gamma 2 corrected // gamma 2 corrected
buffer[offset] = (255.99 * color.x.sqrt()) as u8; buffer[offset] = (255.99 * color.r().sqrt()) as u8;
buffer[offset + 1] = (255.99 * color.y.sqrt()) as u8; buffer[offset + 1] = (255.99 * color.g().sqrt()) as u8;
buffer[offset + 2] = (255.99 * color.z.sqrt()) as u8; buffer[offset + 2] = (255.99 * color.b().sqrt()) as u8;
offset += 4; offset += 4;
} }
} }
@ -90,7 +89,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::f32::MAX) { if let Some(hit_rec) = world.hit(&ray, 0.001, std::f64::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 {
@ -102,8 +101,8 @@ fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 {
} }
} }
} else { } else {
let unit_direction = ray.direction().normalized(); let unit_direction = ray.direction().unit_vector();
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
} }
} }

View File

@ -25,14 +25,16 @@ pub use simple_sphere::SimpleSphere;
pub use surface_normal_sphere::SurfaceNormalSphere; pub use surface_normal_sphere::SurfaceNormalSphere;
use { use {
crate::{types::HitableList, Camera, HORIZONTAL_PARTITION, VERTICAL_PARTITION}, crate::{
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 {
@ -50,7 +52,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 f32 / ny as f32); let camera = self.camera(nx as f64 / ny as f64);
let buf = Arc::new(Mutex::new(buf)); let buf = Arc::new(Mutex::new(buf));
@ -91,7 +93,7 @@ pub trait Demo: std::marker::Sync {
None None
} }
fn camera(&self, aspect_ratio: f32) -> Option<Camera> { fn camera(&self, aspect_ratio: f64) -> 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(

View File

@ -3,12 +3,11 @@ use {
demos::{Chunk, Demo}, demos::{Chunk, Demo},
types::{ types::{
material::{Dielectric, Lambertian, Metal}, material::{Dielectric, Lambertian, Metal},
Hitable, HitableList, Ray, Sphere, Hitable, HitableList, Ray, Sphere, Vec3,
}, },
Camera, Camera,
}, },
rand::Rng, rand::Rng,
ultraviolet::vec::Vec3,
}; };
pub struct PositionableCamera; pub struct PositionableCamera;
@ -19,7 +18,7 @@ impl Demo for PositionableCamera {
} }
fn world(&self) -> Option<HitableList> { fn world(&self) -> Option<HitableList> {
let radius = (std::f32::consts::PI / 4.0).cos(); let radius = (std::f64::consts::PI / 4.0).cos();
Some(HitableList { Some(HitableList {
list: vec![ list: vec![
Box::new(Sphere::with_material( Box::new(Sphere::with_material(
@ -46,7 +45,7 @@ impl Demo for PositionableCamera {
}) })
} }
fn camera(&self, aspect_ratio: f32) -> Option<Camera> { fn camera(&self, aspect_ratio: f64) -> 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(
@ -85,17 +84,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 f32 + rng.gen::<f32>()) / x as f32; let u = (i as f64 + rng.gen::<f64>()) / x as f64;
let v = (j as f32 + rng.gen::<f32>()) / y as f32; let v = (j as f64 + rng.gen::<f64>()) / y as f64;
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 f32; color /= samples as f64;
// gamma 2 corrected // gamma 2 corrected
buffer[offset] = (255.99 * color.x.sqrt()) as u8; buffer[offset] = (255.99 * color.r().sqrt()) as u8;
buffer[offset + 1] = (255.99 * color.y.sqrt()) as u8; buffer[offset + 1] = (255.99 * color.g().sqrt()) as u8;
buffer[offset + 2] = (255.99 * color.z.sqrt()) as u8; buffer[offset + 2] = (255.99 * color.b().sqrt()) as u8;
offset += 4; offset += 4;
} }
} }
@ -103,7 +102,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::f32::MAX) { if let Some(hit_rec) = world.hit(&ray, 0.001, std::f64::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 {
@ -115,8 +114,8 @@ fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 {
} }
} }
} else { } else {
let unit_direction = ray.direction().normalized(); let unit_direction = ray.direction().unit_vector();
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
} }
} }

View File

@ -1,11 +1,10 @@
use { use {
crate::{ crate::{
demos::{Chunk, Demo}, demos::{Chunk, Demo},
types::{Hitable, HitableList, Ray, Sphere}, types::{Hitable, HitableList, Ray, Sphere, Vec3},
Camera, Camera,
}, },
rand::Rng, rand::Rng,
ultraviolet::vec::Vec3,
}; };
pub struct SimpleAntialiasing; pub struct SimpleAntialiasing;
@ -48,16 +47,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 f32 + rng.gen::<f32>()) / x as f32; let u = (i as f64 + rng.gen::<f64>()) / x as f64;
let v = (j as f32 + rng.gen::<f32>()) / y as f32; let v = (j as f64 + rng.gen::<f64>()) / y as f64;
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 f32; color /= samples as f64;
buffer[offset] = (255.99 * color.x) as u8; buffer[offset] = (255.99 * color.r()) as u8;
buffer[offset + 1] = (255.99 * color.y) as u8; buffer[offset + 1] = (255.99 * color.g()) as u8;
buffer[offset + 2] = (255.99 * color.z) as u8; buffer[offset + 2] = (255.99 * color.b()) as u8;
offset += 4; offset += 4;
} }
} }
@ -65,19 +64,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::f32::MAX) { if let Some(hit_rec) = world.hit(&ray, 0.0, std::f64::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().normalized(); let unit_direction = ray.direction().unit_vector();
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
} }
} }

View File

@ -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 f32 / x as f32, j as f32 / y as f32, 0.2]; let color = [i as f64 / x as f64, j as f64 / y as f64, 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;

View File

@ -1,13 +1,10 @@
use { use crate::{
crate::{ demos::{Chunk, Demo},
demos::{Chunk, Demo}, types::{HitableList, Ray, Vec3},
types::{HitableList, Ray}, Camera,
Camera,
},
ultraviolet::vec::Vec3,
}; };
const RADIUS: f32 = 0.5; const RADIUS: f64 = 0.5;
pub struct SimpleSphere; pub struct SimpleSphere;
@ -54,21 +51,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 f32 / x as f32; let u = i as f64 / x as f64;
let v = j as f32 / y as f32; let v = j as f64 / y as f64;
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.x) as u8; buffer[offset] = (255.99 * color.r()) as u8;
buffer[offset + 1] = (255.99 * color.y) as u8; buffer[offset + 1] = (255.99 * color.g()) as u8;
buffer[offset + 2] = (255.99 * color.z) as u8; buffer[offset + 2] = (255.99 * color.b()) as u8;
offset += 4; offset += 4;
} }
} }
} }
} }
fn ray_hit_sphere(center: Vec3, radius: f32, ray: &Ray) -> bool { fn ray_hit_sphere(center: Vec3, radius: f64, 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,
@ -84,9 +81,9 @@ fn ray_hit_sphere(center: Vec3, radius: f32, 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
@ -103,10 +100,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().normalized(); let unit_direction = ray.direction().unit_vector();
// 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

View File

@ -1,13 +1,10 @@
use { use crate::{
crate::{ demos::{Chunk, Demo},
demos::{Chunk, Demo}, types::{HitableList, Ray, Vec3},
types::{HitableList, Ray}, Camera,
Camera,
},
ultraviolet::vec::Vec3,
}; };
const RADIUS: f32 = 0.5; const RADIUS: f64 = 0.5;
pub struct SurfaceNormalSphere; pub struct SurfaceNormalSphere;
impl Demo for SurfaceNormalSphere { impl Demo for SurfaceNormalSphere {
@ -51,14 +48,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 f32 / x as f32; let u = i as f64 / x as f64;
let v = j as f32 / y as f32; let v = j as f64 / y as f64;
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.x) as u8; let ir = (255.99 * color.r()) as u8;
let ig = (255.99 * color.y) as u8; let ig = (255.99 * color.g()) as u8;
let ib = (255.99 * color.z) as u8; let ib = (255.99 * color.b()) as u8;
buffer[offset] = ir; buffer[offset] = ir;
buffer[offset + 1] = ig; buffer[offset + 1] = ig;
@ -72,23 +69,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)).normalized(); let n = (ray.point_at_parameter(t) - Vec3::new(0.0, 0.0, -1.0)).unit_vector();
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().normalized(); let unit_direction = ray.direction().unit_vector();
// 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: f32, ray: &Ray) -> f32 { fn ray_hit_sphere(center: Vec3, radius: f64, ray: &Ray) -> f64 {
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 {

View File

@ -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_f32() now.elapsed().as_secs_f64()
); );
texture.update(None, &buffer, width * 4).unwrap(); texture.update(None, &buffer, width * 4).unwrap();

View File

@ -1,7 +1,4 @@
use { use crate::types::{Material, Ray, Vec3};
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
@ -11,7 +8,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: f32, pub t: f64,
/// 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
@ -25,7 +22,7 @@ pub struct HitRecord<'a> {
} }
pub trait Hitable: Send + Sync { pub trait Hitable: Send + Sync {
fn hit(&self, _ray: &Ray, _t_min: f32, _t_max: f32) -> Option<HitRecord> { fn hit(&self, _ray: &Ray, _t_min: f64, _t_max: f64) -> Option<HitRecord> {
None None
} }
} }

View File

@ -5,7 +5,7 @@ pub struct HitableList {
} }
impl Hitable for HitableList { impl Hitable for HitableList {
fn hit(&self, ray: &Ray, t_min: f32, t_max: f32) -> Option<HitRecord> { fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> 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 {

View File

@ -1,7 +1,6 @@
use { use {
crate::types::{HitRecord, Ray}, crate::types::{HitRecord, Ray, Vec3},
rand::Rng, rand::Rng,
ultraviolet::vec::Vec3,
}; };
pub trait Material: Send + Sync { pub trait Material: Send + Sync {
@ -30,14 +29,14 @@ impl Material for Lambertian {
pub struct Metal { pub struct Metal {
albedo: Vec3, albedo: Vec3,
fuzz: f32, fuzz: f64,
} }
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: f32) -> Self { pub fn with_fuzz(albedo: Vec3, fuzz: f64) -> Self {
Self { albedo, fuzz } Self { albedo, fuzz }
} }
} }
@ -46,13 +45,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().normalized(), hit_rec.normal); let reflected_ray = reflect(ray_in.direction().unit_vector(), 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)
@ -61,11 +60,11 @@ impl Material for Metal {
} }
pub struct Dielectric { pub struct Dielectric {
reflection_index: f32, reflection_index: f64,
} }
impl Dielectric { impl Dielectric {
pub fn new(reflection_index: f32) -> Self { pub fn new(reflection_index: f64) -> Self {
Self { reflection_index } Self { reflection_index }
} }
} }
@ -77,25 +76,26 @@ 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().mag(), / ray_in.direction().length(),
) )
} 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().mag(), (-ray_in.direction().dot(&hit_rec.normal)) / ray_in.direction().length(),
) )
}; };
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::<f32>() < reflect_prob { if rng.gen::<f64>() < 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: f32, reflection_index: f32) -> f32 { fn schlick(cosine: f64, reflection_index: f64) -> f64 {
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: f32) -> Option<Vec3> { fn refract(incident: Vec3, normal: Vec3, ni_over_nt: f64) -> Option<Vec3> {
let uv = incident.normalized(); let uv = incident.unit_vector();
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: f32) -> 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::<f32>(), rng.gen::<f32>(), rng.gen::<f32>()) * 2.0 let mut point = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), rng.gen::<f64>()) * 2.0
- Vec3::new(1.0, 1.0, 1.0); - Vec3::new(1.0, 1.0, 1.0);
while point.mag_sq() >= 1.0 { while point.sq_len() >= 1.0 {
point = Vec3::new(rng.gen::<f32>(), rng.gen::<f32>(), rng.gen::<f32>()) * 2.0 point = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), rng.gen::<f64>()) * 2.0
- Vec3::new(1.0, 1.0, 1.0); - Vec3::new(1.0, 1.0, 1.0);
} }
point point

View File

@ -3,9 +3,11 @@ 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;

View File

@ -1,4 +1,4 @@
use ultraviolet::vec::Vec3; use crate::types::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: f32) -> Vec3 { pub fn point_at_parameter(&self, t: f64) -> Vec3 {
self.a + self.b * t self.a + self.b * t
} }
} }

View File

@ -1,23 +1,20 @@
use { use crate::types::{HitRecord, Hitable, Material, Ray, Vec3};
crate::types::{HitRecord, Hitable, Material, Ray},
ultraviolet::vec::Vec3,
};
pub struct Sphere { pub struct Sphere {
center: Vec3, center: Vec3,
radius: f32, radius: f64,
material: Option<Box<dyn Material>>, material: Option<Box<dyn Material>>,
} }
impl Sphere { impl Sphere {
pub fn new(center: Vec3, radius: f32) -> Self { pub fn new(center: Vec3, radius: f64) -> Self {
Self { Self {
center, center,
radius, radius,
material: None, material: None,
} }
} }
pub fn with_material(center: Vec3, radius: f32, material: Box<dyn Material>) -> Self { pub fn with_material(center: Vec3, radius: f64, material: Box<dyn Material>) -> Self {
Self { Self {
center, center,
radius, radius,
@ -27,11 +24,11 @@ impl Sphere {
} }
impl Hitable for Sphere { impl Hitable for Sphere {
fn hit(&self, ray: &Ray, t_min: f32, t_max: f32) -> Option<HitRecord> { fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> 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

192
src/types/vec3.rs Normal file
View File

@ -0,0 +1,192 @@
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]))
}
}