I am Paul Scharf and ReX Force is an asymetric two player strategy game that I worked on for a few months in the Summer fo 2013.
I built the low-level physics simulation, tilemapping system, and most importantly a custem rendering pipeline, using C# and OpenTK, and integrated them with the systems built by other developers.
That rendering pipeline, and some of the graphical effects I contributed are what would like to highlight here.
Many 3D games use deferred shading as opposed to forward shading for the many advantages it offers.
In 2D games, the technique is used only rarely, and usually only to a limited degree.
With ReX Force we wanted to deeply immerse the player in a dark and gritty world. Visibility is further an important aspect of the game's gameplay.
This is why we decided to implement complex lighting techniques using deferred shading.
Similar to 3D games, to be able to use deferred shading we need to render geometry buffers storing normals and positions for all pixels. To be able to do this, all our assets were originally created in 3D and then rendered into normal maps, which could then be drawn as simple sprites into the appropriate buffer.
When shading the level, we draw the bounding volumes of all our lights and for each fragment sample the geometry buffers, and then shade the fragment with the parameters of the light and the sampled geometry.
All lights of the same type can here be rendered in the same pass by storing the relevant information in their vertices and accumulated using additive blending.
Since the game is completely 2D, there is in principle no height information present in our geometry buffers.
This does not lead to good – or realistic – looking lighting however.
That is why we also store the height of each pixel in our geometry buffers. This can be either extracted from a height channel in our sprite textures, or by defining the geometry directly. Walls, for example, always slant upwards, allowing the player to clearly see the edges of each room when light is shining against them.
To further increase immersion and create a feeling of tension, I also added shadows.
These can in principle be rendered for any light, but since they require one render pass per light, and the number of large lights in our levels was limited, only the largest and most important lights, including those of the player characters cast shadows.
These shadows are rendered by taking all walls within the game and rendering them to quads filling the area of the screen that is covered by the light in question.
When rendering the light, we can then sample that shadow map, and if nothing was drawn to a pixel, it is lit by the light. Otherwise it is in the shadow.
To smooth the shadows and have them fall onto walls realistically, we also store the height up to which each pixel is covered by light.
That system could be easily expanded to allow any object resting on the ground to cast a realistic shadow.
The advantage of this system, over conventional 3D shadow maps is that is renders the shadow map for exactly those pixels on screen that can be lit by the light. It further does so at a 1:1 resolution, neither wasting fill-rate, nor causing low resolution artefacts.
The size of the shadow map could even be decreased to a half or a quarter to increase performance, while still retaining perfectly predictable graphical quality.
One of the major advantages of deferred shading is how many small lights can be rendered using the technique.
This allowed us to add lights to lots of details, like individual bullets, as well as fire particles, and even sparks.
Note in the video below, how the flame thrower lights up the entire hallway in bright yellow light, and how sparks jumping across the floor cause a small amount of blueish light.
Additionally, not only the level geometry, but also every other object is subject to being illuminated, provided a normal map is provided. Note for example the subtle lighting on the discarded bullet casings, as well as on the characters themselves.
Rendering shadow maps for all the player character's lights as described above had another positive side effect.
By blending the different shadow maps together, we had a pixel-perfect visibility map, telling us exactly what pixels are visible, and even to what degree, to the player.
With that information, we can blend together the actual level, and a separately rendered radar map. The radar information is only shown where players can not look directly, taking into account the exact shape of the illuminated area.
A small, but noteworthy effect is how unit selections are rendered. Since one of the players is able to control dozens, if not hundreds of small units that can bunch up in large swarm, ideally we only draw one selection marker for a tight group of units.
I solved that problem with a very organic looking solution by using a custom shader that renders the distance to a selected unit.
By minimum-blending the distances of all selected units we ended up with a buffer that we could simply threshold at the desired values, to draw outlines around groups of selected units.
If you are interested in more details of any of the these effects, or anything else related to the ReX Force project or my other work, feel free to send me a mail at firstname.lastname@example.org.
Also feel free to watch the alpha trailed of the game here.