Alpha Blending I Can See Through

Alpha blending in OpenGL ES is pretty easy to enable. We only need two method calls: gl.glEnable(GL10.GL_BLEND);

gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_0NE_MINUS_SRC_ALPHA);

The first method call should be familiar: it just tells OpenGL ES that it should apply alpha blending to all triangles we render from this point on. The second method is a little bit more involved. It specifies how the source and destination color should be combined. If you remember what we discussed in Chapter 3, the way a source color and a destination color are combined is governed by a simple blending equation. The method glBlendFunc() just tells OpenGL ES which kind of equation to use. The preceding parameters specify that we want the source color to be mixed with the destination color exactly as specified in the blending equation in Chapter 3. This is equal to how the Canvas blended Bitmaps for us.

Blending in OpenGL ES is pretty powerful and complex, and there's a lot more to it. For our purposes, we can ignore all those details, though, and just use the preceding blending function whenever we want to blend our triangles with the framebuffer—the same way we blended Bitmaps with the Canvas.

The second question is where the source and destination colors come from. The latter is easy to explain: it's the color of the pixel in the framebuffer we are going to overwrite with the triangle we draw. The source color is actually a combination of two colors:

The vertex color: This is the color we either specify via glColor4f() for all vertices or on a per-vertex basis by adding a color attribute to each vertex.

The texel color: As mentioned before, a texel is a pixel from a texture. When our triangle is rendered with a texture mapped to it, OpenGL ES will mix the texel colors with the vertex colors for each pixel of a triangle.

So if our triangle is not texture mapped, the source color for blending is equal to the vertex color. If the triangle is texture mapped, the source color for each of the triangle's pixels is a mixture of the vertex color and the texel color. We could specify how the vertex and texel colors are combined by using the glTexEnv() method. The default is to modulate the vertex color by the texel color, which basically means that the two colors are multiplied with each other component-wise (vertex r x texel r, and so on). For all our use cases in this book, this is exactly what we want, so we won't go into glTexEnv(). There are also some very specialized cases where you might want to change how the vertex and texel colors are combined. As with glBlendFunc(), we'll ignore the details and just use the default.

When we load a texture image that doesn't have an alpha channel, OpenGL ES will automatically assume an alpha value of 1 for each pixel. If we load an image in RGBA8888 format, OpenGL ES will happily use the supplied alpha values for blending.

For vertex colors we always have to specify an alpha component, either by using glColor4f(), where the last argument is the alpha value, or by specifying the four components per vertex, where again the last component is the alpha value.

Let's put this into practice with a little example. We want to draw Bob twice: once by using the image bobrgb888.png, which does not have an alpha channel per pixel, and a second time by using the image bobargb8888.png, which has alpha information. Note that the PNG image actually stores the pixels in ARGB8888 format instead of RGBA8888. Luckily the GLUtils.texImage2D() method we use to upload the image data for a texture will do the conversion for us automatically. Listing 7-11 shows the code of our little experiment, using the Texture and Vertices classes.

Listing 7-11. Excerpt from BlendingTestjava; Blending in Action class BlendingScreen extends Screen { GLGraphics glGraphics; Vertices vertices; Texture textureRgb; Texture textureRgba;

public BlendingScreen(Game game) { super(game);

glGraphics = ((GLGame)game).getGLGraphics();

textureRgb = new Texture((GLGame)game, "bobrgb888.png"); textureRgba = new Texture((GLGame)game, "bobargb8888.png");

vertices = new Vertices(glGraphics, 8, 12, true, true); float[] rects = new float[] {

100, 100, 1, 1, 1, 0.5f, 0, 1, 228, 100, 1, 1, 1, 0.5f, 1, 1, 228, 228, 1, 1, 1, 0.5f, 1, 0, 100, 228, 1, 1, 1, 0.5f, 0, 0,

100, 300, 1, 1, 1, 1, 0, 1, 228, 300, 1, 1, 1, 1, 1, 1, 228, 428, 1, 1, 1, 1, 1, 0, 100, 428, 1, 1, 1, 1, 0, 0

vertices.setVertices(rects, 0, rects.length); vertices.setIndices(new short[] {0, 1, 2, 2, 3, 0,

Our little BlendingScreen implementation holds a single Vertices instance where we'll store the two rectangles, as well as two Texture instances—one holding the RGBA8888 image of Bob and the other one storing the RGB888 version of Bob. In the constructor we load both textures from the files bobrgb888.png and bobargb8888.png, and rely on the Texture class and GLUtils.texImag2D() to convert the ARGB8888 PNG to RGBA8888, as needed by OpenGL ES. Next up, we define our vertices and indices. The first rectangle, consisting of four vertices, maps to the RGB888 texture of Bob. The second rectangle maps to the RGBA8888 version of Bob and is rendered 200 units above the RGB888 Bob rectangle. Note that the vertices of the first rectangle all have the color (1,1,1,0.5f) while the vertices of the second rectangle have the color (1,1,1,1).

^Override public void present(float deltaTime) { GL10 gl = glGraphics.getGL();

gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight());

gl.glClearColor(1,0,0,1);

gl.glClear(GL10. GL_COLOR_BUFFER_BIT);

gl.glMatrixMode(GL10.GL_PROJECTION);

gl.glLoadIdentity();

gl.glEnable(GL10.GL_BLEND);

gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);

gl.glEnable(GL10. GL_TEXTURE_2D); textureRgb.bind();

vertices.draw(GL10.GL_TRIANGLES, 0, 6 ); textureRgba.bind();

vertices.draw(GL10.GL_TRIANGLES, 6, 6 );

In our present() method we clear the screen with red and set the projection matrix, as we are used to doing. Next we enable alpha blending and set the correct blend equation. Finally we enable texture mapping and render the two rectangles. The first rectangle is rendered with the RGB888 texture bound, and the second rectangle is rendered with the RGBA8888 texture bound. We store both rectangles in the same Vertices instance and thus use offsets with the vertices.draw() methods. Figure 7-16 shows the output of this little gem.

Figure 7-16. Bob, vertex color blended (bottom) and texture blended (top)

In the case of RGB888 Bob, the blending is performed via the alpha values in the per-vertex colors. Since we set those to 0.5f, Bob is 50 percent translucent.

In the case of RGBA8888 Bob, the per-vertex colors all have an alpha value of 1. However, since the background pixels of that texture have alpha values of 0, and since the vertex colors and the texel colors are modulated, the background of this version of Bob disappears. If we'd have set the per-vertex colors' alpha values to 0.5f as well, then Bob himself would also have been 50 percent as translucent as his clone in the bottom of the screen. Figure 7-17 shows what that would have looked like.

Figure 7-17. An alternative version of RGBA8888 Bob using per-vertex alpha of 0.5f (top of the screen)

And that's basically all we need to know about blending with OpenGL ES in 2D.

However, there is one more very important thing I'd like to point out: Blending is expensive! Seriously, don't overuse it. Current mobile GPUs are not all that good at blending massive amounts of pixels. You should only use blending if absolutely necessary.

0 0

Post a comment