WARNING
Content Under Development

See release page for latest official PDF version.

**Chapter 4: Adding a sphere** Let’s add a single object to our ray tracer. People often use spheres in ray tracers because calculating whether a ray hits a sphere is pretty straightforward. Recall that the equation for a sphere centered at the origin of radius $R$ is $x^2 + y^2 + z^2 = R^2$. The way you can read that equation is “for any $(x, y, z)$, if $x^2 + y^2 + z^2 = R^2$ then $(x,y,z)$ is on the sphere, and otherwise it is not”. It gets uglier if the sphere center is at $(C_x, C_y, C_z)$: $$ (x-C_x)^2 + (y-C_y)^2 + (z-C_z)^2 = R^2 $$ In graphics, you almost always want your formulas to be in terms of vectors so all the x/y/z stuff is under the hood in the `vec3` class. You might note that the vector from center $ C = (C_x,C_y,C_z) $ to point $ P = (x,y,z) $ is $ (p - C) $, and therefore $$ dot((p - C),(p - C)) = (x-C_x)^2 + (y-C_y)^2 + (z-C_z)^2 $$ So the equation of the sphere in vector form is: $$ dot((p - C),(p - C)) = R^2 $$ We can read this as “any point p that satisfies this equation is on the sphere”. We want to know if our ray $ p(t) = A + t*B $ ever hits the sphere anywhere. If it does hit the sphere, there is some $t$ for which $p(t)$ satisfies the sphere equation. So we are looking for any t where this is true: $$ dot((p(t) - C),(p(t) - C)) = R^2 $$ or expanding the full form of the ray $p(t)$: $$ dot((A + t*B - C), (A + t*B - C)) = R^2 $$ The rules of vector algebra are all that we would want here, and if we expand that equation and move all the terms to the left hand side we get: $$ t^2 \cdot dot(B,B) + 2t \cdot dot(B,A-C) + dot(A-C,A-C) - R^2 = 0 $$ The vectors and $R$ in that equation are all constant and known. The unknown is $t$, and the equation is a quadratic, like you probably saw in your high school math class. You can solve for $t$ and there is a square root part that is either positive (meaning two real solutions), negative (meaning no real solutions), or zero (meaning one real solution). In graphics, the algebra almost always relates very directly to the geometry. What we have is: ![Figure 4-1][fig04-1] If we take that math and hard-code it into our program, we can test it by coloring red any pixel that hits a small sphere we place at -1 on the z-axis: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ bool hit_sphere(const vec3& center, float radius, const ray& r) { vec3 oc = r.origin() - center; float a = dot(r.direction(), r.direction()); float b = 2.0 * dot(oc, r.direction()); float c = dot(oc, oc) - radius*radius; float discriminant = b*b - 4*a*c; return (discriminant > 0); } vec3 color(const ray& r) { if (hit_sphere(vec3(0,0,-1), 0.5, r)) return vec3(1, 0, 0); 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); } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ What we get is this: ![Image 4-1][img04-1] Now this lacks all sorts of things -- like shading and reflection rays and more than one object -- but we are closer to halfway done than we are to our start! One thing to be aware of is that we tested whether the ray hits the sphere at all, but $t < 0$ solutions work fine. If you change your sphere center to $z = +1$ you will get exactly the same picture because you see the things behind you. This is not a feature! We’ll fix those issues next. [fig04-1]: ../assets/fig04-1.jpg [img04-1]: ../assets/img04-1.jpg