在缓冲区中绘画。 alpha 通道问题
Painting in buffer. Issue with alpha chanel
我尝试实现类似绘画工具的功能,但遇到了笔刷的阿尔法通道问题。我的画笔是具有透明背景的 .PNG 纹理。我在 RGBA 缓冲区中绘制。我的代码:
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(...)
它有效,但如果我停止画笔,来自画笔的 alpha 通道将累积在缓冲区中以替换颜色。
我不熟悉在 Opengl 中过于接近地混合并且对混合参数有疑问。我的问题是混合还是其他什么?
更新:
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);
}
更新2
好的。我尝试了您的建议,但结果相似,无法解释我的问题。看:
1) glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
和两个预乘 .PNG 纹理 - 黑色和白色。
2) glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
和直 alpha 纹理。
当混合到具有直 alpha 通道的纹理中时,正确的混合方程涉及除以 alpha 值,并且此类方程无法在 OpenGL(和 GPU)提供的线性公式中表达 glBlendFunc
和 glBlendFuncSeparate
.
相反,您应该使用预乘 alpha。原则上,您可以通过调用
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
一次,并确保所有输入和输出都预乘 alpha。
但是,标准 PNG 处于直接 alpha 状态(尽管存在具有预乘 alpha 的非标准 PNG……可能不是您的情况)。要处理它,您可以在加载 PNG 时预乘它们,或者在 drawBrush
:
中使用以下内容
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
这将获得直接的 alpha 着色器输出并将其混合到预乘的 alpha 帧缓冲区中。因此,在将该帧缓冲区渲染到屏幕时,请记住对预乘 alpha(第一个)使用 glBlendFunc
。
我尝试实现类似绘画工具的功能,但遇到了笔刷的阿尔法通道问题。我的画笔是具有透明背景的 .PNG 纹理。我在 RGBA 缓冲区中绘制。我的代码:
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(...)
它有效,但如果我停止画笔,来自画笔的 alpha 通道将累积在缓冲区中以替换颜色。
我不熟悉在 Opengl 中过于接近地混合并且对混合参数有疑问。我的问题是混合还是其他什么?
更新:
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);
}
更新2
好的。我尝试了您的建议,但结果相似,无法解释我的问题。看:
1) glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
和两个预乘 .PNG 纹理 - 黑色和白色。
2) glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
和直 alpha 纹理。
当混合到具有直 alpha 通道的纹理中时,正确的混合方程涉及除以 alpha 值,并且此类方程无法在 OpenGL(和 GPU)提供的线性公式中表达 glBlendFunc
和 glBlendFuncSeparate
.
相反,您应该使用预乘 alpha。原则上,您可以通过调用
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
一次,并确保所有输入和输出都预乘 alpha。
但是,标准 PNG 处于直接 alpha 状态(尽管存在具有预乘 alpha 的非标准 PNG……可能不是您的情况)。要处理它,您可以在加载 PNG 时预乘它们,或者在 drawBrush
:
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
这将获得直接的 alpha 着色器输出并将其混合到预乘的 alpha 帧缓冲区中。因此,在将该帧缓冲区渲染到屏幕时,请记住对预乘 alpha(第一个)使用 glBlendFunc
。