Skip to content

Ray Tracing

Overview

This project is made with a modular approach. There are two main components at play:

  • Editor
  • Engine

The editor allows the designer to create a blueprint for the final scene. It generates a JSON file, containing data, describing the scene which is passed to the engine to do the actual heavy lifting. The engine then generates the final render which later can be saved as file and is also rendered on a window.

Proj_raytracing_intro.svg

editor communicating with engine

Introduction

In computer graphics, ray tracing is one of the rendering techniques where photorealistic images are generated by using techniques which model the physical light phenomena.

Components

There are 2 major components.

Editor

The designer can construct a blueprint for the final render using the editor which generates a JSON file, describing the scene. This project does NOT have any editor yet but it is possible to make one and easily integrate it with the engine since the interface is already implemented.

Engine

The engine contains all the algorithms and behaviors which do the computational heavy lifting to construct an image based on the blueprint provided by the JSON file.
The engine is a composition of 3 components.

  • Scene
  • Camera
  • Window Manager

Scene

Scene1 is the structure itself which is constructed from the blueprint, provided by the JSON configuration.

Camera

Camera2 captures the scene.

Window Manager

Window Manager3 manages the window on which the captured image is displayed.


Other than these components, engine contains an additional flag called save_img which determines if we want to save the output into rendered_img.png or not.

bool save_img = false;

Engine also includes a JSON container which will contain the parsed data.

Json config_data;
void parseJson(std::string);

which is later used to initialize the 3 components.

void setupScene();
void setupWindow();
void setupCam();

Execution Flow

Proj_raytracing_run.svg

Basic flow of Engine::run()

void Engine::run() {
    Hittable_list& HL_ref_scene = S_scene.create_scene();
    sf::Image* I_ref_image = Cam_camera.render(HL_ref_scene);
    if (save_img)
        I_ref_image->saveToFile("./imgs/rendered_img.png");
    WM_window.display(I_ref_image);
}

Objects and Materials

At the moment, there is only sphere4 present in the project.

Sphere

There are 3 things common in all spheres.4

  • Center (a 3D point5)
  • Radius
  • Material

Materials

Materials determine the way light behaves when it hits the surface of a sphere.4

Lambertian

For this material, we are reflecting the light into random directions based on the normal vector and reducing light with each light bounce.6

bool scatter(const Ray& r_in, const Hit_record& rec, Color& attenuation, Ray& scattered) const override {
    auto scatter_direction = rec.normal + random_unit_vector();

    if (scatter_direction.near_zero())
        scatter_direction = rec.normal;

    scattered = Ray(rec.p, scatter_direction);

    attenuation = albedo;
    return true;
}

Metal

Get a perfect reflected ray1 direction.

vec3 reflected = reflect(r_in.direction(), rec.normal);

Add fuzz to it.
Proj_raytracing_fuzz.svg

fuzz being applied to a reflected ray.7

Get a random unit vector6 \(\hat r\), scale it by fuzz.

fuzz * random_unit_vector()

Then add it to the unit vector6 of the reflected ray .7

reflected = unit_vector(reflected) + (fuzz * random_unit_vector());

Then make a ray7 out of it

scattered = ray(rec.p, reflected);

To make sure that the scattered ray7 is projected outside of the surface after hitting it, we check if the dot product6 is positive.
Proj_raytracing_fuzz_correction.svg

Error correction

return (dot(scattered.direction(), rec.normal) > 0);
bool scatter(const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered) const override {
    vec3 reflected = reflect(r_in.direction(), rec.normal);
    reflected = unit_vector(reflected) + (fuzz * random_unit_vector());
    scattered = ray(rec.p, reflected);
    attenuation = albedo;
    return (dot(scattered.direction(), rec.normal) > 0);
}

Read functions in utils to understand how reflection and refraction works.

Dielectric

The refractive index of outside medium is \(\eta^\prime\) and inside one is \(\eta\).
In the main(), the index is passed as

\[\frac {\eta^\prime}{\eta}\]

Therefore, if the ray7 is coming from outside, ri is

\[\frac \eta {\eta^\prime} = \frac 1 {\frac {\eta^\prime} \eta}\]
double ri = rec.front_face ? (1.0 / refraction_index) : refraction_index;

Then for unit vector,6 we have

vec3 unit_direction = unit_vector(r_in.direction());

From the refract() in utils, we know that

double cos_theta = std::fmin(dot(-unit_direction, rec.normal), 1.0);

Now using the identity

\[\sin^2(\theta) + \cos^2(\theta) = 1\]
\[\implies \sin(\theta) = \sqrt{1 - \cos^2(\theta)}\]
double sin_theta = std::sqrt(1.0 - cos_theta*cos_theta);

Light cannot refract if

\[\frac {\eta}{\eta^\prime} \cdot \sin(\theta) > 1\]

In that case, we reflect the light.
For reflection, we can also use Schlick's approximation.

if (cannot_refract || reflectance(cos_theta, ri) > random_double())
    direction = reflect(unit_direction, rec.normal);
else
    direction = refract(unit_direction, rec.normal, ri);

Therefore, we have

bool scatter(const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered) const override {
    attenuation = color(1.0, 1.0, 1.0);
    double ri = rec.front_face ? (1.0/refraction_index) : refraction_index;

    vec3 unit_direction = unit_vector(r_in.direction());

    double cos_theta = std::fmin(dot(-unit_direction, rec.normal), 1.0);
    double sin_theta = std::sqrt(1.0 - cos_theta*cos_theta);

    bool cannot_refract = ri * sin_theta > 1.0;
    vec3 direction;

    if (cannot_refract || reflectance(cos_theta, ri) > random_double())
        direction = reflect(unit_direction, rec.normal);
    else
        direction = refract(unit_direction, rec.normal, ri);

    scattered = ray(rec.p, direction);

    return true;
}

References


  1. Read more about scene

  2. Read more about camera

  3. Read more about window manager

  4. Read more about spheres

  5. Read more about points and vectors

  6. Read more about vectors

  7. Read more about rays