Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Premultiply/Unpremultiply alpha with OpenGL ?

1,229 views
Skip to first unread message

Michele Puccini

unread,
Feb 21, 2007, 4:20:56 AM2/21/07
to
Hello,

I'm implementing Porter/Duff compositing with OpenGL and I need to work with
ARGB 32bit image data.
After experimenting for some time I discovered that the image data must be
premultiplied in order to obtain the correct compositing results.

So the question pops in my mind: is there a way to Premultiply/Unpremultiply
alpha at hardware speeds with OpenGL ?

Cheers,

Mik
--


fungus

unread,
Feb 21, 2007, 5:11:57 AM2/21/07
to

The first question is: "Where is the data?"

If it's in main RAM then forget it. Your main
CPU will be faster than a round trip to the
graphics card (it's "hardware" too...)

If the data is on the graphics card then you
can do it with a pixel shader. Treat the data
as a texture and draw a quad with 1:1 pixel
mapping and process the data along the way.


--
<\___/>
/ O O \
\_____/ FTB. For email, remove my socks.


We’re judging how a candidate will handle a nuclear
crisis by how well his staff creates campaign ads.
It’s a completely nonsensical process.

Michele Puccini

unread,
Feb 21, 2007, 5:25:33 AM2/21/07
to
Hi fungus,

as you guessed the data is graphics mem (i.e. a texture).
So you're suggesting me a pixel shader ?
And what happens when I want to read back (to system memory) the processed
unpremultiplied pixels ?

Mik
--


"fungus" <uma...@SOCKSartlum.com> ha scritto nel messaggio
news:QVUCh.994$ak1...@news.ono.com...

fungus

unread,
Feb 21, 2007, 5:38:45 AM2/21/07
to
Michele Puccini wrote:
> Hi fungus,
>
> as you guessed the data is graphics mem (i.e. a texture).
> So you're suggesting me a pixel shader ?

On a nice graphics card, yes.

> And what happens when I want to read back (to system memory) the processed
> unpremultiplied pixels ?
>

glGetTexImage()/glReadPixels()

Michele Puccini

unread,
Feb 21, 2007, 11:13:02 AM2/21/07
to
Ok, since I need to get the accumulated alpha from the blending operations,
here's my fragment shader for SRC_OVER blending of non premultiplied ARGB
pixels. It works very well. The extra_alpha value is only for testing
purposes.

--8<----

uniform sampler2D texture;

void main()
{
vec4 s = texture2D(texture,gl_TexCoord[0].st);
vec4 d = gl_FragColor;

float extra_alpha = 0.1;

s.a = s.a * extra_alpha;

if (s.a == 0.0) gl_FragColor = d;
else if (s.a == 1.0) gl_FragColor = s;
else if (d.a == 0.0) {gl_FragColor = s;}
else
{
float a = s.a + d.a - s.a * d.a;
gl_FragColor.a = a;
gl_FragColor.r = (s.a*s.r+d.a*d.r-s.a*d.a*d.r)/a;
gl_FragColor.g = (s.a*s.g+d.a*d.g-s.a*d.a*d.g)/a;
gl_FragColor.b = (s.a*s.b+d.a*d.b-s.a*d.a*d.b)/a;
}
}

--8<----

Of course I have no experience at all with fragment shaders and I'm getting
a warning from the compiler: "warning C7050: gl_FragColor might be used
before being initialized".
Any hint ?

Cheers,

Mik
--

"fungus" <uma...@SOCKSartlum.com> ha scritto nel messaggio

news:ZiVCh.2517$M51....@news.ono.com...

Rolf Magnus

unread,
Feb 21, 2007, 6:24:58 PM2/21/07
to
Michele Puccini wrote:

I guess it's about the line:

vec4 d = gl_FragColor;

At that point, you haven't written anything to gl_FragColor yet, so it's
uninitialized.

Michele Puccini

unread,
Feb 22, 2007, 3:41:20 AM2/22/07
to
As the docs say:

"Notice that the fragment shader has no access to the frame buffer. This
implies that operations such as blending occur only after the fragment
shader has run."

The big mistake is that I was thinking of reading the framebuffer contents
with gl_FragColor..

Mmmh.. maybe I could implement my blending by binding a pbuffer object to a
texture (or even a FBO) and then access the dest pixels from there, right ?

Mik


"Rolf Magnus" <rama...@t-online.de> ha scritto nel messaggio
news:erikca$2mk$01$1...@news.t-online.com...

Wolfgang Draxinger

unread,
Feb 26, 2007, 7:17:38 PM2/26/07
to
Michele Puccini wrote:

> Mmmh.. maybe I could implement my blending by binding a pbuffer
> object to a texture (or even a FBO) and then access the dest
> pixels from there, right?

As long as the render target is not the source of any fragment.
If you try to read contents from the buffer you're writing to
you get a lot of problems. GPUs are highly parallel and may do a
lot of operations out of order, so you need some synchronisation
point. The easiest way to do this, is just to make a copy of the
current buffer contents to a texture and use this in the
following procoess. Blending is one of the last remaining fixed
function pipeline steps, that is not yet freely programmable by
OpenGL. But this is a goal for the next bigger advance of OpenGL
development.

Better use a FBO, since PBuffers are a bit clumsy to use as
texture units.

Wolfgang Draxinger
--
E-Mail address works, Jabber: hexa...@jabber.org, ICQ: 134682867

Michele Puccini

unread,
Feb 27, 2007, 4:51:36 AM2/27/07
to
Many thanks Wolfgang,

you have to know that I'm doing all this in order to solve what I consider
an OpenGL problem:

The result of blending (SRC_OVER) a src translucent color with a dest
transparent one (i.e. 0x00000000 argb) is wrong as the src color is always
blend by taking the dest color into account.

But, if the dest color alpha is 0, then it must not affect the result, as
it's completely transparent.

Cheers,

Mik
--

Wolfgang Draxinger

unread,
Feb 27, 2007, 7:41:06 AM2/27/07
to
Michele Puccini wrote:

> Many thanks Wolfgang,
>
> you have to know that I'm doing all this in order to solve what
> I consider an OpenGL problem:
>
> The result of blending (SRC_OVER) a src translucent color with
> a dest transparent one (i.e. 0x00000000 argb) is wrong as the
> src color is always blend by taking the dest color into
> account.
>
> But, if the dest color alpha is 0, then it must not affect the
> result, as it's completely transparent.

What is your blending equation? Maybe it can be done with fixed
function OpenGL alone.

Michele Puccini

unread,
Feb 27, 2007, 9:57:30 AM2/27/07
to

I'm using

glBlendFuncSeparateEXT(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE,
GL_ONE_MINUS_SRC_ALPHA)

It seems this approssimates the SRC_OVER as best (but not as expected).
Src pixels are not premultiplied.

Mik
--


"Wolfgang Draxinger" <wdrax...@darkstargames.de> ha scritto nel messaggio
news:2gldb4-...@darkstargames.dnsalias.net...

Wolfgang Draxinger

unread,
Feb 27, 2007, 5:10:52 PM2/27/07
to
Michele Puccini wrote:

>
> I'm using
>
> glBlendFuncSeparateEXT(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
> GL_ONE, GL_ONE_MINUS_SRC_ALPHA)
>
> It seems this approssimates the SRC_OVER as best (but not as
> expected). Src pixels are not premultiplied.

Actually I wanted to know your desired blending _equation_ i.e.
something like

R_dest := "something which has R_src in it"
G_dest := "something which has G_src in it"
B_dest := "something which has B_src in it"

e.g. the usually used alpha blending is

<R,G,B>_dest := <R,G,B>_dest * (1-A) + <R,G,B>_src * A;

Michele Puccini

unread,
Feb 28, 2007, 3:57:46 AM2/28/07
to
Well, here's the equation I need.

a2*X2 + a1*X1 - a2*a1*X1
X(total) = --------------------------
a1 + a2 - a1*a2

pseudo-code
s = source pixel <a,r,g,b> (float)
d = dest pixel <a,r,g,b> (float)
c = computed pixel <a,r,g,b> (float)

here we compute the final a,r,g,b of s SRC_OVER b

float a = (s.a + d.a - s.a * d.a)
float r = (s.a * s.r + d.a * d.r - s.a * d.a * d.r)/a
float g = (s.a * s.g + d.a * d.g - s.a * d.a * d.g)/a
float b = (s.a * s.b + d.a * d.b - s.a * d.a * d.b)/a

c.a = a; c.r = r; c.g = g c.b = b

now we can write c to the framebuffer.

Cheers,

Mik
--

"Wolfgang Draxinger" <wdrax...@darkstargames.de> ha scritto nel messaggio

news:csmeb4-...@darkstargames.dnsalias.net...

Andy V

unread,
Feb 28, 2007, 9:21:38 PM2/28/07
to
Michele Puccini wrote:
> Well, here's the equation I need.
>
>
>
> a2*X2 + a1*X1 - a2*a1*X1
> X(total) = --------------------------
> a1 + a2 - a1*a2
>
> pseudo-code
> s = source pixel <a,r,g,b> (float)
> d = dest pixel <a,r,g,b> (float)
> c = computed pixel <a,r,g,b> (float)
>
> here we compute the final a,r,g,b of s SRC_OVER b
>
> float a = (s.a + d.a - s.a * d.a)
> float r = (s.a * s.r + d.a * d.r - s.a * d.a * d.r)/a
> float g = (s.a * s.g + d.a * d.g - s.a * d.a * d.g)/a
> float b = (s.a * s.b + d.a * d.b - s.a * d.a * d.b)/a
>
> c.a = a; c.r = r; c.g = g c.b = b
>
> now we can write c to the framebuffer.

Generally you want to keep opacity weighted colors in the frame buffer
-- this would save you those pesky divides. You can do the divide by
alpha after reading the frame buffer back; it will be more accurate anyway.

So, if d is opacity weighted, your formula for r becomes:

r = s.a * s.r + d.r - s.a * d.r
or
r = s.a * s.r + (1 - s.a) * d.r

which is GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA.

For alpha, your formula is:

a = s.a + d.a - s.a * d.a
or
a = s.a + (1 - s.a) * d.a

which is also GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA.

See how simple it can be?

--
Andy V

Michele Puccini

unread,
Mar 1, 2007, 5:28:43 AM3/1/07
to
Thanks Andy,

I'm still in the fog :)

- create a PBuffer or a FBO (640x400, int argb),
- clear it with 0x00000000 (transparent black)
- create a translucent texture (640,400, int argb)
- clear it with 0x1fff0000 (semi-transparent red)
- enable blending
- set blending function to SRC_ALHA,ONE_MINUS_SRC_ALPHA
- draw the texture to the buffer at 0,0 (2d, upper left corner, no lights)
- read the buffer pixels (glReadPixels)
- you should get that the pixels affected from the texure are all ==
0x1fff0000 (according to the blending function)

It's not. You'll get a darker red. You'll notice that the pixels have been
affected by the current buffer color (black). It is wrong, as the background
alpha is 0.

According to the math:
Let's say that s<argb> is the texture pixel
and d<argb> is the background.

we have d.a = 0, so

a = s.a + (1 - s.a) * d.a

becomes

a = sa + (1 - s.a) * 0

or

a = sa

and for the other RGB components..

float r = (s.a * s.r + d.a * d.r - s.a * d.a * d.r)/a

becomes

r = (s.a * s.r + 0 * d.r - s.a * 0 * d.r) / a

or

r = (s.a * s.r) / a

or

r = s.r

se we can say that the result is

a = s.a
r = s.r
g = s.g
b = s.b

as expected.

As we can see, the dest color (the background in our case) does not affect
the result if its alpha is 0.

Perfect! ...but it's not, according to the real-world test.

What the hell am I missing ?

Mik
--

"Andy V" <nob...@nowhere.net> ha scritto nel messaggio
news:CqGdnfs2aukupXvY...@comcast.com...

jbwest

unread,
Mar 1, 2007, 9:55:51 AM3/1/07
to

"Michele Puccini" <m...@c-l-a-s-s-x.it> wrote in message
news:wVxFh.53$Oh6...@nntpserver.swip.net...

What's the color of the polygon you apply the texture to ?
What's your texture blend mode ?


Your source alpha isn't FF, it's 1F, so

r = s.a * s.r + (1 - s.a) * d.r

is

r = 1F/FF * s.r + 0 .

jbw


Michele Puccini

unread,
Mar 2, 2007, 2:54:34 AM3/2/07
to
The polygon color is white, with varying extra alpha (sometimes I seed to
fade out ;)).

