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

问题描述 投票:5回答:6

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

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

如果我使用文件格式 .png,XCodeiOS认为文件是一个图像,所以将每个组件乘以 rgba (预乘α),破坏了我的数据。我应该如何解决这个问题?例子可能是

  • 用两个纹理的组件 rgb (3通道)
  • 除后α
  • 改用其他文件格式

其中,我认为最好的解决方案是使用另一种文件格式。GL压缩文件格式(PVRTC?)不是独立于苹果平台的,而且似乎分辨率很低(4位)(参考).

EDIT:如果我自己下面的回答是真的,那么在iOS中是不可能得到png的4通道数据的。因为OpenGL是要创建图像而不是呈现图像,所以应该可以通过某种方式加载4通道数据,png是图像的文件格式(和压缩取决于所有4个通道 但一个通道的压缩是独立于其他通道的),所以有人会说,我应该使用其他的文件格式。那么,我应该使用其他哪些压缩文件格式,这在iOS中是容易读取集成的?

更新:"combinatorial "提到了一种加载4通道非预增纹理的方法,所以我不得不给他正确的答案。然而,这个解决方案有一些限制,我不喜欢。那么我的下一个问题是 "在iOS中从png文件中访问原始4通道数据":)

我认为不使其能够读取4通道的png数据是一个糟糕的库设计。我不喜欢系统试图比自己更聪明。

ios opengl-es-2.0 textures premultiplied-alpha
6个回答
4
投票

由于你考虑的是PVRTC,那么使用 GLKit 可以作为一种选择。这包括 GLKTextureLoader 它允许你在加载纹理时不需要预先乘以alpha。例如,使用

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

并传递一个包含选项字典。

GLKTextureLoaderApplyPremultiplication = NO

3
投票

您可以简单地要求Xcode不 "压缩 "您的PNG文件,在左上角点击您的项目,选择 "构建设置",找到 "压缩PNG文件"。点击左上角的项目,选择 "Build Settings",找到 "Compress PNG Files "并将选项设置为 "No"。

至于你的其他选项,postdividing不是一个不好的解决方案,但显然你会失去整体的精度,我相信TIFF和BMP也是支持的。PVRTC是PowerVR专用的,所以它不是苹果专用的,但它也不是完全独立于平台的,它是专门设计的有损压缩,在GPU上几乎不需要输入就可以解压。一般来说,你会提高你的纹理分辨率来改善每像素的低位数。

enter image description here


3
投票

你应该使用 libpng 来加载没有预增颜色的PNG。

它是用C语言编写的,应该可以编译到iOS系统。

我在Android上也遇到过类似的问题,也不得不使用第三方库来加载非预增颜色的PNG文件。


2
投票

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

加载非预增色的.png文件是不可能的。

选项 kCGImageAlphaLast 是一个有效的选项,但没有给出一个有效的组合,用于 CGBitmapContextCreate (参考). 然而,它是一个有效的选择,对于 CGImageRef's.

构建设置是什么 COMPRESS_PNG_FILES 在XCode上面提到的,是将.png文件转换为其他文件格式,并且还乘以通道。rgba (参考). 我希望禁用这个选项可以使我的实际.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;
    }

}

其中,"kCGImageAlphaPremultipliedLast "为 "kCGImageAlphaPremultipliedLast"。COMPRESS_PNG_FILES 禁用。所以我认为iOS总是转换.png文件,甚至在运行时。


1
投票

有更好的解决方案,速度更快(约3倍~5倍),而且是跨平台的。

    #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);

0
投票

不是100%你想要的,但我用这个方法解决了这个问题。把阿尔法通道放到一个单独的黑色& 白色的png中,然后保存没有阿尔法的原始png。所以占用的空间是差不多的。然后在我的纹理加载器中加载两张图片并组合成一个纹理。

我知道这只是一个变通的方法,但至少它给出了正确的结果。是的,这是非常恼人的,iOS不允许你从PNG加载纹理,没有预增的alpha。

© www.soinside.com 2019 - 2024. All rights reserved.