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 通道).
当我将文本与 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 通道).