glReadPixels Issue

So I am posting this here instead of on the lwjgl forums as a similar question was asked but no one answered. In the particle editor I am making I have it so you can export the effect into a sprite sheet using glReadPixels. Everything works fine except that the alpha seems off. When you have grayer images that are not colored it is like everything gets jacked up. I thought it had something to do with how opengl has unsigned and java does not but after printing the results back everything is fine no negatives.

here is how I am taking the screenshot or pixels from screen.

public static BufferedImage screenShot(int startX, int startY, int width, int height)
	{
		glReadBuffer(GL11.GL_FRONT);    
	    ByteBuffer buffer = ByteBuffer.allocateDirect((width * height * 4 *4));
	     glReadPixels(startX, startY, width, height, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer);
	     BufferedImage image = BufferedImageUtils.getCompatibleImage(width, height);
	     image = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
	      for (int x = 0; x <  width; x++) {
	         for (int y = 0; y < height; y++) {
	           int i = (x + ( width * y)) * 4;
	           int r = buffer.get(i) & 0xff;
	           int g = buffer.get(i + 1) & 0xff ;
	           int b = buffer.get(i + 2) & 0xff ;
	           int a = buffer.get(i + 3) & 0xff;
	          // a *= 2;
	           //System.out.println("Alpha: "+a);
	           if(r > 0)
	        	   System.out.println("Alpha: " + a + " R: " + r + " G: " + g + " B: " + b + " A: " + a);
	        	 image.setRGB(x, height - (y + 1), ( a << 24) | (r << 16) | (g << 8) |b );
	        	// image.setRGB(x, height - (y + 1), buff.get(x+(width*y)));
	         }
	      }
	      
	     return image;
	}

When I drop trying to get the alpha it looks fine so I have to be doing something wrong here.

Could you give us some example screenshots please? :slight_smile:

The code is correct, ergo, the framebuffer does not contain what you think it does.

Cas :slight_smile:

I was going to but it is hard to show.

Basically, the preview image on the right is off from what it should look like on the left. Now these are not the same image obviously as the left one is a different animation but if you look the left one has a whole lot more “smoke” in it or looks thicker. But the one on the right which is what it looks like after reading the pixels. I have not tried “not” going through java2d when saving the images but when I drop the alpha I get the right image.

Here is what it looks like when I just set the alpha as 0xff

it is like stuff is getting lost.

Ghosted me princec. When I drop the alpha channel it works. My clear color right now is (0,0,0,0). Is there anything I should be changing or check before I read the pixels?

I give up.

I have tried the nightlies of lwjgl and they only broke the gui. I have tried using the images graphics object and g2d.fillrect with the color set and that gave same results. I think java2d could be screwing with some shit… Could someone else try it and see if they get the same results as me? If it works fine for you then I MUST be doing something wrong.

Looks like a premultiplied alpha issue to me.

Cas :slight_smile:

So I am back at it trying to figure this shit out.

Looking at a single white block that fades out 8x8 pixels to keep the printouts readable. The color and alpha do not start at 255 but 250 or so but that should not make a huge difference. The alpha drops properly but so do the colors. Is opengl premultiplying everything when before I grab the pixels? When not using an image everything seems to look fine but when I load up an image that is not completely opaque, I start getting issues.

I know what pre-multiplied alpha is and it seems like the pixels returned are pre-multiplied. When I use the BufferedImage TYPE_ARGB_PRE I get the same results.

How would you reverse pre-multiplied alpha? I think I did it right but it did not help. Would rendering everything to an FBO work? I wouldn’t think so but it is worth a shot.

I normally do not get pissed easily but this one really pisses me off because I can do almost the exact same thing in Java2D much much easier and it works. Hell I don’t mind extra difficulty if the damn thing works.

This is nothing to do with OpenGL; OpenGL doesn’t know anything about pre-multiplied alpha. This is solely to do with Java image manipulation.
What are you doing to ensure your BufferedImage is not using pre-multiplied alpha?

Cas :slight_smile:

