diff options
author | Rosyid Haryadi <rosyid_haryadi@protonmail.com> | 2025-03-02 00:25:53 +0700 |
---|---|---|
committer | Rosyid Haryadi <rosyid_haryadi@protonmail.com> | 2025-03-02 00:25:53 +0700 |
commit | 1207c2c55c772155d02a148d76616881f3f6c125 (patch) | |
tree | 9a504e189f88f0417adcc558c268b408701cad58 | |
parent | 5fb34460b02b2b151dd775e43675bd4f09d2633a (diff) |
upd: anti aliasing
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/calculus.rs | 19 | ||||
-rw-r--r-- | src/camera.rs | 46 | ||||
-rw-r--r-- | src/global.rs | 10 | ||||
-rw-r--r-- | src/interval.rs | 6 |
5 files changed, 62 insertions, 20 deletions
@@ -5,3 +5,4 @@ edition = "2021" [dependencies] image = "0.25.5" +rand = "0.9.0" diff --git a/src/calculus.rs b/src/calculus.rs index c9b0574..a39c8a5 100644 --- a/src/calculus.rs +++ b/src/calculus.rs @@ -1,4 +1,6 @@ pub mod calculus { + use rand::Rng; + #[derive(Copy, Clone, Debug)] pub struct Vec3 { pub x: f32, @@ -68,12 +70,12 @@ pub mod calculus { } } - pub struct Ray<'a> { - pub origin: &'a Point3, - pub direction: &'a Vec3, + pub struct Ray { + pub origin: Point3, + pub direction: Vec3, } - impl<'a> Ray<'a> { + impl Ray { pub fn at(&self, t: f32) -> Point3 { // Get parametric location self.origin.add(&self.direction.scalar_mul(t)) @@ -84,4 +86,13 @@ pub mod calculus { deg * std::f32::consts::PI / 180.0 } + pub fn sample_square() -> Vec3 { + // Returns the vector to a random point in the [-.5,-.5]-[+.5,+.5] unit square. + let mut rng = rand::rng(); + Vec3::new( + rng.random_range(0.0..1.0) - 0.5, + rng.random_range(0.0..1.0) - 0.5, + 0.0) + } + }
\ No newline at end of file diff --git a/src/camera.rs b/src/camera.rs index 587a312..8e9cf68 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,16 +1,18 @@ -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::calculus::calculus::{sample_square, Point3, Ray, Vec3}; +use crate::global::{get_image_height, Color, DisplayBuffer, Pixel, ASPECT_RATIO, CAMERA_CENTER, FOCAL_LENGTH, IMG_WIDTH, SAMPLES_PER_PIXEL, VIEWPORT_HEIGHT}; use crate::interval::Interval; -use crate::object::{HitRecord, Hittable, HittableList, Sphere}; +use crate::object::{HitRecord, Hittable, HittableList}; pub struct Camera { pub aspect_ratio: f32, pub image_width: usize, + pub samples_per_pixel: usize, image_height: usize, center: Point3, pixel_upper_left: Point3, delta_pixel_u: Vec3, delta_pixel_v: Vec3, + pixel_samples_scale: f32, } impl Camera { @@ -23,6 +25,9 @@ impl Camera { let center = CAMERA_CENTER; let focal_length = FOCAL_LENGTH; + let samples_per_pixel = SAMPLES_PER_PIXEL; + let pixel_samples_scale = 1.0 / samples_per_pixel as f32; + let viewport_height = VIEWPORT_HEIGHT; let viewport_width = viewport_height * (image_width as f32 / image_height as f32); @@ -48,6 +53,23 @@ impl Camera { pixel_upper_left, delta_pixel_u, delta_pixel_v, + samples_per_pixel, + pixel_samples_scale, + } + } + + fn get_ray(&self, i: usize, j: usize) -> Ray { + let offset = sample_square(); + let pixel_sample = self.pixel_upper_left + .add(&self.delta_pixel_u.scalar_mul(i as f32 + offset.x)) + .add(&self.delta_pixel_v.scalar_mul(j as f32 + offset.y)); + + let ray_origin = self.center; + let ray_direction = pixel_sample.sub(&ray_origin); + + Ray { + origin: ray_origin, + direction: ray_direction, } } @@ -75,16 +97,14 @@ impl Camera { 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); + let mut pixel_color = Color::new(0.0, 0.0, 0.0); + (0..self.samples_per_pixel).for_each(|_| { + let r = self.get_ray(i, j); + let ray_color = self.ray_color(&r, world); + pixel_color = pixel_color.add(&ray_color); + }); + pixel_color = pixel_color.mul_scalar(self.pixel_samples_scale); + display_buffer[j][i] = Pixel::from_color(&pixel_color); }) }) } diff --git a/src/global.rs b/src/global.rs index fc9e0c9..3f74ba1 100644 --- a/src/global.rs +++ b/src/global.rs @@ -1,4 +1,5 @@ use crate::calculus::calculus::Point3; +use crate::interval::Interval; // "Ideal" aspect ratio, allowing fraction pub const ASPECT_RATIO: f32 = 16.0f32 / 9.0f32; @@ -9,6 +10,8 @@ pub const VIEWPORT_HEIGHT: f32 = 2.0; pub const FOCAL_LENGTH: f32 = 1.0; pub const CAMERA_CENTER: Point3 = Point3 { x: 0f32, y: 0f32, z: 0f32 }; +pub const SAMPLES_PER_PIXEL: usize = 100; + 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 } @@ -52,10 +55,11 @@ pub struct Pixel { impl Pixel { pub fn from_frac(r: f32, g: f32, b: f32) -> Self { + let intensity = Interval::new(0.0, 0.999); Self { - r: (255.999 * r) as u8, - g: (255.999 * g) as u8, - b: (255.999 * b) as u8, + r: (256.0 * intensity.clamp(r)) as u8, + g: (256.0 * intensity.clamp(g)) as u8, + b: (256.0 * intensity.clamp(b)) as u8, } } diff --git a/src/interval.rs b/src/interval.rs index 24b1841..5086ab2 100644 --- a/src/interval.rs +++ b/src/interval.rs @@ -19,6 +19,12 @@ impl Interval { pub fn surrounds(&self, x: f32) -> bool { self.min < x && x < self.max } + + pub fn clamp(&self, x: f32) -> f32 { + if x < self.min { return self.min } + if x > self.max { return self.max } + x + } } impl Default for Interval { |