Opengl Es2 Alpha Test Problems

OpenGL ES2 Alpha test problems

Here is an overview of a few methods to render with transparency in OpenGL, with advantages and disadvantages for each.

Alpha Testing

This is a very limited method, but is sufficient for the specific case the poster asked about. The example shown does not really need transparency because everything is either fully opaque or fully transparent (alpha = 1.0 or alpha = 0.0).

There used to be an alpha test for this purpose in OpenGL, but that is a deprecated feature, and is of course not in ES. You can emulate the same thing in your fragment shader, which will look something like this:

vec4 val = texture2D(tex, texCoord);
if (val.a > 0.5) {
gl_FragColor = val;
} else {
discard;
}

Advantages:

  • Simple.
  • No additional work on app side.

Disadvantages:

  • Only works for full opacity/transparency, cannot deal with semi-transparency.
  • Can hurt performance because it typically means that depth testing before the fragment shader has to be disabled.

Sorting and Blending

Rendering transparency is a primary use case for blending. The most common approach is to set the blend function toSRC_ALPHA, ONE_MINUS_SRC_ALPHA, enable blending, and render with the alpha component of the rendered fragments containing the desired opacity.

If the scene contains a mixture of fully opaque objects and objects with transparency, the fully opaque objects can be rendered first, without a need for them to be sorted. Only the objects with transparency need to be sorted. The sequence is then:

  1. Render fully opaque geometry.
  2. Render non-opaque geometry, sorted back to front.

Advantages:

  • Can handle semi-transparency.
  • Can handle multiple layers of transparent geometry.
  • Rendering itself is very efficient.

Disadvantages:

  • Requires sorting for correct result. For the blend function mentioned above, geometry has to be rendered back to front. Depending on the application, this can be between no big deal and almost impossible. For example, to correctly render intersecting geometry, you may have to start splitting triangles, which is far from attractive.

Depth Peeling

This is a very clever use of OpenGL features, IMHO, and can be a good practical solution. It does require multiple rendering passes. The simple form requires 3 passes:

  1. Render scene with the usual settings (depth testing enabled, depth function LESS, color and depth write enabled), but render only the fully opaque geometry. If opacity is per object, you can handle that by skipping draw calls for non-opaque objects. Otherwise, you will have to discard non-opaque fragments with a shader similar to the one under Alpha Testing above.
  2. Render the non-opaque geometry with the same settings as above, except that color write is disabled.
  3. Render the non-opaque geometry again, but this time with depth function EQUAL, color write enabled again, depth write disabled, and using blending.

A minimal shader can be used for pass 2, since it does not need to produce any valid fragment colors.

Advantages:

  • Simple to implement.
  • Reasonably efficient, does not require sorting.
  • Correctly handles semi-transparency.

Disadvantages:

  • Simple form only draws front-most layer of transparent geometry. This may sound like a major limitation, but the results can actually look very good. There are more advanced forms where additional layers are rendered with additional passes. Beyond the overhead for the those additional passes, it also gets more complex because it requires multiple depth buffers. I believe there's a white paper about it on the NVIDIA web site.

Alpha to Coverage

I haven't used this myself, so the following is based on my limited theoretical understanding. It looks like another interesting method. This requires multi-sampled rendering. The feature is enabled with glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE), which then translates alpha values into a coverage mask, resulting in only parts of the samples being written depending on alpha value. This results in a transparency effect when the multi-sample buffer is downsampled to the final color buffer.

Advantages:

  • Can handle semi-transparency.
  • Correctly handles multiple layers of transparency.
  • Efficient, particularly if MSAA would have been used anyway. No sorting required.

Disadvantages:

  • Requires MSAA. Modern GPUs are very effective at MSAA rendering, so this is no huge deal. Often times, you will probably want to use MSAA anyway.
  • Effective resolution of alpha value is very small, unless I'm missing something. For example, with 4x MSAA, you can only represent 5 possible alpha values (0, 1, 2, 3, 4 samples set in coverage mask).

OpenGL ES 2.0 transparency using alpha testing in shader

OK, so I've found the cause of this glitch.

Even though depth test was disabled, and fragment shader discarded certain pixels making them transparent, triangles were still occluded because of writing to depth buffer.

