在iOS中加载4通道纹理数据

时间:2012-11-11 22:40:23

标签: ios opengl-es-2.0 textures premultiplied-alpha

我想从iOS中的文件加载4通道纹理数据,所以我将纹理视为(连续)地图

[0,1]x[0,1] -> [0,1]x[0,1]x[0,1]x[0,1]

如果我使用fileformat .png,XCode / iOS会将该文件视为图像,因此将每个组件rgba(预乘alpha)相乘,从而破坏我的数据。我该怎么解决这个问题?例子可能是

  • 使用两个带有组件rgb(3通道)的纹理
  • postdivide alpha
  • 使用其他文件格式

其中,我认为最好的解决方案是使用其他文件格式。 GL压缩文件格式(PVRTC?)不是Apple平台独立的,并且似乎是低分辨率(4位)(reference)。

编辑: 如果我自己的答案是真的,那么就无法在iOS中获得png的4通道数据。由于OpenGL是关于创建图像而不是呈现图像,因此应该可以以某种方式加载4通道数据。 png是图像的文件格式(和压缩取决于所有4个通道但是一个通道的压缩独立于其他通道),因此有人可能会争辩说我应该使用其他文件格式。那么我应该使用哪种其他压缩文件格式,哪些易于在iOS中阅读/集成?

更新:“组合”提到了一种加载4通道非预乘纹理的方法,所以我不得不给他正确的答案。但是,该解决方案有一些我不喜欢的限制。我的下一个问题是“从iOS中的png文件访问原始4通道数据”:)

我认为这是一个糟糕的库设计,无法读取4通道png数据。我不喜欢系统试图比我自己聪明。

6 个答案:

答案 0 :(得分:4)

当您考虑PVRTC时,使用GLKit可能是一种选择。这包括GLKTextureLoader,它允许您加载纹理而不预先乘以alpha。例如:

+ (GLKTextureInfo *)textureWithContentsOfFile:(NSString *)fileName options:(NSDictionary *)textureOperations error:(NSError **)outError

并传递包含以下内容的选项字典:

GLKTextureLoaderApplyPremultiplication = NO

答案 1 :(得分:3)

您可以简单地请求Xcode不“压缩”您的PNG文件。单击左上角的项目,选择“构建设置”,找到“压缩PNG文件”并将选项设置为“否”。

至于你的其他选择,后分裂并不是一个糟糕的解决方案,但显然你会失去整体精确度,我相信TIFF和BMP也都支持。 PVRTC是特定于PowerVR的,因此它不是特定于Apple的,但它也不是完全独立于平台的,并且专门设计为有损压缩,即使用GPU上的少量输入解压缩也是微不足道的。您通常会增加纹理分辨率以改善每像素的低位数。

enter image description here

答案 2 :(得分:3)

您应该使用libpng加载没有预乘颜色的PNG。

它是用C编写的,应该为iOS编译。

我遇到过与Android类似的问题,还必须使用第三方库来加载非预乘颜色的PNG文件。

答案 3 :(得分:2)

这是试图回答我自己的问题。

无法加载非预乘的.png文件。

选项kCGImageAlphaLast是有效选项,但没有为CGBitmapContextCreatereference)提供有效组合。但它是CGImageRef的有效选项。

上面提到的XCode中的构建设置COMPRESS_PNG_FILES的作用是将.png文件转换为其他文件格式,并将频道rgba相乘({{3} })。我希望禁用此选项可以在我的实际.png文件中访问频道数据。但我不确定这是否可行。以下示例尝试以低级别访问.png数据,作为CGImageRef

void test_cgimage(const char* path)
{
    CGDataProviderRef dataProvider = CGDataProviderCreateWithFilename(path);
    CGImageRef cg_image = CGImageCreateWithPNGDataProvider(dataProvider, NULL, NO,
                                                           kCGRenderingIntentDefault);
    CGImageAlphaInfo info = CGImageGetAlphaInfo(cg_image);
    switch (info)
    {
        case kCGImageAlphaNone:               printf("kCGImageAlphaNone\n");                break;
        case kCGImageAlphaPremultipliedLast:  printf("kCGImageAlphaPremultipliedLast\n");   break;
        case kCGImageAlphaPremultipliedFirst: printf("kCGImageAlphaPremultipliedFirst\n");  break;
        case kCGImageAlphaLast:               printf("kCGImageAlphaLast\n");                break;
        case kCGImageAlphaFirst:              printf("kCGImageAlphaFirst\n");               break;
        case kCGImageAlphaNoneSkipLast:       printf("kCGImageAlphaNoneSkipLast\n");        break;
        case kCGImageAlphaNoneSkipFirst:      printf("kCGImageAlphaNoneSkipFirst\n");       break;
        default: break;
    }

}

,禁用COMPRESS_PNG_FILES,提供“kCGImageAlphaPremultipliedLast”。所以我认为iOS总是转换.png文件,即使在运行时也是如此。

答案 4 :(得分:1)

有更好的解决方案,速度更快(大约3x〜5x)并且跨平台

    #define STB_IMAGE_IMPLEMENTATION
    #include "stb_image.h"


    // force 4 channel rgba, force flipy

    // image: first pixel is top left, OpenGL assume first is bottom left, so need flipy
    bool flipy = true;
    int width, height, nrChannels;
    stbi_set_flip_vertically_on_load(flipy);

    unsigned char *imageData = stbi_load(path.c_str(), &width, &height, &nrChannels, 4);

    // load data to your texture
    // glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bytes);

    free(imageData);

答案 5 :(得分:0)

不是100%你想要的,但我使用这种方法解决了这个问题:将alpha通道放入一个单独的黑色&白色png并保存没有alpha的原始png。所以占用的空间大致相同。然后在我的纹理加载器中加载两个图像并组合成一个纹理。

我知道这只是一种解决方法,但至少它会给出正确的结果。是的,非常烦人的是,iOS不允许你在没有预乘alpha的情况下从PNG加载纹理。