Interactive Raytracer 4: Reflection rays

Now, I will show the implementation of reflection (from the Whitted approach). It is basically using the reflection law and recurse the ray cast.

Reflection rays

Each object can reflect a ray more or less from a different object. A mirror would reflect the light totally, and a matte object would reflect nothing. Each new reflection is a new ray tracing call, so it can be costly. The number of recursion levels will be fixed, even if an object reflects nothing: this will be implemented through shaders in the future.

Knowing the normal to the intersection point, the reflected ray can be computing: it’s just the symmetric ray of the incident ray.

Some elements will be stored in a material structure that will be used to compute the reflected color:

/// Caracteristics of the primitive at a given point
struct MaterialPoint
{
  /// Normale at the intersection point
  Normal3df normal;
};

In the past, I’ve used this structure to contain diffuse or reflection colors, but usually, an object has not mirror property in red and matte in green, so this was deleted.

Now, I need to update the raytracer:

void Raytracer::computeColor(const Ray& ray, Color& color, unsigned int level) const
{
  float dist;
  long index = scene->getFirstCollision(ray, dist);
  if(index < 0)
    return;

  Primitive* primitive = scene->getPrimitive(index);
  MaterialPoint caracteristics;
  primitive->computeColorNormal(ray, dist, caracteristics);
  color = scene->computeColor(ray.origin() + dist * ray.direction(), caracteristics, primitive);

  if(level < levels)
  {
    Ray ray_sec(ray.origin() + dist * ray.direction(), ray.direction() - (ray.direction() * caracteristics.normal) * 2 * caracteristics.normal);
    ray_sec.direction() *= 1./(sqrt(norm2(ray_sec.direction())));
    Color color_sec(0.);
    computeColor(ray_sec, color_sec, level+1);
    color += mult(color_sec, caracteristics.reflect);
  }

I need to update the original collision method. Contrary to the last post, it isn't enough to test if one primitive is hit by a ray, but I still need to test if the retrieved intersection isn't too close to the original object. Indeed, I could be testing the intersection with the current object because of numerical instabilities. So I've added a minimum distance test:

long SimpleScene::getFirstCollision(const Ray& ray, float& dist)
{
  float min_dist = std::numeric_limits::max();
  long min_primitive = -1;

  for(std::vector::const_iterator it = primitives.begin(); it != primitives.end(); ++it)
  {
    float dist;
    bool test = (*it)->intersect(ray, dist);

    if(test && (0.0001f < dist) && (dist < min_dist))
    {
      min_primitive = it - primitives.begin();
      min_dist = dist;
    }
  }

  if(min_primitive == -1)
    return -1;
  else
  {
    dist = min_dist;
    return min_primitive;
  }
}

Now, when a ray hits a primitive, a material structure is used to get the normal and the other parameters at the intersection point.

The level parameter indicates the recursion level we are at. If the level isn't high enough, the symmetric ray is cast and the reflected color is used to add the color of the object. I do not test if the contribution is too small, it is possible in some implementations, but not here. Also, adding more recursion is not always costly, as after a while, all rays point outside the scene.

Result

Now, this is the result with 3 recursion levels and 3 lights:

IRT with reflection rays

Buy Me a Coffee!
Other Amount:
Your Email Address:

Leave a Reply

Your email address will not be published. Required fields are marked *