from my code (java/lwjgl)

// modulate texture graphics with a color
EXTBlendFuncSeparate.glBlendFuncSeparateEXT(GL11.GL_SRC_ALPHA,
GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);

GL11.glColor4f(1f, 1f, 1f, extraAlpha);

GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE,
GL11.GL_MODULATE);

GL11.glEnable(GL11.GL_BLEND);


--
back to math
--


if

a = s.a

and

d.a = 0

then (from my original code)

float r = (s.a * s.r + d.a * d.r - s.a * d.a * d.r)/a

becomes

r = (s.a * s.r) / s.a

so the result is

r = s.r


Mik
--

"jbwest" <jbw...@comcast.net> ha scritto nel messaggio
news:2aKdna1zMdTtdHvY...@comcast.com...

jbwest

unread,
Mar 2, 2007, 9:31:35 AM3/2/07
to

Dont top post, see below,.


"Michele Puccini" <m...@c-l-a-s-s-x.it> wrote in message

news:bLQFh.9$zP2...@nntpserver.swip.net...

so your RED gets diminished by ExtraAlpha, which when read back, is thus <
FF . As expected.

jbw


Michele Puccini

unread,
Mar 2, 2007, 9:49:10 AM3/2/07
to

"jbwest" <jbw...@comcast.net> ha scritto nel messaggio
news:3pudnQfuJO6gqHXY...@comcast.com...

