OpenGL ES 2.0 - 文本混合问题 (iOS)

OpenGL ES 2.0 - Blending issue with text (iOS)

当我将文本与 OpenGL 混合时,文本周围有灰色阴影。

目前,这是我的混合功能:

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

如果我将其更改为:

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

它有效,我用 Gimp 获得了相同的结果:

但是,无论如何 GL_ONE,文本无法再淡入背景。好像透明度是 0 或 1。

还有 GL_ONE,两张图片之间的淡入淡出会以某种方式使结果图像超级明亮:

而 GL_SRC_ALPHA 它看起来很正常:

所以这两种解决方案都各有利弊。我不想要灰色阴影,但我想保留淡入淡出效果。任何建议将不胜感激。

这是我的片段着色器:

gl_FragColor = (v_Color * texture2D(u_Texture, v_TexCoordinate));

下面是纹理(文本和图像)的加载方式(最后预乘):

+ (GLuint)getGLTextureFromCGIImage:(CGImageRef)cgiImage {
    size_t width = CGImageGetWidth(cgiImage);
    size_t height = CGImageGetHeight(cgiImage);

    GLubyte *spriteData = (GLubyte *) calloc(width * height * 4, sizeof(GLubyte));
    NSUInteger bytesPerPixel = 4;
    NSUInteger bytesPerRow = bytesPerPixel * width;
    NSUInteger bitsPerComponent = 8;

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    CGContextRef spriteContext = CGBitmapContextCreate(spriteData,
                                                       width,
                                                       height,
                                                       bitsPerComponent,
                                                       bytesPerRow,
                                                       colorSpace,
                                                       kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);

    CGColorSpaceRelease(colorSpace);

    CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), cgiImage);
    CGContextRelease(spriteContext);

    return [GLMediaUtils getGLTextureFromPixelsInFormat:GL_RGBA
                                                  width:(int)width
                                                 height:(int)height
                                                 pixels:spriteData];
}

+ (GLuint)getGLTextureFromPixelsInFormat:(GLenum)format
                                   width:(int)width
                                  height:(int)height
                                  pixels:(void *)pixels {

    glActiveTexture(GL_TEXTURE0);

    GLuint texture;
    glGenTextures(1, &texture);


    glBindTexture(GL_TEXTURE_2D, texture);


    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);


    GLenum type = GL_UNSIGNED_BYTE;
    if(format == GL_RGB) // RGB565
        type = GL_UNSIGNED_SHORT_5_6_5;

    glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, pixels);

    free(pixels);
    glFlush();

    return texture;
} 

你快到了。如果你想避免纹理重影,你需要使用预乘 alpha(因为 GL 混合通常是 post-multiplied alpha,这会导致通道上的颜色溢出)。

通常 GL_ONE 有效,因为正确的 alpha 已在纹理上传之前预烘焙到纹理 RGB 颜色通道中。如果您开始在着色器的顶部添加自定义淡入淡出,那么您最终会在着色器中用于混合的 alpha 与用于预乘的 alpha 之间出现不同步。

...所以您需要根据新的 alpha 值对片段着色器进行调整,以恢复到稳定的预乘值。类似于:

vec4 texColor = texture2D(u_Texture, v_TexCoordinate);
// Scale the texture RGB by the vertex color
texColor.rgb *= v_color.rgb;
// Scale the texture RGBA by the vertex alpha to reinstate premultiplication
gl_FragColor = texColor * v_color.a;

另一种方法是将字体仅存储为亮度纹理。您会得到灰色阴影,因为颜色通道在字体之外是黑色的,并且纹理过滤混合了白色和黑色。如果你知道字体颜色是白色,你根本不需要在纹理中存储 RGB 值......(或者如果你很懒,只需保留现有纹理并用白色填充整个纹理 RGB 通道).