I had to disable writing to depth buffer with this code:

GLES20.glDepthMask(false);

Obviously, writing to depth buffer was disabled in Rendermonkey too, resulting correct image.

Thanks for your answers below, but I don't need to use alpha blending - I have to use alpha testing, and it doesn't require sorting of triangles.

Problem with alpha blending when there are many objects in OpenGL ES 2.0/3.0

You cannot have both, Blending and the benefits of the Depth Test. You have to do draw the scene in 2 passes. First draw the opaque objects with depth test on and blending off, then draw the transparent objects with depth test on but no writing to the depth buffer, and blending on, in sorted order from the back to the front. See Transparency Sorting.

  1. enable depth test and disable blending
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthMask(GLES20.GL_TRUE);
GLES20.glDisable(GLES20.GL_BLEND);

  1. draw opaque objects

  2. enable depth test, disable writing to the depth buffer and enable blending

GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthMask(GLES20.GL_FALSE);
GLES20.glEnable(GLES20.GL_BLEND);

  1. draw transparent objects in sorted order from the back to the front.

If the transparent objects are not sorted, then the transparent objects will still be arranged correctly, in compare to the opaque objects, but the transparent object itself may not be arranged correctly. When you use blending, then the order matters. The blending function is not commutative, the result is different if the order of covering transparent objects is changed.

Point sprite alpha blending issue (Android / OpenGL ES 2.0)

If you set the glBlendFunc
with the functions (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) and you use
glBlendEquation with the equation GL_FUNC_ADD then the destination color is
calculated as follows:

C_dest = C_src * A_src + C_dest * (1-A_src)

If you blend for example C_dest = 1 with C_src = 0.5 and A_src = 0.5 then:

C_dest = 0.75 = 1 * 0.5 + 0.5 * 0.5

If you repeat blending the same color C_src = 0.5 and A_src = 0.5 then the destination color becomes darker:

C_dest = 0.625 = 0.75 * 0.5 + 0.5 * 0.5

Since the new target color is always a function of the original target color and the source color, the color can not remain equel when blending 2 times, because the target color has already changed after the 1st time blending (except GL_ZERO).

You have to avoid that any fragment is blended twice. If all fragments are drawn to the same depth (2D) then you can use the depth test for this:

glEnable( GL_DEPTH_TEST );
glDepthFunc( GL_LESS );

// do the drawing with the color

glDisable( GL_DEPTH_TEST );

Or the stencil test can be used. For example, the stencil test can be set to pass only when the stencil buffer is equal to 0.
Every time a fragment is to be written the stencil buffer is incremented:

glClear( GL_STENCIL_BUFFER_BIT );
glEnable( GL_STENCIL_TEST );
glStencilOp( GL_KEEP, GL_KEEP, GL_INCR );
glStencilFunc( GL_EQUAL, 0, 255 );

// do the drawing with the color

glDisable( GL_STENCIL_TEST );

Extension to the answer

Note that you can discard fragments which should not be drawn.
If the fragment in your sprite texture has an alpha channel of 0 you should discard it.

Note, if you discard a fragment neither the color nor the depth and stencil buffer will be written.

Fragment shaders also have access to the discard command. When executed, this command causes the fragment's output values to be discarded. Thus, the fragment does not proceed on to the next pipeline stages, and any fragment shader outputs are lost.

Fragment shader

if ( color.a < 1.0/255.0 )
discard;

How can I get Alpha blending transparency working in OpenGL ES 2.0?

Your Colour vertex attribute values are not being normalized. This means that the vertex shader sees values for that attribute in the range 0-255.

Change the fourth argument of glVertexAttribPointer to GL_TRUE and the values will be normalized (scaled to the range 0.0-1.0) as you originally expected.

see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glVertexAttribPointer.xml

Alpha tesing with ES 3.0

Alpha testing is not available in ES 2.0 and later. So it's not an option in ES 3.0.

The only OpenGL ES versions that supported GL_ALPHA_TEST were 1.0 and 1.1, and both of those are mostly considered obsolete these days.

You can easily replicate what the alpha test used to do in your fragment shader by checking the resulting alpha value, and calling discard if it does not meet the desired condition.



Related Topics



Leave a reply



Submit