Changing how lights are bound in forward passes


In Ogre 1.x; each Entity has it’s own list of lights, sorted by proximity. This way, when rendering, each Entity sends to the shader the closest N lights and, given enough batches, 8 lights per batch would be enough to lit the whole scene.

That’s in theory at least. Until we enable shadow maps: when shadow maps are enabled, the M shadow casting lights closest to the camera are first sent to the shader, then the remaining N-M lights closest to the object are sent.

That means if you have 8 shadow maps, and your limit is 8 lights per batch; you will be sending all the lights closest to the camera. You can no longer lit the whole scene with forward rendering.

Of course, in Ogre 1.x that wasn’t much of a big deal because rendering 8 passes (+ the forward pass) is prohibitively slow. But in Ogre 2.x; it’s not unreasonable anymore. And this limitation could actually start getting noticed.

Handling lights in Ogre 2.x

There is a problem with shadow mapped lights: They need to go first. It’s not because of a whim: They aren’t just sent to constant shader registers. They also come with a texture bound to a specific texture unit #, usually only known at compile time.

We can’t send the first shadow mapping light as light #7 when the shader associates the first light #0 as the light with a shadow map bound to texture unit #0.

Therefore the solution was simple (though the code in CompositorNodeShadow looks a bit complex and could probably be improved): we still build a light list per Entity, sorted by proximity. Then we take a look at the N lights that are going to be sent to the shader. We then look which of those N lights are shadow casters and put them first. We’re done! 🙂

So let see it with some examples. “L” stands for light. “S” stands for shadow casting light (not just setCastShadows set to true, it is actually close enough to the camera to be assigned a shadow map). The shader accepts 4 lights:

Case 1

An object has the following light list (ordered by proximity to it):

LLLL SSSS

None of the shadow casting lights are sent to the shader.

Case 2

An object has the following light list

LSSL LLS

We send the first 2 shadow casting lights first. To the shader we send: SSLL. Note they’re sorted by proximity within their own group.

What happens if my shader expects 3 shadow maps, and only 2 are sent?

Ogre will bind a null texture filled with “1.0” on the third one so that your light never actually shadows. With Ogre 1.x this could only happen when the number of shadow casting lights was lower than the number of shadow maps set in SceneManager.

In Ogre 2.x, this will probably happen very often for objects that are far from camera and have many lights close to it. Note that directional lights are always “closest” to all objects.

What about Deferred Shading & Forward+/Forward Deferred?

This blog post is about Forward lighting, and Deferred Shading/Forward+ have nothing to do with all this.

As a side note, there’s still work to be done so that we disable code specific to forward rendering (like building & sorting the per-entity light list) while still let Shadow mapping work correctly.

Also for Forward+ (aka. Forward Deferred) & Forward+ 2.5D  I plan on making a custom compositor scene pass. Although Forward+ can be achieved with the new compositor, a custom pass would allow to reuse the same render queue from the Z pre pass.

But there’s still a long road before I can get into that.