Preview:
// https://www.scratchapixel.com/lessons/3d-basic-rendering/global-illumination-path-tracing/global-illumination-path-tracing-practical-implementation.html
        // https://alexanderameye.github.io/notes/sampling-the-hemisphere/
        void PointLight::global_illuminate(Ray ray, Output* out, int bounces) { 
            // when using global lighting ignore specural
            if (bounces >= out->maxbounces || static_cast<float>(rand()) / static_cast<float>(RAND_MAX) < out->probterminate) {
                // diffuse calculation = surface->dc * surface->kd * NdotL;
                for (const auto& s : get_surfaces()) {
                    if (s->ray_intersection(ray)) {
                        Eigen::Vector3f light_direction = this->centre - s->point;
                        light_direction.normalize();
                        float cos_pheta = std::max(0.0f, ((s->normal).dot(light_direction)));

                        this->final_colour.x() = s->kd * s->dc.x() * cos_pheta;
                        this->final_colour.y() = s->kd * s->dc.y() * cos_pheta;
                        this->final_colour.z() = s->kd * s->dc.z() * cos_pheta;
                    }
                }
                this->final_colour = this->final_colour / bounces;
                return; // fmax(0.0f, surface->point.normal.dot(L)) * diffuse intensity * diffuse colour
            }
            else {
                for (const auto& s : get_surfaces()) {
                    if (s->ray_intersection(ray)) {
                        // cosine weighted hemisphere sampling
                        float r1 = static_cast<float>(rand()) / static_cast<float>(RAND_MAX); // between 0 and 1
                        float r2 = static_cast<float>(rand()) / static_cast<float>(RAND_MAX); // between 0 and 1

                        float theta = 2 * M_PI * r2;
                        float x = std::sqrt(r1) * cos(theta);
                        float y = std::sqrt(r1) * sin(theta);

                        Eigen::Vector3f new_direction(x, y, std::sqrt(std::max(0.0f, 1-r1)));

                        // create a new ray with out random sampling direction
                        Ray bounce_ray(s->point, new_direction);

                        // here is where we recursive and do the cosine weighted hemisphere sampling to determin direction of random ray

                        Eigen::Vector3f light_direction = this->centre - s->point;
                        light_direction.normalize();
                        float cos_pheta = std::max(0.0f, ((s->normal).dot(light_direction)));

                        this->final_colour = this->final_colour + (s->kd * s->dc * cos_pheta);
                        global_illuminate(bounce_ray,out, bounces + 1);
                    }
                    else {
                        // if we dont hitanything
                        continue;
                    }
                }
                // if our ray doesnt hit anything
                return;
            }
            // check for ray intersection with objects
            // if no we need to remove it from our samples and from our average
            // else return global_illuminate(reflection reflection_ray, bounces-1) * direct illumunation with diffuse * cospheta value (N dot L)

            // at each bounces where we intersect we calculate the light contribution which is only the diffuse value
            // and at the end these product up to the pixel colour value ( add them up and divive by num bounces )
        }
downloadDownload PNG downloadDownload JPEG downloadDownload SVG

Tip: You can change the style, width & colours of the snippet with the inspect tool before clicking Download!

Click to optimize width for Twitter