想象你是“图形搬运公司”的统调师,负责把游戏场景里上千张贴图从磁盘仓库搬进显卡的显存阁楼。通往阁楼的独木桥(内存带宽)每次能搬的行李有限,头顶还有帧预算的倒计时在滴答作响。好在你手下有四位神级收纳师:把衣服按四件一摞叠放的“块压匠”、手持伸缩收纳盒的“灵活姐”、捧着物业统一标准箱的“标准哥”,以及抱着祖传木箱的“老牌伯”。他们的工具箱,就是图形学里的纹理压缩技术——ASTC、ETC2、BC系列。这篇文章带你认识这四位,学会在搬家前选出最聪明的打包方案,让有限显存装下整个世界。

岗位关联

无论你是做AI驱动的3D资产生成、神经渲染,还是实时可视化应用,模型输出最终都要“搬进”GPU。不懂纹理压缩,管线性能就是一句空话——这是从Demo走向商业级产品的必修课。

目录
  1. 可视化演示
  2. 压缩原理:收纳师的叠衣智慧
  3. 纹理格式:行李的材质与规格
  4. ASTC格式:灵活姐的伸缩收纳盒
  5. ETC2格式:标准哥的必选通行证
  6. BC系列:老牌伯的祖传木箱
  7. 选型策略:搬家前的决策
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示Dijkstra算法在加权图上寻找最短路径的过程,从源点S出发逐步扩展已访问节点集合,松弛边权重更新距离,最终找到到终点T的最短路径。

本节配有交互动效(进入视口后自动加载,约 3KB)。

Dijkstra算法 蓝色节点已访问,红色为当前处理节点 绿色路径为最终最短路径 d[v] = min(d[v], d[u]+w(u,v))

压缩原理:收纳师的叠衣智慧

搬家前夜,收纳师阿智走进你的衣柜。他扫了一眼,说:“你这里有十件白T恤,版型一样,就是领口污渍深浅不同。不用十件全带走——每四件叠成一摞,留一件当'代表',其他按它的颜色报数就行。”你担心衣服会不会混了,他笑了:“远看根本分不出来。这是'有损'打包法,省下的空间足够你多装三双鞋。”

纹理压缩的核心,正是这种近似替换。一张纹理图片里,相邻像素的颜色往往高度相似——这在图形学里叫空间冗余。与其把每个像素的精确颜色都存下来,不如把像素切成固定大小的块,每块只存少量代表色,其余像素就近取色。人眼对高频色彩差异本来就不敏感,所以观感几乎无损。块压缩也因此成为GPU纹理的通行方案:以4×4像素为一块,每块独立编码,GPU可以像查字典一样随机访问任意一块,而不用像看PNG/JPEG那样必须把整张图先解码出来。这是纹理压缩与普通图片压缩最本质的区别——它牺牲了压缩率,换来了GPU最需要的“随取随用”。

当然,有损必然有代价。块内代表色越少,压缩率越高,色块边界就越明显,产生俗称的“块效应”。如何权衡,正是接下来所有格式的出发点。

阿智的叠衣法把行李箱体积压到原来的1/8,代价只是衣服上多了几条褶皱——灯光暗下来根本看不见。这就是纹理压缩在绝大多数画面里的真实写照。

压缩率=W×H×bpp原始(W×H16)×B8=16×bpp原始B\text{压缩率} = \frac{W \times H \times \text{bpp}_{原始}}{(\frac{W \times H}{16}) \times \frac{B}{8}} = \frac{16 \times \text{bpp}_{原始}}{B}
这个公式把“压缩率”翻译成一句话:原始数据规模除以压缩后数据规模。分子是一张纹理未压缩时的字节数,分母是块压缩后的字节数。
  1. 分子(原始大小)纹理总像素数乘以每像素字节数。RGBA8格式每像素4字节,其中RGBA8的"8"指每个通道8位。
    W×H×bpp原始W \times H \times \text{bpp}_{原始}
  2. 块数总像素数除以每块像素数。4×4块覆盖16像素,所以块数 = 总像素数 ÷ 16。
    W×H16\frac{W \times H}{16}
  3. 分母(压缩后大小)每块位数B除以8换成字节,再乘块数。BC1每块64位=8字节,ASTC每块128位=16字节。
    W×H16×B8\frac{W \times H}{16} \times \frac{B}{8}
  4. 化简结果约掉W×H,压缩率只取决于原始位率和每块位数。BC1压RGBA8时,16×32÷64=8,即8:1。
    16×bpp原始B\frac{16 \times \text{bpp}_{原始}}{B}

