Skip to content
April 6, 2011 / racoonacoon

The Deferred Renderer

Oh yea! The first version of the deferred renderer is finally complete! Although I must admit that some parts of it are hacked and the performance isn’t exactly what I want to it be yet. Still, I’m happy how relatively easy the thing was to implement, minus the tedious and slow work of refactoring some classes. Here are a couple of comparison screens for our rabid fans to digest:

As you might be able to notice, the differences are few, and that is a good thing! Ideally, deferred shading should look exactly like the forward rendering model, but that isn’t always possible do to performance (and memory) reasons. To understand why this is so (and why anyone would even use deferred rendering in the first place if it is supposed to be the same as forward rendering), we first have to understand the basics and differences between forward and deferred shading models.

Forward Shading

With forward shading you light each polygon as it comes through the shader. That is pretty much all there is to it. Well, except for the disadvantages. One of the problems is less-than-optimal performance. If there are many polygons that overlap one another, say 1000, and the polygons are rendered in a back-to-front order, than the lighting calculations will be ran 1000 times for that same pixel. Not so great. Another problem is shader complexity. With forward rendering the drawing of the vertex and the lighting of the vertex are closely bound to one another. This forces all your shaders to incorporate a large blob of lighting code, which is both nasty and complex.

Deferred Shading

Deferred Shading attempts to reduce some of the disadvantages of Forward Rendering by decoupling the rendering of the vertex and the lighting of the vertex into two separate phases, i.e. it defers the lighting phase. The basic concept is this: instead of actually rendering out the scene, we render out information about the scene. For example, we render out the diffuse color of the image, the normal at that pixel location, and the world position at that point.

Essentially what we are doing is combining the individual data of all our objects in our scene into a single buffer (known as the GBuffer.) Once our scene is rendered in this GBuffer we run a separate full screen pass that combines all this information and applies our lighting equations. The advantage to this is that we don’t have to worry about a pixel going through its lighting equations multiple times if the geometry overlaps, as the light equations are ran only once on the pixels that are guaranteed to be visible. Another advantage to this method is that it greatly simplifies the shaders that render out our geometry. No more do we have to worry about lighting in our mesh shaders. We defer all that complex stuff into a single shader that executes once everything has drawn.

However, there is a cost associated with each additional render target we store data to, so there is a strong incentive to bind the least number of render targets possible. I was binding four render targets, and using the last one to store the specular color (the color of the ‘highlights’ of the texture.) The performance drop was just too much to be justifiable though, so I opted to remove the fourth render target and use the diffuse color (texture color) of the image for the specular color. That is the differences between the images above. You will notice that the Forward Rendering model has more white highlight on the square pillars, while the Deferred Rendering model’s highlights are less noticeable. This is because the Deferred Model has less flexibility in the parameters we can use to light the objects.

This isn’t really want we want though. It would be nice to have the exact same (or at least very very nearly so) tweakable parameters that we do in Forward Shading. Is there a way to get around this? I believe the answer to this is yes. One of the ideas that has been floating around in my head (probably from this pdf) is to store a material id at each pixel in one of our render targets. We could then push over all our Material information into an array on our DeferredRender effects file, and look up the exact lighting parameters for each pixel. This would return to us all the flexibility we had before (or at least most of it), but there are a few things we need to worry about. One thing is the number of Materials we have in the scene. What happens if the material count in our scene exceeds the maximum amount our shader can handle? We also have to worry about the bandwidth cost of pushing over all the material data each frame. I think this will probably work fine with our game as it is, but I’m not positive this will work as a long term solution.

Performance

One of the things I am not too happy about yet is the performance of our current deferred renderer implementation. It is actually slower (by about 20 frames) over our forward shading model. This is probably do to the fact that deferred rendering requires extra bandwidth to store and load the texture data and that I am not yet using the stencil buffer to cull away any pixels not being lit by a light. As it currently stands, each pixel manually loops through each light in the scene and accumulates the influence of the light into that pixel color. So if we have 16 lights and a 1280×720 buffer, we are performing 1280x720x16 = 14,745,600 light calculations per frame. Keep in mind that this is likely less than the forward shading model, where it is likely to perform lighting equations more than once per pixel.

Another thing that could be done to reduce the number of render targets being used is to make heavier use of data packing. I have found out (thanks to this KillZone presentation) that it is possible to store only the X and Y normals and compute Z by using the formula normal.z = sqrt(1.0 – normal.x^2-normal.y^2). It is also possible to only store the Z value at a location and recompute its world x and y (thanks to Wolfgang’s blog for this insight.)  If I use these methods I may be able to reduce the number of render targets I am using to 2, which could help boost the performance a bit.

So there you go, a huge post on deferred shading! My goal now is to try and improve the performance of our deferred renderer so that we can actually have an advantage using it. Besides that I really want to start working on some more gameplay elements. The class we are building the game for will be over in roughly a month, so we don’t have too much more time to construct a game out of this. I would like to get in a couple of more collision types, asynchronous loading, events, multiple scenes, bezier curves, a menu system, and a bunch of more levels. I guess we’ll just have to wait and see how all that turns out 🙂

Advertisements

3 Comments

Leave a Comment
  1. jh / Apr 6 2011 4:48 am

    So…. now I’m rabid?

  2. racoonacoon / Apr 6 2011 1:40 pm

    Rabid in the enthusiastic sense…I hope

Trackbacks

  1. Setting up a Scene class…and breaking everything else « bitwisegames

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: