Best way to handle multiple lights

Started by
6 comments, last by TheChubu 7 years, 11 months ago

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

Advertisement

If the result of my google-fu-ing is correct, OGL 3.3 is basicly targeting D3D10 level hardware, so Shader Model 4 equivalent GLSL is supported, meaning that there is no upper limit on the instruction count in one shader. In this case the second option will be the winner. With the first one you have to render every mesh N times if you have N lights in your scene, which gets really expensive really fast, and wastes a lot of resources (from the CPU side you have to send multiple draw commands per mesh, on the GPU side every pipeline stage would do the same thing, apart from the pixel shader, each time you render with a different light). The first option was what we used before SM4 hardware, where the length of the shaders were limited, it was basicly the reason why deferred rendering got so popular. (With N light and M meshes on SM3 level hardware you needed N*M draw calls with forward rendering, but only N+M with deferred.)

If the result of my google-fu-ing is correct, OGL 3.3 is basicly targeting D3D10 level hardware, so Shader Model 4 equivalent GLSL is supported, meaning that there is no upper limit on the instruction count in one shader. In this case the second option will be the winner. With the first one you have to render every mesh N times if you have N lights in your scene, which gets really expensive really fast, and wastes a lot of resources (from the CPU side you have to send multiple draw commands per mesh, on the GPU side every pipeline stage would do the same thing, apart from the pixel shader, each time you render with a different light). The first option was what we used before SM4 hardware, where the length of the shaders were limited, it was basicly the reason why deferred rendering got so popular. (With N light and M meshes on SM3 level hardware you needed N*M draw calls with forward rendering, but only N+M with deferred.)

Hi , thanks for the reply.

This is what I thought, just wanted to make sure :P

Although, I read somewhere that looping or branching in shaders is not very efficient. Is this true and would it cause an issue using method 2?

Thanks

Assuming you're using shader based lights, and not hardware (which is limited), you'll find yourself running into problems with multiple lights in forward rendering.

For simple scenes, the forward rendering technique will be fast with lighting. But as you increase in the amount of lights in the scene along with geometry, you'll start slogging through a marsh of complexity.


To the related note:

It's pretty common to upload a list of effecting lights per object. Or to test against all lights within the scene or frustum.

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


A modern variation on this would be tiled forward rendering or "Forward+", where you iterate over a list of lights per screen tile instead of per mesh.

current project: Roa

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


A modern variation on this would be tiled forward rendering or "Forward+", where you iterate over a list of lights per screen tile instead of per mesh.

I think you'd need compute shaders for that, which aren't available at the GL level he is targeting.

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator

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


A modern variation on this would be tiled forward rendering or "Forward+", where you iterate over a list of lights per screen tile instead of per mesh.

I think you'd need compute shaders for that, which aren't available at the GL level he is targeting.

Compute shaders in general can be simulated on old GL and DX levels with a little effort (although you need shaders at least). Use pixel shaders to do the compute shader work, receive all input you need from textures which don't contain image data and instead whatever application data you need, and set up render targets such that your pixel shaders output the stuff in some kind of format you can read from later. Of course, more expensive and restricted than actual compute shaders, but if you want to do stuff on the GPU and don't have compute available it often still is an easy performance win (for particle systems for example).

Would be interesting to see those lighting and light culling algorithms applied to something like WebGL (if it is possible at all, given the limitations).

I'd like to see an implementation of tile deferred without compute shaders.

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator

This topic is closed to new replies.

Advertisement