Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

A Final Render

Let’s make the image on the cover of this book — lots of random spheres.

diff --git a/src/main.rs b/src/main.rs
index 51d420b..54ff4ef 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,58 +1,86 @@
 use code::{
     camera::Camera,
     hittable_list::HittableList,
-    material::{Dielectric, Lambertian, Metal},
+    material::{Dielectric, Lambertian, Material, Metal},
     prelude::*,
     sphere::Sphere,
 };
 
 fn main() -> std::io::Result<()> {
     let mut world = HittableList::new();
 
-    let material_ground = Rc::new(Lambertian::new(Color::new(0.8, 0.8, 0.0)));
-    let material_center = Rc::new(Lambertian::new(Color::new(0.1, 0.2, 0.5)));
-    let material_left = Rc::new(Dielectric::new(1.5));
-    let material_bubble = Rc::new(Dielectric::new(1.0 / 1.5));
-    let material_right = Rc::new(Metal::new(Color::new(0.8, 0.6, 0.2), 1.0));
-
-    world.add(Rc::new(Sphere::new(
-        Point3::new(0.0, -100.5, -1.0),
-        100.0,
-        material_ground,
-    )));
+    let ground_material = Rc::new(Lambertian::new(Color::new(0.5, 0.5, 0.5)));
     world.add(Rc::new(Sphere::new(
-        Point3::new(0.0, 0.0, -1.2),
-        0.5,
-        material_center,
+        Point3::new(0.0, -1000.0, 0.0),
+        1000.0,
+        ground_material,
     )));
+
+    for a in -11..11 {
+        for b in -11..11 {
+            let choose_mat: f64 = rand::random();
+            let center = Point3::new(
+                a as f64 + 0.9 * rand::random::<f64>(),
+                0.2,
+                b as f64 + 0.9 * rand::random::<f64>(),
+            );
+
+            if (center - Point3::new(4.0, 0.2, 0.0)).length() > 0.9 {
+                let sphere_material: Rc<dyn Material> = if choose_mat < 0.8 {
+                    // diffuse
+                    let albedo = Color::random() * Color::random();
+
+                    Rc::new(Lambertian::new(albedo))
+                } else if choose_mat < 0.95 {
+                    // metal
+                    let albedo = Color::random_range(0.5, 1.0);
+                    let fuzz = rand::random_range(0.0..0.5);
+
+                    Rc::new(Metal::new(albedo, fuzz))
+                } else {
+                    // glass
+
+                    Rc::new(Dielectric::new(1.5))
+                };
+
+                world.add(Rc::new(Sphere::new(center, 0.2, sphere_material)));
+            }
+        }
+    }
+
+    let material1 = Rc::new(Dielectric::new(1.5));
     world.add(Rc::new(Sphere::new(
-        Point3::new(-1.0, 0.0, -1.0),
-        0.5,
-        material_left,
+        Point3::new(0.0, 1.0, 0.0),
+        1.0,
+        material1,
     )));
+
+    let material2 = Rc::new(Lambertian::new(Color::new(0.4, 0.2, 0.1)));
     world.add(Rc::new(Sphere::new(
-        Point3::new(-1.0, 0.0, -1.0),
-        0.4,
-        material_bubble,
+        Point3::new(-4.0, 1.0, 0.0),
+        1.0,
+        material2,
     )));
+
+    let material3 = Rc::new(Metal::new(Color::new(0.7, 0.6, 0.5), 0.0));
     world.add(Rc::new(Sphere::new(
-        Point3::new(1.0, 0.0, -1.0),
-        0.5,
-        material_right,
+        Point3::new(4.0, 1.0, 0.0),
+        1.0,
+        material3,
     )));
 
     env_logger::init();
 
     Camera::default()
         .with_aspect_ratio(16.0 / 9.0)
-        .with_image_width(400)
-        .with_samples_per_pixel(100)
+        .with_image_width(1200)
+        .with_samples_per_pixel(500)
         .with_max_depth(50)
         .with_vfov(20.0)
-        .with_lookfrom(Point3::new(-2.0, 2.0, 1.0))
-        .with_lookat(Point3::new(0.0, 0.0, -1.0))
+        .with_lookfrom(Point3::new(13.0, 2.0, 3.0))
+        .with_lookat(Point3::new(0.0, 0.0, 0.0))
         .with_vup(Point3::new(0.0, 1.0, 0.0))
-        .with_defocus_angle(10.0)
-        .with_focus_dist(3.4)
+        .with_defocus_angle(0.6)
+        .with_focus_dist(10.0)
         .render(&world)
 }

Listing 88: [main.rs] Final scene


(Note that the code above differs slightly from the project sample code: the samples_per_pixel is set to 500 above for a high-quality image that will take quite a while to render. The project source code uses a value of 10 in the interest of reasonable run times while developing and validating.)

This gives:

Final scene

Image 23: Final scene


An interesting thing you might note is the glass balls don’t really have shadows which makes them look like they are floating. This is not a bug — you don’t see glass balls much in real life, where they also look a bit strange, and indeed seem to float on cloudy days. A point on the big sphere under a glass ball still has lots of light hitting it because the sky is re-ordered rather than blocked.