Maybe your problem is because:

  1. You render your particles to a FBO with blending enabled.
  2. You grab the pixels of the FBO.
  3. You render the FBO texture again with blending enabled.

The second blend will cause a loss of alpha – see “Blending Woes” of this article for another example of the problem.

EDIT: I only skimmed your thread; and it seems you don’t use FBOs, so this probably doesn’t apply. :stuck_out_tongue:

Beyond that; it’s hard to tell what your problem is without seeing any useful code.

What would you like me to post? Name the things you think might be issues and I will post the relevant parts. If you really want I can post the whole damn thing but there are many things not necessary.

Thing is with just passing 0xff as alpha (no alpha) I get the right image. What is on the screen during testing is correct. Opengl is rendering properly. I can post how I setup opengl, render stuff, etc if that will help.

I just tested and the problem seems to be as I guessed earlier.

  1. You render using GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA. This blends the foreground with its alpha.

  2. You then read the pre-multiplied pixels with glReadPixels into a texture (or save it into a PNG and re-load it later).

  3. You then render the pre-multiplied texture with the same blending function as before – and because you are blending it twice, the alpha is reduced.

The blend func looks like this:

finalColor.rgba = texColor.rgba * texColor.a + bgColor.rgba * (1 - texColor.a);

So if you use 50% white (1, 1, 1, 0.5), you end up with a finalColor of (1, 1, 1, 0.25) which is not what you want.

Instead, what you want is GL_ONE, GL_ONE_MINUS_SRC_ALPHA:

finalColor.rgba = texColor.rgba * (1, 1, 1, 1) + bgColor.rgba * (1 - texColor.a);

Which would lead to a final color of (1, 1, 1, 0.5), and still allow you to blend multiple particles atop each other.

When you render with this blend func to the default frame buffer, you will probably just see a white box. However, when you use glReadPixels, you will get an image that is not premultiplied, which can then be rendered back with standard GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA blending.

Another solution would be to just tell the user to use pre-multiplied alpha (or have your app pre-process textures when loading a particle image), then you can just use GL_ONE, GL_ONE_MINUS_SRC_ALPHA everywhere.

I made the system before I knew how much better pre-multiplied alpha is which is why I am not using it right now.

I am using the default frame buffer right now. I will be honest and say that I am not very well versed in opengl so I blame my lack of experience and not the bindings them selves.

I will just change everything to pre-multiplied alpha and have an option for the user to say whether the image is pre-multiplied or not and if it is not I will load the file accordingly. I really hope this will fix it. I will post again if this solves it and maybe the LWJGL wiki needs and update.

sigh

So I would have to change my whole particle system in order to use premultiplied alpha blending which I am ok with but that would also mean the UI would have to change which I am not ok with…for now. When I rewrite my system it will be not as special and more like libgdx but with more features.

Unfortunately, the issue I have now is when using davedes blending mode I get correct information except that nothing is additively blended.

The 3 step, which I should have read better, I do not do. That is to say, info is wrong coming from the frame buffer and does not get messed up after.

Why would you have to change your UI? Just enable regular blending when rendering UI, and premultiplied when rendering your particles.

If you want additive blending without a “premultiplied” result, use GL_ONE, GL_ONE.

I would advise using a frame buffer to streamline the process. In the following example (using lwjgl-basics), the particles are rendered correctly to the FBO (optionally with additive blending). Then, the FBO contains the correct pixel data (if we want to save to PNG, using glGetTexImage). We can also render the FBO to the screen using our standard GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA blending, so no major changes are needed to your app.

UI is done in swing so that is not what I mean by UI issues. I mean that if I wanted to revamp the particle system then I would have to change UI elements.

I am using swing/awt and lwjgl so I am currently not actually using my own FBO. I render the previews with java2d and other such things as making changes to the animations (making them seamless) is much easier in java2d or at least I know how to do it in java2d.

Thank you for the code. I will change things to use an FBO and see if that will work.

You can layer the particles which means that you could have additive atop non atop add atop non etc. It is not just one time rendering all particles with additive or non.

Again thanks for the help.