diff --git a/ria-weekend/src/camera.rs b/ria-weekend/src/camera.rs index 6d17e34..e202352 100644 --- a/ria-weekend/src/camera.rs +++ b/ria-weekend/src/camera.rs @@ -22,7 +22,7 @@ impl Camera { } } - pub fn get_ray(&self, u: f32, v: f32) -> Ray { + pub fn get_ray(&self, u: f64, v: f64) -> Ray { Ray::new( self.origin, self.lower_left_corner + self.horizontal * u + self.vertical * v - self.origin, diff --git a/ria-weekend/src/demos/diffuse_materials.rs b/ria-weekend/src/demos/diffuse_materials.rs new file mode 100644 index 0000000..19a1cd1 --- /dev/null +++ b/ria-weekend/src/demos/diffuse_materials.rs @@ -0,0 +1,70 @@ +use { + crate::{ + demos::Demo, + types::{Hitable, HitableList, Ray, Sphere, Vec3}, + Camera, + }, + rand::Rng, +}; + +pub struct DiffuseMaterials; + +impl Demo for DiffuseMaterials { + fn name(&self) -> &'static str { + "Diffuse Materials" + } + + fn render(&self, buf: &mut [u8], width: usize, height: usize, samples: u8) { + let world = HitableList { + list: vec![ + Box::new(Sphere::new(Vec3::new(0.0, 0.0, -1.0), 0.5)), + Box::new(Sphere::new(Vec3::new(0.0, -100.5, -1.0), 100.0)), + ], + }; + + let camera: Camera = Default::default(); + let mut rng = rand::thread_rng(); + let mut offset = 0; + + for j in (0..height).rev() { + for i in 0..width { + let mut color = Vec3::new(0.0, 0.0, 0.0); + + for _s in 0..samples { + let u = (i as f64 + rng.gen::()) / width as f64; + let v = (j as f64 + rng.gen::()) / height as f64; + + let r = camera.get_ray(u, v); + color += calc_color(r, &world, &mut rng); + } + + color /= samples as f64; + buf[offset] = (255.99 * color.r()) as u8; + buf[offset + 1] = (255.99 * color.g()) as u8; + buf[offset + 2] = (255.99 * color.b()) as u8; + offset += 4; + } + } + } +} + +fn calc_color(ray: Ray, world: &HitableList, rng: &mut rand::rngs::ThreadRng) -> Vec3 { + if let Some(hit_rec) = world.hit(&ray, 0.0, std::f64::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); + 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::(), rng.gen::(), rng.gen::()) * 2.0 + - Vec3::new(1.0, 1.0, 1.0); + while point.sq_len() >= 1.0 { + point = Vec3::new(rng.gen::(), rng.gen::(), rng.gen::()) * 2.0 + - Vec3::new(1.0, 1.0, 1.0); + } + point +} diff --git a/ria-weekend/src/demos/hitable_sphere.rs b/ria-weekend/src/demos/hitable_sphere.rs index a176793..ba76190 100644 --- a/ria-weekend/src/demos/hitable_sphere.rs +++ b/ria-weekend/src/demos/hitable_sphere.rs @@ -25,8 +25,8 @@ impl Demo for HitableSphere { let mut offset = 0; for j in (0..height).rev() { for i in 0..width { - let u = i as f32 / width as f32; - let v = j as f32 / height as f32; + let u = i as f64 / width as f64; + let v = j as f64 / height as f64; let ray = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v); let color = calc_color(ray, &world); @@ -40,7 +40,7 @@ impl Demo for HitableSphere { } 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 // So, This trick of adding 1 to each dimension and then halving // the resulting value shifts the normals from -1<->1 range to diff --git a/ria-weekend/src/demos/linear_gradient_rectangle.rs b/ria-weekend/src/demos/linear_gradient_rectangle.rs index 1764c64..239fe94 100644 --- a/ria-weekend/src/demos/linear_gradient_rectangle.rs +++ b/ria-weekend/src/demos/linear_gradient_rectangle.rs @@ -22,8 +22,8 @@ impl Demo for LinearGradientRectangle { for j in (0..height).rev() { for i in 0..width { - let u = i as f32 / width as f32; - let v = j as f32 / height as f32; + let u = i as f64 / width as f64; + let v = j as f64 / height as f64; let ray = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v); let c = color(ray); diff --git a/ria-weekend/src/demos/mod.rs b/ria-weekend/src/demos/mod.rs index 5c1fa0d..43d6aee 100644 --- a/ria-weekend/src/demos/mod.rs +++ b/ria-weekend/src/demos/mod.rs @@ -1,3 +1,4 @@ +mod diffuse_materials; mod hitable_sphere; mod linear_gradient_rectangle; mod simple_antialiasing; @@ -5,6 +6,7 @@ mod simple_rectangle; mod simple_sphere; mod surface_normal_sphere; +pub use diffuse_materials::DiffuseMaterials; pub use hitable_sphere::HitableSphere; pub use linear_gradient_rectangle::LinearGradientRectangle; pub use simple_antialiasing::SimpleAntialiasing; diff --git a/ria-weekend/src/demos/simple_antialiasing.rs b/ria-weekend/src/demos/simple_antialiasing.rs index a4b1393..22d0b7b 100644 --- a/ria-weekend/src/demos/simple_antialiasing.rs +++ b/ria-weekend/src/demos/simple_antialiasing.rs @@ -28,13 +28,13 @@ impl Demo for SimpleAntialiasing { for i in 0..width { let mut color = Vec3::new(0.0, 0.0, 0.0); for _s in 0..samples { - let u = (i as f32 + rng.gen::()) / width as f32; - let v = (j as f32 + rng.gen::()) / height as f32; + let u = (i as f64 + rng.gen::()) / width as f64; + let v = (j as f64 + rng.gen::()) / height as f64; let r = camera.get_ray(u, v); color += calc_color(r, &world); } - color /= samples as f32; + color /= samples as f64; buf[offset] = (255.99 * color.r()) as u8; buf[offset + 1] = (255.99 * color.g()) as u8; buf[offset + 2] = (255.99 * color.b()) as u8; @@ -45,7 +45,7 @@ impl Demo for SimpleAntialiasing { } 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 // So, This trick of adding 1 to each dimension and then halving // the resulting value shifts the normals from -1<->1 range to diff --git a/ria-weekend/src/demos/simple_rectangle.rs b/ria-weekend/src/demos/simple_rectangle.rs index 2c0fb0c..a4740d6 100644 --- a/ria-weekend/src/demos/simple_rectangle.rs +++ b/ria-weekend/src/demos/simple_rectangle.rs @@ -11,7 +11,7 @@ impl Demo for SimpleRectangle { let mut offset = 0; for j in (0..height).rev() { for i in 0..width { - let color = Vec3::new(i as f32 / width as f32, j as f32 / width as f32, 0.2); + let color = Vec3::new(i as f64 / width as f64, j as f64 / width as f64, 0.2); buf[offset] = (255.99 * color.r()) as u8; buf[offset + 1] = (255.99 * color.g()) as u8; diff --git a/ria-weekend/src/demos/simple_sphere.rs b/ria-weekend/src/demos/simple_sphere.rs index 000cd1e..f0378ab 100644 --- a/ria-weekend/src/demos/simple_sphere.rs +++ b/ria-weekend/src/demos/simple_sphere.rs @@ -3,7 +3,7 @@ use crate::{ Demo, }; -const RADIUS: f32 = 0.5; +const RADIUS: f64 = 0.5; pub struct SimpleSphere; @@ -35,8 +35,8 @@ impl Demo for SimpleSphere { for i in 0..w { // relative offsets // current position to total width/length - let u = i as f32 / w as f32; - let v = j as f32 / h as f32; + let u = i as f64 / w as f64; + let v = j as f64 / h as f64; let ray = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v); let color = calc_color(ray); @@ -49,7 +49,7 @@ impl Demo for SimpleSphere { } } -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, // (x-cx)^2 + (y-cy)^2 + (z-cz)^2 = R * R // should hold true. This equation can be rewritten as, diff --git a/ria-weekend/src/demos/surface_normal_sphere.rs b/ria-weekend/src/demos/surface_normal_sphere.rs index d796604..40d34cb 100644 --- a/ria-weekend/src/demos/surface_normal_sphere.rs +++ b/ria-weekend/src/demos/surface_normal_sphere.rs @@ -1,6 +1,6 @@ use crate::types::{Ray, Vec3}; -const RADIUS: f32 = 0.5; +const RADIUS: f64 = 0.5; pub struct SurfaceNormalSphere; impl crate::Demo for SurfaceNormalSphere { @@ -28,8 +28,8 @@ impl crate::Demo for SurfaceNormalSphere { let mut offset = 0; for j in (0..h).rev() { for i in 0..w { - let u = i as f32 / w as f32; - let v = j as f32 / h as f32; + let u = i as f64 / w as f64; + let v = j as f64 / h as f64; let ray = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v); let color = calculate_color(ray); @@ -61,7 +61,7 @@ fn calculate_color(ray: Ray) -> Vec3 { 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 a = ray.direction().dot(&ray.direction()); let b = 2.0 * pc.dot(&ray.direction()); diff --git a/ria-weekend/src/main.rs b/ria-weekend/src/main.rs index c57f62b..e32a7b2 100644 --- a/ria-weekend/src/main.rs +++ b/ria-weekend/src/main.rs @@ -15,7 +15,7 @@ use { }, }; -const NUM_SAMPLES: u8 = 100; +const NUM_SAMPLES: u8 = 20; fn main() -> Result<(), String> { let sdl_ctx = sdl2::init()?; @@ -72,6 +72,7 @@ fn main() -> Result<(), String> { Some(Keycode::Num4) => active_demo = Box::new(demos::SurfaceNormalSphere), Some(Keycode::Num5) => active_demo = Box::new(demos::HitableSphere), Some(Keycode::Num6) => active_demo = Box::new(demos::SimpleAntialiasing), + Some(Keycode::Num7) => active_demo = Box::new(demos::DiffuseMaterials), None => unreachable!(), _ => (), }; diff --git a/ria-weekend/src/types/hitable.rs b/ria-weekend/src/types/hitable.rs index 6db86fb..0368cf5 100644 --- a/ria-weekend/src/types/hitable.rs +++ b/ria-weekend/src/types/hitable.rs @@ -1,13 +1,13 @@ use crate::types::{Ray, Vec3}; pub struct HitRecord { - pub t: f32, + pub t: f64, pub p: Vec3, pub normal: Vec3, } pub trait Hitable { - fn hit(&self, _ray: &Ray, _t_min: f32, _t_max: f32) -> Option { + fn hit(&self, _ray: &Ray, _t_min: f64, _t_max: f64) -> Option { None } } diff --git a/ria-weekend/src/types/hitable_list.rs b/ria-weekend/src/types/hitable_list.rs index 41f2849..b204992 100644 --- a/ria-weekend/src/types/hitable_list.rs +++ b/ria-weekend/src/types/hitable_list.rs @@ -5,7 +5,7 @@ pub struct HitableList { } impl Hitable for HitableList { - fn hit(&self, ray: &Ray, t_min: f32, t_max: f32) -> Option { + fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option { let mut closest_so_far = t_max; let mut hit_rec: Option = None; for obj in &self.list { diff --git a/ria-weekend/src/types/ray.rs b/ria-weekend/src/types/ray.rs index 8c89470..4075357 100644 --- a/ria-weekend/src/types/ray.rs +++ b/ria-weekend/src/types/ray.rs @@ -18,7 +18,7 @@ impl Ray { self.b } #[inline] - pub fn point_at_parameter(&self, t: f32) -> Vec3 { + pub fn point_at_parameter(&self, t: f64) -> Vec3 { self.a + self.b * t } } diff --git a/ria-weekend/src/types/sphere.rs b/ria-weekend/src/types/sphere.rs index 36ebf78..867974f 100644 --- a/ria-weekend/src/types/sphere.rs +++ b/ria-weekend/src/types/sphere.rs @@ -1,23 +1,17 @@ use crate::types::{HitRecord, Hitable, Ray, Vec3}; pub struct Sphere { center: Vec3, - radius: f32, + radius: f64, } impl Sphere { - pub fn new(center: Vec3, radius: f32) -> Self { + pub fn new(center: Vec3, radius: f64) -> Self { Self { center, radius } } - const fn radius(&self) -> f32 { - self.radius - } - const fn center(&self) -> Vec3 { - self.center - } } impl Hitable for Sphere { - fn hit(&self, ray: &Ray, t_min: f32, t_max: f32) -> Option { + fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option { let oc = ray.origin() - self.center; let a = ray.direction().dot(&ray.direction()); let b = oc.dot(&ray.direction()); diff --git a/ria-weekend/src/types/vec3.rs b/ria-weekend/src/types/vec3.rs index 065e633..906bc37 100644 --- a/ria-weekend/src/types/vec3.rs +++ b/ria-weekend/src/types/vec3.rs @@ -4,50 +4,50 @@ use std::{ }; #[derive(Copy, Clone)] -pub struct Vec3([f32; 3]); +pub struct Vec3([f64; 3]); impl Vec3 { #[inline] - pub const fn new(a: f32, b: f32, c: f32) -> Vec3 { + pub const fn new(a: f64, b: f64, c: f64) -> Vec3 { Vec3([a, b, c]) } #[inline] - pub fn x(&self) -> f32 { + pub fn x(&self) -> f64 { self[0] } #[inline] - pub fn y(&self) -> f32 { + pub fn y(&self) -> f64 { self[1] } #[inline] - pub fn z(&self) -> f32 { + pub fn z(&self) -> f64 { self[2] } #[inline] - pub fn r(&self) -> f32 { + pub fn r(&self) -> f64 { self[0] } #[inline] - pub fn g(&self) -> f32 { + pub fn g(&self) -> f64 { self[1] } #[inline] - pub fn b(&self) -> f32 { + pub fn b(&self) -> f64 { self[2] } #[inline] - pub fn length(&self) -> f32 { + pub fn length(&self) -> f64 { self.sq_len().sqrt() } #[inline] - pub fn sq_len(&self) -> f32 { + 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) -> f32 { + pub fn dot(&self, v: &Vec3) -> f64 { self[0] * v[0] + self[1] * v[1] + self[2] * v[2] } @@ -62,7 +62,7 @@ impl Vec3 { #[inline] pub fn make_unit_vector(&mut self) { - let k = 1.0f32 / (self[0] * self[0] + self[1] * self[1] + self[2] * self[2]); + 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; @@ -115,17 +115,17 @@ impl MulAssign for Vec3 { } } -impl MulAssign for Vec3 { - fn mul_assign(&mut self, o: f32) { +impl MulAssign for Vec3 { + fn mul_assign(&mut self, o: f64) { self[0] *= o; self[1] *= o; self[2] *= o; } } -impl Mul for Vec3 { +impl Mul for Vec3 { type Output = Vec3; - fn mul(self, o: f32) -> Vec3 { + fn mul(self, o: f64) -> Vec3 { Vec3([self[0] * o, self[1] * o, self[2] * o]) } } @@ -138,34 +138,34 @@ impl Div for Vec3 { } } -impl Div for Vec3 { +impl Div for Vec3 { type Output = Vec3; - fn div(self, o: f32) -> Vec3 { + fn div(self, o: f64) -> Vec3 { let o = 1.0 / o; Vec3([self[0] * o, self[1] * o, self[2] * o]) } } -impl DivAssign for Vec3 { - fn div_assign(&mut self, o: f32) { +impl DivAssign 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; + self.0[0] *= o; + self.0[1] *= o; + self.0[2] *= o; } } impl Index for Vec3 { - type Output = f32; + type Output = f64; - fn index(&self, q: usize) -> &f32 { + fn index(&self, q: usize) -> &f64 { &self.0[q] } } impl IndexMut for Vec3 { - fn index_mut(&mut self, q: usize) -> &mut f32 { + fn index_mut(&mut self, q: usize) -> &mut f64 { &mut self.0[q] } }