Compare commits

...

1 Commits

25 changed files with 263 additions and 411 deletions

25
Cargo.lock generated
View File

@ -12,6 +12,12 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "bytemuck"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37fa13df2292ecb479ec23aa06f4507928bef07839be9ef15281411076629431"
[[package]]
name = "c2-chacha"
version = "0.2.3"
@ -207,6 +213,7 @@ dependencies = [
"rand",
"rayon",
"sdl2",
"ultraviolet",
]
[[package]]
@ -261,8 +268,26 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "ultraviolet"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae513d44058e41a4669dc336b13a017ea96ad6984bac3797a3d49744e3ffa0ba"
dependencies = [
"wide",
]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
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,3 +8,4 @@ edition = "2018"
sdl2 = "0.33.0"
rand = "0.7.3"
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()
.for_each(move |i| {
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_x = i * nx;

View File

@ -1,14 +1,11 @@
use {
crate::types::{Ray, Vec3},
rand::Rng,
};
use {crate::types::Ray, rand::Rng, ultraviolet::vec::Vec3};
pub struct Camera {
origin: Vec3,
horizontal: Vec3,
vertical: Vec3,
lower_left_corner: Vec3,
lens_radius: f64,
lens_radius: f32,
// position vectors
u: Vec3,
@ -26,20 +23,20 @@ impl Camera {
look_from: Vec3,
look_at: Vec3,
v_up: Vec3,
vertical_fov: f64,
aspect: f64,
aperture: f64,
focus_distance: f64,
vertical_fov: f32,
aspect: f32,
aperture: f32,
focus_distance: f32,
) -> Self {
// 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_width = aspect * half_height;
let origin = look_from;
let w = (look_from - look_at).unit_vector();
let u = v_up.cross(&w).unit_vector();
let v = w.cross(&u);
let w = (look_from - look_at).normalized();
let u = v_up.cross(w).normalized();
let v = w.cross(u);
let lower_left_corner = origin
- 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 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(
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 {
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 {
p = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), 0.0) * 2.0 - Vec3::new(1.0, 0.0, 0.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
}

View File

@ -3,11 +3,12 @@ use {
demos::{Chunk, Demo},
types::{
material::{Dielectric, Lambertian, Metal},
Hitable, HitableList, Ray, Sphere, Vec3,
Hitable, HitableList, Ray, Sphere,
},
Camera,
},
rand::Rng,
ultraviolet::vec::Vec3,
};
pub struct DefocusBlur;
@ -18,7 +19,7 @@ impl Demo for DefocusBlur {
}
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 {
list: vec![
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 lookat = Vec3::new(0.0, 0.0, -1.0);
let aperture = 2.0;
let distance_to_focus = (lookfrom - lookat).length();
let distance_to_focus = (lookfrom - lookat).mag();
let camera = Camera::new(
lookfrom,
lookat,
@ -88,19 +89,19 @@ impl Demo for DefocusBlur {
for i in start_x..start_x + nx {
let mut color = Vec3::new(0.0, 0.0, 0.0);
for _s in 0..samples {
let u = (i as f64 + rng.gen::<f64>()) / x as f64;
let v = (j as f64 + rng.gen::<f64>()) / y as f64;
let u = (i as f32 + rng.gen::<f32>()) / x as f32;
let v = (j as f32 + rng.gen::<f32>()) / y as f32;
let ray = camera.get_ray(u, v);
color += calc_color(ray, &world, 0);
}
color /= samples as f64;
color /= samples as f32;
// gamma 2 corrected
buffer[offset] = (255.99 * color.r().sqrt()) as u8;
buffer[offset + 1] = (255.99 * color.g().sqrt()) as u8;
buffer[offset + 2] = (255.99 * color.b().sqrt()) as u8;
buffer[offset] = (255.99 * color.x.sqrt()) as u8;
buffer[offset + 1] = (255.99 * color.y.sqrt()) as u8;
buffer[offset + 2] = (255.99 * color.z.sqrt()) as u8;
offset += 4;
}
}
@ -108,7 +109,7 @@ impl Demo for DefocusBlur {
}
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 {
Vec3::new(0.0, 0.0, 0.0)
} else {
@ -120,8 +121,8 @@ fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 {
}
}
} else {
let unit_direction = ray.direction().unit_vector();
let t = 0.5 * (unit_direction.y() + 1.0);
let unit_direction = ray.direction().normalized();
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
}
}

View File

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

View File

@ -1,10 +1,11 @@
use {
crate::{
demos::{Chunk, Demo},
types::{Hitable, HitableList, Ray, Sphere, Vec3},
types::{Hitable, HitableList, Ray, Sphere},
Camera,
},
rand::Rng,
ultraviolet::vec::Vec3,
};
pub struct DiffuseMaterials;
@ -49,13 +50,13 @@ impl Demo for DiffuseMaterials {
let mut color = Vec3::new(0.0, 0.0, 0.0);
for _s in 0..samples {
let u = (i as f64 + rng.gen::<f64>()) / x as f64;
let v = (j as f64 + rng.gen::<f64>()) / y as f64;
let u = (i as f32 + rng.gen::<f32>()) / x as f32;
let v = (j as f32 + rng.gen::<f32>()) / y as f32;
let r = camera.get_ray(u, v);
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
// is quite dark
@ -63,9 +64,9 @@ impl Demo for DiffuseMaterials {
// So, IRL, It *should* look a bit lighter in color
// To do that, We apply gamma correction by a factor of 2
// which means multiple rgb values by 1/gamma aka 1/2
buffer[offset] = (255.99 * color.r().sqrt()) as u8;
buffer[offset + 1] = (255.99 * color.g().sqrt()) as u8;
buffer[offset + 2] = (255.99 * color.b().sqrt()) as u8;
buffer[offset] = (255.99 * color.x.sqrt()) as u8;
buffer[offset + 1] = (255.99 * color.y.sqrt()) as u8;
buffer[offset + 2] = (255.99 * color.z.sqrt()) as u8;
offset += 4;
}
}
@ -73,25 +74,25 @@ impl Demo for DiffuseMaterials {
}
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.
// To combat this problem, We set a bias
// 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);
calc_color(Ray::new(hit_rec.p, target - hit_rec.p), &world, rng) * 0.5
} else {
let unit_direction = ray.direction().unit_vector();
let t = 0.5 * (unit_direction.y() + 1.0);
let unit_direction = ray.direction().normalized();
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
}
}
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);
while point.sq_len() >= 1.0 {
point = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), rng.gen::<f64>()) * 2.0
while point.mag_sq() >= 1.0 {
point = Vec3::new(rng.gen::<f32>(), rng.gen::<f32>(), rng.gen::<f32>()) * 2.0
- Vec3::new(1.0, 1.0, 1.0);
}
point

View File

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

View File

@ -1,7 +1,10 @@
use crate::{
demos::{Chunk, Demo},
types::{Hitable, HitableList, Ray, Sphere, Vec3},
Camera,
use {
crate::{
demos::{Chunk, Demo},
types::{Hitable, HitableList, Ray, Sphere},
Camera,
},
ultraviolet::vec::Vec3,
};
pub struct HitableSphere;
@ -45,14 +48,14 @@ impl Demo for HitableSphere {
let mut offset = 0;
for j in start_y..start_y + ny {
for i in start_x..start_x + nx {
let u = i as f64 / x as f64;
let v = j as f64 / y as f64;
let u = i as f32 / x as f32;
let v = j as f32 / y as f32;
let ray = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v);
let color = calc_color(ray, &world);
buffer[offset] = (255.99 * color.r()) as u8;
buffer[offset + 1] = (255.99 * color.g()) as u8;
buffer[offset + 2] = (255.99 * color.b()) as u8;
buffer[offset] = (255.99 * color.x) as u8;
buffer[offset + 1] = (255.99 * color.y) as u8;
buffer[offset + 2] = (255.99 * color.z) as u8;
offset += 4;
}
}
@ -60,19 +63,19 @@ impl Demo for HitableSphere {
}
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
// So, This trick of adding 1 to each dimension and then halving
// the resulting value shifts the normals from -1<->1 range to
// 0<->1 range
Vec3::new(
hit_rec.normal.x() + 1.0,
hit_rec.normal.y() + 1.0,
hit_rec.normal.z() + 1.0,
hit_rec.normal.x + 1.0,
hit_rec.normal.y + 1.0,
hit_rec.normal.z + 1.0,
) * 0.5
} else {
let unit_direction = ray.direction().unit_vector();
let t = unit_direction.y() * 0.5 + 1.0;
let unit_direction = ray.direction().normalized();
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
}
}

View File

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

View File

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

View File

@ -25,16 +25,14 @@ pub use simple_sphere::SimpleSphere;
pub use surface_normal_sphere::SurfaceNormalSphere;
use {
crate::{
types::{HitableList, Vec3},
Camera, HORIZONTAL_PARTITION, VERTICAL_PARTITION,
},
crate::{types::HitableList, Camera, HORIZONTAL_PARTITION, VERTICAL_PARTITION},
rayon::prelude::*,
std::{
fs::File,
io::Write,
sync::{Arc, Mutex},
},
ultraviolet::vec::Vec3,
};
pub struct Chunk {
@ -52,7 +50,7 @@ pub trait Demo: std::marker::Sync {
let nx = width / VERTICAL_PARTITION;
let ny = height / HORIZONTAL_PARTITION;
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));
@ -93,7 +91,7 @@ pub trait Demo: std::marker::Sync {
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 lookat = Vec3::new(0.0, 0.0, -1.0);
Some(Camera::new(

View File

@ -3,11 +3,12 @@ use {
demos::{Chunk, Demo},
types::{
material::{Dielectric, Lambertian, Metal},
Hitable, HitableList, Ray, Sphere, Vec3,
Hitable, HitableList, Ray, Sphere,
},
Camera,
},
rand::Rng,
ultraviolet::vec::Vec3,
};
pub struct PositionableCamera;
@ -18,7 +19,7 @@ impl Demo for PositionableCamera {
}
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 {
list: vec![
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 lookat = Vec3::new(0.0, 0.0, -1.0);
Some(Camera::new(
@ -84,17 +85,17 @@ impl Demo for PositionableCamera {
for i in start_x..start_x + nx {
let mut color = Vec3::new(0.0, 0.0, 0.0);
for _s in 0..samples {
let u = (i as f64 + rng.gen::<f64>()) / x as f64;
let v = (j as f64 + rng.gen::<f64>()) / y as f64;
let u = (i as f32 + rng.gen::<f32>()) / x as f32;
let v = (j as f32 + rng.gen::<f32>()) / y as f32;
let ray = camera.get_ray(u, v);
color += calc_color(ray, &world, 0);
}
color /= samples as f64;
color /= samples as f32;
// gamma 2 corrected
buffer[offset] = (255.99 * color.r().sqrt()) as u8;
buffer[offset + 1] = (255.99 * color.g().sqrt()) as u8;
buffer[offset + 2] = (255.99 * color.b().sqrt()) as u8;
buffer[offset] = (255.99 * color.x.sqrt()) as u8;
buffer[offset + 1] = (255.99 * color.y.sqrt()) as u8;
buffer[offset + 2] = (255.99 * color.z.sqrt()) as u8;
offset += 4;
}
}
@ -102,7 +103,7 @@ impl Demo for PositionableCamera {
}
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 {
Vec3::new(0.0, 0.0, 0.0)
} else {
@ -114,8 +115,8 @@ fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 {
}
}
} else {
let unit_direction = ray.direction().unit_vector();
let t = 0.5 * (unit_direction.y() + 1.0);
let unit_direction = ray.direction().normalized();
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
}
}

View File

@ -1,10 +1,11 @@
use {
crate::{
demos::{Chunk, Demo},
types::{Hitable, HitableList, Ray, Sphere, Vec3},
types::{Hitable, HitableList, Ray, Sphere},
Camera,
},
rand::Rng,
ultraviolet::vec::Vec3,
};
pub struct SimpleAntialiasing;
@ -47,16 +48,16 @@ impl Demo for SimpleAntialiasing {
for i in start_x..start_x + nx {
let mut color = Vec3::new(0.0, 0.0, 0.0);
for _s in 0..samples {
let u = (i as f64 + rng.gen::<f64>()) / x as f64;
let v = (j as f64 + rng.gen::<f64>()) / y as f64;
let u = (i as f32 + rng.gen::<f32>()) / x as f32;
let v = (j as f32 + rng.gen::<f32>()) / y as f32;
let r = camera.get_ray(u, v);
color += calc_color(r, world.unwrap());
}
color /= samples as f64;
buffer[offset] = (255.99 * color.r()) as u8;
buffer[offset + 1] = (255.99 * color.g()) as u8;
buffer[offset + 2] = (255.99 * color.b()) as u8;
color /= samples as f32;
buffer[offset] = (255.99 * color.x) as u8;
buffer[offset + 1] = (255.99 * color.y) as u8;
buffer[offset + 2] = (255.99 * color.z) as u8;
offset += 4;
}
}
@ -64,19 +65,19 @@ impl Demo for SimpleAntialiasing {
}
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
// So, This trick of adding 1 to each dimension and then halving
// the resulting value shifts the normals from -1<->1 range to
// 0<->1 range
Vec3::new(
hit_rec.normal.x() + 1.0,
hit_rec.normal.y() + 1.0,
hit_rec.normal.z() + 1.0,
hit_rec.normal.x + 1.0,
hit_rec.normal.y + 1.0,
hit_rec.normal.z + 1.0,
) * 0.5
} else {
let unit_direction = ray.direction().unit_vector();
let t = unit_direction.y() * 0.5 + 1.0;
let unit_direction = ray.direction().normalized();
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
}
}

View File

@ -31,7 +31,7 @@ impl Demo for SimpleRectangle {
for j in start_y..start_y + ny {
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 + 1] = (255.99 * color[1]) as u8;
buffer[offset + 2] = (255.99 * color[2]) as u8;

View File

@ -1,10 +1,13 @@
use crate::{
demos::{Chunk, Demo},
types::{HitableList, Ray, Vec3},
Camera,
use {
crate::{
demos::{Chunk, Demo},
types::{HitableList, Ray},
Camera,
},
ultraviolet::vec::Vec3,
};
const RADIUS: f64 = 0.5;
const RADIUS: f32 = 0.5;
pub struct SimpleSphere;
@ -51,21 +54,21 @@ impl Demo for SimpleSphere {
for i in start_x..start_x + nx {
// relative offsets
// current position to total width/length
let u = i as f64 / x as f64;
let v = j as f64 / y as f64;
let u = i as f32 / x as f32;
let v = j as f32 / y as f32;
let ray = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v);
let color = calc_color(ray);
buffer[offset] = (255.99 * color.r()) as u8;
buffer[offset + 1] = (255.99 * color.g()) as u8;
buffer[offset + 2] = (255.99 * color.b()) as u8;
buffer[offset] = (255.99 * color.x) as u8;
buffer[offset + 1] = (255.99 * color.y) as u8;
buffer[offset + 2] = (255.99 * color.z) as u8;
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,
// (x-cx)^2 + (y-cy)^2 + (z-cz)^2 = R * R
// 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
let ac = ray.origin() - center;
let a = ray.direction().dot(&ray.direction());
let b = 2.0 * ray.direction().dot(&ac);
let c = ac.dot(&ac) - radius * radius;
let a = ray.direction().dot(ray.direction());
let b = 2.0 * ray.direction().dot(ac);
let c = ac.dot(ac) - radius * radius;
let discriminant = b * b - 4.0 * a * c;
discriminant > 0.0
@ -100,10 +103,10 @@ fn calc_color(ray: Ray) -> Vec3 {
// This will result in a sphere that is red in color
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
// 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
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,13 @@
use crate::{
demos::{Chunk, Demo},
types::{HitableList, Ray, Vec3},
Camera,
use {
crate::{
demos::{Chunk, Demo},
types::{HitableList, Ray},
Camera,
},
ultraviolet::vec::Vec3,
};
const RADIUS: f64 = 0.5;
const RADIUS: f32 = 0.5;
pub struct SurfaceNormalSphere;
impl Demo for SurfaceNormalSphere {
@ -48,14 +51,14 @@ impl Demo for SurfaceNormalSphere {
for j in start_y..start_y + ny {
for i in start_x..start_x + nx {
let u = i as f64 / x as f64;
let v = j as f64 / y as f64;
let u = i as f32 / x as f32;
let v = j as f32 / y as f32;
let ray = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v);
let color = calculate_color(ray);
let ir = (255.99 * color.r()) as u8;
let ig = (255.99 * color.g()) as u8;
let ib = (255.99 * color.b()) as u8;
let ir = (255.99 * color.x) as u8;
let ig = (255.99 * color.y) as u8;
let ib = (255.99 * color.z) as u8;
buffer[offset] = ir;
buffer[offset + 1] = ig;
@ -69,23 +72,23 @@ impl Demo for SurfaceNormalSphere {
fn calculate_color(ray: Ray) -> Vec3 {
let t = ray_hit_sphere(Vec3::new(0.0, 0.0, -1.0), RADIUS, &ray);
if t > 0.0 {
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;
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;
}
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
// 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
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 a = ray.direction().dot(&ray.direction());
let b = 2.0 * pc.dot(&ray.direction());
let c = pc.dot(&pc) - radius * radius;
let a = ray.direction().dot(ray.direction());
let b = 2.0 * pc.dot(ray.direction());
let c = pc.dot(pc) - radius * radius;
let discriminant = b * b - 4.0 * a * c;
if discriminant >= 0.0 {

View File

@ -143,7 +143,7 @@ fn main() -> Result<(), String> {
println!(
"Demo {} Time Taken(s) = {}",
active_demo.name(),
now.elapsed().as_secs_f64()
now.elapsed().as_secs_f32()
);
texture.update(None, &buffer, width * 4).unwrap();

View File

@ -1,4 +1,7 @@
use crate::types::{Material, Ray, Vec3};
use {
crate::types::{Material, Ray},
ultraviolet::vec::Vec3,
};
pub struct HitRecord<'a> {
/// 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.
/// 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
pub t: f64,
pub t: f32,
/// Ray object otherwise is represented by the Source/Destination points
/// p is what we get when we perform the operation, A + t * B
/// i.e. A vector from Ray source to the point t
@ -22,7 +25,7 @@ pub struct HitRecord<'a> {
}
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
}
}

View File

@ -5,7 +5,7 @@ pub struct 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 hit_rec: Option<HitRecord> = None;
for obj in &self.list {

View File

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

View File

@ -3,11 +3,9 @@ mod hitable_list;
pub mod material;
mod ray;
mod sphere;
mod vec3;
pub use hitable::{HitRecord, Hitable};
pub use hitable_list::HitableList;
pub use material::Material;
pub use ray::Ray;
pub use sphere::Sphere;
pub use vec3::Vec3;

View File

@ -1,4 +1,4 @@
use crate::types::Vec3;
use ultraviolet::vec::Vec3;
pub struct Ray {
a: Vec3,
@ -18,7 +18,7 @@ impl Ray {
self.b
}
#[inline]
pub fn point_at_parameter(&self, t: f64) -> Vec3 {
pub fn point_at_parameter(&self, t: f32) -> Vec3 {
self.a + self.b * t
}
}

View File

@ -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 {
center: Vec3,
radius: f64,
radius: f32,
material: Option<Box<dyn Material>>,
}
impl Sphere {
pub fn new(center: Vec3, radius: f64) -> Self {
pub fn new(center: Vec3, radius: f32) -> Self {
Self {
center,
radius,
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 {
center,
radius,
@ -24,11 +27,11 @@ impl 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 a = ray.direction().dot(&ray.direction());
let b = oc.dot(&ray.direction());
let c = oc.dot(&oc) - self.radius * self.radius;
let a = ray.direction().dot(ray.direction());
let b = oc.dot(ray.direction());
let c = oc.dot(oc) - self.radius * self.radius;
// The discriminant is calculated using b^2 - 4 * a * c
// but in this specific case, If we put the equation in the

View File

@ -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]))
}
}