与掩模的意外纹理混合结果

时间:2014-07-08 05:34:44

标签: android opengl-es-2.0 mask blending

我正在尝试使用GLES20在GLSurfaceView中实现混合效果。

视图有一个颜色(红色)背景,在其背景下混合了一个蒙版纹理和一个带有alpha通道的图像纹理。

以下是掩码和图像示例:

black and white image with shapespicture of Android mascot with transparent background

预期结果:

Android image with partly invisible parts on a red background

这是一个类似的问题,它引导我到目前为止: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设置没有问题):

test android image on top of black and white image mask

我对上述混合函数的选择是基于假设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),但网上有很多资源如何做到这一点。

1 个答案:

答案 0 :(得分:1)

您的解决方案似乎是基于对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纹理时,它们都不会显示,因为混合方程设置为保留目标字母为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。主要区别在于我建议在渲染蒙版时使用颜色蒙板来抑制颜色输出,同时您使用单独的混合功能。