知方号

知方号

纹理压缩介绍(转)

纹理压缩介绍(转)

1 前言

 

最近一段时间AR技术成为了时下热门,越来越多的应用开发者投身到这些技术中来。应用中出现了3D的AR场景,图形学也成为了必备的技术基础。在开发过程中,往往为了追求更好的效果而使用了更加高清的素材,使得本就内存吃紧的手机面对更加严峻的挑战,尤其是对iOS开发者而言。

为了解决这个问题,我们使用了纹理压缩技术。使用这个技术可以大幅度的降低APP的内存(共享显存)占用,从而在有限的内存限制下,使用更丰富的素材。

 

2 什么是纹理压缩

 

常见的图片文件格式,比如PNG,JPG,BMP等,是图像为了存储信息而使用的对信息的特殊编码方式。它存储在磁盘中,或者内存中,但是并不能被GPU所识别。

这些文件格式当被读入后,还是需要经过CPU解压成bitmap,再传送到GPU端进行使用。

纹理格式是能被GPU所识别的像素格式,能被快速寻址并采样。压缩纹理,是一种GPU能直接读取并显示的格式,使得图像无需解压即可进行渲染,节约大量的内存。

 

3 常见的压缩纹理格式

 

3.1 DXT

DXT纹理压缩格式来源于S3(Silicon & Software Systems)公司提出的S3TC(S3 Texture Compression),基本思想是把4x4的像素块压缩成一个64或128位的数据块,是有损压缩方式。DXT1-DXT5是S3TC算法的五种变化,用于各种Windows设备。

 

压缩率:DXT1,DXT4,DXT5为4:1,DXT2、DXT3为2:1

主要支持Windows平台及Tegra系列的GPU的Android手机

支持GPU:

 

3.2 ETC

Ericsson Texture Compression,是由 Khronos 支持的开放标准,在移动平台中广泛采用。它是一种为感知质量设计的有损算法,其依据是人眼对亮度改变的反应要高于色度改变。类似于DXT,ETC也是把4x4的像素块压缩成一个64或128位的数据块,也是有损压缩。

 

这个系列,可以说是适用机型最广的格式。

ETC1支持几乎所有市面上的Android机,所有iPhone

ETC2支持大部分高端Android机,iPhone 5S及以上

3.3 PVRTC

PowerVR Texture Compression,PVRTC格式与基于块的压缩格式,比如S3TC、ETC的不同之处是,它使用2张双线性放大的低分辨率图,根据精度和每个像素的权重,融合到一起来呈现纹理,并且2-bpp和4-bpp都支持ARGB数据。PVRTC格式压缩比较高,也是有损压缩。

这个系列,是iPhone支持最广的格式

只支持长宽相等且为2的幂次方的纹理

支持部分Android机(GPU:PowerVR系列),iPhone全系列机型

支持的GPU

 

3.4 ASTC

ASTC(Adaptive Scalable Texture Compression,自适应扩展纹理压缩),这是ARM提出的,去年被Khronos组织认可,纳入到标准中来,不过并不是强制性的

有多种压缩方式可选,具有不同的压缩率

这个系列,可以说是综合性能和使用便捷性最好的系列。

支持部分高端Android机型,iPhone6及以上机型

 

4 主要优缺点

 

在几乎不损害图片质量和显示性能的情况下,大幅度降低内存(显存)开销,纹理压缩就是这样的一个技术。

不过,任何的技术都有其适用范围和优缺点,需要仔细评估再决定。

4.1 主要优点

占用内存(显存)大幅度降低

无额外性能开销

使用方便,只需少量代码

4.2 主要缺点

硬件相关,要考虑兼容性

压缩纹理文件大小比常规PNG和JPG文件大

需要额外的制作工具,无法直接在移动端生成

 

5 如何使用压缩纹理

 

5.1 保存格式

压缩纹理是图片数据的一种编码方式,我们还缺少一个容器去承载。就像MP4文件是H264的视频的容器一样。

我们选择了使用KTX的格式。

KTX是一个为OpenGL和OpenGLES程序设计的纹理存储格式。它可以简单的辨别里面所存储的纹理格式和其他相关信息。

5.2 文件结构

 

Byte[12] identifierUInt32 endiannessUInt32 glTypeUInt32 glTypeSizeUInt32 glFormatUint32 glInternalFormatUint32 glBaseInternalFormatUInt32 pixelWidthUInt32 pixelHeightUInt32 pixelDepthUInt32 numberOfArrayElementsUInt32 numberOfFacesUInt32 numberOfMipmapLevelsUInt32 bytesOfKeyValueData for each keyValuePair that fits in bytesOfKeyValueData UInt32 keyAndValueByteSize Byte keyAndValue[keyAndValueByteSize] Byte valuePadding[3 - ((keyAndValueByteSize + 3) % 4)]end for each mipmap_level in numberOfMipmapLevels* UInt32 imageSize; for each array_element in numberOfArrayElements* for each face in numberOfFaces for each z_slice in pixelDepth* for each row or row_of_blocks in pixelHeight* for each pixel or block_of_pixels in pixelWidth Byte data[format-specific-number-of-bytes]** end end end Byte cubePadding[0-3] end end Byte mipPadding[3 - ((imageSize + 3) % 4)]end 5.3 使用KTX格式

 

typedef struct __attribute__((packed)){ uint8_t identifier[12]; uint32_t endianness; uint32_t glType; uint32_t glTypeSize; uint32_t glFormat; uint32_t glInternalFormat; uint32_t glBaseInternalFormat; uint32_t width; uint32_t height; uint32_t depth; uint32_t arrayElementCount; uint32_t faceCount; uint32_t mipmapCount; uint32_t keyValueDataLength;} KTXHeader; KTXHeader *header = (KTXHeader *)[data bytes];BOOL endianSwap = (header->endianness == 0x01020304);self.width = endianSwap ? CFSwapInt32(header->width) : header->width;self.height = endianSwap ? CFSwapInt32(header->height) : header->height;self.internalFormat = endianSwap ? CFSwapInt32(header->glInternalFormat) : header->glInternalFormat;uint32_t mipCount = endianSwap ? CFSwapInt32(header->mipmapCount) : header->mipmapCount;uint32_t keyValueDataLength = endianSwap ? CFSwapInt32(header->keyValueDataLength) : header->keyValueDataLength;const uint8_t *bytes = [data bytes] + sizeof(KTXHeader) + keyValueDataLength;constsize_tdataLength = [data length] - (sizeof(KTXHeader) + keyValueDataLength);NSMutableArray *levelDatas = [NSMutableArrayarrayWithCapacity:MAX(mipCount, 1)];const uint32_t blockSize = 16;uint32_t dataOffset = 0;uint32_t levelWidth = self.width, levelHeight = self.height;while (dataOffset

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至lizi9903@foxmail.com举报,一经查实,本站将立刻删除。

上一篇 没有了

下一篇没有了