如何在不剪裁基础纹理的情况下将纹理叠加在另一个上?

时间:2016-03-07 17:19:19

标签: android opengl-es shader opengl-es-2.0

我已经编写了这个简单的着色器来覆盖纹理而不是另一个(基础)纹理 -

 varying highp vec2 textureCoordinate;
 varying highp vec2 textureCoordinate2;

 uniform sampler2D inputImageTexture;
 uniform sampler2D inputImageTexture2;

 void main()
 {
     mediump vec4 base = texture2D(inputImageTexture, textureCoordinate);
     mediump vec4 overlay = texture2D(inputImageTexture2, textureCoordinate2);

     mediump float ra = (overlay.a) * overlay.r + (1.0 - overlay.a) * base.r;
     mediump float ga = (overlay.a) * overlay.g + (1.0 - overlay.a) * base.g;
     mediump float ba = (overlay.a) * overlay.b + (1.0 - overlay.a) * base.b;

     gl_FragColor = vec4(ra, ga, ba, 1.0);
 }

问题 - 除了一个问题外,这个工作正常。如果叠加图像小于基本图像,则叠加图像的外部区域给出α值为1.0,即overlay.a == 1.0。因此,基本图像被叠加图像剪切。叠加层外的区域显示为黑色。

我是opengl的新手,并且期待超出界限,纹理的alpha应该显示为0.0?如何修复着色器代码以实现所需的行为?或者我是否需要修改我的图形管道?

编辑下方的顶点着色器 -

attribute vec4 inputTextureCoordinate2;

varying vec2 textureCoordinate;
varying vec2 textureCoordinate2;

void main()
{
    gl_Position = pos;
    textureCoordinate = uv;
    textureCoordinate2 = inputTextureCoordinate2.xy;
}

3 个答案:

答案 0 :(得分:1)

  

我期待超出界限,纹理的alpha应显示为0.0

你如何在其边界之外对纹理进行采样?在对纹理进行采样时,uv坐标的范围应为0到1.如果坐标超出此范围,则会发生以下两种情况之一:

  • 如果设置了GL_CLAMP_TO_EDGE,则cooridnate将被限制在(0,1)范围内,并且您将对边缘像素进行采样
  • 如果设置了GL_REPEAT,那么将采用坐标的小数部分,并且您将在纹理中间的某处进行采样

有关详细信息,请参阅glTexParameter上的the docs

如果您的用例只是覆盖图像,也许您应该尝试编写pixel shader

  • 将视口设置为基本图像尺寸,并从(-1,1)绘制四边形。
  • 您的片段着色器现在将对每个像素进行操作,称为纹素。使用gl_FragCoord
  • 获取纹素
  • 对基础进行采样并通过纹理像素覆盖,例如使用texelFetch
  • 如果纹理元素位于叠加层之外,请将叠加层的rgba值设置为0

例如

//fragment shader
uniform ivec2 overlayDim;
uniform sampler2D baseTexture;
uniform sampler2D overlayTexture;

void main() {
   vec2 texelf = floor(gl_FragCoord.xy);
   ivec2 texel = (int(texelf.x), int(texelf.y));

  vec4 base = texelFetch(baseTexture, texel, 0);
  vec4 overlay = texelFetch(overlayTexture, texel, 0);

  float overlayIsValid = float(texel.x < overlayDim.x && texel.y < overlayDim.y);

  overlay *= overlayIsValid;

  //rest of code
}

答案 1 :(得分:1)

如果您在纹理范围之外进行采样,则会使用;InstallDir "$PROGRAMFILES\${productName}" InstallDir $PROGRAMFILES\WhapTk GL_TEXTURE_WRAP_S使用GL_TEXTURE_WRAP_T设置的值来控制。

在完整的OpenGL中,您可以将值设置为glTexParameteri(),将边框颜色设置为alpha 0.0的值,然后完成。但是OpenGL ES 2.0中没有纹理边框(该选项在ES 3.2中引入,但在早期版本中没有引入)。

如果没有这个,我可以想到两个选择:

  • 如果您可以控制纹理数据,则可以将一个像素边框设置为透明值。当在范围之外采样时,GL_CLAMP_TO_BORDER会为您提供透明值。
  • 检查片段着色器中的范围。

第二个选项的片段着色器代码看起来像这样(未经测试):

GL_CLAMP_TO_EDGE

与原始代码相比,还要注意使用向量运算。只要有一种很好的方法对向量进行操作,它就会使代码更简单。它还可以使用矢量运算的GPU更容易优化优化器的工作。

答案 2 :(得分:0)

我在代码中发现了这个问题。我有

GLES20.glClearColor(0, 0, 0, 1);

在我的代码中。将其更改为 -

GLES20.glClearColor(0, 0, 0, 0);

解决了这个问题。

同样如@Reto所述,我更改了片段着色器以使用向量操作进行优化。

 void main()
 {
     mediump vec4 overlay = texture2D(inputImageTexture2, textureCoordinate2);

     mediump vec3 col = texture2D(inputImageTexture, textureCoordinate).xyz;
     col = mix(col, overlay.xyz, overlay.a);

     gl_FragColor = vec4(col, 1.0);
 }