5

我想从 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 通道)
  • 除法后阿尔法
  • 使用其他文件格式

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

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

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

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

4

6 回答 6

4

当您考虑PVRTC 时,可以选择使用GLKit 。这包括GLKTextureLoader,它允许您在没有预乘 alpha 的情况下加载纹理。使用例如:

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

并传递一个选项字典,其中包含:

GLKTextureLoaderApplyPremultiplication = NO
于 2012-11-28T06:38:11.427 回答
3

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

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

在此处输入图像描述

于 2012-11-11T22:55:38.930 回答
3

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

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

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

于 2012-11-27T15:42:18.770 回答
2

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

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

该选项kCGImageAlphaLast是一个有效的选项,但没有为CGBitmapContextCreate(参考) 提供有效的组合。然而,它是CGImageRef's 的有效选项。

上面提到的 XCode 中的构建设置COMPRESS_PNG_FILES所做的是将 .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;
    }

}

COMPRESS_PNG_FILES这给出了禁用的“kCGImageAlphaPremultipliedLast” 。所以我认为 iOS 总是会转换 .png 文件,即使在运行时也是如此。

于 2012-11-18T21:14:22.010 回答
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);
于 2020-05-28T13:28:44.890 回答
0

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

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

于 2012-12-18T13:33:51.450 回答