3
\$\begingroup\$

I want to be able to support multiple lights of different types (point, spot & directional). Note that I also want to be able to render transparent/translucent objects, which rules out deferred rendering.

This means that I will need a fixed upper limit on the number of lights. All of the implementations I have seen use a uniform array, for example:

struct PointLight
{
    vec3 color;
    float intensity;
    float radius;
};

uniform PointLight pointLights[8];

How can I best extend this to multiple types of lights? Two possible options are:

  1. Create multiple uniform arrays, one for each type of light.
  2. Define a struct that can be used for all three types of light and then have some way of differentiating the types of lights in the array (e.g. using start index and count uniforms or adding some kind of type flag to the struct).

Both have disadvantages. Both waste memory (although the first more so than the second) and the second either requires additional uniforms or flag checking.

What is the usual method for handling multiple lights of various types?

\$\endgroup\$
  • \$\begingroup\$ It doesn't rule out deferred rendering though; it just requires a different approach. See this tutorial for how it can be achieved: john-chapman.net/content.php?id=13 \$\endgroup\$ – Fault Jun 26 '14 at 12:46
  • \$\begingroup\$ Ok, but I think that it's common to render multiple lights per pass even in deferred rendering. \$\endgroup\$ – Homar Jun 26 '14 at 13:00
3
\$\begingroup\$

There is not THE SOLUTION when it comes to implement complex lighting. As a result I can not tell you the "usual method", but I can give you pointers in the direction.

First, you want to get rid of the notion, that you have one shader and render one pass and you are done. The way multiple lights are handled is by rendering one light at a time and the geometry it affects and then blending (GL_ONE, GL_ONE) it together.

The consensus basically is that for performance reasons you want to have as few if-conditions as possible in your shader. Here you will see often that one shader is used per light type. Then again this is hard to maintain and I for example use an integer.

But if you have add a global ambient term, for example, you can not add this term for each light. The correct solution here is to special pass for the global ambient term. The same is for emissive materials.

You mentioned translucent objects. The same problem you have with deferred lighting, you have it also with multiple passes. Each pass the object becomes more opaque. Although I have not yet tackled this problem (lighting + translucency), my approach would be to render the opaque objects normally. (Maybe even deferred.) And render the translucent objects into a framebuffer as if they where opaque and then blend that. The problem here is when the objects overlap. If overlap is probable you would have to render each translucent object on its own and blend them individually.

If this all works fine, you can look into shadows. One of the reasons why each light is rendered on it's own, is because if you add any form of dynamic lightmap, you would first "render" the light map and then the light.

So basically so lots of different shaders and many frame buffers and this is before doing fancy things like iradiance transfer or ambient occlusion.

\$\endgroup\$
  • 1
    \$\begingroup\$ Thanks for the explanation. It seems that there is no way to avoid multiple passes. In this case I might as well go and use deferred shading. \$\endgroup\$ – Homar Jun 26 '14 at 23:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.