Morning Workout

Rendering Competition WS17/18


This is my final submission for the Rendering Competition in WS 17/18.

Introduction


During the semester I had a lot of ideas, but nothing got really concrete. During the christmas break, I still had no real plan on what to do.

During that time, I spent a lot of time in the gym, and one day, as I sat there, in front of me a similar set up as you can see in the image, I thought, this could be nice. The main focus back then was on the bottle, with its partly reflective material. Later I decided, that the phone and the paper would be nice details.

The scene


The scene resembles the fitness room at the Herrman Neuberger Sportschule in Saarbrücken. It is a scene, that I saw quite some time during the semester. In the middle are three powerlifting areas with bars waiting to be moved. The whole scene is mirrored by a mirror taking in nearly the whole right wall.

The first object that entered the scene was the bottle in the front right corner. I really like the reflectance of the material, mixed with the color on it. The phone and the paper belong to the first things I wanted to include, but it took until the very end to add them. They are rather simple, but in my opinion add important detail to the scene.

I then spent a lot of time modelling the weights, bars and stands and placing them. As can be seen in the evolution, texturing took quite an amount of time to get right. Most of them started as photographs I took. The wood of the podests is done with a gimp pattern fill, using a default wood pattern. I was surpised how good it looked, but it was the best I could manage to create.

The walls are textured with the noise function we were given to create a slightly changing greytone. A bumpmap creates the nice concrete like effect.

Adding the phone and paper was done quickly and after that, 1 week before submission, small but important things came by. How should the lighting be done? Ceiling, yes or no? Do I keep the environment map? Finally I had moved a lot of things around, added some more models and went with a closed room and arealights.

Features


Mirrors

I really like mirrors, they are simple and easy to implement, yet look good. The nature of my scene asks for big mirrors, so I added 2 huge ones. The bottle is partly reflective too and not forgetting the bars. The mirrors have refraction and absorption 0, but the chrome bars have the values of chrome and the bottle, well I don’t know exactly what material it is. I used steel in the beginning and remarked, as I wrote this, that I didn’t change it. Now I got a steel bottle, no time to change this and rerender.

Field of depth

I added a slight depth of field effect, to focus on the foreground, namely the paper, the bottle and the phone.

Scratches

If one takes a close look at the podest, one can see that the texture is not uniform. I added a scratch overlay to it, to make it look more used. I tried the same with the mirrors, but to get a texture that fits was too hard.

Mixed Materials

I quickly realised, that using an image, that depicted different materials, as texture was a bad idea. As can be seen in the first image on the left, the material of my object (phong) is the same in each region of the texture, even though there is wood on one hand and plastic on the other hand. I really didn’t like how this looks but I also didn’t want to assemble the object out of smaller ones.

I then decided to implement a masking material, similiar to the combine material. The difference is, that this one has a texture, which is used as mask to get the weights of the base materials. It can only combine two materials.

The right image shows the effect of this material.

The implentation can be found in maskMaterial.h

Bump mapping

I extended the bumpmapper implemented in the lecture, to be applicable to quads. It works with smooth triangles too, because those are derived from triangles. I replaced the bump coordinates that are passed to the bumpmapper by a coordmapper. In that way, I can pass a planar mapper and it works for quads too. On the left one can see a bump map and the result is on the right.

The implentation can be found in bump.h

Bump mapping — The loader

I really like the effect, a bump map can give and so I decided to use it in more than one place. The problem I faced now, was how to get the bump map onto objects that are neither (smooth) triangles nor quads? A quick look at Wikipedia told me, that *.obj files support bump mapping, so there was a specification. I then extended the provided loader with the basics I needed to be able to load an arbitrary *.obj object with a bump map.

The implentation can be found in obj.cpp

Concurrent rendering

To speed up things, I modified the renderer to use multiple cores. At first I did this in a very naïve way and got it wrong to start.

After that I had a working version, but it didn’t take into account the scene, so some threads finished earlier than others. Finally I render each column of the image on its own. There are as many threads as there are threads available on the system and each one renders such a slice. If it finishes, it takes the next one. The synchronisation is done via C++11 atomic_int. This way the whole processor is used for the whole image.

The implentation can be found in renderer.{cpp,h}

Occluder cache

To speed up things even more, I implemented an occluder cache and early exit for shadow rays.
Each thread keeps track of the last solid intersected for each lights source. In this way, the whole tree has not be traversed for every sample of the same pixel. Also the pixels in the neighbourhood benefit from this.
If there is a cache miss, early exit achieves that the first intersection stops the intersection process. It doesn’t matter who casts a shadow.

Evolution

Some images showing the evolution to the final result.

Issues


About coordmappers, mirrors and materials

I planned on putting some dust an the mirror, to make it less perfect. This would be easy with the mixed material I implemented, so I tought!
The first thing that happened was white. My material did not work for materials that need sampling. Well that’s not surprising:

After I successfully implemented it, I got the weird looking result you can see in the picture. It took me some time to figure out, that using a coordmapper other than a worldmapper for the reflection of the mirror is not the best idea.
I ended up, not querying the coordmapper at all for the sample reflectance, as I don’t see a case, in which this would make any sense.

In the end I didn't use it, as I couldn’t find a texture that produced a nice result.

Timing issues

The usual problem with deadlines and high expectations near the end!
The closer the deadline came, the more stuff I wanted to add, there was always a thing, I thought was missing. The result? Not enough time to render the final image with a high enough sampling to get both arealights and depth of field look perfect. At most not for the full size image.

Undefined Dataraces or something like that …

At some point I decided to implement an occlusion cache, to speed up shadow intersections. This was done quickly and gave me a speedup. I didn’t see the artifacts at first. Later, doing some debugging with a plane hiding nearly everything, I saw shadows where none should be. The straightforward way is not always the best thing to do, especially if you plan on using multithreading.

C++ knows how to frustrate you, even on a single core! It works like a charm, until it stops. You revert your change but it still doesn’t work. Hello undefined behavior, doing the right thing most of the time.

References

Thanks

I want to thank Stefan Lemme and Arsène Pérard-Gayot for the tips and tricks on how to speed up my raytracer.
I especially want to thank my girlfriend Li Seyler for her opininon on nearly all of the images that were produced during this semester and for her patience, especially during the last few weeks.

Have you tried normalizing?