纹理压缩_unity3d 纹理压缩打包 - CSDN
精华内容
参与话题
  • 在OpenGL中使用纹理压缩

    千次阅读 2012-10-07 09:13:08
    纹理压缩技术已经广泛应用在各种3D游戏之中,它们包括:DXTC(Direct X Texture Compress,DirectX纹理压缩,以S3TC为基础)、S3TC(S3 Texture Compress,S3纹理压缩,仅支持S3显卡)、VTC(Volume Texture ...
    纹理压缩技术已经广泛应用在各种3D游戏之中,它们包括:DXTC(Direct X Texture Compress,DirectX纹理压缩,以S3TC为基础)、S3TC(S3 Texture Compress,S3纹理压缩,仅支持S3显卡)、VTC(Volume Texture Compression,体积纹理压缩)、PTC(Palletized Texture Compression,并行纹理压缩)、VQTC(Vector-Quantization Texture Compression,向量纹理压缩)、YAB
     NCTC(Narrow Channel Texture compression,狭窄通道纹理压缩)、FXT 1。下面,我为各位介绍OpenGL中使用纹理压缩的两种主要方法:ARB纹理技术扩展和S3TC。
    一、概述

      与其它压缩技术一样,ARB可以把高分辨率纹理放入未压缩前只能存储低分辨率纹理的空间,并提供渲染管道优化,特点有:

    - 增加渲染速度
    - 减低纹理内存需求
    - 快速纹理下载到纹理内存
    - 低磁盘存储空间需求和高速磁盘存取
    纹理压缩通常分为纹理生成层和运行时间层二部分,纹理生成又分成两种方法:
    - 通常方法:使用GL来压缩图像,存储到磁盘
    - S3TC DDS文件格式:使用ISV的S3TC压缩工具来处理图像,在GL中载入DDS文件
    运行时间层的主要工作分三步:
    - 从磁盘载入压缩图像
    - 上载压缩图像到GL
    - 不压缩动态纹理

    二、具体过程

      最理想的压缩纹理技术是在游戏的开头画面预压缩所有的纹理,减少磁盘存储需要,加速纹理载入速度。下面是用两种不同的方法处理纹理位图,第一种方法使用OpenGL压缩,第二种使用直接支持OpenGL的ISV S3TC文件格式。
    ARB允许未压缩的纹理通过glTexImage2D呼叫来进行纹理压缩,并设置内部格式参数。图1的上半部分为常规方式,下半部分为S3TC扩展集,基本内部格式通过压缩后将变成压缩内部格式。

    1、常规压缩

      使用代理纹理可以在压缩前指示欲压缩的纹理图像,然后再以glGetTexLevelParameteriv加上$#@60;pname$#@62;到GL_TEXTURE_COMPRESSED_ARB,来检查图像是否进行适当的压缩。如果纹理已经得到正确处理,会返回一个非零参量,若是纹理压缩出错,则变回相应的基本内部格式。

      使用普通压缩内部格式,查询OpenGL会自动选择内部格式作为$#@60;internalFormat$#@62;,具体过程是 鰃lGetTexLevelParameteriv再加上$#@60;pname$#@62;参数,变成GL_TEXTURE_INTERNAL_FORMAT。

      下一步,查询OpenGL会通过glGetTexLevelParameteriv呼叫获得压缩纹理图像的容量,然后用$#@60;pname$#@62;生成GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB,建立返回尺寸的缓冲区。glGetCompressedTexImageARB呼叫可取得压缩纹理数据,再用$#@60;img$#@62;取得缓冲区地址。压缩纹理到磁盘后,OpenGL会把下列属性加入到压缩纹理本身之中,以备运行时间层调用,属性包括:

    - 缓冲区容量
    - 压缩内部格式
    - 宽度
    - 高度
    - 边界$#@60;border$#@62;(非S3TC)
    - 深度$#@60;depth$#@62;(3D纹理和非S3TC技术)

      如果使用S3TC,压缩内部格式完成时,$#@60;border$#@62;将变设置为零。这意味着$#@60;border$#@62;不为零会导致“glCompressedTexImage2DARB”发生“INVALID_OPERATION(内部操作)”错误。若是图像仅适用于2D纹理,$#@60;depth$#@62;与S3TC格式无关,一旦$#@60;internalFormat$#@62;(内部格式)变成了S3TC,“glCompressedTexImage1DARB”和“glCompressedTexImage3DARB”会生成“INVALID_ENUM”错误。

      在另一平台使用压缩纹理,压缩内部格式不属于标准的话,必须让平台支持特殊的格式。比如用
    “glGetIntegerv”呼叫加上“GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB”和“GL_COMPRESSED_TEXTURE_FORMATS_ARB”。下面是一段支持压缩内部格式的代码:

    GLint * compressed_format;
    GLint num_compressed_format;
    glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB, &num_compressed_format);
    compressed_format = (GLint*)malloc(num_compressed_format * sizeof(GLint));
    glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS_ARB, compressed_format);

      S3TC无须列出所支持的格式,也不用特殊的扩展集,因此S3TC不提供“GL_COMPRESSED_RGBA_S3TC_DXT1_EXT”格式,所有变化都会被自动认成是“正常”的RGBA格式。

    随后的语句概括了压缩未压缩图像和把它存储进硬盘的过程:
    glBindTexture(GL_TEXTURE_2D, compressed_decal_map);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_ARB, width, height,
    0, GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels);
    glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_ARB, &compressed);
    /* if the compression has been successful */
    if (compressed == GL_TRUE)
    {
    glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT,
    &internalformat);
    glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB,
    &compressed_size);
    img = (unsigned char *)malloc(compressed_size * sizeof(unsigned char));
    glGetCompressedTexImageARB(GL_TEXTURE_2D, 0, img);
    SaveTexture(width, height, compressed_size, img, internalFormat, 0);
    }

      有多种方法可以评估压缩扩展集的画质,比如:图像质量参数RED_BITS, GREEN_BITS等,或压缩纹理映象,或用glGetTexImage呼叫存取非压缩图像缓冲区。

    2、S3TC DDS文件格式

      ISV工具(如:S3的Adobe PhotoShop Plug-in插件和微软DirectX的Dxtex)可以把普通文件格式转换成dds文件。故名思义,DDS(Direct Draw Surface,直接绘画表面)的基本操作是把内存的数据通过DirectX DDS接口下载到硬盘。DDS文件读取程度包含在DirectX的ddraw.h中,还可以得到DDSURFACEDESC2定义。此外,DirectX的架构与OpenGL不同,其屏幕原始坐标也不一样,DirectX的原始屏幕坐标位于左上角,OpenGL则位于左下角。在转换图像或纹理调整之前,必须改变纹理的垂直方向。

    下面是一段DDS读取程序的源码:

    #include $#@60;ddraw.h$#@62;
    gliGenericImage *
    ReadDDSFile(const char *filename, int * bufsize, int * numMipmaps)
    {
    gliGenericImage *genericImage;
    DDSURFACEDESC2 ddsd;
    char filecode[ 4];
    FILE *fp;
    /* try to open the file */
    fp = fopen(filename, "rb");
    if (fp == NULL)
    return NULL;
    /* verify the type of file */
    fread(filecode, 1, 4, fp);
    if (strncmp(filecode, "DDS ", 4) != 0) {
    fclose(fp);
    return NULL;
    }
    /* get the surface desc */
    fread(&ddsd, sizeof(ddsd), 1, fp);
    genericImage = (gliGenericImage*) malloc(sizeof(gliGenericImage));
    memset(genericImage,0,sizeof(gliGenericImage));
    /* how big is it going to be including all mipmaps? */
    *bufsize = ddsd.dwMipMapCount $#@62; 1 ? ddsd.dwLinearSize * 2 : ddsd.dwLinearSize;
    genericImage-$#@62;pixels = (unsigned char*)malloc(*bufsize * sizeof(unsigned char));
    fread(genericImage-$#@62;pixels, 1, *bufsize, fp);
    /* close the file pointer */
    fclose(fp);
    genericImage-$#@62;width = ddsd.dwWidth;
    genericImage-$#@62;height = ddsd.dwHeight;
    genericImage-$#@62;components = (ddsd.ddpfPixelFormat.dwFourCC == FOURCC_DXT1) ? 3 : 4;
    *numMipmaps = ddsd.dwMipMapCount;
    switch(ddsd.ddpfPixelFormat.dwFourCC)
    {
    case FOURCC_DXT1:
    genericImage-$#@62;format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
    break;
    case FOURCC_DXT3:
    genericImage-$#@62;format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
    break;
    case FOURCC_DXT5:
    genericImage-$#@62;format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
    break;
    default:
    free(genericImage-$#@62;pixels);
    free(genericImage);
    return NULL;
    }
    /* return data */
    return genericImage;
    }

      当DDS文件被看成是MIP映射时,它会压缩所有的映射图像,此问题通常发生于GL在一段时间内取出一个映射的时候。DDS文件的缓冲区读取不能直接由GL控制,你必须精确计算每一个MIP映射在缓冲区的偏移量,传递正确的数据地址和属性给

    glCompressedTexImage2DARB,代码如下:
    /* load the .dds file */
    ddsimage = ReadDDSFile("flowers.dds",&ddsbufsize,&numMipmaps);
    height = ddsimage-$#@62;height;
    width = ddsimage-$#@62;width;
    offset = 0;
    div = (ddsimage-$#@62;format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 1 : 0;
    glBindTexture(GL_TEXTURE_2D, dds_compressed_decal_map);
    /* load the mipmaps */
    for (i = 0; i $#@60; numMipmaps && (width $#@62; 1 && height $#@62; 1); ++i)
    {
    size = width * height;
    size $#@62;$#@62;= div;
    glCompressedTexImage2DARB(GL_TEXTURE_2D, i, ddsimage-$#@62;format, width, height,
    0, size, ddsimage-$#@62;pixels + offset);
    GLErrorReport();
    offset += size;
    width $#@62;$#@62;= 1;
    height $#@62;$#@62;= 1;
    }

      最后,大家会注意到微软的DirectX SDK DXTex工具提供了把立体位图(带MIP映射)压缩到单个DDS文件的能力。因为DirectX围绕一个立体位图需要的所有数据而设计,压缩非常容易实现,而且DDS文件阅读器还能按照位图的不同自由定制。如果一个立体位图与DirectX相匹配,那么,它也能够用在OpenGL身上。

    展开全文
  • 为什么需要纹理压缩

    2019-07-01 16:37:57
    “最近在玩什么游戏,推荐一个”。不管是谁,总会说过或听过这个问题吧。 这时候,你脑海里面浮现的也许是这样的...在软件开发,特别是三维应用中,纹理随处可见,但受限于网络环境和硬件能力,纹理也是一大瓶颈...

    转:https://www.cnblogs.com/fuckgiser/p/5497013.html

    “最近在玩什么游戏,推荐一个”。不管是谁,总会说过或听过这个问题吧。

    这时候,你脑海里面浮现的也许是这样的画面

    1

    或许最终小伙伴们也能接受这样的游戏

    3

    但还是会有一些玩家更怀念这样的游戏

    2

    在软件开发,特别是三维应用中,纹理随处可见,但受限于网络环境和硬件能力,纹理也是一大瓶颈。而且在一般的三维应用中,纹理所占大小基本都会在1/2以上,模型中往往超过2/3。或许你会说,纹理不就是一张图吗,有那么重要吗?

    如下两张对比图,可能你会认为前者逼格高,但对于正常人而言,后者显然要好很多。正是有了纹理,如同在骨架上赋予了皮肤,让我们的应用更加的逼真,贴近现实。

    4

    而你能想象到吗?如上的模型一共有三张纹理,其中之一效果如下:

    5

    看上去怪怪的。其实在纹理的压缩中,人们先想到了如何去除冗余信息,对称的部分只保留一份,尽可能让不同的部分紧凑,充分利用好每一个像素来保存有效数据。得益于对称在大自然中的普遍性,这种方式确实极大的减少了纹理像素。

    纹理的拼接是纹理压缩的开始,采用不同的压缩方式对纹理最终的大小影响也是显著的。比如上面的这张纹理在不同压缩格式下的大小差别也是非常显著的(原始文件为tga格式,通过Photoshop转换为其他格式,默认选项):

    6

    如果按照上面表格的逻辑,用jpg或png格式就好了,无损压缩,而且也是最小的。确实在很多情况下这是一个比较好的选择,比如网络带宽有限,这样可以很好的节约带宽和下载时间,而Bmp,tga,png以及jpg都是无损格式。但这类压缩存在一个致命缺陷,他们都是基于整幅图片下进行的压缩,比如霍夫曼编码等,这样像素和像素之间在解码的过程中存在依赖关系,无法直接实现单个像素级别的解析,这就发挥不了显卡的并发能力,更重要的是问题在于无论是png还是jpeg最终在显存中解码后都是RGBA的纹理格式,因此并无法减少显存的占用率。比如一张256*256的RGBA纹理,无论是png还是jpg格式,虽然文件大小不一样,在显卡中的大小仍然是256*256*4的显存空间。

    正是因为传统的图片格式并没有考虑显卡的这种特性,所以很难满足三维应用中的要求。基于这些问题,如下四点可以作为我们选择纹理压缩格式的衡量标准(引自《Texture Compression using Low-Frequency Signal Modulation》)

    • 解析速度
      在纹理操作中,读取纹理数据是关键步骤,所以解码速度至关重要。

    • 随机读取数据
      能快速的随机读取任意像素

    • 压缩率和纹理质量
      既要保证一个不错的压缩效果,也要把纹理损失控制在一定范围内

    • 压缩速度
      通常纹理压缩在渲染前已经提前准备好,所以如果压缩的速度比解析速度慢,也是可以接受的。

    调色板技术

    最初想到的就是调色板技术,这个思路很简单,在当时硬件能力不高的情况下也非常的好用,类似GIF格式,通常保存一个8位或4位的调色板。为什么没有16位的调色板,因为16位的RGB的效果本身就相对不错,所以16位调色板的意义并不大。如下是调色板原理示意。

    7

    对于纹理中颜色个数不超过256,或者愿意适当删减,将颜色数目控制在256以内的话,调色板还是非常高效的压缩技术,相比RGBA的颜色格式要少87.5%的空间。当然,颜色越丰富,所效果损失越严重。如下是同一张纹理的效果对比:

    8

    调色板方式下还有一个非常明显的优势是风格的变化,只需要更改调色板信息,而不用保存多套纹理,就可以很轻松的实现风格的多样化,这种成本很低,而且还很高效。

    9

    但是显卡中并不支持这种调色板纹理方式,或者只有很老的显卡会支持,当然我们可以采用一维纹理的方式来模拟调色板,但这种情况下不能开启纹理过滤功能,因为调色板的颜色顺序是随机的,在插值过程中和我们预期的效果会有出入。

    10

    可见调色板的使用非常灵活,如果运用得当,很多复杂的问题都可以很好的解决,但毕竟显卡不支持这种纹理方式,而且毕竟也有256颜色的限制,在稍微复杂的情况下就有点捉襟见肘。而在顶点着色器上,每次都要操作两次(获取索引值,读取调色板对应的颜色),而且调色板也需要作为参数,或指定一个全局的调色板,这样就会存在内存和显存之间的频繁切换,从性能的角度来也不是最优方案。

    纹理压缩

    调色板方式有着很多的优点,但也有致命的缺点,在调色板的基础上不断改良,最终形成了现在这种相对平衡的压缩方案。

    11

            首先,意识到有损压缩下的显示效果还是不错的,所以压缩后以16位的颜色格式存储,如上是RGB和16位的对比效果图。再次则是自带“调色板”,化整为零,方便自身携带。

    12

    上图是纹理压缩原理图,对于一张原始纹理,会创建两张小纹理A和B,可以认为是原始纹理的缩略图,同时还有一个矩阵M,M的行列和原始纹理的长宽一致,里面的值类似于调色板中的索引,实现纹理A和纹理B的混合。示意图如下:

    13

    可以说目前主流的纹理压缩格式,比如DXT,PVR和ETC纹理压缩在原理上如出一辙,但在细节上会有很多独特的改善。我们逐个道来,对比其中的优劣。

    DXT

           DXT是一种有损纹理压缩算法,微软的Direct中支持,DXT的格式包括DXT1~DXT5,其中DXT1和DXT5较为多见,后面会做详细讨论。可以说DXT是目前应用最广泛的纹理压缩格式,可以认为所有的PC端显卡都支持DXT压缩,维基百科说记录,该专利有效期到2017年10月2号。

    14

    如上图,DXT的压缩思路也比较一致,有两个Color A(00)/B(11),而4*4矩阵中的索引比较简单,在DXT不同的格式中,差值的因子稍有不同,比如在DXT1中,差值得到的另外两个颜色的公式为:C2=  2/3*C0 + 1/3*C1, C3 =  1/3*C0 + 2/3*C1,这里有一个小技巧,尽管分母是3,但都会近似到2的N次幂,比如2/3约等于5/8,为什么?如下是食人怪纹理的DXT1效果:

    15

    DXT算法非常容易理解,而且整体看上去效果不错,但如果对局部特写,会发现在细节上会有很多丢失,这也是算法本身导致的,毕竟每个块只有两个颜色,而其他颜色都是在这两个颜色区间的差值,如果当前区域内还有其他显著颜色则必然会有丢失。

    16

    这种信息的丢失主要集中在比较细的边界中,但DXT1在压缩率上是RGB的6倍,这种问题可以通过提高纹理分辨率的方式来解决,高宽放大41%,这样整个纹理是以前的2倍,但压缩率还能保持为3倍,也是可以接受的。

    在DXT中还有一个主要的损失,就是RGB的24位转为了16位颜色,16位中R&B各占5位,但是G占了6位,这是因为人眼对绿色最为敏感。

    17

    另外一个问题就是DXT3和DXT5之间的对比,相比DXT1不支持透明度(但支持是否透明),DXT5要大一倍(多了64bit),和之前颜色保存方案一样对透明度也保存了两个16位的颜色和对应的调色板,对RGBA的效果也得到了保证,但DXT3思路不一样,它是对每一个像素保存了4bit的透明度,同样也是多了64bit,但此时毕竟只有16个透明度选项,相比DXT5,在压缩率上相当,但对透明色的处理不够细腻,因此在实用性上并不推荐DXT3。

    尽管DXT在细节上有明显硬伤,在总体效果不错,而且确实是一种强大的压缩方式,所以在多数纹理压缩选择中都是最佳方案,几乎可以认为是PC下的标准压缩格式。

    PVR&ETC

    也许是出于专利和商业角度,也许确实DXT在移动端确实无法满足要求,DXT并没有在移动端得到很大的支持,相反,在iOS设备中支持的是PVR压缩,在Android中支持的是ETC压缩。下面详细介绍一下PVR的压缩和ETC解压缩的过程。

           DXT在细节上缺陷明显,最重要的原因是当把纹理分为4*4像素的区域块后,每个块之间都是独立的,尽管这极大的简化了压缩算法,但却丢失了相邻块之间这种普遍的相似性。这是算法本身导致的,而PVR则会考虑该区域块对应的右侧,下侧和右下侧的三个区域块的关联性。

    18

    如上,是PVR中一个block的结构图,同样的两张Color A/B,这里会有透明模式和非透明模式,这种策略是可取的,首先不用单独增加透明度的字段,尽管透明度的增加会牺牲RGB的质量,但在透明情况下,RGB的作用并不如在不透明情况下那样重要,这种损失也是可以接受并弥补。

    首先,在生成Color A/B时,会对原始纹理做一些处理(最简单的就是翻转或旋转90度),得到一张delta image,这个delta可以用来调整生成的Color A/B,比如最简单的一个方式就是点乘每个delta向量。

    然后基于Color A/B来计算该block中对应的M,相比于DXT1中的1/3和2/3,PVR中对应的值分别为:

    19

    在计算M的过程中,会对已有的Color A/B进行优化,这个过程称为SVD(Singular ValueDecomposition),因为非常耗时,时间复杂度是O(n^3),所以仅对中心块进行优化,而对四周不进行此操作。

    20

    虽然PVR也提供了2bpp(bits perpixel)的格式,但效果很烂,所以基本只有4bpp是可用的。另外因为考虑了区域块之间的相关性,还有SVD算法对效果的优化,不难想象,PVR纹理在压缩时性能慢的难以忍受。但没办法,毕竟PVR是iOS官方支持的纹理压缩,只能忍。

    上面是PVR的压缩过程,下面我们对应的看一下ETC下解压缩的过程。

    21

    如上,是ETC中Color A/B的大致分布(其中一种较为简单的情况,总共两种情况,取决于diffbit是0还是1)。而cw1和cw2对应所需的“调色板”。

    在ETC中,对调色板做了一个优化,下面是索引和值的对应关系:

    22

    这个调色板并不复杂,结合M中对应的索引(2bit),获取每个原始像素对应Color A/B的偏移量

    23

    如上是ETC的解压,至此,我们详细介绍了三种主流的压缩格式,他们之间思路相仿,但具体细节上各有所长,为了压缩的性能,可以说里面有很多小的技巧值得我们借鉴。当然孰优孰劣,每个人或许都会有自己的判断。下面是一个详细的各种压缩格式下效果和压缩比的对比图,各位感兴趣的可以自行对比。

     

    24

    25

     

    总结

    终于写完了,本文主要参考资料和图片来自joostdevblog中对DXT和调色板的详细介绍,以及上面提到的Texture Compression这篇经典论文,以及维基百科中关于DXT纹理压缩,以及OpenGL官网上对ETC纹理的介绍。在学习纹理压缩这个过程中,也在思考这样设计的目的,不同纹理之前的细微区别,究竟是商业因素还是技术问题,导致了这些差异,也在试图总结,这么多的trick他们是怎么想到的,虽然看起来不起眼,但里面都是经验和智慧的结晶,整合在一起确实让人叹服,个人也在不断的梳理方方面面,希望能通过自己的理解,能让大家有条理的,相对深入的了解纹理压缩的原因,更好的理解运用这些技术。当然,难免有出错的地方也希望指正。

    但从现实的角度来看,受制于专利和硬件厂商,我们并没太多选择的余地,Android下就要用ETC,iOS下只能PVR,而在PC上不用DXT估计就要被嘲讽了。但这也是一个很棘手的问题,比如在WebGL下,特别是Android下差异化很大,是否支持纹理压缩,甚至在同一个设备不同的浏览器,因为驱动的不一致,可能系统自带的会支持ETC压缩,而微信等QQ浏览器下并不支持。而且华为的手机貌似在浏览器级别下都不支持ETC(硬件支持,还是驱动的问题)。而如果在移动设备上不用压缩,显存是有限的,除非你在数据量上做出牺牲,怎么解决都很矛盾,相比而言,iOS下则要舒服很多。设备的多样性带来的烦恼,让我觉得乔布斯的伟大之处:优秀的同事不是为了计算机而工作,而是因为计算机是传达某种情感的最佳媒介,他们渴望分享,正是因为这种精神,有些人宁愿做诗人也不愿意做银行家,我想把这种精神溶入产品里,而计算机就是我传达情感的媒介。

    好了,鸡汤不是白喝的,长按下图有奇迹,转发更能保平安~

    展开全文
  • 纹理压缩介绍(转)

    千次阅读 2018-10-08 16:58:01
    1 前言   最近一段时间AR技术成为了时下热门,越来越多的应用开发者投身到这些技术中来...为了解决这个问题,我们使用了纹理压缩技术。使用这个技术可以大幅度的降低APP的内存(共享显存)占用,从而在有限的内存...

    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] identifier
    UInt32 endianness
    UInt32 glType
    UInt32 glTypeSize
    UInt32 glFormat
    Uint32 glInternalFormat
    Uint32 glBaseInternalFormat
    UInt32 pixelWidth
    UInt32 pixelHeight
    UInt32 pixelDepth
    UInt32 numberOfArrayElements
    UInt32 numberOfFaces
    UInt32 numberOfMipmapLevels
    UInt32 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<dataLength)
    {
        uint32_t levelSize = *(uint32_t *)(bytes + dataOffset);
        dataOffset += sizeof(uint32_t);
    
        NSData *mipData = [NSDatadataWithBytes:bytes + dataOffsetlength:levelSize];
        [levelDatasaddObject:mipData];
    
        dataOffset += levelSize;
    
        levelWidth = MAX(levelWidth / 2, 1);
        levelHeight = MAX(levelHeight / 2, 1);
    }
    展开全文
  • Unity3D引擎对纹理的处理是智能的:不论你放入的是PNG,PSD还是TGA,它们都会被自动转换成Unity自己的Texture2D格式。  在Texture2D的设置选项中,你可以针对不同的平台,设置不同的压缩格式,如IOS设置成PVRTC4...

      Unity3D引擎对纹理的处理是智能的:不论你放入的是PNG,PSD还是TGA,它们都会被自动转换成Unity自己的Texture2D格式

      在Texture2D的设置选项中,你可以针对不同的平台,设置不同的压缩格式,如IOS设置成PVRTC4,Android平台设置成RGBA16等。嗯,非常的智能。

      但是,在一些进阶的使用中,一些情况是难以满足的。比如,我们NGUI的图集中,在Android平台,使用ETC1纹理+Alpha通道图的方式;IOS平台,使用PVRTC4的纹理;部分要求清晰度较高的,使用RGBA16,但是使用RGBA16的渐变显示图片却惨不忍睹;一些要求高保真的,则需要直接使用RGBA32格式。有些时候,单纯的Unity纹理管理已经无法满足我们的需求了,需要做一下额外工作。

      总结一下我自己的纹理压缩方案:

    纹理压缩的策略
      手游开发(Android/IOS)中,我会使用3个级别的压缩程度:高清晰无压缩、中清晰中压缩、低清晰高压缩;4种压缩方法:RGBA32, RGBA16+Dithering,ETC1+Alpha,PVRTC4。一般足够应付大部分的需求了。
      高清晰无压缩 - RGBA32

                    
                   Unity RGBA32 - 高清晰无压缩.png

      RGBA32等同于原图了,优点是清晰、与原图一致,缺点是内存占用十分大;对于一些美术要求最好清晰度的图片,是首选。

      要注意一些png图片,在硬盘中占用几KB,怎么在Unity中显示却变大?因为Unity显示的是Texture大小,是实际运行时占用内存的大小,而png却是一种压缩显示格式;可以这样理解,png类似于zip格式,是一个压缩文件,只不过在运行时会自动解压解析罢了。

    中清晰中压缩 - RGBA16 + Dithering
      RGBA16+Dithering

                   
                 Unity RGBA16,不抖动处理的渐变图片惨不忍睹

      既然叫RGBA16,自然就是RGBA32的阉割版。
      对于一些采用渐变的图片,从RGBA32转换成RGBA16,能明显的看出颜色的层叠变化,如上图。

                    

      采用Floyd Steinberg抖动处理后,除非放大,否则肉眼基本看不出区别
      RGBA16的优点,内存占用是RGBA32的1/2;搭配上Dithering抖动,在原尺寸下看清晰度一模一样;缺点,Unity原生不支持Dithering抖动,需要自己做工具对图片做处理;对于需要放大、拉伸的图片,Dithering抖动的支持不好,会有非常明显的颗粒感。

    如何进行Dithering抖动?



      Texture Packer工具中Image Format选择RGBA4444,Dithering选择FloydSteinberg在我的项目中,TexturePacker具有非常重要的作用,像UI的图集生成,预先生成好正方形的IOS PVRTC4图集和非正方形的Android ETC1图集、 缩放原图50%等工作都由TexturePacker完成。

      同样,对图像进行抖动处理,也是预先在TexturePacker使用FloydSteinberg算法进行图像抖动,再在Unity中导入使用。

      TexturePacker提供命令行工具,可以做成自动化的工具。具体方法这里不详述。

    RGB16

                    
                        Unity RGB16

      而RGB16,是主要针对一些,不带透明通道,同时长宽又不是2的次方的图片;对于这些图片,使用RGB16可以降低一半的内存,但是效果会略逊于RGB32。

      当然了,RGB16其实也是可以搭配抖动,也能提升显示效果;但同样的Dithering抖动对拉伸放大是不友好的。

    低清晰高压缩 - ETC1+Alpha/PVRTC4
      很多初学者都会疑惑,为什么游戏开发中经常看到一些图片,需要设置成2的次方?因为像ETC1、PVRTC4等这类在内存中无需解压、直接被GPU支持的格式,占用内存极低,而且性能效率也是最好的。
    但是,相对RGBA32,还是能肉眼看出质量有所下降的。

    ETC1
      ETC1+Alpha一般应用在Android版的UI图集中,ETC1不带透明通道,所以需要外挂一张同样是ETC1格式的Alpha通道图。方法是,在原RGBA32的原图中,提取RGB生成第一张ETC1,再提取A通道,填充另一张ETC1的R通道;游戏运行时,Shader将两张ETC1图片进行混合。

      生成Alpha通道图的方法可参考:
    http://blog.csdn.net/u010153703/article/details/45502895

      要配合ETC1+Alpha,还需要Shader支持,这里提供参考直接修改NGUI的Unlit/Transparent With Colored的Shader。

                
                        Paste_Image.png

    PVRTC4
      PVRTC4在Unity中是直接支持的,不过要注意的细节是,它必须是二次方正方形;也就是说,长宽在二次方的同时,还必须要相等。

    几种纹理格式的对比

    格式 内存占用 质量 透明 二次方大小 建议使用场合
    RGBA32 1 ★★★★★ 无需 清晰度要求极高
    RGBA16+Dithering 1/2 ★★★★ 无需 UI、头像、卡牌、不会进行拉伸放大
    RGBA16 1/2 ★★★ 无需 UI、头像、卡牌,不带渐变,颜色不丰富,需要拉伸放大
    RGB16+Dithering 1/2 ★★★★ 无需 UI、头像、卡牌、不透明、不会进行拉伸放大
    RGB16 1/2 ★★★ 无需 UI、头像、卡牌、不透明、不渐变,不会进行拉伸放大
    RGB(ETC1) + Alpha(ETC1) 1/4 ★★★ 需要二次方,长宽可不一样 尽可能默认使用,在质量不满足时再考虑使用上边的格式
    RGB(ETC1) 1/8 ★★★ 需要二次方,长宽可不一样 尽可能默认使用,在质量不满足时再考虑使用上边的格式
    PVRTC4 1/8 ★★ 需要二次方正方形,长宽一样 尽可能默认使用,在质量不满足时再考虑使用上边的格式
    • 内存占用,相对于RGBA32做比较
    • 质量星级,更多是本人感受,仅供参考

      在项目中,尽可能是使用ETC1和PVRTV4等GPU直接支持的图片格式,不仅内存占用低、性能也更好;当出现质量不及格时,再逐步的提升压缩格式,来满足需要。

      因此,实际项目中要混搭各种纹理格式。

    展开全文
  • 个人理解的 纹理压缩

    千次阅读 2016-12-26 14:57:17
    2. 压缩纹理的必要性 1)首先要说一下图像文件格式和纹理格式的区别。 常用的图像文件格式有BMP,TGA,JPG,GIF,PNG等; 常用的纹理格式有R5G6B5,A4R4G4B4,A1R5G5B5,R8G8B8, A8R8G8B8等。  文件格式是图像为了...
  • 图片纹理压缩方式

    2017-11-29 11:32:03
    Unity3D引擎对纹理的处理是智能的:不论你放入的是PNG,PSD还是TGA,它们都会被自动转换成Unity自己的Texture2D格式。  在Texture2D的设置选项中,你可以针对不同的平台,设置不同的压缩格式,如IOS设置成PVRTC4,...
  • 常用纹理和纹理压缩格式

    万次阅读 2015-04-10 19:12:42
    常用纹理和纹理压缩格式的介绍:RGB/RGBA,DXT1/DXT5、ETC、PVRTC等。
  • 自适应可伸缩纹理压缩(ASTC)是由JørnNystad等人开发的基于块的有损纹理压缩算法。 ARM有限公司和AMD。 ASTC的全部细节首先在Olson等人的论文中在High Performance Graphics 2012会议上公开展示。 标题为“自适应...
  • 细谈纹理压缩格式

    千次阅读 2019-04-22 18:01:19
    我们先不谈纹理压缩在做什么,我们先看下自然状态下没有压缩的纹理的问题 不压缩的纹理有什么问题 目前的渲染管线中,对于3D物体表面的细节,主要还是靠纹理贴图来表现,分辨率越高,精度越高的纹理,在细节表现上...
  • 手游纹理压缩解决方案,保证图片效果的同时减少运行时内存,
  • OpenGL ES 纹理压缩之ETC (一)

    千次阅读 2017-11-13 15:49:50
    纹理压缩之ETC首先来看看一张512像素X 512像素的图片占用的内存大小 512像素X 512像素的纹理图片所占用的内存大小为:512 X 512 X 4(一个像素4个字节) / 1024KB = 1MB 一张512 X 512 (像素)图片在内存将占用了1M ...
  • 移动端纹理压缩格式 设备资源的限制和制作过程中对表现的无限追求永远是矛盾点,不会随设备的发展而转移。纹理压缩就是这种矛盾的一种解决方式,不同于png、jgp这种硬盘压缩方式而言,DXT,ETC等纹理压缩方式可以在...
  • 参考为什么需要纹理压缩移动端纹理压缩格式干货:Unity游戏开发图片纹理压缩方案Creator使用压缩纹理常用纹理和纹理压缩格式移动设备的纹理压缩方案各种移动GPU压缩纹理的使用方法 一、DXT PVR ETC 在软件开发,...
  • Unity - 纹理压缩

    2020-08-03 15:15:24
    一、为何要进行纹理压缩: 纹理显示到屏幕上,需要CPU和GPU协作完成。CPU对资源进行计算解码,然后将纹理数据传输给GPU。GPU通过CPU的数据,将数据渲染到缓冲区。 纹理压缩减少了资源在CPU中进行解压缩的过程。 ...
  • DXT纹理压缩格式解析

    千次阅读 2015-12-05 15:56:13
    我们知道游戏中对于3D物体表面细节... 所以各个平台上都在使用纹理压缩的技术,让纹理贴图在内存占用和显示效果能达到一个尽可能的平衡。在DirectX中,使用一种叫做DXT的纹理压缩技术,目前这种技术被大部分显卡所支持
  • 对cocos creator 2.1.x的项目,分别作了图片压缩,etc1纹理压缩和gzip压缩。 图片压缩: png 使用 pngquant 压缩 jpg 使用 mozjpeg 压缩 etc1纹理压缩 工具 ARM 的 Mali Texture Compression Tool png 生成带...
  • [Unity优化]图片纹理压缩方案

    千次阅读 2017-11-11 18:20:08
    Unity图片纹理压缩方案Unity对纹理的处理是智能的:不论你放入的是PNG,PSD还是TGA,它们都会被自动转换成Unity自己的Texture2D格式。 在Texture2D的设置选项中,你可以针对不同的平台,设置不同的压缩格式,如IOS...
  • OpenGL纹理压缩

    千次阅读 2012-10-07 09:19:32
    一、纹理压缩是专为3D渲染而设计的纹理贴图压缩算法,它与图像压缩算法的区别:  1.解码速度。为了尽可能低的影响实时渲染的效率,压缩纹理的解压速度必须足够快,最好是不需解压就能直接用于渲染。  2.随机访问...
  • Unity3D for Android 纹理压缩支持

    万次阅读 2015-09-18 16:39:31
    首先附图:Unity3D for Android支持的纹理压缩格式 纹理压缩可以通过减少内存来显著地提高OpenGL的性能,使内存使用的效率更高。 Android设备支持很多格式的纹理压缩,这些纹理压缩格式并不是在所有的设备上面...
  • 纹理压缩(Texture compression) -- DDS

    千次阅读 2015-08-19 10:56:43
    原文转载自: 纹理压缩  贴图是在 3D 场景中,增加真实性的一个重要的工具。就像一般的影像一样,贴图的大小愈大,它的图像就愈精细。事实上,贴图往往需要比一般的影像更大。因为,在 3D 场景中,观察者可能会...
1 2 3 4 5 ... 20
收藏数 18,022
精华内容 7,208
关键字:

纹理压缩