【问题标题】:OpenGL ES on iOS texture loading - how do i get from a RGBA8888 .png file to a RGB565 texture?iOS 纹理加载上的 OpenGL ES - 如何从 RGBA8888 .png 文件转换为 RGB565 纹理?
【发布时间】:2012-10-19 06:17:21
【问题描述】:

所以我正在使用一堆 2048x2048 的精灵表,它们很快就会填满内存。照原样,我正在使用以下方法(通过 Ray Wenderlich)来加载纹理:

- (GLuint)setupTexture:(NSString *)fileName 
{    
    CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage;
    if (!spriteImage) 
    {
        NSLog(@"Failed to load image %@", fileName);
        exit(1);
    }
    size_t width = CGImageGetWidth(spriteImage);
    size_t height = CGImageGetHeight(spriteImage);
    GLubyte * spriteData = (GLubyte *) calloc(width*height*4, sizeof(GLubyte));
    CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8,     width*4,CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);    
    CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage);
    CGContextRelease(spriteContext);
    GLuint texName;
    glGenTextures(1, &texName);
    glBindTexture(GL_TEXTURE_2D, texName);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
    GLenum err = glGetError();
    if (err != GL_NO_ERROR)
        NSLog(@"Error uploading texture. glError: 0x%04X", err);    
    free(spriteData);        
    return texName;    
}

所以我的问题是,我如何丢弃 CGImage 中的 alpha 信息,然后将图像“下采样”到每像素更少的位数,最后告诉 OpenGL?

【问题讨论】:

    标签: ios opengl-es textures


    【解决方案1】:

    我使用这段代码将CGContextDrawImage写入的数据转换为RGB565。它使用优化的 NEON 代码在支持 NEON 的设备上一次处理 8 个像素(每个运行 armv7 代码的 iOS 设备都有这样的芯片)。你看到的data指针和你的spriteData指针一样,我只是懒得重命名。

    void *temp = malloc(width * height * 2);
    uint32_t *inPixel32  = (uint32_t *)data;
    uint16_t *outPixel16 = (uint16_t *)temp;
    uint32_t pixelCount = width * height;
    
    #ifdef __ARM_NEON__
    for(uint32_t i=0; i<pixelCount; i+=8, inPixel32+=8, outPixel16+=8)
    {
        uint8x8x4_t rgba  = vld4_u8((const uint8_t *)inPixel32);
    
        uint8x8_t r = vshr_n_u8(rgba.val[0], 3);
        uint8x8_t g = vshr_n_u8(rgba.val[1], 2);
        uint8x8_t b = vshr_n_u8(rgba.val[2], 3);
    
        uint16x8_t r16 = vmovl_u8(r);
        uint16x8_t g16 = vmovl_u8(g);
        uint16x8_t b16 = vmovl_u8(b);
    
        r16 = vshlq_n_u16(r16, 11);
        g16 = vshlq_n_u16(g16, 5);
    
        uint16x8_t rg16 = vorrq_u16(r16, g16);
        uint16x8_t result = vorrq_u16(rg16, b16);
    
        vst1q_u16(outPixel16, result);
    }
    #else                
    for(uint32_t i=0; i<pixelCount; i++, inPixel32++)
    {
        uint32_t r = (((*inPixel32 >> 0)  & 0xFF) >> 3);
        uint32_t g = (((*inPixel32 >> 8)  & 0xFF) >> 2);
        uint32_t b = (((*inPixel32 >> 16) & 0xFF) >> 3);
    
        *outPixel16++ = (r << 11) | (g << 5) | (b << 0);               
    }
    #endif
    
    free(data);
    data = temp;
    

    【讨论】:

    • uint8x8_t 类型定义在arm_neon.h
    • 太棒了!必须将 glTexImage2D 行更改为以下参数:glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, spriteData);
    • 很好的答案,你看不到很多 NEON 代码。为了更加与硬件无关,您是否考虑过使用 Accelerate 框架来实现这一点?
    • @BradLarson 是的,这是我的第一次尝试,但对我来说加速框架使用了更神秘的名称,我不知道该寻找什么。我还想针对 iOS 3.0,因此我决定“直接”使用 NEON,因为它也可以在 iOS 3.x 上运行。
    【解决方案2】:

    我可能会建议在此处使用 PVRTC 压缩纹理,而不是简单地更改色彩空间。这应该比 RGB565 版本在 GPU 上使用更少的内存,因为这些纹理保持压缩状态,而不是扩展为未压缩的位图。

    在 iOS 的 OpenGL ES 编程指南的“Best Practices for Working with Texture Data”部分中,Apple 表示

    纹理压缩通常提供最佳的内存平衡 节省和质量。适用于 iOS 的 OpenGL ES 支持 PowerVR 纹理 通过实现压缩 (PVRTC) 格式 GL_IMG_texture_compression_pvrtc 扩展。有两个级别 PVRTC 压缩,每通道 4 位和每通道 2 位, 在未压缩的 32 位上提供 8:1 和 16:1 的压缩比 纹理格式分别。压缩的 PVRTC 纹理仍然提供 质量不错,尤其是在 4 位级别。

    如果您的应用程序不能使用压缩纹理,请考虑使用 较低精度的像素格式。

    这表明压缩纹理的内存大小会比使用更小的颜色空间的纹理更小。

    Apple 在他们的PVRTextureLoader 示例应用程序中有一个很好的例子,我重用了其中的一些代码在textured cube sample application 中加载 PVRTC 纹理。您可以查看 PVRTextureLoader 中的“编码图像”构建阶段,了解如何在编译时编写将 PNG 纹理转换为 PVRTC 纹理的脚本。

    【讨论】:

    • 您的回答鼓励我再次查看 PVRTexturLoader 示例。前一天晚上我曾尝试让它与我的纹理一起工作,但失败了,但我现在已经让它工作了。巨大的内存占用改进!谢谢拉森先生
    【解决方案3】:

    查看 internalFormat 参数。或许您可以指定 GL_R3_G3_B2 每个像素仅使用 8 位,或者 GL_RGB5 可能有用。有几个选项可能有用。

    更多信息请参见Texture Internal Formats

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-02-19
      • 2016-08-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-02
      相关资源
      最近更新 更多