Deferred Shading!
Deferred shading is an interesting method for drawing a 3D scene in which lighting calculations are delayed until after the scene has been drawn into screen space. By deferring the light calculation, lighting computations must only be performed for each visible pixel which, depending on the strength of culling algorithms used, can vastly reduce the amount of fragments for which lighting must be calculated. More interestingly, it allows for a very large number of possible lights in the scene.
Deferred shading is accomplished by rendering all of the data necessary into some immediate storage called the G-buffer. In my implementation, I used one one buffer for color values, one for view-space normals, and one for fragment positions. After these values have been rendered to texture, another pass is performed for each light in the scene, using the values in the G-buffer to calculate luminance for each screen fragment. This luminance texture is then merged with the color values in the G-buffer to produce the final image.
Below are the Color, Normal, Position, and Luminance textures used to create the final scene (a somewhat dreary take on Happy Cabbage Adventure):