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:

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.