View Full Version : Premultiplied alpha trouble
Fenris
2004.11.02, 03:59 PM
Hi all!
I've been working on premultiplying all the El Ballo texture files, and they look just fine in Preview and Photoshop. However, when I load them into the actual game, they blow up and produce very odd color values. Here's a composited screenshot of what's happening: top half is using my pre-multiplied textures and bottom half is using the non-premmed ones. I've tried with glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA) as well, but with the same results.
http://www.rusted.se/premprob.jpg
Can anyone guess as to what is going wrong here? It looks as if pure yellow goes into pure cyan, which would mean that (pure red + pure green) becomes (pure green + pure blue) (#FFFF00 -> #00FFFF). Also, if you look at the text in the upper half, right below GL_SRC_ALPHA: that text is black in the texture, but is completely transparent here. Looks to me as if the pixels become badly swizzled somewhere, but the same code works if I just use the non-premultiplied textures instead. Any guesses?
OneSadCookie
2004.11.02, 04:09 PM
The correct blend function for premultiplied images is ONE, ONE_MINUS_SRC_ALPHA.
Are you sure you haven't accidentally swizzled your textures' color channels during premultiplication? That's what it looks like's happened to me...
Fenris
2004.11.02, 08:29 PM
OK, fixed that. Turned out to be a very subtle bug in my file loading code (which ought to have broken every file loaded since the dawn of this game) which caused a pointer offset which, yes, in a way swizzled the channels. Now, however, everything looks great. Except, whenever I draw with an glColor* alpha less than 1, I get a strange glowing effect. Similarily, half-transparent textures look awfully dark. Any hints?
OneSadCookie
2004.11.02, 08:32 PM
any glColor calls you make must also be premultiplied.
Eg, if you would have done glColor4f(r, g, b, a); , instead do glColor4f(r*a, g*a, b*a, a);
arekkusu
2004.11.02, 10:36 PM
Why do you want to premultiply everything? Unlike Quartz, there's really no penalty in GL.
OneSadCookie
2004.11.02, 11:30 PM
because premultiplication avoids halos around sprites when they're bilinearly filtered, and it's simpler than ensuring that the color channels bleed into the transparent areas appropriately.
arekkusu
2004.11.02, 11:58 PM
RGB * alpha * one = RGB * alpha. There's no difference between premultipling the alpha, using GL_ONE for the src blending factor and not premultipling the alpha, using GL_SRC_ALPHA for the src blending factor. Except, of course, that premultipling destroys your RGB data so you can't turn the alpha channel off whenever you want to.
Fenris
2004.11.03, 06:05 AM
Well, my problem is that I have bright halos around my quads. (This is 2D all the way, you see) Arekkusu, could you elaborate on your second post? Not quite following you here...
arekkusu
2004.11.03, 06:12 AM
There's no requirement to premultiply your texel data.
If you take some RGB+A image from photoshop, and premultiply it (for example with tiffutil, or programatically) then you build an RGBA texture out of it and blend it into your scene with glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA). That's fine, the resulting framebuffer pixel is src_rgb * src_alpha * one + dst_rgb * (one - src_alpha);
Alternatively you can take some RGB+A image from photoshop as is, build an RGBA texture out of it and blend it into your scene with glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). The resulting framebuffer pixel is src_rgb * src_alpha + dst_rgb * (one - src_alpha).
It's exactly the same. That's what I was saying. I've done it both ways.
If you have fringes, check your image data. In hex.
OneSadCookie
2004.11.03, 06:33 AM
As I said, the problem is only when the nonpremultiplied image is bilinearly filtered. halfway between an opaque green texel and a transparent white texel, a semitransparent light-green fragment is generated, which produces the effect of a light halo. Halfway between an opaque green texel and a transparent black texel, a semitransparent dark-green fragment is generated, which produces the effect of a dark halo. Only if the transparent texel is the same shade of green as the opaque one is the halo eliminated.
By contrast, if the image is premultiplied there is only one case -- an opaque green texel next to a transparent black texel. In this case the filtered fragment (semitransparent dark green) is actually a premultiplied pure green, the desired color. No halo is visible.
So, premultiplication isn't the only solution, but it is the most practical solution -- premultiplication of the image is easier than ensuring the transparent texels are the same color as the surrounding opaque texels.
reubert
2004.11.03, 03:26 PM
an alternative: http://www.vterrain.org/Plants/Alpha/
Fenris
2004.11.03, 05:43 PM
Thanks for the insightful discussion, both Keith and Arekkusu (actually, I have no idea what your real name is. :) ) It seems to work fine now, and my haloes are gone. Wohoo! :)
Reubert, that's very cool, and I've seen it in files exported from Illustrator, just never knew what the heck was going on. Thanks for posting!
arekkusu
2004.11.03, 07:44 PM
(arekkusu == (http://www.takase.com/Names/NameInJapaneseA.htm) alex)
Fenris
2004.11.04, 01:39 PM
Alex, got that. Very cool web page. Too bad Ivan just translates into Iwan, which isn't very cool at all.
EDIT: Oh, you edited your post. Disregard the web page stuff. :)
phydeaux
2004.11.04, 03:40 PM
I haven't used premultiplied alpha before (it sounds like a good idea) though correct me if I'm wrong- if you have a particularly transparent amount of color, then your precision goes down provided you are using integral channels (e.g. if you were making a 1/2 transparent image, then you'd only use values 0-127 instead of the whole range of 0-255 of a byte.) I assume that's the downside of using premultiplied alpha?
After a quick google, this is the problem I had in mind: http://www.teamten.com/lawrence/graphics/premultiplication/
Though it does sound like the benefits outweigh the downside. In my game I actually intentionally make some of the black haloes because it gives an interesting look to some of the icons. The rest of my images I have a blurred copy of the image in the transparent pixels to make sure surrounding pixels aren't black or white.
OneSadCookie
2004.11.04, 03:50 PM
Yes, you lose precision as you add transparency. It's probably really only good for masks, rather than a real alpha channel...
Bring on the 16-bit floating-point images ;)
arekkusu
2004.11.04, 07:04 PM
That page you linked explains it, but to say it another way: the premultiplication solution Keith described is solving an artwork problem. That is, when you have some artwork with translucent portions (like antialiased edges) against a background color, the background matte shows up as a fringe when you composite. But that's strictly an artwork problem, not a compositing problem. If I take a rectangular photo and draw some arbitrary alpha channel on top of it, and use that as an RGBA unpremultiplied texture, it doesn't have any fringe, because there is no matte-- the original pixels from the photo are all there. if your artwork has fringes, well, it's because you drew your artwork wrong. Consider the mipmap case-- what color is your artwork going to be when it's reduced to a 1x1 pixel? Mostly the matte color? You better fix your matte.
Now, in Quartz, premultiplication is good. If I loaded that photo+alpha channel via NSImage, it'd premultiply it for me automatically (so looking at [img bitmapData], all of the transparent pixels would be black) and then [img drawAtPoint] etc are a bit faster because they can skip a multiply per pixel during repeated compositing. Note that you can't ever show the original image (turning off the alpha mask), because it's destroyed during premultiplication.
But with OpenGL, it doesn't make any difference. If you have blending on, you're taking a speed hit from reading the framebuffer destination value back. The source factor (GL_ONE or GL_SRC_ALPHA, typically) doesn't make any difference speed wise. And, if you want to, you can turn off, or fade or invert, the alpha mask, because the original image data is all still there.
Fenris
2004.11.04, 07:10 PM
Valuable information, Alex! Thanks!
However, as I'm sure you've gathered by now, our artwork needs those haloes removed, and Keith's proposal works a treat. Still, I'll keep it in mind for the future. Thanks for your time, both of you!
aaronsullivan
2004.11.05, 07:54 PM
I just put a photoshop "stroke" effect on the outside of my image (rgba) that has a color similar to the image and then put the transparency down to about 2-5%, more if I want a sort of outline. Snowball is a bit sloppy in places, but generally you won't see any halos.
I like this group of alternative solutions, though.
Fenris
2004.11.06, 05:15 AM
Yeah, I guess that would've worked, if I hadn't needed to tile and overlay sprites without any visible seams. :)
By the way, I'm looking forward to Snowball! ;)
AnotherJake
2004.11.08, 01:36 AM
Too bad Ivan just translates into Iwan, which isn't very cool at all.
My grandmother and grandfather didn't speak English very well since they were Finnish so they called me Yaggo. Ain't nothin' wrong with Iwan if you ask me! :p
aaronsullivan
2004.11.08, 03:47 AM
I kind of like Yaggo. :D
vBulletin® v3.6.8, Copyright ©2000-2008, Jelsoft Enterprises Ltd.