主要问题:
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实际上不会重写像素。