Is blending of multiple light sources for translucent objects noncommutative?

Hi.

I have a basic forward renderer. The architecture is fairly unsurprising: First, opaque objects are batched by lights and then for each light, the opaque objects belonging to that light are rendered in an arbitrary order (using the depth buffer).

A scene with a single opaque object lit by four lights:

Then, translucent objects are rendered from furthest to nearest. The above scene, but with a translucent object added:

In both cases, I use simple one-light shaders, blending the result of each light’s contribution with additive blending:

Pseudocode:


for (l : lights) {
  opaques = getOpaquesForLight(l);
  for (o : opaques) {
    renderOpaque (l, o);
  }
}

for (t : translucents) {
  lights = getLightsForTranslucent(t);
  for (l : lights) {
    renderTranslucent(l, o);
  }
}

The renderer uses premultiplied alpha blending throughout. See this blog entry for details:

https://home.comcast.net/~tom_forsyth/blog.wiki.html#[[Premultiplied%20alpha]]

So essentially, for a translucent object, I’m doing the following:


Translucent t;
boolean first = true;
for (l : lights) {
  if (first) {
    setBlendingMode(ONE, ONE_MINUS_SRC_ALPHA);
  } else {
    setBlendingMode(ONE, ONE);
  }
  render(l, t);
  first = false;
}

The initial (ONE, ONE_MINUS_SRC_ALPHA) pass has the effect of setting the overall
opacity of the rendered surface, and subsequent passes with (ONE, ONE) have the effect
of adding in the contributions of the other lights.

I don’t think anything I’m describing is surprising to anyone here.

The problem: Lights for translucent objects are actually collected in an arbitrary order. That is,
if I have a translucent object T lit by lights L0, L1, L2, L3, then on the first frame, T may be rendered with
lights in the order L1, L3, L0, L2 and on the next frame the order might be L3, L0, L2, L1, and so on.
There’s currently no guarantee of any particular order.

I didn’t think this was an issue, as unless I’m grossly mistaken, the blending operations should be
commutative. The data structure I was using to collect lights for objects previously actually did give
a consistent order every frame. However, I switched to a simpler structure here that removed the
consistent ordering (as it didn’t seem to be important), and the result is this:

http://waste.io7m.com/2014/05/27/lights.mp4

As you can see, the light contributions for the translucent object are rendered in a different order
in every frame. The resulting image for each possible order seems to be subtly different each time.
I’ve verified that the exact same shader parameters are assigned for all lights each time, so it seems
to be solely down to the order that operations occur.

Is blending in this fashion noncommutative? In other words, do I have to ensure that I collect lights
in the same order each time I render an object? I don’t want to put that ordering requirement in and
then find out later that there’s actually a problem elsewhere and that I’m really just working around it.

To clarify, the differing blending modes indicated are only for translucent/transparent objects.

Rendering opaque objects is simple (ONE, ONE) additive blending all the way and that is definitely commutative.

Hmm, if the order of lights is “random”, that implies the first light is random, so the lit object color going into the blending is different for different first light sources. Or put differently, if I understand it correctly, the rendering of lights 2-n can be in any order, but the first light needs to be consistent across frames.

I think you’re right, I’m just unable to prove it currently.

I’ve just discovered that this is only an issue with the specular terms. Clearly I’m doing something with respect to alpha that breaks commutativity.