Multi-pass or deferred shading?

Area593

Newcomer
Hi guys and girls,

I'm only new to this forum, but I've got a bit of a question you might be able to help with. I did a search of articles in this forum, but didn't manage to find anything the really helps to answer the question.

I am developing a game engine that will be used to create a 3D environment that is a bit different than most of the stuff you come across these days. For example, the environment I'm going for is completely devoid of shadows as it just wouldn't fit the concept of the game (not a bad thing, certainly frees up some CPU and GPU cycles).

As a result I want to set the mood of the game using multiple different light sources, where each of the outdoor sections are each lit by their own set of lights, and each of the indoor sections also has it's own set of lights.
I've created a vertex & pixel shader combo that does a good job, both performance and quality wise, but the pixel shader does take a bit of time to load on startup (about 20 seconds with 4 lights per area, or about 45 seconds for 10 lights, which is the limit of what I can do with shader model 3), although the frame rate is very smooth once it is loaded, and it fully supports pixel accurate point and spot lights with cone angle and attenuation support.
I'd rather not move to shader model 4, as it would require upgrading my main production machine to windows seven, which I'm not willing to do as some of the software I use for my other jobs has not yet been released for Win7.

My question is, would I be better off using deferred shading or multi-pass rendering to be able to add more light sources, and what would the advantages and disadvantages be of each method, keeping in mind that I will not be adding any form of shadowing. Also, the engine already has full HDR support, with tone mapping, blue shift and bloom, so the lighting needs to be able to support HDR also.

Many thanks in advance for any advice you may be able to provide.
 
Deferred Shading, or Deferred Lighting, you can do so many effects once you go deferred that it's prolly what you want.
And you can have tons of lights on mid-range hardware already.
 
Deferred Shading, or Deferred Lighting, you can do so many effects once you go deferred that it's prolly what you want.
And you can have tons of lights on mid-range hardware already.

Thanks heaps for the reply. From what I've been reading on these forums it seems that is definitely the way to go, regardless of the cool effects I can add. I had an idea to store the data for all of the current lights (culled to only contain the currently visible ones), arranged in a single floating point texture and read them from the texture as needed. Is this a good way to get around shader model 3's maximum constant limitation, or would all of the sampling per pixel cause too much overhead to be useable?

I would like to be able to run with at least 64 simultaneous lights, and at the moment the shader model only lets me provide data for a maximum of fourteen lights before I hit the maximum constant ceiling, and I've already culled as much of the light data I could before loosing the amount of control I want over them. Or is there a better way?

Thanks again.
 
Hi,


Deferred shading is attractive when you have a lot of small lights. If your lights are quite large in screen-space, you'll eat a lot of fill rate and could end up worse off compared to forward rendering.

You could support N lights with multiple accumulative forward passes, in no more than N/10 passes if you can do 10 lights in one pass. If your geometry is cheap and your lights large, this may be attractive.


I had an idea to store the data for all of the current lights (culled to only contain the currently visible ones), arranged in a single floating point texture and read them from the texture as needed.

Personally, I think the light pre-pass type deferred renderers are the most promising variant but I'm not aware of anything released using one as its main lighting mechanism yet. I'd be wary of using floating point buffers, it can really cripple some hardware (especially consoles). 16bit fixed point would give you 8 stops for HDR - you could get more encoding an fp32 into 4x8bit fixed point but would have to put up with some artefacts (procedure easily found with Google).

Killzone 2 used a more traditional deferred approach, laying all the shading model constants down into buffers in one pass over the geometry (see http://www.guerrilla-games.com/publications/dr_kz2_rsx_dev07.pdf). Very nice, but you have to watch out with MRTs on older PC hardware (although I think the 7-series were the last cards to be really unhappy with MRTs).


If you want very fine control over your lights, it's worth knowing that the common-place "lighting channels" scheme can be trickier in a deferred model.
 
Deferred shading is attractive when you have a lot of small lights. If your lights are quite large in screen-space, you'll eat a lot of fill rate and could end up worse off compared to forward rendering.

You could support N lights with multiple accumulative forward passes, in no more than N/10 passes if you can do 10 lights in one pass. If your geometry is cheap and your lights large, this may be attractive.

I am looking at using a lot of small lights, with them attentuating over a relatively small distance (between 0.5 to 3.0 units, 1 unit = 1 meter in the game world). This may seem counter intuitive to just using less, larger lights, but to obtain the effects that I have developed for the game world I really need a lot of smaller ones.

Personally, I think the light pre-pass type deferred renderers are the most promising variant but I'm not aware of anything released using one as its main lighting mechanism yet. I'd be wary of using floating point buffers, it can really cripple some hardware (especially consoles). 16bit fixed point would give you 8 stops for HDR - you could get more encoding an fp32 into 4x8bit fixed point but would have to put up with some artefacts (procedure easily found with Google).

I think you may have misunderstood what I mean about putting the light data into a floating point texture. What I meant was to place the actual parameters for each light in the scene into a floating point texture. For example, the parameters I need for each light (to do it the way I want) are:

- Light Type float
- Light Position float3
- Light Direction float3
- Light Color float3
- Light Start Attenuation
- Light End Attenuation
- Light Inner Cone Angle
- Light Outer Cone Angle

It may seem strange to have both a start and end attenuation control, but there are some really cool effects that can be had by setting them both to the same value, the same with the inner and outer cone angles (for spotlights), and as such I want to keep all of the data I've shown. I've already culled some data that could be computed by the CPU in order to have less parameters per light being sent to the video card's PS constants.
I know how I could place this into a texture, and read it back. I also know the hardware I'm writing the engine for (PC) is quite capable of handling floating point textures as I'm using them at multiple points in the HDR pipeline (and have tested in on multiple video cards with perfect results), and I'm not planning on porting to console at any point. I am more concerned about the large amount of sampling required to draw each pixel with this method, such as reading every parameter for every light to calculate every pixel. I realise this method would only work with deferred rendering, as the sampling overhead to do it on a per model basis during the frame pass would be horrid (I think :D).

Killzone 2 used a more traditional deferred approach, laying all the shading model constants down into buffers in one pass over the geometry (see http://www.guerrilla-games.com/publications/dr_kz2_rsx_dev07.pdf). Very nice, but you have to watch out with MRTs on older PC hardware (although I think the 7-series were the last cards to be really unhappy with MRTs).

Excuse my stupidity, it's been a couple of years since I've done any 3D coding, and I'm still trying to remember it all, but what are MRTs? :oops: As it is I only just started using HLSL two weeks ago, as ever since I started using shaders a few years ago I always used shader ASM. :D

Thanks :smile:
 
MRT stands for multiple render targets - basically, you return more than one colour from your pixel shader.

I think you may have misunderstood what I mean about putting the light data into a floating point texture. What I meant was to place the actual parameters for each light in the scene into a floating point texture. For example, the parameters I need for each light (to do it the way I want) are:

- Light Type float
- Light Position float3
- Light Direction float3
- Light Color float3
- Light Start Attenuation
- Light End Attenuation
- Light Inner Cone Angle
- Light Outer Cone Angle

That is quite a lot of data to pull in :).

More importantly though - how will you handle overlapping lights if you put all the light parameters into screen space buffers?

I'd suggest you have a look at the Killzone presentation if you're keen on a deferred approach, since it's quite detailed and you could render as many lights as you desire with a similar process.

In general though, you need to render your geometry parameters into screen space and then light the scene - not render your light parameters into screen space and then render materials from there. :D
 
MRT stands for multiple render targets - basically, you return more than one colour from your pixel shader.

Thanks, I should heave realised, lol. :oops:

That is quite a lot of data to pull in :).

More importantly though - how will you handle overlapping lights if you put all the light parameters into screen space buffers?

I still don't think you quite understand. ;) What I meant was to store the lighting data into a texture in a linear fashion, not on a pixel per pixel (screen space) basis. However I think I've had too much caffeine, as after doing the maths I've realised that doing it this way would result in requiring 256 tex2D calls per final pixel that is rendered for 64 lights (just to sample the lighting params), and as a result it is not really going to work.

I'm very new to the technique of deferred shading, as I only heard about it yesterday, and I don't think I completely understood it properly until I read the Killzone presentation.

Basically, I am trying to get around the limitation of Shader Model 3's 224 maximum float constant limitation (without moving to Shader Model 4 & DX10). I think I've come across a solution though, as a result of reading the Killzone presentation. It's slightly different from the way that they do it, but should work better for what I want to implement. Thanks for the link to that one. :smile:

I think I will do the frame pass the way they mentioned to get the material and normal data, etc, into screen space, followed by doing multiple deferred shading passes with each pass calculating several lights, and the data from each pass being accumulated. (Is this similar to the way it's normally done?)

In general though, you need to render your geometry parameters into screen space and then light the scene - not render your light parameters into screen space and then render materials from there. :D

I did already realise that from my initial research yesterday :D, it was just that the actual process of the deferred shader pass(es) themselves that I was confused about.

Many thanks again for your help.
 
I still don't think you quite understand.

Lol, i'm prone to that ;)

You'll not have any shader length or constant store problems rendering as many lights as you please in a deferred scheme. I couldn't really say if it will be faster than a multi-pass approach, but it'll almost certainly be more satisfying to implement!
 
A quick question, still slightly confused, is deferred shading (the actual deferred shading part, not the getting stuff into screen space part) normally done in a single pass (I assume by looping in the PS to calculate each light)?
 
Deferred shading is attractive when you have a lot of small lights. If your lights are quite large in screen-space, you'll eat a lot of fill rate and could end up worse off compared to forward rendering.
Common misconception... the same things you do with a multi-pass forward renderer can do with a deferred renderer - i.e. handle multiple lights in one pass.

A quick question, still slightly confused, is deferred shading (the actual deferred shading part, not the getting stuff into screen space part) normally done in a single pass (I assume by looping in the PS to calculate each light)?
As above, it can be but it depends on the implementation.

It really is a completely simple question on whether or not it's cheaper to store and reload your interpolated rasterizer attributes or regenerate them, and that will depend on how many attributes you have and how expensive it is to render your geometry. That comparison has been steadily shifting towards the deferred method lately for a number of reasons.

Personally, I think the light pre-pass type deferred renderers are the most promising variant but I'm not aware of anything released using one as its main lighting mechanism yet.
I wouldn't even call this a variant, and it won't be interesting to do this per-pixel in the long run. A Uncharted-style or something like Johan Andersson's compute-shader tile-based deferred renderer (SIGGRAPH last year) is way more flexible and better all-around. IMHO there's no reason to accept the additional constraints and penalties of so-called "pre-pass" renderers.

I'm speaking about DX10+ hardware here where deferred rendering almost always makes more sense. On older hardware there are a number of considerations.
 
Thank heaps for all of the input, I love learning about this stuff, and I've learned more new concepts in the last week than I have in a long time. :smile:

I'm speaking about DX10+ hardware here where deferred rendering almost always makes more sense. On older hardware there are a number of considerations.

Considering I'm sticking with DX9 for this engine, the issue I see with using deferred rendering is that I would still need to end up running multiple passes for lighting as I would still be stuck with the float constant limitations of Shader Model 3, same as if I stick with forward rendering. Plus I would then have to end up running another forward pass to do all all of the transparency. In the end I think it would be better just to forward render everything, and set up the lights in groups so that, for example, the interior of each building has it's own set of lights, and the exterior area also has it's own set of lights. This way I should be easily able to work around the current limit of 10 lights per pass, and still only require one pass per set of geometry, as I'm already rendering the interior and exterior of each building separately.

I will definitely be moving to Win7 and Shader Model 4 or 5 for my next engine though, as the flexibility provided, especially as far as shader complexity, is exactly what I want. And I'll definitely be looking at using a deferred shading method at that stage (mainly so I can learn more from implementing it). :smile:
 
Plus I would then have to end up running another forward pass to do all all of the transparency.
This is actually not an issue in most cases... blending particles or even glass and things like that generally don't have typical lighting models on them anyways and are already handled specially by a forward renderer. It's no more work to do the exact same thing in a deferred renderer. For alpha tested and A2C stuff it works the same in a deferred renderer and actually tends to be somewhat more efficient.

In the end I think it would be better just to forward render everything, and set up the lights in groups so that, for example, the interior of each building has it's own set of lights, and the exterior area also has it's own set of lights. This way I should be easily able to work around the current limit of 10 lights per pass, and still only require one pass per set of geometry, as I'm already rendering the interior and exterior of each building separately.
Makes sense. If you can fit everything you need to do semi-efficiently into memory for a single pass, definitely go ahead and do that. In the future, with increasing complexity it is becoming more and more necessary to split things up, and just dumping the rasterizer outputs in screen space starts to be desirable.

Good luck!
 
Common misconception... the same things you do with a multi-pass forward renderer can do with a deferred renderer - i.e. handle multiple lights in one pass.

Fair enough - I ate a lot of frame rate with big lights when I tried it, but I'm perfectly willing to accept I wasn't trying hard enough :). I certainly didn't come up with the screen-space grid to process multiple lights at once (which is a neat idea).


I wouldn't even call this a variant, and it won't be interesting to do this per-pixel in the long run. A Uncharted-style or something like Johan Andersson's compute-shader tile-based deferred renderer (SIGGRAPH last year) is way more flexible and better all-around. IMHO there's no reason to accept the additional constraints and penalties of so-called "pre-pass" renderers.

I do agree, up to a point. :)

I really see them all as the same thing. The key feature for me is its building screen-space lighting data from depth & normal buffers and reference that data in a later material pass. This determines the bulk properties of the rendering process - it requires 3 major passes, you have a depth buffer available, your lights cost is proportional to their screen space size (and you can have lots! :D) and you're going to draw geometry multiple times.

Where you go from there, what exactly you buffer in the lighting stage, using volumes versus grids and so on is really dependant on what you're doing - what your scene and lighting model looks like, how much implementation time you have, how many extra processors are available and so on.
 
I really see them all as the same thing. The key feature for me is its building screen-space lighting data from depth & normal buffers and reference that data in a later material pass. This determines the bulk properties of the rendering process - it requires 3 major passes, you have a depth buffer available, your lights cost is proportional to their screen space size (and you can have lots! :D) and you're going to draw geometry multiple times.

Where you go from there, what exactly you buffer in the lighting stage, using volumes versus grids and so on is really dependant on what you're doing - what your scene and lighting model looks like, how much implementation time you have, how many extra processors are available and so on.
Yes agreed on all points. I also don't see the point in really trying to draw a firm distinction between various techniques... as people move to systems where you need more complex and larger data sets and algorithms they tend to move towards something more deferred. The details though, as you note, are specific to the application and platform.
 
Back
Top