纹理格式:行李的材质与规格

阿智又问:“这些衣服都是什么材质?”你愣住。他解释:真丝旗袍(带透明纱的RGBA纹理)和纯棉T恤(不透明的RGB纹理),打包方式完全不同;丝绸包袱皮上绣的花(高位深色彩)不能压太狠,粗帆布则随便折腾。要决定怎么压缩,先得看清行李的原始规格。

这里的“原始规格”就是纹理格式。最常见的两种底层存储:RGB8每像素24位、三个通道,完全不透明;RGBA8多了一个8位的alpha通道记录透明度,每像素32位。位深决定每个通道的色彩级数——8位=256级,16位=65536级,后者用于HDR光照贴图时体积直接翻倍。这些数字看着枯燥,但当你把开关放在2048×2048的RGBA8纹理上,16MB的显存开销瞬间就突出来了:一个开放世界关卡同时驻留500张贴图,就是8GB,还没算mipmap额外1/3的开销。

带宽上的压力同样不容小觑。GPU每渲染一帧都在从显存读取纹理,采样次数乘上纹理数据量就是帧级带宽消耗。高分辨率手机屏的采样率极高,带宽往往比显存容量更先撞墙。所以,压缩的第一个目的就是给带宽卸担子。

摸清每件行李的材质和规格,收纳师才知道该用几分力气。格式表,就是压缩之前的“盘点清单”。

内存占用(Bytes)=W×H×bpp8\text{内存占用(Bytes)} = W \times H \times \frac{\text{bpp}}{8}
这是所有显存优化的“盘点公式”:宽、高、位深三样乘起来,再除以8从位换字节。
  1. 总像素数宽度乘高度。2048×2048 = 4,194,304。
    W×HW \times H
  2. 每像素位数RGBA8为32位,RGB8为24位,RGBA16F为64位。
    bpp\text{bpp}
  3. 换算字节除以8。4,194,304 × 32 ÷ 8 = 16,777,216 字节。
    W×H×bpp8\frac{W \times H \times \text{bpp}}{8}
  4. 直观口径16,777,216 字节 ÷ 1024 ÷ 1024 ≈ 16MB。位深翻倍,显存翻倍。
    16MB→bpp×232MB16\text{MB} \xrightarrow{\text{bpp}\times 2} 32\text{MB}

ASTC格式:灵活姐的伸缩收纳盒

第二天清晨,一阵雷厉风行的脚步声传来。一位女士把工具箱往地上一放,掏出一整套神秘的伸缩收纳盒:“我的盒子从4×4厘米到12×12厘米有19种尺寸,每盒容量固定一样。衣服少、料子精贵的,用小盒;衣服多、料子糙的,用大盒压紧。整个手机小区的物业都认我这套。”她叫灵活姐,真名ASTC——自适应可扩展纹理压缩。

灵活姐的绝活在于把块尺寸的选择权交给了开发者。传统格式块大小固定,而ASTC提供了从4×4到12×12共19档选择,每一块的数据量恒定128位。块选得越大,摊到每个像素上的位数越少,压缩比越高。4×4块是8bpp,6×6块约3.56bpp,8×8块2bpp,12×12块约0.89bpp。于是“衣柜塞不下”从一道难题变成了一道选择题:UI贴图用4×4保清晰,大尺度地形用12×12省显存。

