【问题标题】:How to overlay texture over another, without clipping the base texture?如何在不裁剪基础纹理的情况下将纹理覆盖在另一个之上?
【发布时间】:2016-03-08 20:16:49
【问题描述】:

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

 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);
 }

问题 - 除一个问题外,此方法有效。如果叠加图像小于基本图像,则叠加图像的外部区域的 alpha 值为 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;
}

【问题讨论】:

  • 你也可以发布你的顶点着色器代码吗?
  • @Swifter:添加了顶点着色器

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


【解决方案1】:

我期待在它的边界之外,纹理的 alpha 应该显示为 0.0

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

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

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

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

  • 将视口设置为基本图像尺寸并从 (-1, 1) 绘制一个四边形。
  • 您的片段着色器现在将对每个像素(称为纹素)进行操作。使用gl_FragCoord 获取纹素
  • 通过 texel 对基础和覆盖层进行采样,例如使用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
}

【讨论】:

    【解决方案2】:

    如果您在纹理范围之外进行采样,会发生什么由您使用glTexParameteri()GL_TEXTURE_WRAP_SGL_TEXTURE_WRAP_T 设置的值控制。

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

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

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

    第二个选项的片段着色器代码可能如下所示(未经测试):

    mediump vec3 col = texture2D(inputImageTexture, textureCoordinate).xyz;
    
    if (all(greaterThan(textureCoordinate2, vec2(0.0))) &&
        all(lessThan(textureCoordinate2, vec2(1.0))))
    {
        mediump vec3 overlay = texture2D(inputImageTexture2, textureCoordinate2).xyz;
        col = mix(col, overlay, overlay.a);
    }
    
    gl_FragColor = vec4(col, 1.0);
    

    与您的原始代码相比,还要注意向量运算的使用。只要有对向量进行操作的好方法,它就会使代码更简单。它还将使具有矢量运算的 GPU 的优化器工作更轻松。

    【讨论】:

      【解决方案3】:

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

      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);
       }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-03-08
        • 2014-05-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-26
        相关资源
        最近更新 更多