Hi.

I've followed the advice from:

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

edit: sorry, forum software breaks link - it really does have square brackets in the URI

I've developed a renderer that uses premultiplied alpha throughout (all textures are
premultiplied, and the output of the renderer itself is a premultiplied alpha image in
a framebuffer). The renderer works by first clearing the framebuffer to (0, 0, 0, 0), then
rendering all opaque objects in any order (using the depth buffer to prevent overdraw),
and then rendering all translucent objects from furthest to nearest. I don't think there's
anything controversial there other than maybe that it produces an image that has
translucent regions, because the result is intended for use in further compositing down
the line, whereas I imagine most renderers produce a completely opaque image (and
so typically clear to (0,0,0,1) or some other opaque background color).

I'm having issues when it comes to rendering translucent objects.

The renderer uses standard and unsurprising multipass lighting. Here's an image of
an opaque object rendered with five lights (one large white light is offscreen):



This is rendered in the usual style: The square in the middle is rendered five times,
once with each light, with the contributions from each light being summed with additive blending.
Basically:

Code :
glBlendFunc(GL_ONE, GL_ONE);
 
for (o : objects) {
  for (l : lights) {
    render(o, l);
  }
}

However, and this is taken directly from the blog post above, for translucent
lighting, one needs to set a different blending mode for the first light, but then
switch back to additive blending for the rest. So, essentially:

Code :
for (o : objects) {
 
  boolean first = true;
  for (l : lights) {
    if (first) {
      glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
    } else {
      glBlendFunc(GL_ONE, GL_ONE);
    }
 
    render(o, l);
    first = false;
  }
}

This does appear to give roughly the right results, except that for some
reason, I have to perform premultiplication on the actual resulting
fragment color produced by the shader. That is, I have to do:

Code :
  out_rgba = vec4(lit.xyz * albedo.w, albedo.w);

... where out_rgba is my fragment shader's output, lit is the
final color of the surface with lighting applied, and albedo is the
surface albedo (either sampled from a premultipled texture, or specified
as a premultiplied color via a uniform, etc). Otherwise, the shaders used
for translucent objects are character-for-character identical to their opaque
equivalents (the shaders are generated, and differ only in the final line that
performs the premultiplication).

Here's what happens when rendering the above object as a translucent without
performing the premultiplication in the shader (note the excessive brightness):



... and here's what happens when I do perform the premultiplication
in the shader:



... which is more like what I'd expect to see and seems almost pixel identical
other than the fact that the object is (deliberately) slightly translucent.

My question is: Why do I have to perform this extra step? It seems silly to have to
have "translucent" variants of all of the existing shaders when they're otherwise
identical except for the final line. Is it possible to get the blending hardware to do
this step for me?