ASTC不止会伸缩。它还同时支持RGBA四通道和HDR半浮点数据,从UI到光照贴图全覆盖。更关键的是,它已纳入OpenGL ES 3.2规范,又是iOS(A8芯片起)的默认格式,Adreno、Mali、Apple等主流移动GPU全部内置硬件解压单元——说它是移动端事实标准毫无水分。

灵活姐的伸缩盒子,让“阁楼不够装”变成了一道简单的乘法题——选对盒子,手机的小阁楼也能装下整个衣帽间。

bpp=128Wblock×Hblock\text{bpp} = \frac{128}{W_{block} \times H_{block}}
ASTC的位率公式:每一块固定带128位数据,块面积越大,每个像素分到的位数越少。
  1. 分子128ASTC每个压缩块固定携带128位,这是它的“盒内容量”。
    128 bits128\ \text{bits}
  2. 分母块面积4×4块覆盖16像素,12×12块覆盖144像素。
    Wblock×HblockW_{block} \times H_{block}
  3. 相除得bpp128÷16=8,128÷144≈0.89。
    12816=8,128144≈0.89\frac{128}{16}=8, \quad \frac{128}{144}\approx 0.89
  4. 工程换算同一张1024²纹理,4×4档占1MB,12×12档仅占114KB,相差近9倍。
    10242×8÷8=1MB,10242×0.89÷8≈114KB1024^2 \times 8 \div 8 = 1\text{MB}, \quad 1024^2 \times 0.89 \div 8 \approx 114\text{KB}

ETC2格式:标准哥的必选通行证

物业管理员这时拍板了:“不管你们用什么盒子,移动小区每户的货架都必须能打开标准哥的箱子。”角落里一直沉默的“标准哥”终于抬起头。他的箱子不如灵活姐花哨,规格只有两种,尺寸统一,但物业规定所有楼栋的货架(GPU驱动)都必须配备他的开箱工具——对玩家而言,这意味着无论什么手机都能解包。

标准哥的原型是ETC2(Ericsson Texture Compression 2),OpenGL ES 3.0规范中的强制格式。它的前辈ETC1发布于2005年,不支持alpha通道,只有4个亮度方向的编码模式,画面质量一般。ETC2在ETC1基础上增加了多种精确色彩模式,补齐了alpha支持,并向后兼容ETC1——能解ETC2的设备一定能解ETC1,这让老资产的迁移成本几乎为零。

规格上ETC2非常清晰:4×4像素块,RGB模式每块64位(4bpp),RGBA模式每块128位(8bpp)。从2013年之后的移动设备,无论安卓还是iOS基本都内置硬件解码器。对开发者,它的最大价值是“无需判断机型”的兼容性兜底:哪怕设备不支持ASTC,ETC2也能保证游戏跑起来。代价是压缩率固定、不支持HDR,图像质量上限明显低于ASTC。

标准哥的箱子虽不惊艳,却是移动小区盖章认证的“万能钥匙”——新户老户,打不开它,就甭想进门。

ETC2 RGB: 64 bits4×4 pixels=4 bpp\text{ETC2 RGB: } \frac{64\ \text{bits}}{4 \times 4\ \text{pixels}} = 4\ \text{bpp}
标准哥的箱子规格恒定:64位装16个像素的RGB,不多不少。
  1. 固定位数ETC2的RGB块固定64位,这是它的“箱体容量”。
    64 bits64\ \text{bits}
  2. 块面积4×4像素覆盖16个像素。
    4×4=16 pixels4 \times 4 = 16\ \text{pixels}
  3. 位率64÷16=4,即每像素4位,为RGBA8的1/8。
    6416=4 bpp\frac{64}{16} = 4\ \text{bpp}
  4. RGBA变体加入alpha后块变128位,位率变8bpp,压缩率降为4:1。
    12816=8 bpp\frac{128}{16} = 8\ \text{bpp}

BC系列:老牌伯的祖传木箱

