Skip to content

Commit

Permalink
14 - a lot of sphere
Browse files Browse the repository at this point in the history
  • Loading branch information
grishy committed Feb 20, 2024
1 parent 6ed2e44 commit 39ecc08
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 35 deletions.
61 changes: 48 additions & 13 deletions src/camera.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,34 @@ pub struct Camera {
pixel00_loc: Point3, // Location of pixel 0, 0
pixel_delta_u: Vector3, // Offset to pixel to the right
pixel_delta_v: Vector3, // Offset to pixel below
samples_per_pixel: i32, // Count of random samples for each pixel
max_depth: i32, // Maximum depth of recursion
defocus_angle: f64,
defocus_disk_u: Vector3, // Defocus disk horizontal radius
defocus_disk_v: Vector3, // Defocus disk vertical radius
samples_per_pixel: i32, // Count of random samples for each pixel
max_depth: i32, // Maximum depth of recursion
}

impl Camera {
pub fn new() -> Camera {
let aspect_ratio = 16.0 / 9.0;
let image_width = 1600;
let samples_per_pixel = 600;
let max_depth = 200;
let vfov: f64 = 30.0;
let look_from = Point3::new(-2.0, 2.0, 1.0);
let look_at = Point3::new(0.0, 0.0, -1.0);
let samples_per_pixel = 500;
let max_depth = 30;
let vfov: f64 = 20.0;
let look_from = Point3::new(13.0, 2.0, 3.0);
let look_at = Point3::new(0.0, 0.0, 0.0);
let vup = Vector3::new(0.0, 1.0, 0.0);

// TODO: Focus does not work
let defocus_angle = 0.1; // Variation angle of rays through each pixel
let focus_dist = 10.2; // Distance from camera lookfrom point to plane of perfect focus

let image_height: i32 = (image_width as f64 / aspect_ratio) as i32;

let theta = vfov.to_radians();
let h = (theta / 2.).tan();

let focal_length = (look_from - look_at).magnitude();
let viewport_height = 2. * h * focal_length;
let viewport_height = 2. * h * focus_dist;
let viewport_width = viewport_height * (image_width as f64 / image_height as f64);

let center = look_from;
Expand All @@ -62,16 +68,24 @@ impl Camera {
let pixel_delta_v = viewport_v / image_height as f64;

// Calculate the location of the upper left pixel.
let viewport_upper_left = center - focal_length * w - viewport_u / 2. - viewport_v / 2.;
let viewport_upper_left = center - focus_dist * w - viewport_u / 2. - viewport_v / 2.;
let pixel00_loc = viewport_upper_left + 0.5 * (pixel_delta_u + pixel_delta_v);

// Calculate the camera defocus disk basis vectors.
let defocus_radius = (defocus_angle / 2.0 as f64).to_radians().tan() * focus_dist;
let defocus_disk_u = defocus_radius * u;
let defocus_disk_v = defocus_radius * v;

Camera {
image_width,
image_height,
center,
pixel00_loc,
pixel_delta_u,
pixel_delta_v,
defocus_angle,
defocus_disk_u,
defocus_disk_v,
samples_per_pixel,
max_depth,
}
Expand Down Expand Up @@ -141,18 +155,29 @@ impl Camera {
pb.finish()
}

// Get a randomly sampled camera ray for the pixel at location i,j.
// Get a randomly-sampled camera ray for the pixel at location i,j, originating from
// the camera defocus disk.
fn get_ray(&self, x: i32, y: i32) -> ray::Ray {
let pixel_center =
self.pixel00_loc + (x as f64 * self.pixel_delta_u) + (y as f64 * self.pixel_delta_v);
let pixel_sample = self.pixel_sample_square();

let ray_origin = self.center;
let ray_origin = if self.defocus_angle < 0.0 {
self.center
} else {
self.defocus_disk_sample()
};
let ray_direction = (pixel_center + pixel_sample) - self.center;

ray::Ray::new(ray_origin, ray_direction)
}

// Returns a random point in the camera defocus disk.
fn defocus_disk_sample(&self) -> Point3 {
let rd = Camera::random_in_unit_disk();
self.center + (self.defocus_disk_u * rd.x) + (self.defocus_disk_v * rd.y)
}

// Returns a random point in the square surrounding a pixel at the origin.
fn pixel_sample_square(&self) -> Vector3 {
let mut rng = rand::thread_rng();
Expand All @@ -162,6 +187,16 @@ impl Camera {

(px * self.pixel_delta_u) + (py * self.pixel_delta_v)
}

fn random_in_unit_disk() -> Vector3 {
let mut rng = rand::thread_rng();
loop {
let p = Vector3::new(rng.gen_range(-1.0..1.0), rng.gen_range(-1.0..1.0), 0.0);
if p.magnitude_squared() < 1.0 {
return p;
}
}
}
}

fn ray_color(ray: &ray::Ray, depth: i32, world: &HittableList) -> Color {
Expand All @@ -188,7 +223,7 @@ fn ray_color(ray: &ray::Ray, depth: i32, world: &HittableList) -> Color {
},
None => {
let unit_direction = na::Unit::new_normalize(ray.dir);
let a = 0.5 * (unit_direction.y) + 1.0;
let a = 0.8 * (unit_direction.y) + 1.0;
(1.0 - a) * Color::new(1.0, 1.0, 1.0) + a * Color::new(0.5, 0.7, 1.0)
}
}
Expand Down
1 change: 0 additions & 1 deletion src/hittable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,3 @@ impl HitRecord {
};
}
}

79 changes: 58 additions & 21 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod ray;
mod sphere;
mod types;

use rand::Rng;
use std::ops::Range;
use std::sync::Arc;
use types::*;
Expand Down Expand Up @@ -44,35 +45,71 @@ fn main() {
let mut world = HittableList::new();

// Materials
let material_ground = Arc::new(material::Lambertian::new(Color::new(0.8, 0.8, 0.0)));
let material_center = Arc::new(material::Lambertian::new(Color::new(0.1, 0.2, 0.5)));
let material_left = Arc::new(material::Dielectric::new(1.5));
let material_right = Arc::new(material::Metal::new(Color::new(0.8, 0.6, 0.2), 0.0));

let material_ground = Arc::new(material::Lambertian::new(Color::new(0.5, 0.5, 0.5)));
world.add(Arc::new(sphere::Sphere::new(
Point3::new(0.0, -100.5, -1.0),
100.0,
Point3::new(0.0, -1000.0, -1.0),
1000.0,
material_ground,
)));

let mut rng = rand::thread_rng();

for a in -11..11 {
for b in -11..11 {
let choose_mat = rng.gen_range(0.0..1.0);
let center = Point3::new(
a as f64 + 0.9 * rng.gen_range(0.0..1.0),
0.2,
b as f64 + 0.9 * rng.gen_range(0.0..1.0),
);

if (center - Point3::new(4.0, 0.2, 0.0)).magnitude() > 0.9 {
let sphere_material: Arc<dyn material::Material + Send + Sync> = if choose_mat < 0.8
{
// diffuse
let albedo = Color::new(
rng.gen_range(0.0..1.0),
rng.gen_range(0.0..1.0),
rng.gen_range(0.0..1.0),
);
Arc::new(material::Lambertian::new(albedo))
} else if choose_mat < 0.95 {
// metal
let albedo = Color::new(
rng.gen_range(0.0..0.5),
rng.gen_range(0.0..0.5),
rng.gen_range(0.0..0.5),
);
let fuzz = rng.gen_range(0.0..0.5);
Arc::new(material::Metal::new(albedo, fuzz))
} else {
// glass
Arc::new(material::Dielectric::new(1.5))
};
world.add(Arc::new(sphere::Sphere::new(center, 0.2, sphere_material)));
}
}
}

let material_1 = Arc::new(material::Dielectric::new(1.5));
world.add(Arc::new(sphere::Sphere::new(
Point3::new(0.0, 0.0, -1.0),
0.5,
material_center,
)));
world.add(Arc::new(sphere::Sphere::new(
Point3::new(-1.0, 0.0, -1.0),
-0.4,
material_left.clone(),
Point3::new(0.0, 1.0, 0.0),
1.0,
material_1,
)));

let material_2 = Arc::new(material::Lambertian::new(Color::new(0.4, 0.2, 0.1)));
world.add(Arc::new(sphere::Sphere::new(
Point3::new(-1.0, 0.0, -1.0),
0.5,
material_left.clone(),
Point3::new(-4.0, 1.0, 0.0),
1.0,
material_2,
)));

let material_3 = Arc::new(material::Metal::new(Color::new(0.7, 0.6, 0.5), 0.0));
world.add(Arc::new(sphere::Sphere::new(
Point3::new(1.0, 0.0, -1.0),
0.5,
material_right,
Point3::new(4.0, 1.0, 0.0),
1.0,
material_3,
)));

// Render
Expand Down

0 comments on commit 39ecc08

Please sign in to comment.