Rays and Simple Camera
Introduction: From Math to Light
In the previous lesson, you completed a deep dive into the vec3 class and gained a solid understanding of vector mathematics. You learned how vectors represent positions, directions, and colors, and you explored operations like addition, scalar multiplication, dot products, and cross products. You saw how the same vec3 class serves multiple purposes through type aliases like point3 and color. That lesson gave you the mathematical toolkit you'll need for everything that follows.
Now it's time to put that toolkit to work. In this lesson, we're making the crucial transition from mathematical foundations to actual ray tracing. We're going to create our first rays, build a simple virtual camera, and cast those rays into a scene. By the end of this lesson, you'll generate your first "traced" image — a beautiful gradient background that simulates a sky. While this might seem simple compared to rendering complex 3D objects, it represents a fundamental milestone: you'll be casting rays through pixels and determining colors based on those rays, which is the core mechanism of ray tracing.
The outcome of this lesson is concrete and visual. You'll implement a ray class that represents a ray with an origin and direction. You'll set up a virtual camera with specific parameters like aspect ratio, viewport dimensions, and focal length. You'll write code that casts a ray from the camera through each pixel of your image. And you'll implement a ray_color() function that returns a gradient color based on the ray's direction, creating a smooth transition from white at the bottom to blue at the top — just like a real sky.
This lesson is where ray tracing truly begins. Everything before this was preparation; everything after this will build on what you create today. The rays you define here will eventually intersect with spheres, planes, and other objects. The camera you build will evolve to support different viewing angles and perspectives. The color function will grow more sophisticated to handle lighting, shadows, and reflections. But it all starts with these fundamentals: rays, a camera, and a simple way to determine color. Let's begin by understanding what a ray really is.
Understanding Rays: The Math Behind P(t) = A + tb
At the heart of ray tracing is a deceptively simple mathematical concept: the ray. A ray is defined by two pieces of information — an origin point and a direction vector. Together, these two pieces let us describe an infinite line that starts at the origin and extends forever in the specified direction. In mathematical notation, we express a ray as a function of a parameter t:
P(t) = A + t·b
Let's break down what each part of this equation means. A is the origin of the ray, a point in 3D space represented as a point3 (which, as you know, is just a vec3). This is where the ray begins. b is the direction vector, also a vec3, which tells us which way the ray is pointing. The parameter t is a real number that lets us move along the ray. When t is zero, P(0) = A + 0·b = A, so we're at the origin. When t is one, P(1) = A + b, so we've moved one unit of the direction vector away from the origin. When t is two, we've moved two units, and so on.
The beauty of this formulation is that by varying t, we can reach any point along the ray. If t is positive, we move forward from the origin in the direction of b. If t is negative (though we typically don't use negative values in ray tracing), we'd move backward. This parameterization gives us a way to "walk" along the ray and check for intersections with objects in the scene. When we ask, "Does this ray hit that sphere?" we're really asking, "Is there some value of t where P(t) lies on the surface of the sphere?"
Let's make this concrete with a real-world analogy. Imagine you're standing in a field with a laser pointer. Your position is the origin A. The direction you're pointing the laser is the direction vector b. The laser beam itself is the ray — an infinite line extending from your hand in the direction you're pointing. Now imagine you want to know where the laser beam is at different distances. If you want to know where it is one meter away, you calculate A + 1·b. If you want to know where it is five meters away, you calculate A + 5·b. The parameter t represents the distance along the beam.
In ray tracing, we use rays to simulate the paths that light takes through a scene. In the real world, light travels from sources (like the sun or a lamp) and bounces around until some of it reaches our eyes. In ray tracing, we reverse this process for efficiency. We shoot rays from the camera (representing our eye) into the scene and trace them backward to see what they hit. Each ray represents a potential path that light could have taken to reach the camera. By determining what each ray hits and how light would interact with those surfaces, we can calculate the color that should appear at each pixel.
This is why rays are the foundation of ray tracing. Every pixel in your final image corresponds to at least one ray cast from the camera. The color of that pixel is determined by tracing that ray through the scene, finding what it intersects, and calculating how light would behave at that intersection. Even in this lesson, where we're not yet intersecting any objects, we're still using rays to determine pixel colors — we're just basing the color on the ray's direction rather than on any intersection.
The ray equation P(t) = A + t·b is simple, but it's powerful. It gives us a mathematical way to represent light paths, to traverse those paths, and to ask questions about what lies along them. As we move forward, you'll see this equation appear again and again in different contexts. When we calculate ray-sphere intersections, we'll substitute the ray equation into the sphere equation and solve for t. When we calculate reflections, we'll use the ray equation to determine where reflected rays originate and which direction they travel. The ray is the fundamental building block of everything we'll do.
