Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OpenGL ES (IPhone) alpha blending looks weird

I'm writing a game for IPhone in Opengl ES, and I'm experiencing a problem with alpha blending:

I'm using glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA) to achieve alpha blending and trying to compose a scene with several "layers" so I can move them separately instead of having a static image. I created a preview in photoshop and then tried to achieve the same result in the iphone, but a black halo is shown when I blend a texture with semi-transparent regions.

I attached an image. In the left is the screenshot from the iphone, and in the right is what it looks like when I make the composition in photoshop. The image is composed by a gradient and a sand image with feathered edges.

Is this the expected behaviour? Is there any way I can avoid the dark borders?

Thanks.

EDIT: I'm uploading the portion of the png containing the sand. The complete png is 512x512 and has other images too.

I'm loading the image using the following code:

NSString *path = [NSString stringWithUTF8String:filePath];
NSData *texData = [[NSData alloc] initWithContentsOfFile:path];
UIImage *image = [[UIImage alloc] initWithData:texData];
if (image == nil) NSLog(@"ERROR LOADING TEXTURE IMAGE");

GLuint width = CGImageGetWidth(image.CGImage);
GLuint height = CGImageGetHeight(image.CGImage);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
void *imageData = malloc( height * width * 4 );
CGContextRef context = CGBitmapContextCreate( imageData, width, height, 8, 4 * width, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big );
CGColorSpaceRelease( colorSpace );
CGContextClearRect( context, CGRectMake( 0, 0, width, height ) );
CGContextTranslateCTM( context, 0, height - height );
CGContextDrawImage( context, CGRectMake( 0, 0, width, height ), image.CGImage );

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);

CGContextRelease(context);

free(imageData);
[image release];
[texData release];

alt textalt text

like image 953
Damian Avatar asked Oct 25 '10 05:10

Damian


4 Answers

I need to answer my own question:

I couldn't make it work using ImageIO framework so I added libpng sources to my project and loaded the image using it. It works perfect now, but had I to solve the following problem:

The image was loaded and showed fine in the simulator but was not loading at all on the real device. I found on the web that what's going on is that the pixel ordering in PNG image-format files is converted from RGBA to BGRA, and the color values are pre-multiplied by the alpha channel value as well, by a compression utility 'pngcrush' (for device-specific efficiency reasons, when programming with the UIKit interface).

The utility also renames a header of the file, making the new PNG file unusable by libpng. These changes are done automatically when PNG files are deployed onto the iPhone. While this is fine for the UIKit, libpng (and other non-Apple libraries) generally can't then read the files.

The simple solutions are:

  1. rename your PNG files with a different extension.
  2. for your iPhone -device- build add the following user-defined setting:

    IPHONE_OPTIMIZE_OPTIONS | -skip-PNGs

I did the second and it works perfect on simulator and device now.

like image 112
Damian Avatar answered Sep 17 '22 01:09

Damian


Your screenshot and photoshop mockup suggest that the image's color channels are being premultiplied against the alpha channel.

like image 45
rpetrich Avatar answered Sep 20 '22 01:09

rpetrich


I have no idea what your original source images look like but to me it looks like it is blending correctly. With the blend mode you have you're going to get muggy blends between the layers.

The photoshop version looks like you've got proper transparency for each layer, but not blending. I suppose you could experiement with glAlphaFunc if you didn't want to explicitly set the pixel alphas exactly.

--- Code relating to comment below (removing alpha pre-multiplication) ---

int pixelcount = width * height;
unsigned char* off = pixeldata;
for (int pi=0; pi<pixelcount; ++pi)
{
    unsigned char alpha = off[3];
    if( alpha!=255 && alpha!=0 )
    {
        off[0] = ((int)off[0])*255/alpha;
        off[1] = ((int)off[1])*255/alpha;
        off[2] = ((int)off[2])*255/alpha;
    }
    off += 4;
}
like image 36
Montdidier Avatar answered Sep 19 '22 01:09

Montdidier


I am aware this post is ancient, however I had the identical problem and after attempting some of the solutions and agonising for days I discovered that you can solve the pre-multiplied RGBA png issue by using the following blending parameters;

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

The GL_ONE parameter replaced the GL_SRC_ALPHA parameter in my case.

I can now use my RGBA PNGs without the gray alpha edges effect which made my glyph text look nasty.

Edit: Okay, one more thing, for fading etc (setting the alpha channel in code), you will need to pre-multiply manually when the blending is set as above, like so;

glColor4f(r*a, g*a, b*a, a);
like image 44
AnthonyBlake Avatar answered Sep 17 '22 01:09

AnthonyBlake