Given a vector of (x, y, z), we know the square of the length of the vector is
square_of_length = x * x + y * y + z * z;
The right side of the above equation is the summation on component-wise self multiplications. Extending this pattern to two vectors, (x1, y1, z1) and (x2, y2, z2), we have dot product on them,
dot_product = x1 * x2 + y1 * y2 + z1 * z2;
So now we have a new operator on two vectors, called dot product operator, and usually it is also denoted by '*'. You can overload this operator in Point class as,
float operator*(Point const& rhs) const { float dot_pro = 0.0; dot_pro += x * rhs.x; dot_pro += y * rhs.y; dot_pro += z * rhs.z; return dot_pro; }
The equation of a sphere of radius r, and center at (cx,cy,cz) is,
sqrt( (x-cx)^2 + (y-cy)^2 + (z-cz)^2 ) = r; // a point (x,y,z) is on the sphere if this equation holds.
Using dot-product, we can translate the equation into,
Point P(x,y,z); Point C(cx, cy, cz); Vector V = P - C; V * V - r * r = 0
Remember our interpolation between two points P1 and P2,
P1 * (1-t) + P2 * t
Rearrange the terms, and consider the t value, we get
/* For a point P to be on the ray, starting at P1 and going through P2, it must be some interpolation of P1 and P2, and in addition the interpolation factor t must be positive. */ P = P1 + (P2 - P1) * t, and t >= 0.
Suppose the ray is starting from P1, and going through P2; and the sphere is of radius r, and center at C. If there is any intersection (hit) between the ray and the sphere, then there must be point P, which is both on the ray, and on the sphere, which means
P = P1 + (P2 - P1) * t. (P - C)^2 = r^2.
Plug the first equation into the second one, and use dot product, we get a quadratic equation in variable t,
(P2 - P1) * (P2 - P1) * t^2 + 2 * P1 * (P2 - P1) * t + P1 * P1 - r^2 = 0.
Now the intersection (hit) problem can be readily solved by working on a quadratic equation.
/* If the ray, starting from p1 and going through p2, hit the sphere, compute the closer hit point into hit_pnt(if this argument not 0), and return true. Else return false. */ bool hitSphere(float r, Point const& center, Point const& p1, Point const& p2, Point* hit_pnt=0) { Vector p12 = p2 - p1; /* Any point on the line from p1 to p2, can be interpolated as P = p1 * (1-t) + p2 * t = (p2 - p1) * t + p1 = p12 * t + p1. P is between p1 and p2 if t is in (0,1), and on the same side to p1 as p2 is if t > 0. */ /* Solve the quadratic equation: (p12 * t + p1 - cnt)^2 - r^2 = 0; */ /* First convert it into the standard form: A * t^2 + B * t + C = 0. */ double A = p12 * p12; double B = (p1 - center) * p12 * 2; double C = (p1 - center) * (p1 - center) - r*r; /* Compute the determinant B^2 - 4AC. */ float delta = (B*B-4*A*C); if(delta<0) return false; // no real solution, and no hit point. delta = sqrt(delta); double t = (-B-delta)/(2*A); // choose the solution which is closer to p1. if(t <= 0 ) return false; // the hit point is on the opposite side to p1 as p2 is to p1. if(hit_pnt) *hit_pnt = p1 + p12 * t; return true; }
The ray is starting at eye point, which is (0, 0, eye_at), and going through the lower left corner of the pixel, which is (col - hReso/2, row - vReso/2, 0).
As usual, we need to do a little physics beside the geometry. Since we have already done with diffuse lighting computation, all you have to do is to compute the normal at the hit point and also to compute the the lighting direction at that point.
Point light_pos; Vector light_dir = light_pos - hit_pnt; Vector normal = hit_pnt - sphere_center;
Now you can add a member function in your Canvas class,
void RayTracingShpere(float r, Point const& center);
and try to generate a ray-traced image of sphere in your main(),
... canvas.RayTracingShpere(r, center); canvas.WritePPM(); ...
to xchen AT cs DOT utah DOT eduvoid RayTracingShpere(float r, Point const& center)
1.3.6