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:

Image 3: A simple red sphere