WARNING
Content Under Development

See release page for latest official PDF version.

**Chapter 6: Antialiasing** When a real camera takes a picture, there are usually no jaggies along edges because the edge pixels are a blend of some foreground and some background. We can get the same effect by averaging a bunch of samples inside each pixel. We will not bother with stratification, which is controversial but is usual for my programs. For some ray tracers it is critical, but the kind of general one we are writing doesn’t benefit very much from it and it makes the code uglier. We abstract the camera class a bit so we can make a cooler camera later. One thing we need is a random number generator that returns real random numbers. We need a function that returns a canonical random number which by convention returns random real in the range $0 ≤ ran < 1$. The “less than” before the 1 is important as we will sometimes take advantage of that. A simple approach to this is to use the `rand()` function that can be found in ``. This function returns a random integer in the range 0 and RANDMAX. Hence we can get a real random number as desired with the following code snippet: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ #ifndef RANDOMH #define RANDOMH #include inline double random_double() { return rand() / (RAND_MAX + 1.0); } #endif ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ did not traditionally have a standard random number generator, but newer versions of C++ have addressed this issue with the `` header (if imperfectly according to some experts). If you want to use this, you can obtain a random number with the conditions we need as follows: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ #ifndef RANDOMH #define RANDOMH #include #include inline double random_double() { static std::uniform_real_distribution distribution(0.0, 1.0); static std::mt19937 generator; static std::function rand_generator = std::bind(distribution, generator); return rand_generator(); } #endif ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ For a given pixel we have several samples within that pixel and send rays through each of the samples. The colors of these rays are then averaged: ![Figure 6-1](../assets/fig06-1.jpg) Putting that all together yields a camera class encapsulating our simple axis-aligned camera from before: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ #ifndef CAMERAH #define CAMERAH #include "ray.h" class camera { public: camera() { lower_left_corner = vec3(-2.0, -1.0, -1.0); horizontal = vec3(4.0, 0.0, 0.0); vertical = vec3(0.0, 2.0, 0.0); origin = vec3(0.0, 0.0, 0.0); } ray get_ray(float u, float v) { return ray(origin, lower_left_corner + u*horizontal + v*vertical - origin); } vec3 origin; vec3 lower_left_corner; vec3 horizontal; vec3 vertical; }; #endif ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Main is also changed: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ int main() { int nx = 200; int ny = 100; int ns = 100; std::cout << "P3\n" << nx << " " << ny << "\n255\n"; hitable *list[2]; list[0] = new sphere(vec3(0,0,-1), 0.5); list[1] = new sphere(vec3(0,-100.5,-1), 100); hitable *world = new hitable_list(list,2); camera cam; for (int j = ny-1; j >= 0; j--) { for (int i = 0; i < nx; i++) { vec3 col(0, 0, 0); for (int s=0; s < ns; s++) { float u = float(i + random_double()) / float(nx); float v = float(j + random_double()) / float(ny); ray r = cam.get_ray(u, v); col += color(r, world); } col /= float(ns); 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"; } } } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Zooming into the image that is produced, the big change is in edge pixels that are part background and part foreground: ![Image 6-1](../assets/img06-1.jpg)