Skip to content

Universumgames/raytracer-metalcpp

Repository files navigation

Multi Parallel Raytracer

Summary

Our goal was to create a relatively simple raytracer in C++. Out of curiosity we wanted to create not only one simple Raytracer but extend this idea for this project to raytrace in C++ using a sequential implementation as comparison to a Metal Shader Implementation. Depending on which operating system you are on, cmake automatically fetches most dependencies and compiles with the available shader (CUDA on Windows and Metal on MacOS, since no other option is either possible or probable). If you want to run this project without any shader and just try to CPU-raytrace you can even compile this project on linux. The only dependencies required for this project to compile is SFML (libsfml-dev) and their dependencies. There is a simple make target provided to install the required dependencies with the linux apt package manager ( make install_deps_apt).

We did not come up with all the formulas and ideas on our own and used some libraries as well as learning resources to get this project so far. For a detailed list of all resources and libraries we used along the journey we compiled an overview in Resources.md.

An important note concerning the shader programming and raytracing with shaders is the use of the compute pipelines. We did not use the rendering pipeline and their raytracing features, we used the normal compute pipelines and needed to implement our own data structure like vertex buffers.

Configuration and Project Structure

Every aspect of the raytracing application can be configured when calling the executable, a detailed list of all available options can be found when calling the executable with the --help flag. The defaults are set in the main.cpp file. The C++ source files can be found in the src/ folder, the shaders in the shaders/ folder. Needed assets like scene files and 3D models are located in the scene/ directory.

Scene Configuration

This raytracer uses scene configuration files in JSON format to define the objects, materials, lights, and camera settings for rendering. These configuration files allow users to customize the scene being rendered by specifying various parameters. The format for these json files is described in detail in the scene/README.md file.

Features

This raytracer has multiple implementations for raytracing: Sequential C++ implementation, OpenMP Multithreading and a Metal Shader implementation. Each implementation works identically on a high level, but the underlying logic had to be adapted to the respective platform. The scenes that can be rendered are defined in JSON files by referencing 3D models in STL format. Multiple bounces and multiple rays per pixel (samples) are supported to achieve good rendering effects, but each object only supports a single color. Upon rendering the scene the objects are loaded and prepared for raytracing. Part of this preparation is building a Bounding Volume Hierarchy (BVH) to accelerate the ray-object intersection tests.

Inner working

The raytracer works by simulating the path of light rays as they interact with objects in a 3D scene. To achieve this, for each pixel on the screen multiple rays are cast into the scene with slightly different directions to simulate anti-aliasing and soft shadows. Each ray checks for intersections with objects in the scene using the BVH for acceleration. When a ray intersects an object, the color of the intersection is stored and the ray is reflected or refracted based on the specularIntensity of the object. If the ray hits a light source, the color of the light is stored separately and the ray tracing for that path ends. This process is repeated for a specified number of bounces, allowing for multiple reflections and refractions. After all the bounces are finished for a ray, the colors from each hit are multiplied (using color values 0.0-1.0) to determine the ray color. Finally after all the rays are traced, the average color of all rays (the samples) per pixel is calculated and written to the output image.

Output

The output of the program will the rendered image saved as a PNG file. The default destination is raytraced.jpg, but can be changed using the command line arguments. In addition the programm will time the execution and store the timing results in the timelog.csv file for later analysis.

Analysis and Comparison of the Implementations

To view and compare the performance of the sequential and parallel implementations, you can use the provided python/speedVisualizer.py and python/speedUpCalculator.py scripts. These scripts will read the timelog.csv file and generate visualizations of the execution times and speedup achieved by the parallel implementation.

Comparing the different implementations and scenes that were rendered for the benchmark, we can see that the triangle count, the number of bounces as well as the screen resolution (combined with the number of samples per pixel, which basically increases the virtual screen size) have a significant impact on the performance. While at the beginning, with low settings the sequential implementation is close to the parallel implementations in terms of duration needed for the rendering, the difference increases significantly with higher settings. Where as the sequential implementation takes almost one and a half hours to render the most complex scene in the benchmarks, the metal shader implementation is able to render the same scene in a matter of half a dozen seconds.

Those two numbers alone already show that the power of parallelization and using the GPU for such tasks. Looking at the speedup graphs generated with the provided python scripts, we can see that the speedup achieved by the parallelisations varies drastically depending on the scene and settings. While some scenes achieve a speedup of over 1000x compared to the sequential implementation, other scenes only achieve a speedup of around 4x. This variation can be attributed to losses due to overhead of copying data buffers to and from the GPU. However, overall the parallel implementations show a significant improvement in performance compared to the sequential implementation, especially for complex scenes with high triangle counts and multiple bounces.

About

A simple raytracer programmed in C++ in three parallelisations (none, OpenMP and Metal)

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors