Exploiting Depth Testing
In traditional dedicated 2D rendering hardware, the application has to render the sprite layers from back-to-front, to ensure that blending functions correctly.
In our example, the application is using a 3D API to render a 2D scene. What additional tools does the 3D API offer to reduce redundancy? The main tool used in removing redundant work from a 3D scene render is called the depth test.
Every vertex in a triangle has a Z component in its position, which is output from the vertex shader. This Z value encodes how close that vertex is to the camera. The rasterization process interpolates the vertex values, and assigns a depth to each fragment that needs fragment shading.
This fragment depth value can then be tested against the existing value stored in the depth buffer. If it is not closer to the camera than the data already in the framebuffer, then the GPU discards the fragment without ever submitting it to the shader core for processing. This is because the GPU now knows that the fragment is not needed.
Sprite rendering engines already track the layering of each sprite, so that they stack correctly for alpha blending. We can now map this layer number to the Z coordinate value assigned to the vertices of each GPU-assigned sprite, and render our scene as if it has 3D depth.
We then use a framebuffer with a depth attachment, enable depth writes, and finally render the sprites and background image in a front-to-back order. The depth test removes the parts of the sprites and the parts of the background which are hidden behind other sprites.
Now, if we run this for our simple test scene, we get the following image:
You can see that the square sprite geometry does not exactly match the shape of the opaque pixels. The transparent parts of the sprites, that are closest to the camera, are not producing any color values because of the alpha test. However, the transparent parts of the sprites are still setting a depth value.
When the sprites on a lower layer are rendered, the depth testing means that the pieces that should be visible underneath the transparent parts of an earlier sprite are being incorrectly removed. As a result, only the OpenGL ES clear color is showing.