iOS上的OpenGL ES纹理加载-我如何从rgb8888 .png文件到RGB565纹理



所以我正在使用一堆2048 × 2048的精灵表,这很快就填满了内存。就这样,我使用以下方法(通过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关于它?

我使用此代码将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;

与其简单地改变颜色空间,我建议在这里使用pvrtc压缩的纹理。这应该比RGB565版本使用更少的gpu内存,因为这些纹理仍然是压缩的,而不是扩展成未压缩的位图。

在iOS OpenGL ES编程指南的"使用纹理数据的最佳实践"一节中,Apple说

纹理压缩通常提供最好的内存平衡节约和质量。iOS版的OpenGL ES支持PowerVR纹理压缩(PVRTC)格式GL_IMG_texture_compression_pvrtc扩展。有两个层次PVRTC压缩,每通道4位和每通道2位,其中在未压缩的32位文件上提供8:1和16:1的压缩比纹理格式。压缩后的PVRTC纹理仍然提供良好的质量水平,特别是在4位级别。

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

,这表明你将得到更小的内存大小的压缩纹理甚至比那些使用更小的颜色空间。

苹果在他们的PVRTextureLoader示例应用程序中有一个很好的例子,我在一个纹理立方体示例应用程序中重用了一些代码来加载PVRTC纹理。您可以查看PVRTextureLoader中的"Encode Images"构建阶段,了解如何在编译时将PNG纹理转换为PVRTC纹理。

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

最新更新