Around 2006, a friend mentioned that I should port my Windows Forms-based solitaire game to OpenGL. I had only a vague notion of what OpenGL was around this time, and cautiously journeyed into what would become my primary area of interest for many years to follow. The timing could not have been more perfect; I entered the graphics API scene right around the time when GPUs were transitioning from fixed-function register combiners to programmable vertex and fragment shaders. This allowed me to experiment with shaders long before geometry, hull/domain and compute shaders materialized and allowed me to learn the concepts incrementally. In 2008, I expanded my horizons by learning Direct3D 10 and was able to master it thanks to my experience with OpenGL. I partly attribute my strong interest in computer graphics to the fact that one of my favorite mathematical topics is linear algebra.
I started simple. I had already implemented the actual solitaire engine and just wanted to get some cards to render to the framebuffer, while handling mouse events. As can be seen in the screenshot, I started out with immediate mode.
To add a bit of realism, I threw in some random per-card rotation to reflect the fact that life isn't perfect.
This didn't feel 3D enough for me, so I experimented with some different perspective transformations next.
Next, I experimented with bump mapping, lighting and attenuation.
I learned about image-based lighting (IBL) and couldn't resist throwing that in.
Now it was time to add a bit of polish. I added a background and refined the card borders, lighting and bump mapping.
That didn't stop me from experimenting, though. I couldn't resist mixing multiple textures together in the fragment shader to create a "paint-splattered cards" effect.
I generalized the code to support other games too, such as escalator.
At the idea of a friend, I implemented a real-time sketch effect with Gabor filters in the fragment shader. Around this time, I also ported the game to Direct3D to expand my horizons.
I fine-tuned the card edges and tested the lighting using the 6D mouse.
Why not add some dynamic fingerprints for realism?
I added additional deck styles.
All fun aside, this is what I ended up with. As stated earlier, I used immediate mode when I first started out, but evolved to instanced rendering, texture arrays and buffer objects.
Complete with a win effect: