Best way to handle multiple lights

Hi there,

I am currently in the process of developing a game engine and I am now at the point where I am going to implement lighting.

I have done lighting before, but wanted some input on the best way to handle multiple lights in a FORWARD RENDERER (I will implement Deferred later on).

(Currently using OpenGL 3.3)

There are 2 ways I can think of:

1 - Have a shader per light type and draw the mesh for each of the different lights, using each of the shaders

2 - Upload an array of lights into a single shader, and do a single draw of the mesh looping over the lights each frame

Can anyone provide me with some information regard the performance of the two methods, or any alternate methods that would be better than the ones provided.

Thanks

Method 1 is going to integrate cleaner when you come to do shadows.

How so? Would you mind explaining why?

Thanks

OK, to elaborate, and I’m assuming a forward renderer here (although it’s not wildly different for deferred), a typical way of handling multiple lights looks like:

  • Clear screen to black
  • Do z-prepass
  • Switch to additive blending
  • For each light, draw all objects it hits

This way multiple overlapping lights work properly, and the array of lights Method 2 is viable.

Where you have trouble is shadows. A shadow isn’t something that’s composited onto the scene as a post-processing effect; it’s an integral part of lighting and it’s caused by the light not hitting an object. So with the additive blending approach, that just means we somehow determine if an object is in shadow, and if so we add 0 rather than adding the light contribution. That way multiple overlapping lights and multiple overlapping shadows work correctly.

So now our renderer looks like:

  • Clear screen to black
  • Do z-prepass
  • Switch to additive blending
  • For each light, figure the areas in shadow then draw all objects it hits

This breaks the “loop through an array of lights” approach in your shader because your shader just doesn’t have the knowledge of geometry required to figure what’s in shadow. Even if it did, it would be hugely expensive to do this per-fragment; it needs to be done at a much coarser level.

So just to be totally clear, what breaks it is that before you add shadows the loop looks like:

  • lit surfaces
  • lit surfaces
  • lit surfaces
  • lit surfaces

After you add them it looks like:

  • mark shadows
  • lit surfaces
  • mark shadows
  • lit surfaces
  • mark shadows
  • lit surfaces
  • mark shadows
  • lit surfaces

And this is basically the shadow and light passes that the game Doom 3 uses.

There are of course (aren’t there always) special cases, and if the kind of rendering you want to do suits those, then none of the above applies. One such case might be where you decide that you’ll just have a single shadow-casting light and multiple smaller “fill” lights for which you’re not going to bother with shadows. But if you want a more general/flexible approach, it’s one light at a time, accumulated using additive blending.

[QUOTE=mhagain;1282395]OK, to elaborate, and I’m assuming a forward renderer here (although it’s not wildly different for deferred), a typical way of handling multiple lights looks like:

  • Clear screen to black
  • Do z-prepass
  • Switch to additive blending
  • For each light, draw all objects it hits

This way multiple overlapping lights work properly, and the array of lights Method 2 is viable.

Where you have trouble is shadows. A shadow isn’t something that’s composited onto the scene as a post-processing effect; it’s an integral part of lighting and it’s caused by the light not hitting an object. So with the additive blending approach, that just means we somehow determine if an object is in shadow, and if so we add 0 rather than adding the light contribution. That way multiple overlapping lights and multiple overlapping shadows work correctly.

So now our renderer looks like:

  • Clear screen to black
  • Do z-prepass
  • Switch to additive blending
  • For each light, figure the areas in shadow then draw all objects it hits

This breaks the “loop through an array of lights” approach in your shader because your shader just doesn’t have the knowledge of geometry required to figure what’s in shadow. Even if it did, it would be hugely expensive to do this per-fragment; it needs to be done at a much coarser level.

So just to be totally clear, what breaks it is that before you add shadows the loop looks like:

  • lit surfaces
  • lit surfaces
  • lit surfaces
  • lit surfaces

After you add them it looks like:

  • mark shadows
  • lit surfaces
  • mark shadows
  • lit surfaces
  • mark shadows
  • lit surfaces
  • mark shadows
  • lit surfaces

And this is basically the shadow and light passes that the game Doom 3 uses.

There are of course (aren’t there always) special cases, and if the kind of rendering you want to do suits those, then none of the above applies. One such case might be where you decide that you’ll just have a single shadow-casting light and multiple smaller “fill” lights for which you’re not going to bother with shadows. But if you want a more general/flexible approach, it’s one light at a time, accumulated using additive blending.[/QUOTE]

Thanks for the reply!

This explains it perfectly and is basically what I had already thought but wasn’t entirely sure.

So would the typical approach be method 1, or would it be something different?

Thank you