WARNING
Content Under Development

See release page for latest official PDF version.

**Chapter 3: Rays, a simple camera, and background** 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 $p(t) = A + t*B$. Here $p$ is a 3D position along a line in 3D. $A$ is the ray origin and $B$ is the ray direction. The ray parameter $t$ is a real number (float in the code). Plug in a different $t$ and $p(t)$ moves the point along the ray. Add in negative $t$ and you can go anywhere on the 3D line. For positive $t$, you get only the parts in front of $A$, and this is what is often called a half-line or ray. The example $C = p(2)$ is shown here: ![Figure 3-1](../assets/fig03-1.jpg) The function $p(t)$ in more verbose code form I call “point_at_parameter(t)”: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ #ifndef RAYH #define RAYH #include "vec3.h" class ray { public: ray() {} ray(const vec3& a, const vec3& b) { A = a; B = b; } vec3 origin() const { return A; } vec3 direction() const { return B; } vec3 point_at_parameter(float t) const { return A + t*B; } vec3 A; vec3 B; }; #endif ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Now we are ready to turn the corner and make a ray tracer. At the core of a ray tracer is to send rays through pixels and compute what color is seen in the direction of those rays. This is of the form calculate which ray goes from the eye to a pixel, compute what that ray intersects, and compute a color for that intersection point. When first developing a ray tracer, I always do a simple camera for getting the code up and running. I also make a simple `color(ray)` function that returns the color of the background (a simple gradient). I’ve often gotten into trouble using square images for debugging because I transpose $x$ and $y$ too often, so I’ll stick with a 200×100 image. I’ll put the “eye” (or camera center if you think of a camera) at $(0,0,0)$. I will have the y-axis go up, and the x-axis to the right. In order to respect the convention of a right handed coordinate system, into the screen is the negative z-axis. I will traverse the screen from the lower left hand corner and use two offset vectors along the screen sides to move the ray endpoint across the screen. Note that I do not make the ray direction a unit length vector because I think not doing that makes for simpler and slightly faster code. ![Figure 3-2](../assets/fig03-2.jpg) Below in code, the ray $r$ goes to approximately the pixel centers (I won’t worry about exactness for now because we’ll add antialiasing later): ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ #include #include "ray.h" vec3 color(const ray& r, hitable *world, int depth) { vec3 unit_direction = unit_vector(r.direction()); float t = 0.5*(unit_direction.y() + 1.0); return (1.0-t)*vec3(1.0, 1.0, 1.0) + t*vec3(0.5, 0.7, 1.0); } int main() { int nx = 200; int ny = 100; std::cout << "P3\n" << nx << " " << ny << "\n255\n"; vec3 lower_left_corner(-2.0, -1.0, -1.0); vec3 horizontal(4.0, 0.0, 0.0); vec3 vertical(0.0, 2.0, 0.0); vec3 origin(0.0, 0.0, 0.0); for (int j = ny-1; j >= 0; j--) { for (int i = 0; i < nx; i++) { float u = float(i) / float(nx); float v = float(j) / float(ny); ray r(origin, lower_left_corner + u*horizontal + v*vertical); vec3 col = color(r); int ir = int(255.99*col[0]); int ig = int(255.99*col[1]); int ib = int(255.99*col[2]); std::cout << ir << " " << ig << " " << ib << "\n"; } } } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The `color(ray)` function linearly blends white and blue depending on the up/downess of the $y$ coordinate. I first made it a unit vector so $-1.0 < y < 1.0$. I then did a standard graphics trick of scaling that to $0.0 < t < 1.0$. When $t = 1.0$ I want blue. When $t = 0.0$ I want white. In between, I want a blend. This forms a “linear blend”, or “linear interpolation”, or “lerp” for short, between two things. A lerp is always of the form $$ blendedValue = (1-t)*startValue + t*endValue, $$ with $t$ going from zero to one. In our case this produces: ![Image 3-1](../assets/img03-1.jpg)