I try to implement something like a paint tool and stuck with a problem alpha chanel of brush. My brush is a .PNG texture with transparent background. I draw in the RGBA buffer. My code:
initFBO(...)
...
glEnable(GL_BLEND);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
drawBrush(...)
glBindFramebuffer(GL_FRAMEBUFFER, 0);
drawFBO(...)
It works but if I stop my brush, alpha channel from brush will be to accumulate in the buffer to replace color from.
I'm not familiar with blending in Opengl too close and have issue with blending parameters. Is my problem in the blending or something else?
Update:
initFBO(GLuint &framebuff, GLuint &text)
{
glGenFramebuffers(1, &framebuff);
glBindFramebuffer(GL_FRAMEBUFFER, framebuff);
glGenTextures(1, &text);
glBindTexture(GL_TEXTURE_2D, text);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, screenWidth, screenHeight, 0, GL_RGB, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, text, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
drawBrush(glm::vec2 mousePosition, GLuint texture, Shader shader)
{
glm::mat4 model;
model = glm::translate(model, glm::vec3(mousePosition, 1.0));
GLfloat brushVertices[] =
{
-0.1f, 0.1f, 0.0f, 0.0f, 1.0f,
-0.1f,-0.1f, 0.0f, 0.0f, 0.0f,
0.1f, 0.1f, 0.0f, 1.0f, 1.0f,
0.1f,-0.1f, 0.0f, 1.0f, 0.0f
};
glGenVertexArrays(1, &brushVAO);
glGenBuffers(1, &brushVBO);
glBindVertexArray(brushVAO);
glBindBuffer(GL_ARRAY_BUFFER, brushVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(brushVertices), &brushVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glBindVertexArray(0);
shader.set();
glUniform1i(glGetUniformLocation(shader.Program, "texture"), 0);
glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model));
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glBindVertexArray(brushVAO);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArray(0);
}
drawFBO(GLuint texture, Shader shader)
{
GLfloat screenquadVertices[] =
{
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-1.0f, -1.0f,0.0f, 0.0f, 0.0f,
1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f, 0.0f
};
glGenVertexArrays(1, &screenquadVAO);
glGenBuffers(1, &screenquadVBO);
glBindVertexArray(screenquadVAO);
glBindBuffer(GL_ARRAY_BUFFER, screenquadVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(screenquadVertices), &screenquadVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glBindVertexArray(0);
shader.set();
glUniform1i(glGetUniformLocation(shader.Program, "texture"), 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glBindVertexArray(screenquadVAO);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArray(0);
}
Update2
Ok. I tried both your recomend but results are simmilar and don't explain my problem. Look at:
1) glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
and two premultiplied .PNG texture - witha black and with white.
2) glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
and straight-alpha texture.
When blending into a texture with a straight alpha channel, the correct blending equations involve a division by the alpha values, and such equations cannot be expressed within the linear formulas that OpenGL (and GPUs) provide with the glBlendFunc
and glBlendFuncSeparate
.
Instead, you should use premultiplied alpha. In principle you do that by calling
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
once, and ensuring that all your inputs and outputs are premultiplied alpha.
However, standard PNGs are in straight alpha (though non-standard PNGs with premultiplied alpha exist... probably not your case). To deal with it you can either premultiply the PNGs when you load them, or use the following when in drawBrush
:
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
That will get a straight alpha shader output and blend it into a premultiplied alpha framebuffer. Therefore, when rendering that framebuffer to the screen, remember to use the glBlendFunc
for premultiplied alpha (the first one).