Home » Programming » C++ Raytracer » Ray Tracer Part Three – Adding a Sphere

Ray Tracer Part Three – Adding a Sphere

gridcoin728x90_40b-99980e

Now lets create something to add to our scene.  One of the simplest objects we can add is a sphere, so lets get started with that first. We can define a sphere in our world as having a position, and a radius. But first lets create an abstract Object class, that all objects can derive from.

Object Class

Every object in our scene will share some common properties.  They will all have a color, and they will all have properties related to how they are illuminated.  They will also all need an intersect method.  In C++ the basic Object class would look something like the code below:

class Object {
public:
    Color color;
    float kd; // diffuse coefficient
    float ks; // specular coefficient
    float pr; // reflectvity
    float n;  // phong shininess
    float IOR; // index of refraction
    Object(Color color, float kd, float pr, float ks, float n, float IOR, bool transparent){
        ..... set the attributes
    }
    virtual int intersect(Point &origin, Ray &ray, float &distance, Ray &normal) = 0;
}

In the intersect method, the ‘distance’ reference contains the closest intersect distance thus far, and if the object being tested is intersected closer than this, distance will be modified;

Sphere Class

The sphere class thus needs to provide an implementation of the intersect method.  The following formula can be used to determine if a ray intersects a sphere, and the distance of the hit.

A*(distance^2) +B*distance + C = 0;

where

A = castRay.Dot(castRay)
B = 2*(origin - sphereCentre).Dot(castRay)
C = (origin-castRay).Dot(origin-castRay) - radius^2

This is a quadratic equation, so the two solutions for the intersect distance can be obtained using the quadratic formula.

When we test a ray we want to consider 3 possible situations.

1) The ray misses the sphere – When the discriminant is less than zero. or the intersect distance is greater than that of an intersect test with another object

2) The ray hits the sphere from the outside – When the discriminant is greater than zero, and the two calculated intersect distances are positive.

3) The ray hits the sphere from the inside – When the discriminant is greater than zero, and the lower distance is negative.

A = ray.Dot(ray);
originToCentreRay =  originOfRay - centreOfSphere;
B = originToCentreRay.Dot(ray)*2;
C = originToCentreRay.Dot(originToCentreRay) - radius*radius;

discriminant = B*B -4*A*C;
if(discriminant > 0){
   discriminant = std::sqrtf(discriminant);
   float distance1 = (-B - discriminant)/(2*A);
   float distance2 = (-B + discriminant)/(2*A);
   if(distance2 > FLT_EPSILON) //allow for rounding
   {
      if(distance1 < FLT_EPSILON) // allow for rounding
      {
         if(distance2 < distance) 
         {
            distance = distance2;
            normal.x = 10000;
            return -1; // In Object

         }
      }
      else
      {
         if(distance1 < distance)
         {
            normal.x = 10000;
            distance = distance1;
            return 1;
         }
      }
   }
}// Ray Misses
return 0;

You will notice that first the discriminant is checked to ensure it is greater than zero. If the discriminat is negative the ray does not intercept the sphere.  If the lesser of the two intersections is negative and the greater distance is positive, then the origin is inside the sphere.  A value of -1 is returned here as it is useful for correcting the normal later.  It is also necesary to ensure that this intersection is closer than any previous object intersection.

The normal.x value has been set here to an arbitrarily large number, so that other code knows it needs to explicitly calculate the normal. Of course the normal vector could be calculated simply here, however I found a slight performance improvement by only calculating the normal once we know of the nearest object.

Normal vector

In order to calculate lighting, reflection and refraction rays, we need to know the normal vector at the point of intersection.  All objects will need this method, so lets add it to the Object abstract class.

virtual void getNormal(Point& intersectPoint, Ray& normal) = 0;

In the sphere class, this will look something like this

void getNormal(Point& intersectpoint, Ray& normal)
{
   normal = intersectPoint - sphereCentre;
   normal.Normalise();
}

That’s it, now we’re ready to move on to applying illumination and shading to the sphere.

 


Leave a comment

Contact

Email: sjh148@uclive.ac.nz