Deferred Shading

Here’s another neat rendering technique. This one is called Deferred shading. The idea with this one is that instead of rendering and calculating lights while you rendering your geometry; instead, you render out relevant spacial information about the geometry to the Render TOP. For example you output the position, the normal, the UV, the shininess of the material, the diffuse color of the material to the Render TOP instead. You do this using floating point color buffers, and multiple render targets. (since a single color buffer can’t hold all of this information)

Then using this information you can apply a light to the scene as a post process. The benefits of this are:

  1. There is no limitation (except for speed) of the number of lights you can use. In a normal render you’d be limited to 3-7 lights (depending if they are point or spot lights).
  2. Greatly increasing the geometry in the scene will still result in the exact same lighting cost as simple geometry. This is because the light cost is calculated by ((widthheightcomplexity of light shader) in this technique. Adding more geometry to the scene doesn’t change any of those values. The technique is particularly useful in scenes with a large amount of overdraw (look into the wiki for an article on overdraw).
    It’s use is of course situational, but a useful idea to have in your pocket in case the need arises.
    DeferredShading.toe (6.15 KB)

thanks a lot for that example Malcolm. very useful

Instead of having a separate TOP for each light, is it also possible to add all the lights in a single TOP? If yes, will it be slower/faster?
This does not work well with transparent objects, does it?

Also, it would be nice if everybody could post the FPS they get with this file
I get 21fps with a 7800GT
So I’m gonna need a new card soon

Ya this technique is poor with transparent objects, but there are some workarounds.
You could definitely merge the GLSL TOP shaders together so you do multiple lights per pass, and yes it’d be faster. I just left them split out to make it easier to see whats happening.

Thanks Malcolm,
it’s definitely easier to follow with each light split to a separate TOP. Having an example which does it in one TOP would be great one day.

If you find some time, could you also please elaborate on the transparency workarounds?

I’ve never actually tried to implement the workarounds, just seen them mentioned in a few articles. Your best bet is to google deferred shading and find papers on it for the workarounds.

will do. thx

Is these techniques also ussefal ive you would like to project a Gobo ore picture? There is no shadow techniques?

These is a Example about the idea that I have to create. A lot off light fixtures that creates a volumetric light beam and the possibility to project lights on the 3d model. Whit ore whiteout a gobo.

Hey there!

I’ve been playing around with this a bit. I have a question though about how to deal with anti-aliasing. It seems to me that using this method makes anti-aliasing difficult, but I also don’t fully understand how anti-aliasing works on renders. Is there a way to get it back and still use deferred shading? You can see in my example that anti-aliasing the initial render yields artifacts because of the way the different buffers need to be used in the lighting pass.

I’m attaching a file that is a sort of hybrid that uses the deferred technique but is actually still using light COMPs in a post render ( of a flat tile using a GLSL mat) to take advantage of the built in lighting functions. This doesn’t get around the lighting limit though since its still specifying light COMPs in a render, but I think it is faster still.
deferredTests.2.toe (15.1 KB)

The best way now would be to not antialias in your main render, do all your deferred rendering work and then use the Anti-Alias TOP at the end to apply anti-aliasing.

Hey guys!

I was trying to run that last example in a non-commercial version of TD 099, but it doesn’t work. What do you think might be the case? I’ve found no errors in the network apart from the textures being limited by non-commercial resolution.

That last example "deferredTests.2.toe "works fine here. Using 099 2016.1360
What are your system specs?

Not working on macOS because of a few things. First the GLSL version of the shader, I changed that to 3.30 and then a few declarations must be changed. After that the Render TOP still doesn’t render the scene correctly (unless you switch to Cube Map rendering for some reason) so I’ll submit this part as a bug.


I’ve been playing with this and had help from Mike Walczyk to iron out another example of this technique. Figured I’d share with this crowd as it’s pretty nifty. … d_lighting

The Practice tox has a flattend look at the process with comments - left to right.

Oh yes! this is very cool, thx mat

Hey folks. Doing a bit of deferred shading using these good ol’ techniques ( thanks @raganmd for consolidating and expanding on this!) . I’ve noticed now that the GLSL in the final screen space shader has some work in it towards an “in-place” coneLookupScaleBias variable, but then doesn’t get used. I assume @malcolm, when making the original example, brought this in and then realized it would be easier to just use the sTDSineLookup function instead of implementing the custom cone/delta/rolloff lookup texture since it would have to be per light and open up a can of worms.

I am making a setup with a very large quantity of cone lights that would share the same cone/delta/rolloff values so am attempting to re-incorporate these features by making my own 1D lookup for the shader and to pre-compute the ScaleBias as a const that gets updated only as i configure the setup, so doesn’t need to be a uniform or dynamically computed in the shader.

I was hoping to get some more insight into how the 1D texture is created as well as to make sure the code I’ve found in that example is the correct way of generating the ScaleBias vec2. This is that code:

// spot
float fullcos = cos(radians((uConeAngle / 2.0) + uConeDelta));
fullcos = (fullcos * 0.5) + 0.5;
float scale = 0.5 / (1.0 - fullcos);
float bias = (0.5 - fullcos) / (1.0 - fullcos);
vec2 coneLookupScaleBias = vec2(scale, bias);

Then i assume the rolloff parameter gets applied in the creation of the 1D single channel ramp texture.

Also also, I gather that this lookup is in fact one way to achieve IES profiles right? This is essentially what an IES profile is right?



Yep, your equation is correct. Here is how the lookup is generated. It’s just a cosine shaped rolloff, definitively didn’t think about IES profiles when making this ~15 years ago though.

float stepsize = (1.0f - (fullcos )) / (float)(LOOKUP_SIZE - 1);
float cosang = cos(radians(uConeAngle));
// Edge is 0 so extend conditions of Hold result in any overshoot == 0.0
arr[0] = 0.0;
for (int i = 1; i < LOOKUP_SIZE; i++)
    float val = ((float)(i) * stepsize) + fullcos;

    if (val < fullcos)
        arr[i] = 0;
    else if (val > cosang)
        arr[i] = 1.0;
        float ival = degrees(acos(val));
        float dif = ((ival - uConeAngle) / uConeDelta) * 180.0f;
        val = pow((1.0f + cos(radians(dif))) * 0.5f,roll);
        arr[i] = val;