summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRosyid Haryadi <rosyid_haryadi@protonmail.com>2025-03-02 00:25:53 +0700
committerRosyid Haryadi <rosyid_haryadi@protonmail.com>2025-03-02 00:25:53 +0700
commit1207c2c55c772155d02a148d76616881f3f6c125 (patch)
tree9a504e189f88f0417adcc558c268b408701cad58
parent5fb34460b02b2b151dd775e43675bd4f09d2633a (diff)
upd: anti aliasing
-rw-r--r--Cargo.toml1
-rw-r--r--src/calculus.rs19
-rw-r--r--src/camera.rs46
-rw-r--r--src/global.rs10
-rw-r--r--src/interval.rs6
5 files changed, 62 insertions, 20 deletions
diff --git a/Cargo.toml b/Cargo.toml
index c25a669..c383576 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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 {