Jump to content
  • Advertisement
Sign in to follow this  
ehmdjii

OpenGL premultiplied alpha

This topic is 4397 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

hello, i hear a lot that a technique called "premultiplied alpha" can be used to get rid of the dark halos when using alpha-textures and the standard blend mode of: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); unfortunately i dont quite understand it and i also havent found an openGL implementation of it, so i would be glad if you could give me any pointers into premultiplied alpha. thanks a lot!

Share this post


Link to post
Share on other sites
Advertisement
Blending with GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA means that the resulting color would be
C' := CB * A + CF * ( 1 - A )
where C means a color vector, index B means the background, index F means the incoming fragment, and A means the normalized (i.e. A in [0,1]) alpha. This method works fine for all values being in the allowed ranges.

Now, you can pre-multiply the fragment with 1-A
CF' := CF * ( 1 - A )
and then using GL_SRC_ALPHA, GL_ONE to yield in
C' := CB * A + CF'
what is obviously the same as above.

IMO, if the texture used for the fragments is properly prepared, there is no problem with alpha blending at all. We are currently working on a sprite system for our game engine, and we have had the problem of white peaks on the border of sprite shapes in the case of extra alpha channels (not in the case of the standard alpha channel; we're speaking of PhotoShop here that allows several alpha channels). After looking at the process we saw that the peaks arised from how the artists have generated the alpha already in PhotoShop. They arised from the fact that PhotoShop has blended the image already to achieve antialiasing, and the border color (black) was different from the background color (white) when the additional alpha channel was generated, so that too much of the background was taken into account (notice that we don't want _any_ background when exporting the image). Dark peaks will occur in the inverse case. The problem disappeared as soon as the background color and border color were equal when the alpha was generated.

Notice that using PhotoShop's standard alpha channel only doesn't show this problem. (This is also sometimes called using pre-multiplied alpha, as our artists have stated.) This is because it solely works like the formula given above.

So, how is the process you use to generate the textures?

Share this post


Link to post
Share on other sites
thank you very much for your detailed explanaition.

how can i implement the formulas you gave using openGL?


for the textures i also use photoshop and save them as PNGs, which dont seem to have a "real" alpha channel. at least it doesnt show up in photoshop.

thanks!

Share this post


Link to post
Share on other sites
The formulas are simply achieved by using the blending factors I've written down. If you have a normal (i.e. not pre-multiplied image) then use
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
and if you have a pre-multiplied image then use (as I've described above)
glBlendFunc(GL_SRC_ALPHA, GL_ONE);

AFAIK PNG can have a normal alpha channel. Unfortunately, ATM we cannot import PNG but PhotoShop directly in our engine, so I can't just do a quick test with PNG.

In our engine we currently do the following: We allocate an uint8_t array, load the red, green, blue, and alpha channels of PhotoShop as they are (okay, they are re-arranged since PhotoShop stores its images banked and not interleaved), push that data into a
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,width,height,0,GL_RGBA,GL_UNSIGNED_BYTE,data);
and use the aforementioned
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
with it. Until now we've no problems found with it (but perhaps we don't have looked at the right places yet ;)


EDIT: If of interest, here are the belonging code snippets. This routine prepares the texture:

void
GLRenderer::activateTexture(const ImageTexture& texture,uint32_t unit) {
::glActiveTexture(GL_TEXTURE0+unit);
// generating an OpenGL texture object if necessary ... else ...
const uint32_t textureID = texture.textureID();
if(_arrayOfTexObjects[textureID]==0) {
const GLuint width = texture.image()->width();
const GLuint height = texture.image()->height();
const void* data = texture.image()->backingPixelMap().buffer();
::glGenTextures(1,&_arrayOfTexObjects[textureID]);
::glPixelStorei(GL_UNPACK_ALIGNMENT,1);
::glBindTexture(GL_TEXTURE_2D,_arrayOfTexObjects[textureID]);
::glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
::glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
// determining mode
// TODO rudimentary implementation yet
GLenum mode = (*texture.image())[Image::Channel::ALPHA] ? GL_RGBA : GL_RGB;
// generating texture
::glTexImage2D(GL_TEXTURE_2D,0,mode,width,height,0,mode,GL_UNSIGNED_BYTE,data);
} else {
::glBindTexture(GL_TEXTURE_2D,_arrayOfTexObjects[textureID]);
}
// enabling texturing
::glEnable(GL_TEXTURE_2D);
}



And this routine renders the sprite onto a quad:

void
GLRenderer::renderRectangle(const Region2s& region,const ImageTexture& texture,const Region2s& imageRegion) {
activateTexture(texture);
::glColor4f(1.0f,1.0f,1.0f,1.0f);
float u0 = float(imageRegion.minimum0())/texture.image()->width();
float u1 = float(imageRegion.maximum0())/texture.image()->width();
float v0 = float(imageRegion.minimum1())/texture.image()->height();
float v1 = float(imageRegion.maximum1())/texture.image()->height();
if(texture.image()->rightToLeft()) {
float temp;
temp = u0, u0 = u1, u1 = temp;
}
if(!texture.image()->bottomToTop()) {
float temp;
temp = v0, v0 = v1, v1 = temp;
}
const bool withBlending = (*texture.image())[Image::Channel::ALPHA];
if(withBlending) {
::glEnable(GL_BLEND);
::glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
}
::glBegin(GL_QUADS);
::glTexCoord2f(u0,v0);
::glVertex2sv(region.minimum());
::glTexCoord2f(u0,v1);
::glVertex2s(region.minimum0(),region.maximum1());
::glTexCoord2f(u1,v1);
::glVertex2sv(region.maximum());
::glTexCoord2f(u1,v0);
::glVertex2s(region.maximum0(),region.minimum1());
::glEnd();
::glDisable(GL_TEXTURE_2D);
if(withBlending) {
::glDisable(GL_BLEND);
}
}

Share this post


Link to post
Share on other sites
thanks for your help!

i thought that when doing premultiplied alpha, you have to multiply the R, G and B channels with the alpha value to get the correct results when doing blending with glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA).

is that correct?

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!