Alpha blending issue with multiple rendering iterations into the same texture (OpenGL)
Asked Answered
T

1

6

The scenario

I'm creating a frame buffer object and binding a texture to colour attachment 0. I'm not using a depth buffer. After creating it, I unbind it.

At some point in time later, the frame buffer is bound, a triangle strip is rendered to it (some parts partially transparent), and then it's unbound again. This is repeated a number of times with different triangle strips.

Ultimately, what's drawn to the main frame buffer is a textured quad with the texture that's attached to the frame buffer object I created.

The problem

I'm finding that the partially transparent parts of the triangle strips drawn into the texture which overlap with other triangle strips aren't being blended properly. They appear to be blended with white rather than the colour that's already in the texture. Even if I fill the texture with solid green (for example), the colour being picked up while blending is still white.

Here's a few snippets of the code I'm using to do all of this:

Initialization

glGenTextures(1, &texture_id);
glBindTexture(GL_TEXTURE_2D, texture_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glGenFramebuffers(1, &framebuffer_id);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_id, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);

Render To Texture (iterated on)

glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

// On the first render iteration, do_clear is true
if (do_clear)
{
    glClearColor(r, g, b, a);
    glClear(GL_COLOR_BUFFER_BIT);
}

// ... render the current triangle strip ...

glBindFramebuffer(GL_FRAMEBUFFER, 0);

Render Texture To Main Framebuffer

// Set up the texture (making no assumptions about current state)
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture_id);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

// Set up the vertex buffer (quad made up of triangle strip)
glBindBuffer(GL_ARRAY_BUFFER, vertices_id);
glVertexAttribPointer(VERTEX_ATTRIB_POSITION_TAG, 3, GL_FLOAT, GL_FALSE, sizeof(MyRenderVertex), BUFFER_OFFSET(0));
glEnableVertexAttribArray(VERTEX_ATTRIB_POSITION_TAG);
glVertexAttribPointer(VERTEX_ATTRIB_TEXCOORD_TAG, 2, GL_FLOAT, GL_FALSE, sizeof(MyRenderVertex), BUFFER_OFFSET(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(VERTEX_ATTRIB_TEXCOORD_TAG);
glVertexAttribPointer(VERTEX_ATTRIB_COLOR_TAG, 4, GL_FLOAT, GL_FALSE, sizeof(MyRenderVertex), BUFFER_OFFSET(5 * sizeof(GLfloat)));
glEnableVertexAttribArray(VERTEX_ATTRIB_COLOR_TAG);
glVertexAttribPointer(VERTEX_ATTRIB_NORMAL_TAG, 3, GL_FLOAT, GL_FALSE, sizeof(MyRenderVertex), BUFFER_OFFSET(9 * sizeof(GLfloat)));
glEnableVertexAttribArray(VERTEX_ATTRIB_NORMAL_TAG);

// Draw the textured geometry
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

// Reset everything
glDisableVertexAttribArray(VERTEX_ATTRIB_POSITION_TAG);
glDisableVertexAttribArray(VERTEX_ATTRIB_TEXCOORD_TAG);
glDisableVertexAttribArray(VERTEX_ATTRIB_COLOR_TAG);
glDisableVertexAttribArray(VERTEX_ATTRIB_NORMAL_TAG);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindTexture(target, 0);
glActiveTexture(0);

An Example of What I'm Seeing

The parts where you see the white lines are where the triangle strips overlap. They should be partially transparent and blended with the black that was previously drawn.

Bad Blending

Update

I've made a few discoveries since I posted this:

  • The "white" part is actually fully transparent, so it'll just show the colour of whatever is rendered behind the texture
  • I replaced the more complex triangle meshes with randomly placed squares that are made up of vertices which go from fully transparent on one side of the square to fully opaque on the other side, and I don't see the same blending problem. Here's a picture of the squares:

Good Blending

So it appears to be a problem with the triangle meshes I'm using and not the blending.

Actually, looking very closely at the "Good Blending" image, I can see that the fully opaque parts of a square are actually being lightened when another square is rendered over top of it. So the problem is there, it's just not quite as extreme.

Thirlage answered 8/10, 2014 at 16:6 Comment(2)
I can't quite explain the "white" part. I think you might get the best result by clearing the FBO to black. Are you using shaders? Is the fragment shader generating the right alpha value? In any case, this looks fairly similar to an earlier question I answered in detail (bounty!), maybe it will be of help: #24347085.Decalogue
I've tried clearing the FBO to all sorts of colours with no success. And yes, I'm using vertex and fragment shaders (very similar to the ones in chapter 8 of the OpenGL ES 2.0 programming guide). I did see that other question, and I have tried a bunch of different blending functions to no avail. See my update above -- I believe it's the problem with the triangle meshes.Thirlage
P
8

When you render to texture, try

glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);

instead of:

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

The case is in that, when you render to texture the alpha channel is also blended.

To make things clear, let's consider rendering a half transparent edge over an opaque triangle strip that was rendered before. Let's the alpha value of edge be 0.5 (source alpha) and the alpha in render buffer be 1.0 (destination value). So the resulting value would be:

r = SrcAlpha * SrcAlpha  + DstAlpha * (1.0 - SrcAlpha) = 0.5 * 0.5 + 0.5 * 0.5 = 0.5

As you see, the value of alpha in render buffer is not equal to 1.0, as you were expecting. As a result, the source color would be blended with the destination color in main frame buffer.

So you do not need to blend alpha channel. For example, you can simply add source alpha value to destination alpha value, this can be achieved by specifying different blending function for alpha channel using glBlendFuncSeparate.

See this and this for more details.

Also make sure, that you have set a proper blending equation:

glBlendEquation(GL_FUNC_ADD);

See this for more details

Plumbic answered 8/10, 2014 at 21:37 Comment(2)
Excellent suggestions (I'd upvote this answer if I could)! I did previously experiment with glBlendFuncSeparate(), but not with those particular parameters. I tried both of your suggestions, but still no luck. It appears to be related to the vertices used in the triangle meshes somehow punching "holes" in my texture.Thirlage
Disregard that last comment. I forgot that the triangle mesh rendering is being batched, and that the batch renderer applies its own blending function. Once I put your blending equation into the batch renderer, it fixed the problem!Thirlage

© 2022 - 2024 — McMap. All rights reserved.