This is a summary of one of my school project.

I have chosen to present the results obtained during the practical work on ray tracing. As a reminder, the purpose of Ray-Tracing is to create and display effects based on calculations of rays coming from the camera and projections.

Technologies used : C++, OpenCV, Cmake.

 The majority of the code revolves around 3 .cpp files:

 • primitive.cpp, which contains the definition of different geometric primitives and the calculation of intersections between the ray-tracing rays and a possible primitive.

• render_engine.cpp, which contains different functions related to rays, colors, shadows, and reflections.

• The rest of the image elements are managed by the scene.cpp program, which is related to cameras, materials, and illumination.

The render() function in render_engine.cpp allows the creation of rays. The necessary arguments are a camera and a direction vector (u,v). We need a point and an orientation to create a ray, which is observed through the structure of the ray class.

You can find the code at the following link.

Methods and Results: Initially, the algorithm displayed 3 spheres (figure 1), using an existing ray-tracing algorithm. First, we created an image buffer with the provided image class, then we filled our scene with the scene_parameter class, consisting of 3 spheres, a plane, and a light source. Finally, the rendering was obtained via the render function, which calls the ray-tracing, and a save function was used to save our rendering.

Intersections

As we can see in this initial rendering, the arrangement of our scene has nothing to do with the desired result, notably the plane is not horizontal at all. To address this issue, we need to define different intersection methods so that a ray can return the desired information depending on the object it encounters (a ray does not reflect in the same way on a sphere as it does on a plane).

Nearest Intersection search

Once our objects are properly shaped, we aim to determine the first intersection, if it exists, between our ray and a primitive. This will allow us to define an intersection between the plane and the spheres. After finding the nearest intersection, you can see what we obtain on the following picture:

Shadow

In this section, we have to code the function is_in_shadow(). We know that the shadow calculation starts from the same point as the intersection location. Therefore, we will use the offset() method in the form of radius.offset() before calculating whether the current point is in the shadow of another object with respect to the given light.

For this, we use the normal vector between the starting point of the ray and the end point. We apply the offset() method to this ray. The function will then return a Boolean corresponding to the result of compute_intersection() with parameters being the previously calculated ray, the scene, and the index of the primitive (if there is one) between the ray and the point. We obtain the following image containing the shadows:

Illumination

We will add Phong illumination model for each illuminated point. We apply this principle by calculating the color of each pixel based on three different components: Ambient, Diffuse, and Specular. To achieve this, we complete the compute_illumination() function in render_engine.cpp as well as shading_parameter.cpp.

Ray Reflection

In this section, we aim to achieve ray reflection on the sphere level. We will check the values of the rays sent and their reflection. If there is an intersection, then we add the color of the material of the object on which the light is reflected using compute_illumination() multiplied by the attenuation parameter. To decrease the attenuation parameter as this function is executed, we multiply this parameter by mat.reflection(). Then we re-orient the ray by assigning it a new normal.

Anti-Aliasing

Here we aim to eliminate the effects due to the projection of a single ray per pixel. To do this, we want to increase the number of rays emitted per pixel, without letting our program run for too long before displaying the image. We will limit ourselves to emitting 4 to 5 rays per pixel. The goal is to slightly shift the orientation of a ray to avoid non-contrasting variations during color changes. To achieve this, we will complete the render() function in the render_engine() class, which manages the overall rendering of our image.