From d903b4a57c4dea91e8c6985370b9a604689b735d Mon Sep 17 00:00:00 2001 From: ishanjain28 Date: Thu, 13 Feb 2020 20:19:13 +0530 Subject: [PATCH] Added Diffuse material image and bug fixes. 1. Added Diffuse Materials image. 2. Using f32 everywhere was causing slight artifacts in images so replaced all use of f32 with f64 3. Fixed a implementation bug in DivAssign on Vec3 --- ria-weekend/src/camera.rs | 2 +- ria-weekend/src/demos/diffuse_materials.rs | 70 +++++++++++++++++++ ria-weekend/src/demos/hitable_sphere.rs | 6 +- .../src/demos/linear_gradient_rectangle.rs | 4 +- ria-weekend/src/demos/mod.rs | 2 + ria-weekend/src/demos/simple_antialiasing.rs | 8 +-- ria-weekend/src/demos/simple_rectangle.rs | 2 +- ria-weekend/src/demos/simple_sphere.rs | 8 +-- .../src/demos/surface_normal_sphere.rs | 8 +-- ria-weekend/src/main.rs | 3 +- ria-weekend/src/types/hitable.rs | 4 +- ria-weekend/src/types/hitable_list.rs | 2 +- ria-weekend/src/types/ray.rs | 2 +- ria-weekend/src/types/sphere.rs | 12 +--- ria-weekend/src/types/vec3.rs | 52 +++++++------- 15 files changed, 126 insertions(+), 59 deletions(-) create mode 100644 ria-weekend/src/demos/diffuse_materials.rs 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] } }