把目光投向PC和主机小区,物业指着一位老师傅:“他祖传的木箱在这里用了二十多年,微软、索尼、任天堂的楼都认。”老牌伯笑眯眯地从仓库拖出一排木箱,箱体刻着BC1到BC7的编号。每级木箱的用料和隔板设计都不一样——这是BC系列,前身是S3公司1999年推出的DXT,随DirectX成为PC和主机平台默认选项。

BC系列的家族谱系井井有条。BC1(即DXT1)最经典:两个16位RGB565端色,加16个2位索引,共64位、4bpp,支持1位alpha,只区分“透明/不透明”。BC2和BC3把alpha分别扩展到4位和8位,每块128位、8bpp。BC5专为双通道设计,是法线贴图的绝配;BC6H面向HDR环境贴图;BC7用复杂的模式选择机制逼近无损质量,常用于高精度漫反射材质。这套体系在PC图形API中地位牢固,但离开PC小区就寸步难行——绝大多数移动GPU没有BC系列的硬件解码单元,反过来,ASTC/ETC2在传统PC驱动上同样是“陌生人”。

老牌伯的木箱样式老旧,但每一块板、每一条槽都有二十年经验沉淀——在PC大宅里搬重物,老手艺依然最稳妥。

BC1: 2×16 bits+16×2 bits=64 bits\text{BC1: } 2 \times 16\ \text{bits} + 16 \times 2\ \text{bits} = 64\ \text{bits}
BC1的账本:两个端色各16位,16个像素各2位索引,加起来就是一个4×4块的家当。
  1. 端色块内记录两个16位基色,RGB565格式,一亮一暗。
    2×16=32 bits2 \times 16 = 32\ \text{bits}
  2. 索引16个像素各占2位,取值0~3,表示端色之间的插值比例。
    16×2=32 bits16 \times 2 = 32\ \text{bits}
  3. 求和32+32=64位,除以16像素得4bpp。
    6416=4 bpp\frac{64}{16} = 4\ \text{bpp}
  4. 解码GPU先恢复两个端色,再按索引在亮暗色间线性插值,一次还原整块16像素。
    C=Cdark+index3×(Clight−Cdark)C = C_{dark} + \frac{index}{3} \times (C_{light} - C_{dark})

选型策略:搬家前的决策

搬家日清晨,你站在阁楼门口,手里攥着四位收纳师的报价单。同一件真丝旗袍,灵活姐说用小盒保平整,标准哥说用标准箱虽然皱了点但保通行,老牌伯说他那BC7的匣子在PC家是极好的……你终于顿悟:没有最好的箱子,只有最合适的。

选型本质是“目标平台×纹理类型×质量控制”的三角权衡。国际惯例写成一条决策链:移动端优先ASTC,6×6或8×8档位在体积与质量之间取平衡;老设备(仅支持OpenGL ES 2.0/3.0)用ETC2兜底;PC和主机用BC7保质量,兼容性优先时退到BC3/BC1。法线贴图在PC端优先BC5——双通道设计专门保护法线朝向精度,避免光照出现“麻点”;移动端则用ASTC配合质量优先模式。UI贴图是高危区,文字和图标边缘经不起强压缩,建议ASTC 4×4或BC7,且mipmap必须一起压缩,否则远处的纹理采样会出现色块跳变。

工程链路也别忽略:PVRTexTool、compressonator、astcenc等工具链的版本差异直接影响压缩耗时。CI构建时对非关键贴图可以用更高压缩档位,把打包时间从小时级压到分钟级。

搬家结束,四位收纳师的箱子各就各位:真丝旗袍在灵活姐的小盒里毫发无损,日常T恤进了标准哥的箱子,祖传老家具由老牌伯扛上了阁楼。新家的每个角落,都刚刚好。

