在片段着色器中将透明纹理覆盖在另一个上时,黑色边框

时间:2012-08-28 13:06:20

标签: iphone opengl-es opengl-es-2.0 glsl

我的场景由一个我用两个纹理着色的平面组成。第一个,最底部的纹理是来自iPhone相机的实心图像,第二个图像是一种取景器,我需要覆盖在相机输入上(具有透明度)。我在透明纹理中的实体边界处得到这些黑色暗线。

Black borders around solids

我一直在对此进行一些研究,并且认识到这个伪像是插值结合预乘alpha的结果。由于XCode会自动将所有PNG图像转换为预乘的png,而我编写的代码加载图像也会受到预乘的alpha上下文的影响,我有点卡在确切问题所在的位置。

我尝试过以下解决方案:

  • 确保alpha值为0的像素的rgb值也设置为0
  • 关闭了xcode的自动png压缩功能,使其保留了我提供的纹理
  • 摆弄片段着色器以避免mix()调用
  • 在创建位图上下文时更改了AlphaInfo值

重要提示:我没有使用glBlendFunc(),我将两个纹理一起送入1个片段着色器并尝试将它们混合在那里。所以通过这个gl-call的解决方案不会让我更进一步。

这是我用来加载透明纹理的代码:

shared_ptr<ImageData> IOSFileSystem::loadImageFile(string path, bool flip) const
{
    cout << path << endl;
    // Result
    shared_ptr<ImageData> result = shared_ptr<ImageData>();

    // Convert cpp string to nsstring
    NSString *convertedPathString = [NSString stringWithCString:path.c_str() encoding:[NSString defaultCStringEncoding]];
    NSString *fullPath = [NSString stringWithFormat:@"%@%@", [[NSBundle mainBundle] resourcePath], convertedPathString];

    // Check if file exists
    if([[NSFileManager defaultManager] fileExistsAtPath:fullPath isDirectory:NO])
    {
        // Load image
        UIImage *image = [[[UIImage alloc] initWithContentsOfFile:fullPath] autorelease];
        CGImageRef imageRef = image.CGImage;

        // Allocate memory for the image
        size_t width = CGImageGetWidth(imageRef);
        size_t height = CGImageGetHeight(imageRef);
        GLubyte *spriteData = (GLubyte*) calloc(width * height * 4, sizeof(GLubyte));

        // Create drawing context
        CGContextRef context = CGBitmapContextCreate(spriteData, width, height, 8, width * 4, CGImageGetColorSpace(imageRef), kCGImageAlphaPremultipliedLast);

        // Flip for OpenGL coord system
        if(flip)
        {
            CGContextTranslateCTM(context, 0, image.size.height);
            CGContextScaleCTM(context, 1.0, -1.0);
        }

        // Draw & release
        CGContextDrawImage(context, CGRectMake(0.0, 0.0, width, height), imageRef);
        CGContextRelease(context);

        // Put result in shared ptr
        // Don't free() the spritedata because our shared pointer will take care of that
        // Since the shared pointer doesn't know how to free "calloc" data, we have to teach it how: &std::free
        shared_ptr<GLubyte> spriteDataPtr = shared_ptr<GLubyte>(spriteData, &std::free);
        result = shared_ptr<ImageData>(new ImageData(path, width, height, spriteDataPtr));
    }
    else
    {
        cout << "IOSFileSystem::loadImageFile -> File does not exist at path.\nPath: " + path;
        exit(1);
    }

    return result;
}

以下是我在纹理上设置像素的方法:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, 
    GL_RGBA, GL_UNSIGNED_BYTE, pixels);

这是片段着色器的精简版本:

void main(void)
{
    lowp vec4 camera = texture2D(texture0, destinationTexCoord);
    lowp vec4 viewfinder = texture2D(texture1, destinationTexCoord);

    lowp vec4 result = mix(camera, viewfinder, viewfinder.a);

    gl_FragColor = result;
}

2 个答案:

答案 0 :(得分:2)

这可能是因为PNG图像中的premultiplied alpha。尝试修改混合模式:

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // instead of (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

答案 1 :(得分:0)

通过调查glBlendFunc调用的工作原理,我找到了解决问题的方法。我使用this editor来检查用于与GL_ONEGL_ONE_MINUS_SRC_ALPHA混合的公式。

这导致了以下片段着色器代码:

lowp vec4 camera = texture2D(texture0, destinationTexCoord);
lowp vec4 viewfinder = texture2D(texture1, destinationTexCoord);

lowp vec4 result = viewfinder + camera * vec4(1.0 - viewfinder.a);