No, consider the extra alpha = 1.

GL11.glColor4f(1f, 1f, 1f, 1f);

Basically the blending SHOULD NOT take the background color into account
when it is completely transparent (alpha = 0). The math says that, but not
the opengl results. Maybe because the 99.9% of the code around is targeted
to a window context and relies on the fact that the background is always
opaque (alpha = 1). But when you render to a PBuffer or to a FBO..


Mik
--


jbwest

unread,
Mar 2, 2007, 9:05:56 PM3/2/07
to

"Michele Puccini" <m...@c-l-a-s-s-x.it> wrote in message
news:IPWFh.123$zP2...@nntpserver.swip.net...

Umm, you have texture ALPHA as 1F, and you have MODULATE, so the ALPHA is
1F/FF * ExtraAlpha.
Try it with a WHITE background.

jbw


Michele Puccini

unread,
Mar 3, 2007, 5:36:43 AM3/3/07
to

"jbwest" <jbw...@comcast.net> ha scritto nel messaggio
news:y_-dnWQVuLllSnXY...@comcast.com...

>>> so your RED gets diminished by ExtraAlpha, which when read back, is thus
>>> < FF . As expected.
>>>
>>> jbw
>>
>> No, consider the extra alpha = 1.
>>
>> GL11.glColor4f(1f, 1f, 1f, 1f);
>>
>> Basically the blending SHOULD NOT take the background color into account
>> when it is completely transparent (alpha = 0). The math says that, but
>> not the opengl results. Maybe because the 99.9% of the code around is
>> targeted to a window context and relies on the fact that the background
>> is always opaque (alpha = 1). But when you render to a PBuffer or to a
>> FBO..
>>
>>
>> Mik
>> --
>>
> Umm, you have texture ALPHA as 1F, and you have MODULATE, so the ALPHA is
> 1F/FF * ExtraAlpha.
> Try it with a WHITE background.
>
> jbw

