Hall of Ray

Alexander Prysinski

Hall of Ray

Summary

When I first started thinking about the rendering competition, I had big plans regarding my scene and the many additional features I wanted to include. But then, as time flew by, I noticed that my raytracer would not get the chance to produce such great images, because on the one hand, everything was partially broken (e.g., my BVH would miss intersections, the coordmappers would not work, ...), and on the other hand, we would not really discuss advanced features such as Monte-Carlo Path Tracing, etc, in the course of the lecture. The initial ideas started to fade, and only having a few days left until the deadline, I took out Blender and modeled a very simple scene, using as few triangles as possible and no mapped textures at all. However, in those last few days I got enthusiastic, first adding multithreading to my raytracer, followed by fixing the broken BVH implementation. So now that my raytracer's performance had increased greatly, I thought it would be for nothing if it was going to render only some simplistic scene in a few minutes. Instead, I had the glorious idea to do naive indirect illumination for the lambertian and phong materials, by sampling in random directions on the hemisphere recursively. After rendering, post-processing is applied: A slight gaussian blur filter to compensate for remaining noise, followed by a simple bloom effect to make bright light bleed more into the scene. Finally, the image is gamma-corrected before exporting to a PNG file.

The Scene

The scene itself consists of the interior of a building that is split into two parts. The illuminated entrance lies on the frontside of the lower room and uses an area light to produce soft shadows. On the backside, there is a stairway leading to the upper hallway. Additionally, in the lower room there are ten pillars, and in the middle there is a red carpet leading to the stairs. At the end of the upper hallway, there is a mirror, but also all the lower walls are mirrors. They are using the FuzzyConductorMaterial, while all the other objects use the CombineMaterial with a mix of LambertianMaterial and PhongMaterial. The lower floor makes use of a modified CheckerboardTexture. For performance reasons, simple shapes have been used to reduce the count of polygons. The pillars therefore use smooth shading to compensate a bit for it, and the pillars in the back and behind the camera consist of fewer triangles than the pillars right in front of the camera.

The Image

The image itself lives mostly from the fuzzy reflections that are basically everywhere: It gives the impression of much more going on than there actually is. You can see multiple reflections of the bright entrance, which itself is hidden from the viewer, as it is positioned right behind the camera. When looking at the pillars, you can see the smooth shading and also the subtle highlights produced by the Phong part of the combine material. When closely looking at the lower sections of the front pillars and the front floor and carpet, the indirect light becomes visible, but it is still very subtle even after 1024 samples per pixel. You might also spot the difference between the more detailed pillars (regarding geometry) in the front, and the less detailed ones in the back, but ideally they should not be distinguishable by the viewer. Furthermore, it is obvious that some depth of field has been used, with a focus distance of about 20 meters. For the post-processing effects (gaussian blur, bloom, gamma correction), please read the next section.

Additional Features

Obj-Loader FuzzyConductorMaterial

The objmat.cpp has been extended by looking for the illum key and if it equals 3 it assigns a FuzzyConductorMaterial instead.

Multithreading

To speed up execution, one thread is spawned per image line. The renderer.cpp has been modified accordingly, and also random.cpp as suggessted in the Teams forum, such that every thread has its own random seed.

Indirect Lighting

The lambertian and phong material have been set to secondary sampling and send rays into random directions on their hemisphere. This is a naive method for indirect lighting, yielding very limited effect and much noise, at least for reasonable amounts of samples. But since the scene is very simple, the sample count has been increased a bit. At the same time, the recursion limit of the recursive raytracer has been set to 4 only. Overall, the result is a subtle brightening in some areas, which can also be seen in the comparison below. It is of course not comparable to a state-of-the-art solution (e.g., Monte-Carlo Path Tracing).

Final image (128 samples only due to timing constraints) without and with indirect, and indirect only:

No indirect (128 samples) Indirect (128 samples) Indirect Only, Denoised and Gamma Corrected (128 samples)

Top without and bottom with indirect (128 samples only due to timing constraints)


Denoising

Simple denoising is done with a small gaussian blur, to get rid of some noise. You can find the implementation in rc.cpp.

Bloom

Bloom is applied to the image. It first extracts bright pixels of the image according to their luminance (see Color.cpp) using a linear threshold, then downsamples them, followed by alternating between upsampling and blurring. The result is then mixed with the source image. Again, the implementation is in rc.cpp.

Final image without and with bloom, and bloom only (not gamma corrected):

No Bloom Bloom Bloom Only (Without Gamma Correction)

Gamma Correction

Since the final image is exported as PNG, gamma correction is applied to preserve the perceived brightness. To this end, the Color.cpp function gamma() has been implemented.

Conclusion

Why should my image win? Because it is simple, yet beautiful.