The ray Class
The one thing that all ray tracers have is a ray class and a computation of what color is seen along a ray. Let’s think of a ray as a function \( \mathbf{P} (t) = \mathbf{A} + t \mathbf{b} \). Here \( \mathbf{P} \) is a 3D position along a line in 3D. \( \mathbf{A} \) is the ray origin and \( \mathbf{b} \) is the ray direction. The ray parameter \( t \) is a real number (double in the code). Plug in a different \( t \) and \( \mathbf{P} (t) \) moves the point along the ray. Add in negative \( t \) values and you can go anywhere on the 3D line. For positive \( t \), you get only the parts in front of \( \mathbf{A} \), and this is what is often called a half-line or a ray.
Figure 2: Linear interpolation
We can represent the idea of a ray as a class, and represent the function \( \mathbf{P} (t) \) as a function that we'll call ray::at(t)
:
use crate::vec3::{Point3, Vec3};
#[derive(Debug, Default, Clone, Copy)]
pub struct Ray {
origin: Point3,
direction: Vec3,
}
impl Ray {
pub fn new(origin: Point3, direction: Vec3) -> Self {
Self { origin, direction }
}
pub fn origin(&self) -> Point3 {
self.origin
}
pub fn direction(&self) -> Vec3 {
self.direction
}
pub fn at(&self, t: f64) -> Point3 {
self.origin + t * self.direction
}
}
Listing 7: [ray.rs] The ray class
(For those unfamiliar with C++, the functions ray::origin() and ray::direction() both return an immutable reference to their members. Callers can either just use the reference directly, or make a mutable copy depending on their needs.) 1
-
The careful reader may have noticed that, in the Rust approach, both the
Vec3
andRay
structs implement Copy. This method is generally more common than returning a reference and cloning it for mutability when needed. ↩