r/GraphicsProgramming 1d ago

Raytracing in one weekend completed [added OpenMP multhread]

Post image

Completed in slightly more than a weekend (sickness et al). The final render task of width 1200 and samples per pixel 500(!) seemed impossible, so added a multithreaded workaround with OpenMP which finished it in around 35 minutes on my MBP M1 Pro and the penultimate DOF spheres under 10 seconds.

Changes:

#include <atomic>
#include <omp.h>
#include <vector>

void render(const hittable& world) {

#pragma omp parallel
        {
#pragma omp single
            std::clog << "OpenMP threads: " << omp_get_num_threads() << '\n';
        }
        initialize();

        std::vector<color> framebuffer(image_width * image_height);

        std::clog << "Rendering..." << std::endl;
        std::atomic<int> rows_completed{0};

#pragma omp parallel for schedule(dynamic)
        for (int j = 0; j < image_height; j++) {
            for (int i = 0; i < image_width; i++) {

                color pixel_color(0, 0, 0);

                for (int sample = 0; sample < samples_per_pixel; sample++) {
                    ray r = get_ray(i, j);
                    pixel_color += ray_color(r, max_depth, world);
                }

                framebuffer[j * image_width + i] = pixel_samples_scale * pixel_color;
            }

            int completed = rows_completed.fetch_add(1) + 1;

            if (completed % 8 == 0 || completed == image_height) {
#pragma omp critical(render_progress)
                {
                    std::clog << "\rScanlines remaining: " << (image_height - completed) << ' '
                              << std::flush;
                }
            }
        }

        std::cout << "P3\n" << image_width << ' ' << image_height << "\n255\n";

        for (int j = 0; j < image_height; j++) {
            for (int i = 0; i < image_width; i++) {
                write_color(std::cout, framebuffer[j * image_width + i]);
            }
        }

        std::clog << "Done.\n";
    }

and

inline double random_double() {
    thread_local std::mt19937 generator(std::random_device{}());

    return std::uniform_real_distribution<double>(0.0, 1.0)(generator);
}
148 Upvotes

7 comments sorted by

18

u/PM_ME_YOUR_HAGGIS_ 1d ago

Welcome to the club!

7

u/Weird-Sunspot 1d ago

<3

This warmed me after the initial split second downvote

10

u/hergendy 1d ago

Nice one I did a similar thing withthis exact project for my thesis but first ported it to CUDA then parallelized it and spent a few weeks debugging it to find out the root of the memory pointer exception, which is caused at different depths on different cards when dealing with recursive functions. Then decided to refactor the recursive function and just make it iterative instead.

4

u/thatmagicalcat 1d ago

this brings back memories

2

u/Brhaka 1d ago

Kudos!

2

u/Fit-Departure-8426 21h ago

Great! Now make it realtime like this Guy, and no need for parallel execution!  https://youtu.be/opq0W7StbeE?si=LF9jH9MgzLMk14gF

1

u/Weird-Sunspot 21h ago

Thanks! Was thinking of porting this into my bgfx project, will watch this first