片段着色器混合和gl_LastFragColor未声明的标识符在android 2.3, OPENGL ES 2.0



主要问题:

1)如果目标颜色不透明,我如何才能让片段着色器不将当前像素更新为透明像素?

2) gl_LastFragColor工作为android 2.3 OpenGL ES 2.0?

背景:

我试图用OpenGL ES 2.0使用批处理纹理编写文本。片段着色器的挑战是,如果目标像素颜色的alpha值为1,则只能更新文本的像素颜色,我知道这意味着像素不属于文本。

文本图集的纹理存储在位图中:bitmap . config。ALPHA_8

几个小时后,我注意到三个问题:

第一个问题:不知何故,alpha(1)被用作不透明的,而0被用作透明的。

第二个问题:假设我显示"Hi"。虽然我的绘图器应该通过纹理H,然后i来绘制"Hi",但它的做法是:"i",然后"H"。

我知道这一点,因为如果我手动颠倒"Hi"从"I"开始的顶点顺序,那么我就得到了我的文本。这是一个问题,因为问题3。

第三个问题:我认为这是主要问题。假设字母H具有宽度为0.5的纹理框,但我使用宽度为1的纹理,其中第一个0.25是带有alpha 1的白色,下一个0.5是带有alpha 0的白色,最后一个0.25是带有alpha 1的白色。"i"也一样

如果先对"i"进行纹理处理,然后再对"H"进行纹理处理,则宽度将相交(例如,"DD"one_answers"DD"具有不同的组合宽度)。但是在"H"被渲染到"i"之后,它会更新宽度上的所有像素,使其透明,而字母o的大部分会消失。这是正确的行为,如果"i"先渲染,然后"H"和混合被禁用。

所以我试图使用gl_LastFragColor来检查之前的像素更新它,但我得到:未声明的标识符。所以我被困在这个片段代码:

片段代码:

vec4 myTexture;
myTexture = texture2D(u_Texture, v_TexCoordinate);
if( myTexture[3] < 0.01){ // The texture is Bitmap.Config.ALPHA_8. If alpha is 0 . . .
    gl_FragColor = vec4(0.0,0.0,0.0,0.0);  // 0.0 is somehow transparent.
}
else{
    gl_FragColor = vec4(1.0,1.0,1.0,1.0); // display text in white. Somehow 1.0 is opaque.
    gl_FragColor = myTexture;
}

我调用draw作为:

    GLES20.glEnable (GLES20.GL_BLEND);
    GLES20.glBlendFunc (GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
    //      GLES20.glBlendFunc (GLES20.GL_ONE_MINUS_SRC_ALPHA, GLES20.GL_SRC_ALPHA); This makes alpha of 1 transparent!!

    GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, buffers[2]);
    GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrderLength, GLES20.GL_UNSIGNED_SHORT, 0);
    GLES20.glDisable (GLES20.GL_BLEND);

注。如果我将alpha从1切换到0,或者类似的技巧,我得到同样的问题,只是透明度颠倒了。

我认为你可以不使用gl_LastFragColor(正如Andon指出的那样,这不是标准OpenGL ES的一部分)就能做到你想要实现的目标。

我会查看使用目的地alpha的混合函数。您可能需要使用它来找出确切满足您需求的内容,但可以从以下内容开始:

glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);

这将使framebuffer中已经具有1.0 alpha值的像素保持不变,并且如果framebuffer中的alpha值为0.0,则用当前片段替换framebuffer像素。

要使用目标alpha,在创建GLSurfaceView时选择一个具有alpha平面的framebuffer配置。在调用setRenderer之前添加这个将处理这个:

setEGLConfigChooser(8, 8, 8, 8, 16, 0);

还要确保在glClearColor调用中使用0.0作为alpha的明确值。

为了确保纹理的透明部分不被绘制,你通过将gl_FragColor设置为完全透明所做的事情应该与此一起工作。另一个选择是你在着色器中discard这些片段。

这是不能移植的

gl_LastFragColor是NV和Apple通过(几乎相同的)扩展添加的。

苹果的扩展名为GL_APPLE_shader_framebuffer_fetch,你不会在Android上说扩展。NV具有相同的扩展,只需将APPLE替换为NV,并确保您拥有必要的Tegra GPU。

假设你觉得自己仅限于运行NV Tegra gpu的Android设备,你可以通过在着色器的顶部放置以下行来在你的GLSL着色器中获得这个功能:

#extension GL_NV_shader_framebuffer_fetch : require

如果其他供应商支持此功能,他们可能会使用GL_EXT_shader_framebuffer_fetch。然而,最重要的是,这是一个扩展,并且在iOS之外的任何地方都不能广泛使用。

我明白了。我在这里看到了一个很好的解释,使用discard。

我最后的代码是:

void main()
{
vec4 myTexture;
myTexture = texture2D(u_Texture, v_TexCoordinate);
if( myTexture[3] < 0.01){
    discard;
}
else{
    gl_FragColor = myTexture;
}
}

Discard实际上不会重写像素。

最新更新