diff options
author | Rosyid Haryadi <rosyid_haryadi@protonmail.com> | 2025-03-01 23:07:43 +0700 |
---|---|---|
committer | Rosyid Haryadi <rosyid_haryadi@protonmail.com> | 2025-03-01 23:07:43 +0700 |
commit | 5fb34460b02b2b151dd775e43675bd4f09d2633a (patch) | |
tree | a596b722257e7c376255ba4a3372f4cabe7a370c | |
parent | fb30b41fb1f4ebbe5274d2cb93e98a1f2bec445f (diff) |
refactor camera
-rw-r--r-- | src/camera.rs | 91 | ||||
-rw-r--r-- | src/global.rs | 69 | ||||
-rw-r--r-- | src/main.rs | 19 | ||||
-rw-r--r-- | src/renderer.rs | 67 |
4 files changed, 143 insertions, 103 deletions
diff --git a/src/camera.rs b/src/camera.rs new file mode 100644 index 0000000..587a312 --- /dev/null +++ b/src/camera.rs @@ -0,0 +1,91 @@ +use crate::calculus::calculus::{Point3, Ray, Vec3}; +use crate::global::{get_image_height, Color, DisplayBuffer, Pixel, ASPECT_RATIO, CAMERA_CENTER, FOCAL_LENGTH, IMG_WIDTH, VIEWPORT_HEIGHT}; +use crate::interval::Interval; +use crate::object::{HitRecord, Hittable, HittableList, Sphere}; + +pub struct Camera { + pub aspect_ratio: f32, + pub image_width: usize, + image_height: usize, + center: Point3, + pixel_upper_left: Point3, + delta_pixel_u: Vec3, + delta_pixel_v: Vec3, +} + +impl Camera { + pub fn new() -> Self { + let aspect_ratio = ASPECT_RATIO; + + let image_width = IMG_WIDTH; + let image_height = get_image_height(image_width, aspect_ratio); + + let center = CAMERA_CENTER; + let focal_length = FOCAL_LENGTH; + + let viewport_height = VIEWPORT_HEIGHT; + let viewport_width = viewport_height * (image_width as f32 / image_height as f32); + + 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 / image_width as f32); + let delta_pixel_v = viewport_ver_vector.scalar_mul(1.0 / image_height as f32); + + let viewport_upper_left = center + .sub(&Vec3 { x: 0f32, y: 0f32, z: focal_length}) + .sub(&viewport_hor_vector.scalar_mul(0.5)) + .sub(&viewport_ver_vector.scalar_mul(0.5)); + let pixel_upper_left = viewport_upper_left.add( + &delta_pixel_u.add(&delta_pixel_u).scalar_mul(0.5) + ); + + Self { + aspect_ratio, + image_width, + image_height, + center, + pixel_upper_left, + delta_pixel_u, + delta_pixel_v, + } + } + + fn ray_color(&self, ray: &Ray, world: &HittableList) -> Color { + let mut rec = HitRecord::default(); + let ray_t = Interval::new(0.0, f32::INFINITY); + if world.hit(ray, ray_t, &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(); + let a = 0.5 * (unit_direction.y + 1.0); + let color1 = Color::new(1.0, 1.0, 1.0).mul_scalar(1.0 - a); + let color2 = Color::new(0.5, 0.7, 1.0).mul_scalar(a); + color1.add(&color2) + } + + pub fn render(&self, display_buffer: &mut DisplayBuffer, world: &HittableList) { + (0..self.image_height).for_each(|j| { + (0..self.image_width).for_each(|i| { + let pixel_center = self.pixel_upper_left + .add(&self.delta_pixel_u.scalar_mul(i as f32)) + .add(&self.delta_pixel_v.scalar_mul(j as f32)); + let ray_direction = pixel_center.sub(&self.center); + let ray = Ray { + origin: &self.center, + direction: &ray_direction, + }; + let color = self.ray_color(&ray, &world); + display_buffer[j][i] = Pixel::from_color(&color); + }) + }) + } +}
\ No newline at end of file diff --git a/src/global.rs b/src/global.rs index 9357920..fc9e0c9 100644 --- a/src/global.rs +++ b/src/global.rs @@ -1,18 +1,48 @@ use crate::calculus::calculus::Point3; - - // "Ideal" aspect ratio, allowing fraction pub const ASPECT_RATIO: f32 = 16.0f32 / 9.0f32; -pub const IMG_WIDTH: usize = 400; -const IMG_HEIGHT_TMP: usize = (IMG_WIDTH as f32 / ASPECT_RATIO) as usize; -pub const IMG_HEIGHT: usize = if (IMG_HEIGHT_TMP < 1) { 1 } else { IMG_HEIGHT_TMP }; +pub const IMG_WIDTH: usize = 1000; +pub const IMG_HEIGHT: usize = get_image_height(IMG_WIDTH, ASPECT_RATIO); pub const VIEWPORT_HEIGHT: f32 = 2.0; -pub const VIEWPORT_WIDTH: f32 = VIEWPORT_HEIGHT * (IMG_WIDTH as f32 / IMG_HEIGHT as f32); pub const FOCAL_LENGTH: f32 = 1.0; pub const CAMERA_CENTER: Point3 = Point3 { x: 0f32, y: 0f32, z: 0f32 }; +pub const fn get_image_height(image_width: usize, aspect_ratio: f32) -> usize { + let image_height = (image_width as f32 / aspect_ratio) as usize; + if image_width < 1 { 1 } else { image_height } +} + +pub struct Color { + pub r: f32, + pub g: f32, + pub b: f32, +} + +impl Color { + pub fn new(r: f32, g: f32, b: f32) -> Self { + Self { r, g, b } + } + + pub fn mul_scalar(&self, scalar: f32) -> Color { + Self { + r: self.r * scalar, + g: self.g * scalar, + b: self.b * scalar, + } + } + + pub fn add(&self, other: &Self) -> Color { + Self { + r: self.r + other.r, + g: self.g + other.g, + b: self.b + other.b + } + } +} + + #[derive(Debug, Copy, Clone)] pub struct Pixel { pub r: u8, @@ -43,30 +73,3 @@ impl Default for Pixel { pub type DisplayBuffer = [[Pixel; IMG_WIDTH]; IMG_HEIGHT]; -pub struct Color { - pub r: f32, - pub g: f32, - pub b: f32, -} - -impl Color { - pub fn new(r: f32, g: f32, b: f32) -> Self { - Self { r, g, b } - } - - pub fn mul_scalar(&self, scalar: f32) -> Color { - Self { - r: self.r * scalar, - g: self.g * scalar, - b: self.b * scalar, - } - } - - pub fn add(&self, other: &Self) -> Color { - Self { - r: self.r + other.r, - g: self.g + other.g, - b: self.b + other.b - } - } -} diff --git a/src/main.rs b/src/main.rs index 3c67e9e..02efc49 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,19 +1,32 @@ mod view; mod global; mod calculus; -mod renderer; +mod camera; mod object; mod interval; +use crate::calculus::calculus::Point3; use crate::global::*; -use crate::renderer::render; +use crate::camera::Camera; +use crate::object::{HittableList, Sphere}; use crate::view::{render_viewer, View}; fn main() { let mut display_buffer: DisplayBuffer = [[Pixel::default(); IMG_WIDTH]; IMG_HEIGHT]; + 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) + ); + + let camera = Camera::new(); // test_render(&mut display_buffer); - render(&mut display_buffer); + camera.render(&mut display_buffer, &world); let view = View { diff --git a/src/renderer.rs b/src/renderer.rs deleted file mode 100644 index 72ecf4e..0000000 --- a/src/renderer.rs +++ /dev/null @@ -1,67 +0,0 @@ -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::interval::Interval; -use crate::object::{HitRecord, Hittable, HittableList, Sphere}; - -fn ray_color(ray: &Ray, world: &HittableList) -> Color { - let mut rec = HitRecord::default(); - let ray_t = Interval::new(0.0, f32::INFINITY); - if world.hit(ray, ray_t, &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(); - let a = 0.5 * (unit_direction.y + 1.0); - let color1 = Color::new(1.0, 1.0, 1.0).mul_scalar(1.0 - a); - let color2 = Color::new(0.5, 0.7, 1.0).mul_scalar(a); - color1.add(&color2) -} - -pub fn render(display_buffer: &mut DisplayBuffer) { - 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); - - let viewport_upper_left = CAMERA_CENTER - .sub(&Vec3 { x: 0f32, y: 0f32, z: FOCAL_LENGTH}) - .sub(&viewport_hor_vector.scalar_mul(0.5)) - .sub(&viewport_ver_vector.scalar_mul(0.5)); - let pixel_upper_left = viewport_upper_left.add( - &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 - .add(&delta_pixel_u.scalar_mul(i as f32)) - .add(&delta_pixel_v.scalar_mul(j as f32)); - let ray_direction = pixel_center.sub(&CAMERA_CENTER); - let ray = Ray { - origin: &CAMERA_CENTER, - direction: &ray_direction, - }; - let color = ray_color(&ray, &world); - display_buffer[j][i] = Pixel::from_color(&color); - }) - }) -}
\ No newline at end of file |