Yes, the white bg approximates the results, but there are still problems in
the compositing approach.
I still suspect on something missing in the opengl implementation when
blending to translucent buffers. Of course I'm missing something..

Mik
--


jbwest

unread,
Mar 3, 2007, 4:41:17 PM3/3/07
to

"Michele Puccini" <m...@c-l-a-s-s-x.it> wrote in message
news:%ccGh.37$_07...@nntpserver.swip.net...

Do you understand why changing the background changed the result? Do you
understand the impact of having the texture alpha at 1F ? OpenGL is doing
what it's advertised to do; if you create the correct equation, you can do
what you want.


jbw


Michele Puccini

unread,
Mar 5, 2007, 3:44:44 AM3/5/07
to
"jbwest" <jbw...@comcast.net> ha scritto nel messaggio
news:uf-dnf34_fftdnTY...@comcast.com...
>

> FBO..
>>>>
>>>>
>>>> Mik
>>>> --
>>>>
>>> Umm, you have texture ALPHA as 1F, and you have MODULATE, so the ALPHA
>>> is 1F/FF * ExtraAlpha.
>>> Try it with a WHITE background.
>>>
>>> jbw
>>
>> Yes, the white bg approximates the results, but there are still problems
>> in the compositing approach.
>> I still suspect on something missing in the opengl implementation when
>> blending to translucent buffers. Of course I'm missing something..
>>
>> Mik
>> --
>
> Do you understand why changing the background changed the result? Do you
> understand the impact of having the texture alpha at 1F ? OpenGL is doing
> what it's advertised to do; if you create the correct equation, you can do
> what you want.
>
> jbw