选型优先级: 移动端:ASTC(6×6,8×8)≻ETC2;PC/主机:BC7≻BC3≻BC1\text{选型优先级: } 移动端: ASTC(6\times6,8\times8) \succ ETC2 \quad;\quad PC/主机: BC7 \succ BC3 \succ BC1
一句话口诀:移动端先想ASTC,不行退ETC2;PC端先想BC7,速度优先用BC1。
  1. 移动端首选ASTC 6×6≈3.56bpp,8×8=2bpp,质量体积兼顾。
    ASTC6×6≈3.56 bpp,ASTC8×8=2 bppASTC_{6\times6} \approx 3.56\ \text{bpp}, \quad ASTC_{8\times8} = 2\ \text{bpp}
  2. 兜底方案ETC2固定4bpp,兼容所有OpenGL ES 3.x设备。
    ETC2=4 bppETC2 = 4\ \text{bpp}
  3. PC/主机首选BC7用8bpp换最接近无损质量;开发期可先用BC1快速迭代。
    BC7=8 bpp,BC1=4 bppBC7 = 8\ \text{bpp}, \quad BC1 = 4\ \text{bpp}
  4. 核心准则选型不是选“性能最强”,而是选“目标设备上不出错且视觉达标”的方案。
    兼容性∧视觉质量=最优解\text{兼容性} \wedge \text{视觉质量} = \text{最优解}

AI 应用场景

移动游戏性能优化

阶段:资产生产与交付(技术美术 / 渲染性能调优)

解决的问题:手游安装包体膨胀、低端机显存不足。传统做法直接把PNG或未压缩RGBA打进包体,包体达数GB,低端安卓机频繁闪退。用ASTC 6×6批量压缩后,包体降约60%,显存占用与内存压力同步大幅缓解。

具体案例:某卡牌手游用ASTC 4×4打包UI图集、8×8打包场景大图后,安装包从1.8GB降到680MB,低端安卓机首帧加载时间从8秒缩短到3秒。素材预处理由Python脚本驱动,在CI流水线中自动转换。

云游戏与云渲染带宽优化

阶段:云端渲染与流送管道

解决的问题:云游戏在服务器端渲染,纹理上传到GPU的带宽受限于服务器内存总线和虚拟化开销。将高精度BC7转成ASTC后,可降低单实例显存占用与带宽压力,直接改善并发密度。

具体案例:某云游戏平台将500张PBR贴图从BC7(8bpp)转为ASTC 6×6(3.56bpp),单实例显存占用下降55%,帧率因带宽缓解提升12%。转换后通过脚本对比压缩前后PSNR,确保视觉差异在阈值内。

神经辐射场资产烘焙

阶段:NeRF/3DGS训练后烘焙与轻量化

解决的问题:AI生成的3D资产以多通道贴图形式输出,直接进PC显存占用巨大。利用BC5压缩法线分量、BC7压缩反照率,可将NeRF烘焙纹理体积压缩至原来的1/3,加载速度显著提升。

具体案例:电商3D展示项目将NeRF重建的物体烘焙为512×512×4通道纹理,PC端用BC7保持视觉一致,加载时间从5秒降到1.2秒。移动端预览则自动切换ASTC 6×6,实现“一次烘焙、多端交付”。

Python 演示

