From cce19776033d22293f9de2d83ffc8d79251b0e06 Mon Sep 17 00:00:00 2001 From: ishanjain28 Date: Tue, 25 Jun 2019 23:14:37 +0530 Subject: [PATCH] working on hitable based implementation 1. Reversed order in which images are rendered 2. Removed unused imports. 3. Added Hitable based Surface Normal Sphere. --- .../demos/hitable_surface_normal_sphere.rs | 71 +++++++++++++++++++ .../src/demos/linear_interpolation_y.rs | 8 +-- ria-weekend/src/demos/mod.rs | 2 + ria-weekend/src/demos/ppm_example.rs | 2 +- ria-weekend/src/demos/simple_sphere.rs | 10 +-- .../src/demos/surface_normal_sphere.rs | 19 +++-- ria-weekend/src/hitable.rs | 46 ++++++++++++ ria-weekend/src/main.rs | 26 +++++-- ria-weekend/src/shapes/mod.rs | 3 + ria-weekend/src/shapes/sphere.rs | 46 ++++++++++++ 10 files changed, 206 insertions(+), 27 deletions(-) create mode 100644 ria-weekend/src/demos/hitable_surface_normal_sphere.rs create mode 100644 ria-weekend/src/hitable.rs create mode 100644 ria-weekend/src/shapes/mod.rs create mode 100644 ria-weekend/src/shapes/sphere.rs diff --git a/ria-weekend/src/demos/hitable_surface_normal_sphere.rs b/ria-weekend/src/demos/hitable_surface_normal_sphere.rs new file mode 100644 index 0000000..f434050 --- /dev/null +++ b/ria-weekend/src/demos/hitable_surface_normal_sphere.rs @@ -0,0 +1,71 @@ +use crate::{ + hitable::{HitRecord, Hitable, HitableList}, + ray::Ray, + shapes, + vec3::Vec3, +}; + +const RADIUS: f32 = 0.5; +pub struct HitableSurfaceNormalSphere; + +impl crate::Demo for HitableSurfaceNormalSphere { + fn name(&self) -> String { + "hit-table_surface_normal_sphere".to_owned() + } + + fn render(&self, buf: &mut Vec, w: usize, h: usize) { + // in my case, The resolution is 1200x800 + // These numbers are calculated by first calculating the aspect ratio + // and then just figuring out lower left corner, Width(2 x aspect ratio width) + // Height(2 x aspect ratio height) + let lower_left_corner = Vec3::new(-2.0, -1.0, -1.0); + let horizontal = Vec3::new(4.0, 0.0, 0.0); + let vertical = Vec3::new(0.0, 2.0, 0.0); + let origin = Vec3::new(0.0, 0.0, 0.0); + + let list: Vec> = vec![ + Box::new(shapes::Sphere::new(Vec3::new(0.0, 0.0, -1.0), RADIUS)), + Box::new(shapes::Sphere::new(Vec3::new(0.0, -100.5, -1.0), 100.0)), + ]; + let world = HitableList::new(list); + + 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 ray = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v); + + let color = calculate_color(ray, &world); + let ir = (255.99 * color.r()) as u8; + let ig = (255.99 * color.g()) as u8; + let ib = (255.99 * color.b()) as u8; + + buf[offset] = ir; + buf[offset + 1] = ig; + buf[offset + 2] = ib; + offset += 4; + } + } + } +} + +fn calculate_color(ray: Ray, world: &HitableList) -> Vec3 { + let mut hit_rec = HitRecord { + t: 0.0, + normal: Vec3::new(0.0, 0.0, 0.0), + point: Vec3::new(0.0, 0.0, 0.0), + }; + if world.hit(&ray, 0.001, std::f32::MAX, &mut hit_rec) { + Vec3::new( + 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 = 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 + } +} diff --git a/ria-weekend/src/demos/linear_interpolation_y.rs b/ria-weekend/src/demos/linear_interpolation_y.rs index 079d004..ca7d82a 100644 --- a/ria-weekend/src/demos/linear_interpolation_y.rs +++ b/ria-weekend/src/demos/linear_interpolation_y.rs @@ -13,14 +13,14 @@ impl crate::Demo for LinearInterpolationY { // These numbers are calculated by first calculating the aspect ratio // and then just figuring out lower left corner, Width(2 x aspect ratio width) // Height(2 x aspect ratio height) - let lower_left_corner = Vec3::new(-3.0, -2.0, -1.0); - let horizontal = Vec3::new(6.0, 0.0, 0.0); - let vertical = Vec3::new(0.0, 4.0, 0.0); + let lower_left_corner = Vec3::new(-2.0, -1.0, -1.0); + let horizontal = Vec3::new(4.0, 0.0, 0.0); + let vertical = Vec3::new(0.0, 2.0, 0.0); // Observer position let origin = Vec3::new(0.0, 0.0, 0.0); let mut offset = 0; - for j in 0..h { + for j in (0..h).rev() { for i in 0..w { // relative offsets // current position to total width/length diff --git a/ria-weekend/src/demos/mod.rs b/ria-weekend/src/demos/mod.rs index f3228c9..45defc3 100644 --- a/ria-weekend/src/demos/mod.rs +++ b/ria-weekend/src/demos/mod.rs @@ -1,8 +1,10 @@ +mod hitable_surface_normal_sphere; mod linear_interpolation_y; mod ppm_example; mod simple_sphere; mod surface_normal_sphere; +pub use hitable_surface_normal_sphere::HitableSurfaceNormalSphere; pub use linear_interpolation_y::LinearInterpolationY; pub use ppm_example::PpmExample; pub use simple_sphere::SimpleSphere; diff --git a/ria-weekend/src/demos/ppm_example.rs b/ria-weekend/src/demos/ppm_example.rs index 96cfac5..fc2d4e2 100644 --- a/ria-weekend/src/demos/ppm_example.rs +++ b/ria-weekend/src/demos/ppm_example.rs @@ -8,7 +8,7 @@ impl crate::Demo for PpmExample { fn render(&self, buf: &mut Vec, w: usize, h: usize) { let mut offset = 0; - for j in 0..h { + for j in (0..h).rev() { for i in 0..w { let color = Vec3::new((i as f32) / (w as f32), (j as f32) / (h as f32), 0.2); diff --git a/ria-weekend/src/demos/simple_sphere.rs b/ria-weekend/src/demos/simple_sphere.rs index 77db1fa..e3ac9c2 100644 --- a/ria-weekend/src/demos/simple_sphere.rs +++ b/ria-weekend/src/demos/simple_sphere.rs @@ -1,6 +1,6 @@ use crate::{ray::Ray, vec3::Vec3}; -const RADIUS: f32 = 0.8; +const RADIUS: f32 = 0.5; pub struct SimpleSphere; @@ -14,14 +14,14 @@ impl crate::Demo for SimpleSphere { // These numbers are calculated by first calculating the aspect ratio // and then just figuring out lower left corner, Width(2 x aspect ratio width) // Height(2 x aspect ratio height) - let lower_left_corner = Vec3::new(-3.0, -2.0, -1.0); - let horizontal = Vec3::new(6.0, 0.0, 0.0); - let vertical = Vec3::new(0.0, 4.0, 0.0); + let lower_left_corner = Vec3::new(-2.0, -1.0, -1.0); + let horizontal = Vec3::new(4.0, 0.0, 0.0); + let vertical = Vec3::new(0.0, 2.0, 0.0); // Observer position let origin = Vec3::new(0.0, 0.0, 0.0); let mut offset = 0; - for j in 0..h { + for j in (0..h).rev() { for i in 0..w { // relative offsets // current position to total width/length diff --git a/ria-weekend/src/demos/surface_normal_sphere.rs b/ria-weekend/src/demos/surface_normal_sphere.rs index e1f114d..6fa0654 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::{demo::Demo, ray, ray::Ray, vec3::Vec3}; +use crate::{ray::Ray, vec3::Vec3}; -const RADIUS: f32 = 0.8; +const RADIUS: f32 = 0.5; pub struct SurfaceNormalSphere; impl crate::Demo for SurfaceNormalSphere { @@ -13,14 +13,14 @@ impl crate::Demo for SurfaceNormalSphere { // These numbers are calculated by first calculating the aspect ratio // and then just figuring out lower left corner, Width(2 x aspect ratio width) // Height(2 x aspect ratio height) - let lower_left_corner = Vec3::new(-3.0, -2.0, -1.0); - let horizontal = Vec3::new(6.0, 0.0, 0.0); - let vertical = Vec3::new(0.0, 4.0, 0.0); + let lower_left_corner = Vec3::new(-2.0, -1.0, -1.0); + let horizontal = Vec3::new(4.0, 0.0, 0.0); + let vertical = Vec3::new(0.0, 2.0, 0.0); // Observer position let origin = Vec3::new(0.0, 0.0, 0.0); let mut offset = 0; - for j in 0..h { + 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; @@ -64,14 +64,13 @@ fn ray_hit_sphere(center: Vec3, radius: f32, ray: &Ray) -> f32 { // when expanded we get // t * t * dot(B, B) + 2 * t * dot(B, A-C) + dot(A-C, A-C) - R*R = 0 - let oc = ray.origin() - center; + let pc = ray.origin() - center; let a = ray.direction().dot(&ray.direction()); - let b = 2.0 * oc.dot(&ray.direction()); - let c = oc.dot(&oc) - radius * radius; + 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 { - // return quadratic root (-b - discriminant.sqrt()) / (2.0 * a) } else { -1.0 diff --git a/ria-weekend/src/hitable.rs b/ria-weekend/src/hitable.rs new file mode 100644 index 0000000..753b744 --- /dev/null +++ b/ria-weekend/src/hitable.rs @@ -0,0 +1,46 @@ +use crate::ray::Ray; +use crate::vec3::Vec3; + +#[derive(Debug, Copy, Clone)] +pub struct HitRecord { + pub t: f32, + pub point: Vec3, + pub normal: Vec3, +} + +pub trait Hitable { + fn hit(&self, ray: &Ray, time_min: f32, time_max: f32, hit_rec: &mut HitRecord) -> bool; +} + +pub struct HitableList { + inner: Vec>, +} + +impl HitableList { + pub fn new(items: Vec>) -> HitableList { + HitableList { inner: items } + } +} + +impl Hitable for HitableList { + fn hit(&self, ray: &Ray, t_min: f32, t_max: f32, hit_rec: &mut HitRecord) -> bool { + let mut temp_hit_rec: HitRecord = HitRecord { + t: 0.0, + point: Vec3::new(0.0, 0.0, 0.0), + normal: Vec3::new(0.0, 0.0, 0.0), + }; + let mut hit_anything = false; + let mut closest_to_far = t_max; + + for obj in &self.inner { + if obj.hit(&ray, t_min, closest_to_far, &mut temp_hit_rec) { + hit_anything = true; + closest_to_far = hit_rec.t; + hit_rec.point = temp_hit_rec.point; + hit_rec.t = temp_hit_rec.t; + hit_rec.normal = temp_hit_rec.normal; + } + } + hit_anything + } +} diff --git a/ria-weekend/src/main.rs b/ria-weekend/src/main.rs index 34f872d..93ec344 100644 --- a/ria-weekend/src/main.rs +++ b/ria-weekend/src/main.rs @@ -1,27 +1,27 @@ mod demo; mod demos; +mod hitable; mod ray; +mod shapes; mod vec3; use demo::Demo; -use demos::{LinearInterpolationY, PpmExample, SimpleSphere, SurfaceNormalSphere}; +use demos::{ + HitableSurfaceNormalSphere, LinearInterpolationY, PpmExample, SimpleSphere, SurfaceNormalSphere, +}; use sdl2::{ event::{Event, WindowEvent}, keyboard::Keycode, pixels::PixelFormatEnum, - rect::Rect, - render::{Canvas, Texture, TextureValueError}, - video::Window, - EventPump, Sdl, }; fn main() -> Result<(), String> { let sdl_ctx = sdl2::init()?; let video_subsys = sdl_ctx.video()?; - let (mut width, mut height): (usize, usize) = (1200, 800); + let (mut width, mut height): (usize, usize) = (1200, 600); - let mut window = video_subsys + let window = video_subsys .window("Ray tracing in a weekend", width as u32, height as u32) .position_centered() .build() @@ -46,6 +46,11 @@ fn main() -> Result<(), String> { let mut active_demo: Box = Box::new(PpmExample); //println!("{:?} {:?} {:?}", texture.query(), texture.color_mod(), texture.alpha_mod()); + + // TODO: Should update when window is unfocus since the project window retains + // data from overlapped window + // TODO: Maybe consider using condition variable to make loop {} not run at full + // speed at all times pinning a core at 100% let mut should_update = true; loop { for event in event_pump.poll_iter() { @@ -83,6 +88,13 @@ fn main() -> Result<(), String> { should_update = true; active_demo = Box::new(SurfaceNormalSphere); } + Event::KeyUp { + keycode: Some(Keycode::Num5), + .. + } => { + should_update = true; + active_demo = Box::new(HitableSurfaceNormalSphere); + } Event::KeyUp { keycode: Some(Keycode::S), .. diff --git a/ria-weekend/src/shapes/mod.rs b/ria-weekend/src/shapes/mod.rs new file mode 100644 index 0000000..7ad4cd6 --- /dev/null +++ b/ria-weekend/src/shapes/mod.rs @@ -0,0 +1,3 @@ +pub mod sphere; + +pub use sphere::Sphere; diff --git a/ria-weekend/src/shapes/sphere.rs b/ria-weekend/src/shapes/sphere.rs new file mode 100644 index 0000000..5d57fb9 --- /dev/null +++ b/ria-weekend/src/shapes/sphere.rs @@ -0,0 +1,46 @@ +use crate::{ + hitable::{HitRecord, Hitable}, + ray::Ray, + vec3::Vec3, +}; + +pub struct Sphere { + center: Vec3, + radius: f32, +} + +impl Sphere { + pub fn new(center: Vec3, radius: f32) -> Sphere { + Sphere { center, radius } + } +} + +impl Hitable for Sphere { + fn hit(&self, ray: &Ray, t_min: f32, t_max: f32, hit_rec: &mut HitRecord) -> bool { + println!("t_max: {}", t_max); + 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; + // TODO: I don't yet understand how 4 was canceled from this equation here + let discriminant = b * b - 4.0 * a * c; + + if discriminant > 0.0 { + let mut root = (-b - discriminant.sqrt()) / a; + if root < t_max && root > t_min { + hit_rec.t = root; + hit_rec.point = ray.point_at_parameter(hit_rec.t); + hit_rec.normal = (hit_rec.point - self.center) / self.radius; + return true; + } + root = (-b + discriminant.sqrt()) / a; + if root < t_max && root > t_min { + hit_rec.t = root; + hit_rec.point = ray.point_at_parameter(hit_rec.t); + hit_rec.normal = (hit_rec.point - self.center) / self.radius; + return true; + } + } + return false; + } +}