> Do you understand why changing the background changed the result?

No, because it should not.
Any fragment with alpha = 0 is a NO-OP for SRC_OVER.

> Do you understand the impact of having the texture alpha at 1F ?

Yes, it should blend to the background.
But when the background's alpha is 0, the blend should not happen at all.
From the math I see that:

A SRC_OVER B = A (if B has alpha = 0)
A SRC_OVER B = B (if A has alpha = 0)
A SRC_OVER B = A (if A has alpha = 1)

> If you create the correct equation, you can do what you want.

Which is the equation, then ?

Cheers,

Mik
--


Andy V

unread,
Mar 5, 2007, 8:09:16 PM3/5/07
to
Michele Puccini wrote:
> Thanks Andy,
>
> I'm still in the fog :)
>
> - create a PBuffer or a FBO (640x400, int argb),
> - clear it with 0x00000000 (transparent black)
> - create a translucent texture (640,400, int argb)
> - clear it with 0x1fff0000 (semi-transparent red)

Guessing at your frame buffer organization, I assume this is red ff and
alpha 1f. This is not an opacity weighted color. Using (SRC_ALPHA,
ONE_MINUS_SRC_ALPHA) assumes that the destination buffer contains
opacity weighted colors -- you have violated this. Instead, your
destination should be cleared to 1f1f0000.

--
Andy V

jbwest

unread,
Mar 5, 2007, 10:32:45 PM3/5/07
to

"Michele Puccini" <m...@c-l-a-s-s-x.it> wrote in message
news:ZLQGh.42$NF...@nntpserver.swip.net...

> "jbwest" <jbw...@comcast.net> ha scritto nel messaggio
> news:uf-dnf34_fftdnTY...@comcast.com...
>>
>
>> FBO..
>>>>>
>>>>>
>>>>> Mik
>>>>> --
>>>>>
>>>> Umm, you have texture ALPHA as 1F, and you have MODULATE, so the ALPHA
>>>> is 1F/FF * ExtraAlpha.
>>>> Try it with a WHITE background.
>>>>
>>>> jbw
>>>
>>> Yes, the white bg approximates the results, but there are still problems
>>> in the compositing approach.
>>> I still suspect on something missing in the opengl implementation when
>>> blending to translucent buffers. Of course I'm missing something..
>>>
>>> Mik
>>> --
>>
>> Do you understand why changing the background changed the result? Do you
>> understand the impact of having the texture alpha at 1F ? OpenGL is doing
>> what it's advertised to do; if you create the correct equation, you can
>> do what you want.
>>
>> jbw
>
>> Do you understand why changing the background changed the result?
>
> No, because it should not.
> Any fragment with alpha = 0 is a NO-OP for SRC_OVER.

use glAlphafunc for source alpha == 0.

>
>> Do you understand the impact of having the texture alpha at 1F ?
>
> Yes, it should blend to the background.
> But when the background's alpha is 0, the blend should not happen at all.
> From the math I see that:
>
> A SRC_OVER B = A (if B has alpha = 0)
> A SRC_OVER B = B (if A has alpha = 0)
> A SRC_OVER B = A (if A has alpha = 1)
>
>> If you create the correct equation, you can do what you want.
>
> Which is the equation, then ?
>
> Cheers,
>
> Mik
> --
>

A SRC_OVER B = B (if A has alpha = 0)

This is handled simply by an alpha-test of not zero (glAphafunc)

A SRC_OVER B = A (if B has alpha = 0)

A SRC_OVER B = A (if A has alpha = 1)

Something that includes DEST_ALPHA, not the SRC_ALPHA based equation
referred to before.

jbw

>
>
>
>
>


0 new messages