diff options
author | Rosyid Haryadi <rosyid_haryadi@protonmail.com> | 2025-03-01 16:43:50 +0700 |
---|---|---|
committer | Rosyid Haryadi <rosyid_haryadi@protonmail.com> | 2025-03-01 16:43:50 +0700 |
commit | 084875ac8e9aa12cf6cc1892def9e4bc5fd4cdda (patch) | |
tree | 99fbc7b743fdf7a2b26ded0e8073c9a02be5240b | |
parent | 2d7457db1c0c719de3e1f76161a35af3fb5ae0c9 (diff) |
object list, something is still wrong
-rw-r--r-- | src/calculus.rs | 6 | ||||
-rw-r--r-- | src/main.rs | 1 | ||||
-rw-r--r-- | src/object.rs | 109 | ||||
-rw-r--r-- | src/renderer.rs | 54 |
4 files changed, 142 insertions, 28 deletions
diff --git a/src/calculus.rs b/src/calculus.rs index ac33514..67138d1 100644 --- a/src/calculus.rs +++ b/src/calculus.rs @@ -1,4 +1,5 @@ pub mod calculus { + #[derive(Copy, Clone)] pub struct Vec3 { pub x: f32, pub y: f32, @@ -78,4 +79,9 @@ pub mod calculus { self.origin.add(&self.direction.scalar_mul(t)) } } + + pub fn deg2rad(deg: f32) -> f32 { + deg * std::f32::consts::PI / 180.0 + } + }
\ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 9bdb6ed..db7d2ee 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ mod view; mod global; mod calculus; mod renderer; +mod object; use crate::global::*; use crate::renderer::render; diff --git a/src/object.rs b/src/object.rs new file mode 100644 index 0000000..ff965f0 --- /dev/null +++ b/src/object.rs @@ -0,0 +1,109 @@ +use crate::calculus::calculus::{Point3, Ray, Vec3}; + +#[derive(Clone)] +pub struct HitRecord { + pub position: Point3, + pub normal: Vec3, + pub t: f32, + pub front_face: bool, +} + +impl Default for HitRecord { + fn default() -> Self { + Self { + position: Point3 {x:0.0, y:0.0, z:0.0}, + normal: Vec3 {x:0.0, y:0.0, z:0.0}, + t: 0.0, + front_face: false, + } + } +} + +impl HitRecord { + pub fn set_face_normal(&mut self, r: &Ray, outward_normal: Vec3) { + self.front_face = r.direction.dot_prod(&outward_normal) < 0.0; + self.normal = if self.front_face { outward_normal } else { outward_normal.scalar_mul(-1.0) }; + } +} + +pub trait Hittable { + fn hit(&self, r: &Ray, ray_tmin: f32, ray_tmax: f32, rec: &mut HitRecord) -> bool; +} + +pub struct Sphere { + center: Point3, + radius: f32, +} + +impl Sphere { + pub fn new(center: Vec3, radius: f32) -> Self { + let r = f32::max(0.0, radius); + Self { center, radius } + } +} + +impl Hittable for Sphere { + fn hit(&self, r: &Ray, ray_tmin: f32, ray_tmax: f32, rec: &mut HitRecord) -> bool { + let oc = self.center.sub(&r.origin); + let a = r.direction.mag_sqr(); + let h = r.direction.dot_prod(&oc); + let c = oc.mag_sqr() - self.radius * self.radius; + let discriminant = h * h - a * c; + if discriminant < 0.0 { + return false; + } + + let sqrtd = discriminant.sqrt(); + + let mut root = (h - sqrtd) / a; + if root <= ray_tmin || ray_tmax <= root { + root = (h + sqrtd) / a; + if root <= ray_tmin || ray_tmax <= root { + return false; + } + } + + rec.t = root; + rec.position = r.at(rec.t); + let outward_normal = rec.normal + .sub(&self.center) + .scalar_mul(1.0 / self.radius); + rec.set_face_normal(r, outward_normal); + + true + } +} + +pub struct HittableList { + objects: Vec<Box<dyn Hittable>>, +} + +impl HittableList { + pub fn new() -> Self { + Self { objects: vec![] } + } + + pub fn push<T: Hittable + 'static>(&mut self, object: T) { + self.objects.push(Box::new(object)); + } +} + +impl Hittable for HittableList { + fn hit(&self, r: &Ray, ray_tmin: f32, ray_tmax: f32, rec: &mut HitRecord) -> bool { + let mut temp_rec = HitRecord::default(); + let mut hit_anything = false; + let mut closest_so_far = ray_tmax; + + self.objects.iter().for_each( + |object| { + if object.hit(r, ray_tmin, closest_so_far, &mut temp_rec) { + hit_anything = true; + closest_so_far = temp_rec.t; + *rec = temp_rec.clone(); + } + } + ); + + hit_anything + } +}
\ No newline at end of file diff --git a/src/renderer.rs b/src/renderer.rs index 49d8046..2fb76d2 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1,18 +1,18 @@ use crate::calculus::calculus::{Point3, Ray, Vec3}; use crate::global::{Color, DisplayBuffer, Pixel, CAMERA_CENTER, FOCAL_LENGTH, IMG_HEIGHT, IMG_WIDTH, VIEWPORT_HEIGHT, VIEWPORT_WIDTH}; +use crate::object::{HitRecord, Hittable, HittableList, Sphere}; -fn ray_color(ray: &Ray) -> Color { - let t = hit_sphere(&Point3 {x: 0.0, y: 0.0, z: -1.0}, 0.5, ray); - if t > 0.0 { - let normal = ray - .at(t) - .sub(&Vec3 {x: 0.0, y: 0.0, z: -1.0}) - .unit(); - return Color::new( - normal.x + 1.0, - normal.y + 1.0, - normal.z + 1.0, - ).mul_scalar(0.5); +fn ray_color(ray: &Ray, world: &HittableList) -> Color { + let mut rec = HitRecord::default(); + if world.hit(ray, 0.0, f32::INFINITY, &mut rec) { + let color = Color::new(1.0, 1.0, 1.0) + .add(&Color::new( + rec.normal.x, + rec.normal.y, + rec.normal.z) + ) + .mul_scalar(0.5); + return color; } let unit_direction = ray.direction.unit(); @@ -22,22 +22,9 @@ fn ray_color(ray: &Ray) -> Color { color1.add(&color2) } -fn hit_sphere(center: &Point3, radius: f32, ray: &Ray) -> f32 { - let oc = center.sub(&ray.origin); - let a = ray.direction.mag_sqr(); - let h = ray.direction.dot_prod(&oc); - let c = oc.mag_sqr() - radius * radius; - let discriminant = h * h - a * c; - if discriminant < 0.0 { - -1.0 - } else { - (h - discriminant.sqrt()) / a - } -} - pub fn render(display_buffer: &mut DisplayBuffer) { - let viewport_hor_vector = Vec3{ x: VIEWPORT_WIDTH as f32, y: 0.0, z: 0.0 }; - let viewport_ver_vector = Vec3 { x: 0.0, y: -1f32 * VIEWPORT_HEIGHT as f32, z: 0.0 }; + let viewport_hor_vector = Vec3{ x: VIEWPORT_WIDTH, y: 0.0, z: 0.0 }; + let viewport_ver_vector = Vec3 { x: 0.0, y: -1f32 * VIEWPORT_HEIGHT, z: 0.0 }; let delta_pixel_u = viewport_hor_vector.scalar_mul(1.0 / IMG_WIDTH as f32); let delta_pixel_v = viewport_ver_vector.scalar_mul(1.0 / IMG_HEIGHT as f32); @@ -50,6 +37,17 @@ pub fn render(display_buffer: &mut DisplayBuffer) { &delta_pixel_u.add(&delta_pixel_u).scalar_mul(0.5) ); + let mut world = HittableList::new(); + + world.push( + Sphere::new(Point3::new(0.0, 0.0, -1.0), 0.5) + ); + + // world.push( + // Sphere::new(Point3::new(0.0, -100.5, -1.0), 100.0) + // ); + + (0..IMG_HEIGHT).for_each(|j| { (0..IMG_WIDTH).for_each(|i| { let pixel_center = pixel_upper_left @@ -60,7 +58,7 @@ pub fn render(display_buffer: &mut DisplayBuffer) { origin: &CAMERA_CENTER, direction: &ray_direction, }; - let color = ray_color(&ray); + let color = ray_color(&ray, &world); display_buffer[j][i] = Pixel::from_color(&color); }) }) |