4
\$\begingroup\$

Is this possible? To load textures in non-premultiplied format (Straight alpha?)?

My game has 1 or 2 images which have semi-transparent pixels and I need to be able to fade them in and out but this doesn't seem possible easily.

As far as I understand Android always loads graphics in premultiplied alpha format, but if this is the case, how to blend them correctly?

Any help would be appreciated.

EDIT I have included some code:

String strFShader =
"precision mediump float;" +
"varying vec2 v_texCoords;" +
"uniform sampler2D u_baseMap;" +
"void main()" +
"{" +
"gl_FragColor = texture2D(u_baseMap, v_texCoords);" +
"gl_FragColor.a *= "+opVal+";"+    //where opVal is a value from 0.0f to 1.0f
"}";

And then......

//Enable Alpha blending and set blending function

GLES20.glEnable(GLES20.GL_BLEND); 
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);

//Draw it
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

//Disable Alpha blending
GLES20.glDisable(GLES20.GL_BLEND);

Using this, the semi-transparent parts of the image will display too dark (including a dark border where anti-aliased edges should be) but will fade in and out (It's just no good though as the image isn't correct).

If I change the blending mode to:

//Enable Alpha blending and set blending function

GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);

Then it will display perfectly at full opacity (1.0f) but becomes 'oversaturated' (This is the best way I can describe it) the more transparency you introduce (ie 0.9f down to 0.0f).

Thank you for the suggestions, as highlighted in the accepted answer in the end I didn't need to load without with "straight alpha" as I was able to correctly work with the pre-multiplied version that Android loads by default by changing the 2nd line of my Fragment shader to:

"gl_FragColor *= "+opVal+";"+
\$\endgroup\$
4
  • \$\begingroup\$ How are you loading them? What have you tried? \$\endgroup\$ Apr 10, 2013 at 14:59
  • \$\begingroup\$ @PandaPajama, I've only been using the standard available Bitmap class. But this always loads them with premultiplied alpha which is no good for me as I need to be able to blend these with the background and this isn't possible (with good results) unless the images (png's in my case) can be loaded (or decoded) into versions with straight Alpha. \$\endgroup\$ Apr 10, 2013 at 15:14
  • \$\begingroup\$ Why can't you use premultiplied alpha? Normal blending including fading should work just fine with them with a correct OpenGL blend function. Also this thread gives some hints on how to load the images without premultiplication if you still need that. \$\endgroup\$
    – msell
    Apr 10, 2013 at 16:15
  • \$\begingroup\$ Hi @msell, thanks. I can't find any way to keep the images in premultiplied format and be able to fade them in and out correctly. (This is only a problem with semi transparent pixels. (please see my edit) - if you have any suggestions that would be great!) I've actually already read that article and implemented the method of manually loading the image in non pre-multiplied format as suggested, but this isn't a very efficient method as highlighted in that article as it uses up to 3 times the amount of memory that simply loading the image would. Again, any further suggestions would be welcome. \$\endgroup\$ Apr 10, 2013 at 17:04

2 Answers 2

4
\$\begingroup\$

There are ways to load images without premultiplication in Android as suggested in this thread. It's also possible to do that without any additional memory penalty at least by doing it completely on the native side, but I don't go to the details here.

If you can use premultiplied alpha and it doesn't e.g. make your content pipeline harder, you should. It solves among some other issues bleeding of neighbour texels when using bilinear filtering for translucent images.

Premultiplied alpha just means that all input color values are already multiplied with the alpha value. Normal alpha blending then requires different blend function. Instead of SRC_ALPHA,1-SRC_ALPHA you need to use 1,1-SRC_ALPHA as you have already tried. What you were missing was taking premultiplied alpha into account in the shader. Since you are multiplying your texture alpha with another value, you have to do the same for texture color channels as well. Thus

gl_FragColor = texture2D(u_baseMap, v_texCoords);
gl_FragColor.a *= opacity;

becomes simply

gl_FragColor = texture2D(u_baseMap, v_texCoords) * opacity;

This should be enough to make your fading work correctly.

\$\endgroup\$
4
  • \$\begingroup\$ Thanks @msell, I thought it may be something to do with my shader, however if I implement your suggestion I get absolutely nothing, regardless of the value I set opacity to, nothing gets rendered. Any idea what's going on? Thanks again for any suggestions. \$\endgroup\$ Apr 10, 2013 at 18:15
  • \$\begingroup\$ Thank you @msell! I have been stuggling with this for a while now. For some reason, the line "gl_FragColor = texture2D(u_baseMap, v_texCoords) * opacity;" doesn't work - I've no idea why, I just get a blank screen, however, if I leave the first line in place and change the second line line to ""gl_FragColor *= "+op+";"+" it works perfectly, (it's essentially the same as your suggestion I guess but just split over 2 lines?) No idea why I can't get it to work all on one line, but hey it's working and I thank you sir. \$\endgroup\$ Apr 10, 2013 at 18:28
  • \$\begingroup\$ I'm glad this solved it. For the black screen issue, are you checking for GLSL compiler warnings and errors? That probably gives a hint why it's not working. \$\endgroup\$
    – msell
    Apr 10, 2013 at 19:25
  • \$\begingroup\$ I wasn't but I will check that for sure - thanks again! \$\endgroup\$ Apr 10, 2013 at 19:59
1
\$\begingroup\$

I remember there was a way to do so, but I would like to suggest you use premultiplied alpha for transparencies, as it is much better than non-premultiplied.

It only makes sense to load without premultiplication when your alpha values mean something different than transparency, but that doesn't seem to be your case.

There's a more thorough explanation of why premultiplied is better in here. But for the time being, to have your code work with premultiplied alpha, you simply have to stop mutliplying by alpha when blending. This means that instead of

GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);

you should do

GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
\$\endgroup\$
3
  • \$\begingroup\$ @PadndaPajama- thanks very much for the suggestion, but as you can see from my edit this doesn't actually work. If I use GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA the image will display OK if I specify 1.0f (ie full opacity) but the further I reduce it (ie, the more transparency I introduce), the worse the image gets (saturation-wise) until it's down to 0.0f where it should be invisible, but actually it's still being displayed in as oddly over-saturated version of itself. \$\endgroup\$ Apr 10, 2013 at 18:03
  • \$\begingroup\$ If you're going to multiply by alpha inside the fragment shader, then you just have to also multiply by the same value for all the other components. \$\endgroup\$ Apr 10, 2013 at 18:08
  • 2
    \$\begingroup\$ I would also like to suggest you promote opVal to a uniform instead of creating several shaders and switching them in runtime. \$\endgroup\$ Apr 10, 2013 at 18:09

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .