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

Creating Our First Raytraced Image

If we take that math and hard-code it into our program, we can test our code by placing a small sphere at \( −1 \) on the z-axis and then coloring red any pixel that intersects it.

diff --git a/src/main.rs b/src/main.rs
index f31dc16..e3d9091 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,67 +1,81 @@
 use code::{
     color::{Color, write_color},
     ray::Ray,
-    vec3::{Point3, Vec3, unit_vector},
+    vec3::{Point3, Vec3, dot, unit_vector},
 };
 
+fn hit_sphere(center: Point3, radius: f64, r: Ray) -> bool {
+    let oc = center - r.origin();
+    let a = dot(r.direction(), r.direction());
+    let b = -2.0 * dot(r.direction(), oc);
+    let c = dot(oc, oc) - radius * radius;
+    let discriminant = b * b - 4.0 * a * c;
+
+    discriminant >= 0.0
+}
+
 fn ray_color(r: Ray) -> Color {
+    if hit_sphere(Point3::new(0.0, 0.0, -1.0), 0.5, r) {
+        return Color::new(1.0, 0.0, 0.0);
+    }
+
     let unit_direction = unit_vector(r.direction());
     let a = 0.5 * (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)
 }
 
 fn main() -> Result<(), Box<dyn std::error::Error>> {
     // Image
 
     const ASPECT_RATIO: f64 = 16.0 / 9.0;
     const IMAGE_WIDTH: i32 = 400;
 
     // Calculate the image height, and ensure that it's at least 1.
     const IMAGE_HEIGHT: i32 = {
         let image_height = (IMAGE_WIDTH as f64 / ASPECT_RATIO) as i32;
         if image_height < 1 { 1 } else { image_height }
     };
 
     // Camera
 
     let focal_length = 1.0;
     let viewport_height = 2.0;
     let viewport_width = viewport_height * (IMAGE_WIDTH as f64) / (IMAGE_HEIGHT as f64);
     let camera_center = Point3::new(0.0, 0.0, 0.0);
 
     // Calculate the vectors across the horizontal and down the vertical viewport edges.
     let viewport_u = Vec3::new(viewport_width, 0.0, 0.0);
     let viewport_v = Vec3::new(0.0, -viewport_height, 0.0);
 
     // Calculate the horizontal and vertical delta vectors from pixel to pixel.
     let pixel_delta_u = viewport_u / IMAGE_WIDTH as f64;
     let pixel_delta_v = viewport_v / IMAGE_HEIGHT as f64;
 
     // Calculate the location of the upper left pixel.
     let viewport_upper_left =
         camera_center - Vec3::new(0.0, 0.0, focal_length) - viewport_u / 2.0 - viewport_v / 2.0;
     let pixel00_loc = viewport_upper_left + 0.5 * (pixel_delta_u + pixel_delta_v);
 
     // Render
 
     env_logger::init();
     println!("P3");
     println!("{IMAGE_WIDTH} {IMAGE_HEIGHT}");
     println!("255");
 
     for j in 0..IMAGE_HEIGHT {
         log::info!("Scanlines remaining: {}", IMAGE_HEIGHT - j);
         for i in 0..IMAGE_WIDTH {
             let pixel_center =
                 pixel00_loc + (i as f64) * pixel_delta_u + (j as f64) * pixel_delta_v;
             let ray_direction = pixel_center - camera_center;
             let r = Ray::new(camera_center, ray_direction);
 
             let pixel_color = ray_color(r);
             write_color(std::io::stdout(), pixel_color)?;
         }
     }
     log::info!("Done.");
 
     Ok(())
 }

Listing 11: [main.rs] Rendering a red sphere


What we get is this:

A simple red sphere

Image 3: A simple red sphere