Custom Raytracer (2021)

From Scratch C++  -  Solo Development  -  8 weeks development time  -  Graphics Programmer - PC

In this project, I was tasked with making a raytracer from scratch. This allowed me to develop some graphics programming skills.


I was very happy with the final result of this project as I managed to implement many material types such as lambertian, dialetric, and metallic on both Spheres and Ellipsoids.


Since this was a CPU raytracer, performance was not the best so I decided to also implement a bounding volume hierarchy.


This is a snippet of the GetPixelColor function which is the main function that gets the color to send to the screen:


color Scene::GetPixelColor(const Ray & ray, int bounces)

{

        recordHit hit;

        if (bounces <= 0)

            return color(0, 0, 0);// The ray has bounced too many times and so no longer gets light


        bvh.root->Traverse(&world, ray, hit, bvh.pool);

        if (hit.mat != nullptr)// if hit a sphere from screen

        {

            vec3 rayDirection = ray.GetDirection();

            switch (hit.mat->matType)

            {

            case 1:// Lambertian

            {

                return LambertianShading(&hit);

            }

            case 2:// Reflections

            {

                return Reflect(rayDirection, hit, bounces);

            }

            case 3:// Dielectrics

            {

                float rayDdotN = hit.normal.dot(-rayDirection);

                float n1 = 1;

                float n2 = 1.5f;

                float n1divn2 = n1 / n2;

                float k = 1 - ((n1divn2) * (n1divn2)) * (1 - rayDdotN * rayDdotN);

                if (k < 0)

                {

                    return Reflect(rayDirection, hit, bounces - 1);

                }

                else

                {

                    // Fresnel equation

                    float sinI = sqrtf(1 - rayDdotN * rayDdotN);

                    float cosT = sqrtf(1 - (n1divn2 * sinI) * (n1divn2 * sinI));

                    float sPolarized = (rayDdotN - n2 * cosT) / (rayDdotN + n2 * cosT);

                    float rPolarized = (cosT - n2 * rayDdotN) / (cosT + n2 * rayDdotN);

                    float Fr = 0.5f * ((sPolarized * sPolarized) + (rPolarized * rPolarized));

                    Ray refractedRay = Ray(hit.point, n1divn2 * rayDirection + hit.normal * (n1divn2 * rayDdotN - sqrtf(k)));

                    return Fr * Reflect(rayDirection, hit, bounces - 1) + (1 - Fr) * GetPixelColor(refractedRay, bounces - 1);

                }

            }

            default:

                break;

            }

        }


        // Background gradient

        float t = 0.5f * (ray.GetDirection().x + 2);

        return (1.0f - t) * color(1, 1, 1) + t * color(0.45f, 0.45f, 1.0f);

}