Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 367
bitjeep Avatar asked Oct 08 '14 16:10

bitjeep


1 Answers

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

like image 58
Podgorskiy Avatar answered Sep 21 '22 01:09

Podgorskiy