HW1: Raytrace a Sphere

2010, Dr. Lawlor, CS 481/681, CS, UAF

For this assignment, build a simple OpenGL program that raytraces one sphere.  Your program must:
You can use any proxy geometry you like, but I recommend just drawing a slightly larger sphere (e.g., glutSolidSphere).  You need not use OpenGL's (horrible) interface for lighting and shading; just hardcoding the sphere properties and light sources into your program is fine for now.

Intersecting Camera Rays with a Sphere

First, figure out your world-coordinates camera location.  Call this the vec3 C.   HINT: put in some "reference" geometry, and get your camera motion working and coordinate system settled before trying to do any raytracing!

Next, figure out the direction from the camera to the pixel you're currently drawing--if your proxy geometry is at a vec3 G (for example, a varying value), then the direction from the camera to the proxy is G-C.  
varying vec3 C; // camera location, world coordinates
varying vec3 G; // proxy geometry location, world coordinates
vec3 D = normalize(G-C); // points from camera to geometry
This means points along the camera ray satisfy this "parametric equation" for any float t:
	vec3 P = C + t * D; // move down the camera ray a distance t
We just need to determine the t value along our camera ray where the ray intersects our sphere.  When does a ray intersect a sphere?  Well, points on the sphere satisfy
	r = length(P);
Recall that length(P) = sqrt(P.x*P.x + P.y*P.y + P.z*P.z).  The square root is annoying, so square both sides:
	r*r = P.x*P.x + P.y*P.y + P.z*P.z;
It's a lot more compact to write the right-hand side as a vector dot product of P with P:
	r*r = dot(P,P);
Now if we substitute in our parametric ray equation, we get:
	r*r = dot(C + t * D,C + t * D);
Rearranging terms (since dot product distributes over vector addition):
	r*r = dot(C,C) + 2*t*dot(D,C) + t*t*dot(D,D);
The only unknown is the float t.  This is a quadratic equation in t.  If we write it with the standard quadratic form 0 = c + b*t+a*t*t, the coefficients are:
	float a=dot(D,D);
float b=2.0*dot(D,C);
float c=dot(C,C)-r*r;
From the quadratic equation, then, we get:
	float det=b*b-4.0*a*c; // quadratic determinant
if (det<0.0) discard; /* ray misses sphere */
float t=(-b-sqrt(det))/(2.0*a); // -b+sqrt... is ray leaving sphere
The sphere-ray intersection point P is then C+t*D.  Because a sphere's surface normal always points directly away from the origin of the sphere (our origin), the normal is equal to this intersection point, but normalized:
	vec3 P=C+t*D;
vec3 N=normalize(P);
You can then shade using the usual diffuse and specular components.

My raytraced sphere looks like this.  Note the grid, used to keep track of where I am in 3D space.
smooth grey GPU-raytraced sphere