python90 行
"""纹理压缩格式对比与简单块压缩演示""" import mathimport random  def texture_size(width, height, bpp):    """计算纹理内存占用, 单位字节"""    return int(width * height * bpp / 8)  def format_info(width, height):    """输出常见纹理格式的内存占用对比"""    formats = {        "RGBA8 未压缩": 32,        "BC1/DXT1": 4,        "BC3/DXT5": 8,        "ETC2 RGB": 4,        "ETC2 RGBA": 8,        "ASTC 4x4": 8,        "ASTC 6x6": 3.56,        "ASTC 8x8": 2.0,        "ASTC 12x12": 0.89,    }    print(f"{'格式':<14} {'bpp':>6} {'内存占用':>12} {'压缩率':>8}")    print("-" * 50)    orig = texture_size(width, height, 32)    for name, bpp in formats.items():        size = texture_size(width, height, bpp)        ratio = orig / size if size > 0 else 0        print(f"{name:<14} {bpp:>6.2f} {size:>12,} B {ratio:>7.1f}x")  def simple_block_compress(pixels, block_size=4):    """演示块压缩核心思想: 每块保留2个代表色, 其余像素就近取色"""    height = len(pixels)    width = len(pixels[0])    out = [[(0, 0, 0)] * width for _ in range(height)]    blocks_y = height // block_size    blocks_x = width // block_size    for by in range(blocks_y):        for bx in range(blocks_x):            block = []            for dy in range(block_size):                for dx in range(block_size):                    block.append(pixels[by * block_size + dy][bx * block_size + dx])            # 用平均色生成亮暗两个代表色            avg_r = sum(p[0] for p in block) // len(block)            avg_g = sum(p[1] for p in block) // len(block)            avg_b = sum(p[2] for p in block) // len(block)            colors = [                (min(255, avg_r + 20), min(255, avg_g + 20), min(255, avg_b + 20)),                (max(0, avg_r - 20), max(0, avg_g - 20), max(0, avg_b - 20)),            ]            for dy in range(block_size):                for dx in range(block_size):                    px = block[dy * block_size + dx]                    best = min(colors, key=lambda c: (                        (c[0] - px[0]) ** 2 + (c[1] - px[1]) ** 2 + (c[2] - px[2]) ** 2                    ))                    out[by * block_size + dy][bx * block_size + dx] = best    return out  def main():    width, height = 2048, 2048    print("=== 纹理压缩格式对比 (2048x2048) ===")    format_info(width, height)     print("\n=== 简单块压缩演示 (16x16随机纹理, 4x4块) ===")    random.seed(42)    pixels = [[(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))               for _ in range(16)] for _ in range(16)]    compressed = simple_block_compress(pixels, block_size=4)    total_err = 0    for y in range(16):        for x in range(16):            orig = pixels[y][x]            comp = compressed[y][x]            total_err += sum((orig[i] - comp[i]) ** 2 for i in range(3))    mse = total_err / (16 * 16 * 3)    psnr = 10 * math.log10(255 * 255 / mse) if mse > 0 else 99    print(f"MSE误差: {mse:.2f}, PSNR: {psnr:.2f} dB")    print("说明: 真实BC1用2个端色加调色板插值, 此处用平均色做教学演示")  if __name__ == "__main__":    main()
目的:展示常见纹理格式的显存占用对比,并用一个简化版块压缩演示“每块保留代表色”这一核心思想。
  1. def texture_size(width, height, bpp)实现“盘点公式”:宽乘高乘位率再除8,得到字节数。
  2. formats = {...}内置九种格式的bpp表,从RGBA8到ASTC 12×12全覆盖。
  3. ratio = orig / size以RGBA8为基准算压缩率,直观看到BC1的8:1与ASTC 12×12的36:1。
  4. simple_block_compress(pixels, block_size=4)把像素切成4×4块,模拟块压缩第一层。
  5. avg_r / avg_g / avg_b块内求平均色作为亮端色基准,再派生一个暗端色。
  6. best = min(colors, key=lambda c: ...)每个像素选择欧氏距离最近的代表色,模拟GPU的索引查表。
  7. mse / psnr用均方误差和PSNR量化有损压缩的质量,呼应正文的“质量权衡”。

常见误区

误区我平时都用PNG做游戏贴图,引擎会帮我在加载后解压成原始像素。纹理压缩应该也差不多,只是解压得快一点吧?

正解纹理压缩和PNG/JPEG有本质区别。PNG必须整张解码后才能用,而纹理压缩采用固定大小块编码,GPU硬件可以直接随机访问任意一个块、即时解码,不需要整张纹理的解压缓冲。这正是它能为渲染管线省内存、省带宽的前提。如果非要类比,PNG像整床被子真空打包,要用得整个拆开;纹理压缩像叠成小方砖,拿哪块拆哪块。

为何常见:因为大家日常接触的图片格式都是“加载→解码→使用”的流程,很难想到GPU会有一种“不解压也能用”的格式。

误区ASTC 12×12的包体最小,直接全部用12×12压缩,玩家反正不会盯着贴图看。

