我正在尝试使用GLES20在GLSurfaceView中实现混合效果。
该视图具有彩色(红色)背景,在此背景上将蒙版纹理和具有alpha通道的图像纹理混合在一起。
这是一个示例蒙版和图像:
,
预期结果:
这是一个类似的问题,到目前为止指导了我:OpenGL - mask with multiple textures
不幸的是,我无法达到类似的结果。这是我的onDraw()方法的相关部分:
// Bind default FBO and use shader program
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glUseProgram(program);
// Set the vertex attributes
glVertexAttribPointer(texCoordHandle, 2, GL_FLOAT, false, 0, mTexVertices);
glEnableVertexAttribArray(texCoordHandle);
glVertexAttribPointer(posCoordHandle, 2, GL_FLOAT, false, 0, mPosVertices);
glEnableVertexAttribArray(posCoordHandle);
// Clear the buffer color - draw red color background
glClear(GL_COLOR_BUFFER_BIT);
// Apply transformation - irrelevant
glUniformMatrix4fv(mvpMatrixHandle, 1, false, matrix, 0);
// Bind the mask texture for drawing
glUniform1i(contentTextureHandle, 0);
glBindTexture(GL_TEXTURE_2D, textures[MASK_TEX]);
// Write alpha value 1.0 to white regions and 0.0 to black - don't change the RGB values
glBlendFuncSeparate(
GL_ZERO, // remove the source rgb colors
GL_ONE, // keep the destination rgb colors
GL_SRC_COLOR, // !! put high alpha (1.0) where image is white
GL_ZERO); // remove destination alpha
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Bind the content image
glBindTexture(GL_TEXTURE_2D, textures[CONTENT_TEX]);
// Change the blend function to write the regions with low alpha
glBlendFuncSeparate(
GL_ONE_MINUS_DST_ALPHA, // black regions (alpha 0): keep src rgb, white regions: remove src rgb
GL_DST_ALPHA, // black regions: remove dst rgb, white regions: keep dst rgb
GL_ONE_MINUS_DST_ALPHA, // black regions: keep src alpha, white regions: remove src alpha
GL_DST_ALPHA); // black regions: remove dst rgb, white regions: keep dst alpha
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
但是,组合这些混合功能的结果仅产生红色背景。
我已经尝试了两天的各种功能,但我认为上面的功能应该是正确的。另外,在调试时我改用
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
,并且两个图像都显示在彼此的顶部(即纹理或GL设置没有问题):我对上述混合函数的选择是基于funcSeparate背后的公式为:
Orgb = srgb *Srgb + drgb * Drgb
Oa = sa * Sa + da * Da
O:输出,s-源,d-目标,其余部分是glBlendFuncSeparate(Srgb,Drgb,Sa,Da)的参数
有什么想法为什么纹理不能如预期那样融合或如何获得期望的结果?
解决
我遵循Reto Koradi的建议,并将混合功能合并到片段着色器中:
"precision mediump float;\n" +
"uniform sampler2D tex_sampler;\n" +
"uniform sampler2D mask_sampler;\n" +
"varying vec2 v_texcoord;\n" +
"void main() {\n" +
" vec4 mask = texture2D(mask_sampler, v_texcoord);\n" +
" vec4 text = texture2D(tex_sampler, v_texcoord);\n" +
" gl_FragColor = vec4(text.r * (1.0 - mask.r), text.g * (1.0 - mask.r), text.b * (1.0 - mask.r), text.a * (1.0 - mask.r));\n" +
"}\n";
请注意,我将预乘位图与混合函数
.glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)
一起使用。我打算通过切换到具有相反颜色的蒙版来减少
(1.0 - mask.r)
部分(即在白色区域而不是黑色区域显示纹理)。正确地绑定两个纹理(glActiveTexture和glUniform1i)有点棘手,但是在线上有很多资源可以做到这一点。
最佳答案
您的解决方案似乎是基于对GL_SRC_COLOR
作为混合功能的一部分如何影响alpha分量的误解。 GL_SRC_COLOR
将源颜色的(R, G, B)
用作源的RGB
部分的乘数,而源颜色的A
用作源的A
的乘数。因此,对于源部分,它实际上只是得出每个分量的平方。
因此,当您使用此混合功能时:
glBlendFuncSeparate(GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ZERO);
应用于alpha分量的
GL_SRC_COLOR
使用源颜色的alpha值作为乘数。此混合方程式等效于:glBlendFuncSeparate(GL_ZERO, GL_ONE, GL_SRC_ALPHA, GL_ZERO);
看起来蒙版纹理的Alpha是常数
1.0
,这意味着当使用此混合功能渲染蒙版纹理时,Alpha值都设置为1.0
。然后,在渲染android纹理时,由于混合方程式设置为保留目标alpha为1.0
的所有像素的现有内容,因此不会显示任何内容。您需要解决的问题是确保蒙版纹理渲染的alpha输出代表您要应用的蒙版值。您可以通过更改纹理本身以在alpha组件中包含遮罩值来实现。但是由于ES 2.0都是基于着色器的,因此您也可以在片段着色器中非常轻松地处理它。假设您当前在片段着色器中有以下内容:
vec4 texVal = texture2D(...);
gl_FragColor = texVal;
由于蒙版纹理是黑白的,因此可以将任何颜色值用作输出alpha。您没有使用此渲染阶段的颜色输出,因此这些组件可以是任何东西。如果我们只将它们保留在
0.0
上,则可能看起来像这样:vec4 texVal = texture2D(...);
gl_FragColor = vec4(0.0, 0.0, 0.0, texVal.r);
您最终所做的事情与我最近对一个相关问题的答案类似:LibGDX texture blending with OpenGL blending function。主要区别在于,当您使用单独的混合功能时,我建议在渲染遮罩时使用颜色遮罩来抑制颜色输出。