
做游戏性能优化越久我就越觉得纹理压缩值得单独拿出来讲。它直接影响显存占用、加载速度和 GPU 带宽很多时候游戏卡顿、发热、载入慢根因根本不是特效太多或者 Shader 太复杂而是一堆没做纹理压缩的贴图把资源吃满了。尤其是移动端项目纹理压缩做不做、格式选没选对经常就是“能玩”和“流畅玩”的分界线。这篇文章我会从格式选择、压缩工具、Unity 和 Unreal 里的配置方式、效果验证、常见坑点这几个方向展开。如果你是刚接触渲染优化想给低端机提帧率这篇文章可以当一份可操做的清单用如果你已经做过一轮纹理压缩但遇到画面变糊、内存没降、部分机型花屏也可以重点看后面的排查部分。1. 为什么游戏性能优化要先处理纹理压缩1.1 纹理压缩压的是 GPU 显存和带宽不只是磁盘占用很多人第一次接触纹理压缩时会下意识地把它理解成“把贴图文件压小一点”。这个理解不够准确。纹理压缩真正压掉的是 GPU 显存占用以及 GPU 在渲染每一帧时读取纹理数据的带宽开销。先看一个基础数据一张 2048×2048 的 RGBA32 图片在没有压缩的情况下显存里占的大小大约是 2048×2048×4算下来是 16MB。如果你把格式换成 ASTC 6×6每像素大约 3.56bit这张贴图在显存里只占 1.8MB 左右。差了将近 9 倍。如果你的项目里有几十张甚至上百张大贴图光这一项就能把显存预算拉开一个量级。显存减少之后GPU 在逐帧读取纹理时产生的带宽压力也会明显下降。很多手机上游戏的发热、掉帧问题就是因为 GPU 每一帧都要从显存里搬大量未压缩纹理的数据带宽被吃满了算力再强也提不上帧率。1.2 纹理压缩和普通图片压缩根本不是一回事这一点值得单独说清楚。网上压缩图片常用 JPEG、PNG、WebP这些格式解压之后才能拿到完整像素数据属于“整图编码解码”的思路。JPEG 不适合直接作为 GPU 渲染纹理PNG 虽然无损但 GPU 不能直接对它进行随机访问采样。GPU 需要的纹理压缩格式是能够被图形硬件直接采样、直接过滤的块压缩格式。它把一张纹理切成一个个固定大小的像素块比如 4×4 或者 6×6然后对每个块单独编码。这样 GPU 在渲染时只读需要的那部分块不用先解压整张图也不会因为全图解压而卡顿。这种设计天生就是给“实时渲染”用的。所以你在做纹理压缩时不要去想“这个格式压缩率多少”而要想“这个格式能不能被目标 GPU 直接采样、硬件支不支持、每像素占多少位”。1.3 先处理纹理因为它在美术资源里占比最大在大多数游戏项目里纹理资源占整体包体和内存的比例都是最高的。角色贴图、场景贴图、UI 贴图、特效序列帧随便数一下很容易就堆到几百 MB。相比之下网格数据、动画数据、音频数据通常不会比纹理更大。实测时经常会遇到这种情况项目已经做了 Draw Call 优化合批也做了Shader 也精简了但低端机还是掉帧。打开性能分析工具一看显卡内存占用接近上限带宽指标也没余量。这时候最先要查的就是纹理。找到一张 4096 分辨率的墙皮贴图或者一张没压缩的 UI 大背景问题往往一下就暴露了。2. 先搞清楚纹理压缩格式ASTC、ETC2、BC7 怎么选2.1 主流纹理压缩格式速览先列一张速查表方便后面选格式时对照格式每像素位数支持 Alpha典型场景说明BC1 / DXT14 bit部分PC、主机RGB 贴图压缩率高Alpha 支持较弱BC3 / DXT58 bit支持PC、主机带透明贴图比 BC1 质量好占用翻倍BC58 bit两个通道PC、主机法线贴图常用 RG 存储法线BC6H8 bit视情况HDR 贴图支持半精度浮点数据BC78 bit支持PC、主机、高质量移动质量好兼容性不错ETC14 bit不支持老款 Android太老不推荐新项目使用ETC24 bit支持Android OpenGL ES 3.0Android 基础格式之一ASTC28 bit支持主流移动端、部分 PC灵活质量调节范围大这里要说明一下PC 和移动端的硬件差异很明显。PC 平台通常以 BC 系列为主移动端现在基本都是 ASTC 和 ETC2 的组合。如果你只做 PC 项目重心放 BC7 就可以了如果目标是 iOS 和 AndroidASTC 是当前性价比最高的选择。2.2 移动端优先看 ASTC再拿 ETC2 做兜底ASTC 是 ARM 设计的一种自适应纹理压缩格式。最灵活的地方在于块尺寸可调从 4×4 到 12×12 都能选。块越大压缩率越高但画质损失也越明显。我现在做移动端项目时默认配置走的是 ASTC 6×6 或 ASTC 8×8。如果是角色脸部、UI 这种细节敏感的贴图会用 ASTC 4×4 或者 ASTC 5×5普通地板、墙体、大场景贴图用 6×6 和 8×8 都没问题。ETC2 的定位也很清楚它是 Android OpenGL ES 3.0 之后普遍支持的基础格式支持 Alpha压缩质量为 4bit。新项目可以只把 ASTC 作为目标但如果你的用户覆盖了一部分很老的 Android 设备就要考虑加一层 ETC2 兜底。2.3 PC 和主机端优先 BC7法线贴图单独用 BC5PC 和主机平台的纹理压缩选型比移动端简单一些因为硬件兼容性比较统一。BC7 是 8bit 格式支持高质量 RGB 和 RGBA 压缩整体画质表现很好边界和渐变处理都不错。大多数 PC 项目的默认纹理压缩格式可以直接设成 BC7。法线贴图是例外。法线贴图里存的是切线空间法线方向用 RGB 三个通道表达但蓝通道通常变化不大有很多实现甚至能通过 Z 方向重建出来。所以 PC 平台更推荐用 BC5它用两个通道存储 RG 法线数据蓝通道由 Shader 重建。这样压缩质量更高也省带宽。如果你有 HDR 环境贴图之类的资源可以留意 BC6H。它是专门给半浮点 HDR 数据用的格式能把高动态范围数据压缩得很好。2.4 带 Alpha、法线、UI 贴图不能一概而论我见过不少项目要么所有贴图统一 BC7要么所有贴图统一 ASTC 4×4结果不是包体偏大就是 UI 文字细节糊掉。实际项目里最好按用途分类普通颜色贴图PC 用 BC7移动端用 ASTC 6×6。带 Alpha 的透明贴图PC 优先 BC7移动端优先 ASTC 4×4 或 5×5因为 Alpha 边缘对压缩比较敏感。法线贴图PC 用 BC5移动端用 ASTC 4×4 或 5×5。HDR 贴图PC 用 BC6H移动端如果支持则用 ASTC HDR不支持就考虑降级方案。UI 贴图如果不是特别吃内存移动端也尽量别压得太狠否则文字边缘容易发虚。PC 端可以直接保持优质压缩或使用未压缩格式看预算而定。别迷信单一格式。纹理压缩不是越省越好是在画质、内存、带宽和设备兼容之间找一个平衡点。3. 纹理压缩实操工具、原图规则和压缩流程3.1 工具选择GPU 厂商工具和引擎内置压缩做纹理压缩不一定非要自己写工具。现在主流引擎都内置了平台纹理压缩流程Unity 和 Unreal 在打包时会根据构建目标自动转换格式。如果你只是做普通游戏的资源优化用引擎设置就够了。如果你更想手动处理批量贴图或者项目不是基于主流引擎可以用这几类工具AMD Compressonator支持 BC 系列、ASTC 等格式有图形界面和命令行版本适合批量转换。NVIDIA Texture Tools ExporterPC 上常见对 BC7、BC5 支持够用操作简单。KTX-Software 工具集适合处理 KTX2 和纹理压缩跨平台项目用起来方便。引擎自带的资源导入器和批量改资源脚本适合在项目内部统一处理。这里你可以把它们看成“压纹理的开关”具体哪个工具更顺手取决于你的平台和流程。更稳妥的做法还是把格式选择交给引擎的构建管线因为你手动压了一张 DDS 或 KTX放到不同平台不一定能被 GPU 直接采样。3.2 压缩前的原图整理规则工具和引擎只能基于原图做处理原图质量不够压缩后问题会被放大。我一般会先检查这些点宽高尽量设置成 2 的幂次方比如 512、1024、2048、4096。这样 mipmap 链生成最干净各种平台兼容性也最好。不要从低分辨率图片放大后强行压缩。一张 512 的图放大到 2048再压成 ASTC 8×8画面会很肉而且显存占用还不小。导出原图时尽量用 PNG 或 TGA 这类无损格式不要拿 JPEG 的二次压缩图去做纹理压缩基础图。如果贴图带透明通道检查一下 Alpha 通道是否真的需要。很多时候一张贴图的 Alpha 通道是全白但美术导出时没去掉这会让压缩格式变重占用也更高。区分 sRGB 贴图和线性贴图。颜色贴图按 sRGB 处理法线贴图和粗糙度贴图按线性处理。这个设置错了画面颜色会不对。3.3 可复现的压缩步骤清单这里给一套我常用的流程基本能覆盖多数项目把原图整理成统一目录确认尺寸、格式、Alpha 通道。在引擎里导入贴图先不要直接拖进场景。确认目标平台设置对应平台覆盖格式。开启 mipmap 生成除非是 UI 贴图或特殊用途贴图。设置最大纹理尺寸防止个别超大贴图拖垮低端机。构建一次测试包或者用引擎的资源分析工具观察压缩结果。在真机上用性能工具看显存、带宽、帧率和画质反馈。如果是命令行工具批量处理流程也类似。下面是示例性质的命令写法实际参数要以你使用工具的版本为准# 示例把 input.png 压缩成 BC7 格式的 DDS 文件 texture-tool -i input.png -o output.dds -f BC7 # 示例把 input.png 压缩成 ASTC 6x6 格式的 KTX2 文件 texture-tool -i input.png -o output.ktx2 -f ASTC -block 6x6这种写法主要用来演示流程。实际工具的命令名和参数经常不一样不要直接照抄重点是把原图、目标格式、输出文件这三件事分开处理。3.4 压缩结果怎么判断成功判断压缩成功不能只看文件是不是变小了。要关注四个指标文件大小比原图变小但并不代表显存占用也变小因为引擎构建流程还会再处理一次。显存占用这是最重要的一项。在真机上观察纹理内存而不是看磁盘上的资源文件大小。加载速度纹理数据越小从闪存读取和上传到 GPU 的时间越短。画质压缩后是否出现明显色块、条纹、边缘锯齿。如果是说明压缩格式或块大小没选对。我一般会先挑几张视觉上最复杂的贴图做试点确认格式没问题后再批量处理。4. Unity 里落地纹理压缩从导入设置到运行时验证4.1 为什么要以引擎设置为主Unity 里做纹理压缩核心不是手动做 DDS 文件而是利用 Texture Importer 的平台覆盖功能。因为 Unity 在打包时会根据构建目标把源图转换成对应平台能直接采样的纹理格式。如果你在外部把图压成 BC7然后直接塞给 Android 包很多移动 GPU 可能根本没法采样画面会直接崩。所以正确做法是导入高质量源图在 Unity 里设置每个平台的覆盖格式让构建管线去处理。4.2 最常用的一套 Unity 纹理压缩配置我一般会在 Unity 里这样做选中一张纹理在 Inspector 里打开 Import Settings。开启 Generate Mipmaps除非是尺寸固定的 UI 或特效图。推荐选 Compressed让引擎自己决定基础格式。在平台设置里选择 Android、iOS、PC 分别覆盖。Android 常用 ASTC 6×6高端机可以接受 ASTC 4×4低端机用 ASTC 8×8。iOS 同样可以 ASTC质量档位看项目预算。PC 用 BC7法线贴图用 BC5。Max Size 按资源类型设置场景大贴图 2048 够用重要角色贴图看需求不要无脑 4096。如果你资源量很大可以写一个 Unity 编辑器扩展脚本批量设置。比如下面这个思路它会遍历指定目录下的所有纹理然后给 Android 平台设置 ASTC 6×6using UnityEditor; using UnityEngine; public static class TextureBatchTool { [MenuItem(Tools/Texture/Set Android ASTC 6x6)] public static void SetAndroidASTC() { string[] guids AssetDatabase.FindAssets(t:Texture2D, new[] { Assets/Textures }); foreach (string guid in guids) { string path AssetDatabase.GUIDToAssetPath(guid); TextureImporter importer AssetImporter.GetAtPath(path) as TextureImporter; if (importer null) continue; TextureImporterPlatformSettings settings importer.GetPlatformTextureSettings(Android); settings.overridden true; settings.format TextureImporterFormat.ASTC_6x6; settings.maxTextureSize 2048; importer.SetPlatformTextureSettings(settings); } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); Debug.Log(Texture batch processing finished.); } }这段脚本的功能很直接遍历目录、获取导入器、设置平台格式。Unity 版本不同枚举名可能略有差异但整体思路是可以复用的。更复杂的批处理可以加 CSV 配置按资源目录或命名规则设置不同格式。4.3 运行时确认纹理真的压缩了配置完了不代表没问题。最容易踩的坑是编辑器里看设置是对的但真机上纹理内存并没有降下来。我建议在运行时加一个纹理信息输出函数把加载后的纹理格式打出来public static void LogTextureInfo(Texture2D tex) { if (tex null) return; Debug.Log($Texture: {tex.name}, Format: {tex.format}, Size: {tex.width}x{tex.height}); }如果在 Android 真机上看到的格式是 RGBA32 或 ARGB32而不是 ASTC说明纹理没有走压缩流程或者加载方式有问题。常见原因包括资源被动态加载时走了非标准路径或者用了Texture2D.LoadImage之类的方法它会把图片强制性解成常规像素格式。真机上还要用 Unity Profiler 的 Memory 模块确认 Texture 内存。不要只盯包体大小包体变小的压缩格式未必代表真机显存一定小。4.4 平台覆盖设置常见的误区一个很容易犯的问题是美术和开发共用一台 PC开发把 PC 平台设置成 BC7忘了覆盖 Android 平台。结果资源默认使用 Unity 的自动压缩或 RGBA在 Android 上内存直接爆炸。这种问题出现频率很高批量检查材质时优先看平台覆盖是否都开了。还有一个误区是只开了 override但选错了格式。比如给 iOS 选了 ETC2设备虽然兼容但画质和带宽不是最优。iOS 真机上能用 ASTC 就优先用 ASTC不要因为是多年前的项目就一直停在 ETC2。5. Unreal Engine 里的纹理压缩配置思路和 Unity 不一样5.1 Texture Group 决定优先级和压缩结果Unreal 里做纹理压缩核心概念是 Texture Group。它不是单一格式设置而是给不同用途的贴图分组每组有独立的压缩设置和 mipmap 规则。打开 UE 的纹理资源在细节面板里可以看到 Compression Settings 和 Texture Group。常见的组有World普通场景贴图质量档位适中。Character角色贴图质量要求高。UIUI 纹理通常需要清晰边缘。NormalMap法线贴图专用组。Mobile移动端优化组压缩会更激进。在移动端项目里我通常会把大多数场景贴图的 Texture Group 设置成 Mobile 相关分组让引擎按 ASTC 或 ETC 的方式处理。PC 和主机项目则保持默认的 World、Character 等分组压缩格式以 BC 系列为主。5.2 我在 UE 中检查纹理压缩的顺序检查纹理在 UE 里是否压对了可以按下面的顺序走选中一张纹理查看 Compression Settings 和 Texture Group。在纹理编辑界面里切换预览平台比如 Android、iOS、Windows。打开纹理统计工具按内存占用大小排序找出排名前几的纹理。运行项目并打开 GPU 相关性能分析观察纹理流送和显存占用。在移动真机上看实际渲染效果。很多 UE 项目启用虚拟纹理或纹理流送后显存占用会动态变化。这时候更要关注“顶点是否因为纹理读取太频繁而卡顿”不只看总量。5.3 法线贴图和 UI 纹理要多留一个心眼UE 里如果法线贴图被压错格式画面会出现非常奇怪的明暗变化。PC 上法线贴图最好用 BC5不要用 BC1 或 BC7 把三个通道全存这样既浪费带宽又容易让法线方向产生误差。UI 纹理在 UE 里默认压缩可能也会糊。UI 素材尽量保持可接受的清晰度如果允许可以让它使用质量更高的压缩设置或者按项目情况关闭压缩。6. 压缩完怎么判断有没有效果6.1 先分清瓶颈是 GPU 带宽还是 Draw Call纹理压缩不是万能药。如果游戏卡顿是因为 Draw Call 太多CPU 已经忙不过来了你把纹理压得再小帧率可能也不会明显提升。这时候最该做的是合批、减少渲染批次而不是继续优化纹理。我一般会先用一个简单方法判断瓶颈把渲染分辨率降低或者把 Shader 里的光照计算简化一下观察帧率变化。如果帧率大幅提升说明 GPU 是瓶颈纹理压缩就值得继续深挖。如果帧率变化很小瓶颈很可能在 CPU 或逻辑层优先查 Draw Call 和脚本耗时。6.2 记录帧率、显存、加载时间和发热做优化一定要有数据。不要凭感觉说“好像流畅了一点”。我会在同一个场景固定镜头路径使用同一台真机记录这几项平均帧率和最低帧率。显存或 GPU 内存占用。场景加载耗时。一段时间后的机身温度和电池掉电速度。性能分析工具里的 GPU 带宽指标。每次改动纹理格式后重复一次同样的测试再对比数据。只要测试条件不变优化效果很容易看出来。6.3 画质对比不能只看手机小屏压缩格式选错最容易出现的是渐变区域色带和暗部细节丢失。给用户测画质时不要只看手机或 PC 小窗口要截取局部细节放大对比。重点关注天空渐变、暗色墙面、皮肤高光、头发边缘、UI 文字边缘这些敏感区域。一张好的压缩贴图应该在放大后依然保持边缘干净没有明显色块。如果有色带可以换更小块的压缩格式比如从 ASTC 8×8 降到 6×6 或 4×4。7. 纹理压缩常见问题排查7.1 花屏、紫屏、颜色完全不对这类问题通常不是压缩参数不对而是格式与平台不匹配。比如在 Android 上强行使用 DXT/BC 格式很多 GPU 直接输出异常颜色。也可能是纹理坐标没有对齐或 Shader 采样时用了错误的纹理格式。排查顺序先看真机 GPU 支持列表确认格式再采样纹理按压缩格式设置正确的导入类型最后检查 Shader 里 UV 和通道使用是否有误。7.2 画面糊了先看 mipmap 和 Max Size画面发糊不一定是压缩块太大。很多时候是 Max Size 设太小了比如 2048 的贴图被限制到 512远处看当然模糊。也可能是 mipmap 没有开启导致纹理在缩小采样时出现闪烁和失真。排查顺序先看贴图分辨率再看 Max Size然后确认 mipmap 是否开启最后检查各向异性过滤设置。图片近处清楚、远处糊基本就是 mipmap 或过滤问题。7.3 显存没降反而升了这是最让人头疼的情况。明明设置了 ASTC但真机显存还是很高。常见原因有这么几种同一个贴图被加载了多份没有共用纹理实例。运行时用了LoadImage之类的方法强制变成非压缩格式。某些平台开始用了压缩格式但 Shader 或后处理里创建了中间 RT显存消耗不在纹理本身。编辑器预览和真机表现不一致只看编辑器内存没有意义。排查时先在 Profiler 里按资源类型排序看看 Texture 是不是真的占大头再确认采样格式。如果 Texture 格式是 ASTC内存还是高再看是否重复加载。7.4 批量资源排除顺序有一类问题不是单个纹理导致的而是项目里混杂了大量超大纹理。我会在性能工具里按纹理内存排序把最大的前 20 张贴图列出来逐张确认压缩格式和 Max Size。很多时候只要处理了前几张 4096 分辨率的贴图低端机内存压力就能缓解一大半。8. 一些边界条件压纹理前最好先记住8.1 格式支持不等于一定稳定纹理压缩格式能不能用要看 GPU 硬件支持也要看驱动实现。有些设备标称支持 ASTC但遇到非标准尺寸或超大 mipmap 时仍可能出现黑块或加载慢。移动端尽量把纹理尺寸控制在合理范围长宽保持 2 的幂次方不要为了省内存把几百张贴图塞在不规则的图集里。8.2 压缩率不是越高越好ASTC 8×8 确实省但画质下降明显。UI、角色脸、法线贴图这类资源压缩率过高会让细节丢失。要让合适的贴图用合适的压缩率而不是全项目一刀切。我的经验是大面积低细节贴图用 8×8中等细节用 6×6高细节用 4×4UI 和法线贴图单独评估。8.3 纹理压缩只是性能优化的一部分系统级的电源模式、后台服务、临时文件清理这些会影响整机资源但解决不了 GPU 因为纹理读取过大而产生的带宽问题。纹理压缩解决的是“GPU 每帧要搬多少纹理数据”这个核心矛盾它需要和 mipmap、纹理图集、纹理流送、Draw Call 优化配合使用。只做纹理压缩不处理资源重复加载和网格体复杂度项目依然可能卡。真正落地项目时我建议先把单张重点纹理压好在真机上确认格式、显存、画质都没问题再批量推广到整个项目。这样既能快速见效也能避免一次改动太多导致问题难定位。