正解压缩率越高,块内能保留的颜色信息越少。暗部色带、物体边缘的块状模糊、远处纹理的闪烁,这些都是过度压缩的典型症状。UI和文字贴图尤其敏感,一个“按钮”图标被压成马赛克,玩家一眼就能看出来。正确做法是按纹理类型分级:UI用紧凑块、大场景用大块,并且压缩后一定要在目标设备上肉眼验收。

为何常见:人类视觉确实对细节不敏感,但“不敏感”不等于“无感知”,尤其在动态画面和渐变区域,压缩瑕疵会被眼睛自动放大。

误区ETC2是OpenGL ES必选格式,所有手机都支持,那全部贴图用ETC2不就好了?

正解ETC2的必选性只是“设备必须能解码”,不代表它适合所有纹理。ETC2不支持HDR,对法线贴图和高质量UI贴图的质量上限也明显低于ASTC。现代移动端的主力是ASTC,ETC2更多承担“老设备兜底”的角色。就好比标准哥的箱子虽然哪都能开,但装高定礼服和精密仪器时,还是得请灵活姐出马。

为何常见:“必选格式”这四个字给人极强的安全感,让人误以为“必选”就等于“最好”。

误区ASTC是ARM公司搞的,ARM的芯片用在安卓上,所以iPhone应该不支持ASTC吧?

正解恰恰相反。ASTC从iOS 9(A8芯片)起就是苹果全系的默认纹理格式,同时也是OpenGL ES 3.2和Vulkan的规范扩展。移动端的真实格局是:iOS和现代Android都支持ASTC,反而是部分老安卓设备需要ETC2兜底。一句话——灵活姐的名气,最早正是靠苹果小区打出来的。

为何常见:人们习惯把“芯片厂商”和“手机阵营”绑定,却忽略了跨平台的行业标准一旦确立,往往就会被所有厂商采纳。

误区我压缩了纹理,游戏安装包的体积应该会变小,这跟zip压缩包的道理一样。

正解纹理压缩确实能减小包体——因为压缩后的数据量更少,打包进磁盘自然更小。但这并非它被创造的首要目的。纹理压缩的核心价值在于“GPU显存中的表示方式”:它决定了运行时占多少显存、每帧采样消耗多少带宽。磁盘上的zip只是文件层面的事,GPU不认zip。理解这个区别,才能明白为什么游戏引擎里“纹理格式”和“打包压缩”是两个独立的选项。

为何常见:生活中“压缩”一词很容易让人联想到文件压缩包,但这两种压缩服务于完全不同的硬件阶段。

面试题

Q1. 纹理压缩与普通图片压缩(PNG/JPEG)的关键区别是什么?

单选题 · easy

  • A.纹理压缩在GPU端实时解压整图
  • B.纹理压缩支持块级随机访问解码← 正确答案
  • C.PNG的压缩率永远高于纹理压缩
  • D.纹理压缩只适合2D图片不适合3D

答案:B。纹理压缩用固定大小块独立编码,GPU可以只解码被采样到的那一块,不需要像PNG那样整张解压。这就像收纳师叠出的小方砖,拿哪块拆哪块,而不是把整个大包裹先解开。A、C、D都与纹理压缩的设计初衷矛盾。

Q2. BC1(DXT1)格式每块(4×4像素)占用多少位?

单选题 · mid

  • A.每块占32位约2bpp
  • B.每块占64位约4bpp← 正确答案
  • C.每块占128位约8bpp
  • D.每块占256位约16bpp

答案:B。BC1由两个16位端色加16个2位索引组成,总位数为2×16+16×2=64位,除以16像素得4bpp。正如老牌伯最实惠的木箱——两件“代表衣服”加每个衣袋上的两个标签。

Q3. 下列关于ETC2与ASTC的说法,哪些是正确的?

多选题 · mid

  • A.ETC2是OpenGL ES 3.0必选格式← 正确答案
  • B.ASTC支持HDR与RGBA数据← 正确答案
  • C.BC1是移动端硬件必选格式
  • D.ETC2的RGBA模式占64位

