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.