Blending Theres Nothing Behind

Assume we want to enable blending for the red triangle at z = -3 in our scene. Say we set each vertex color's alpha component to 0.5f so that anything behind the triangle shines through. In our case the green triangle at z = -5 should shine through. Let's think about what OpenGL ES will do and what else will happen:

■ OpenGL ES will render the first triangle to the z-buffer and colorbuffer.

■ Next OpenGL ES will render the green triangle, because it comes after the red triangle in our Vertices3 instance.

■ The portion of the green triangle behind the red triangle will not get shown on the screen, due to the pixels getting rejected by the depth test.

■ Nothing will shine through the red triangle in the front, since nothing was there to shine through when it was rendered.

When we use blending in combination with the z-buffer, we have to make sure that all transparent objects are sorted by increasing distance from the camera position and render them back to front. All opaque objects must be rendered before any transparent objects. The opaque objects don't have to be sorted, though.

Let's write a simple example that demonstrates this. We keep our current scene composed of two triangles and set the alpha component of the vertex colors of the first triangle (z = -3) to 0.5f. According to our rule, we have to first render the opaque objects—in our case the green triangle (z = -5)—and then all the transparent objects, from furthest to closest. In our scene there's only one transparent object: the red triangle.

We copy over all the code from the last example to a new class called ZBlendingTest and rename the contained ZBufferScreen to ZBlendingScreen. All we need to do is change are the vertex colors of the first triangle, and enable blending and rendering the two triangles in order in the present() method. Listing 10-5 shows the two relevant methods.

Listing 10-5. Excerpt from ZBlendingTest.java: Blending with the Z-buffer Enabled public ZBlendingScreen(Game game) { super(game);

vertices = new Vertices3(glGraphics, 6, 0, true, false); vertices.setVertices(new float[] { -0.5f, -0.5f, -3, 1, 0, 0, 0.5f,

0.5f, -0.5f, -3, 1, 0, 0, 0.5f, 0.0f, 0.5f, -3, 1, 0, 0, 0.5f, 0.0f, -0.5f, -5, 0, 1, 0, 1, 1.0f, -0.5f, -5, 0, 1, 0, 1, 0.5f, 0.5f, -5, 0, 1, 0, 1}, 0, 7 * 6);

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

gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

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

gl.glMatrixMode(GL10.GL_PROJECTION);

gl.glLoadIdentity();

GLU.gluPerspective(gl, 67, glGraphics.getWidth() / (float)glGraphics.getHeight(), 0.1f, 10f); gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity();

gl.glEnable(GL10. GL_DEPTH_TEST); gl.glEnable(GL10.GL_BLEND);

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

vertices.draw(GL10.GL_TRIANGLES, 3, 3); vertices.draw(GL10.GL_TRIANGLES, 0, 3); vertices.unbind();

gl.glDisable(GL10.GL_BLEND); gl.glDisable(GL10. GL_DEPTH_TEST);

In the constructor of the ZBlendingScreen class, we only change the alpha components of the vertex colors of the first triangle to 0.5. This will make the first triangle transparent. In the present() method we do the usual things, like clearing the buffers and setting up the matrices. We also enable blending and set a proper blending function. The interesting bit is how we render the two triangles now. We first render the green triangle, which is the second triangle in the Vertices3 instance, as it is opaque. All opaque objects must be rendered before any transparent objects are rendered. Next we render the transparent triangle, which is the first triangle in the Vertices3 instance. For both drawing calls, we just use proper offsets and vertex counts as the second and third arguments to the vertices.draw() method. Figure 10-7 shows the output of this program.

Figure 10-7. Blending with the z-buffer enabled

Let's reverse the order in which we draw the two triangles like this:

vertices.draw(GL10.GL_TRIANGLES, 0, 3); vertices.draw(GL10.GL_TRIANGLES, 3, 3);

So we first draw the triangle starting from vertex 0 and then draw the second triangle starting from vertex 3. This will render the red triangle in the front first and the green triangle in the back second. Figure 10-8 shows the outcome.

Figure 10-8. Blending done wrong; the triangle in the back should shine through.

Our objects only consist of triangles so far, which is of course a little bit simplistic. We'll revisit blending in conjunction with the z-buffer again when we render more complex shapes. For now let's summarize how to handle blending in 3D:

1. Render all opaque objects.

2. Sort all transparent objects in increasing distance from the camera (furthest to closest).

3. Render all transparent objects in the sorted order, furthest to closest.

The sorting can be based on the object center's distance from the camera in most cases. You'll run into problems if one of your objects is large and can span multiple other objects. Without very advanced tricks we can't work around that issue. There are a couple of bulletproof solutions that work great with the desktop variant of OpenGL, but can't be implemented on most Android devices due to their limited GPU functionality. Luckily this is a very rare thing, and we can almost always stick to simple center-based sorting.

0 0

Post a comment