答案:AB。ETC2自OpenGL ES 3.0起为必选格式;ASTC支持HDR与RGBA四通道。BC1是PC/主机标准,移动端并不必选;ETC2的RGBA模式实际为128位(8bpp)。标准哥的通行证只有两种规格,其中RGBA箱是双倍容量。

Q4. ASTC 8×8块的位率约为?

单选题 · hard

  • A.约8bpp的高保真档位
  • B.约4bpp的中等档位
  • C.约2bpp的经济档位← 正确答案
  • D.约1bpp的超压缩档位

答案:C。ASTC每块固定128位,128÷(8×8)=2bpp。这就是灵活姐“大盒压紧”的威力——盒内容量固定,盒子面积越大,摊到每件衣服上的成本越低。

Q5. 关于BC7与BC1,下列说法正确的是?

多选题 · hard

  • A.BC7支持8bit alpha而BC1仅1bit← 正确答案
  • B.BC1压缩率更高且质感更优
  • C.BC7模式复杂质量接近于无损← 正确答案
  • D.BC7硬件解压比BC1更快速

答案:AC。BC1只支持1位透明(要么透明要么不透明),BC7支持完整8位alpha;BC7用更复杂的模式选择机制逼近无损质量,代价是编码更慢。老牌伯的高级匣子用料讲究,但打箱更费功夫,与解压速度无关。

Q6. 在PC平台压缩法线贴图,下列哪种选择最合理?

单选题 · hard

  • A.选择BC1获得最高压缩率
  • B.选择BC3获得RGBA支持
  • C.选择BC5保留双通道精度← 正确答案
  • D.选择ETC2保证跨平台兼容

答案:C。法线贴图依赖RG两个通道编码朝向,B通道可由Z重建。BC5专为双通道设计,每通道8位,能最大程度保护法线精度,避免光照出现麻点。就像精密仪器要用专用防震箱,而非随便找个普通木箱塞进去。

Q7. 一张2048×2048的RGBA8纹理,显存占用约为?

单选题 · easy

  • A.约4MB的显存开销
  • B.约8MB的显存开销
  • C.约16MB的显存开销← 正确答案
  • D.约32MB的显存开销

答案:C。RGBA8即每像素32位=4字节,2048×2048×4=16,777,216字节约16MB。这就是“行李盘点公式”:宽乘高乘每像素字节数,规格越大占的地儿越多。

Q8. 下列哪些是ASTC相比ETC2的优势?

多选题 · mid

  • A.支持灵活块尺寸调整压缩率← 正确答案
  • B.在移动端硬件上广泛支持← 正确答案
  • C.支持HDR与RGBA通道← 正确答案
  • D.压缩率固定无法调整

答案:ABC。ASTC有19档块尺寸、支持HDR、移动端硬件普及,这些都是对ETC2的明显优势。D选项恰好说反了——固定压缩率是ETC2的特性而非ASTC。灵活姐的伸缩盒与标准哥的固定箱,差别正在于此。

Q9. ETC2 RGB模式的位率是?

单选题 · hard

  • A.2bpp的经济压缩率
  • B.4bpp的标准压缩率← 正确答案
  • C.8bpp的高保真压缩率
  • D.16bpp的无损压缩率

答案:B。ETC2的RGB块固定64位,覆盖4×4共16像素,64÷16=4bpp,相当于RGBA8的1/8。标准哥的标准箱容量恒定:64位装16个像素,不多不少。

Q10. 纹理压缩对渲染管线最重要的作用是?

单选题 · easy

  • A.减少游戏逻辑的计算开销
  • B.降低显存占用与带宽消耗← 正确答案
  • C.加快中央处理器的主频
  • D.提高显示器的刷新频率

答案:B。纹理压缩让更多贴图装进有限显存,同时减少GPU每帧读取纹素的带宽压力,直接解决渲染管线最常遭遇的性能瓶颈。收纳师的核心价值,就是让你的阁楼装得下、独木桥搬得完。