游戏开发中的图像格式选择 - 纹理压缩与渲染性能
为什么游戏开发需要不同的图像格式
游戏中的图像(纹理)与 Web 图像有根本不同的需求。GPU 需要能直接在显存中随机访问的压缩格式,而非需要完整解压的 JPEG/PNG。
游戏纹理的特殊需求:
- GPU 直接采样:纹理必须以 GPU 可直接读取的格式存储在显存中。JPEG/PNG 需要完整解压为未压缩像素才能使用
- 随机访问:渲染时 GPU 需要随机访问纹理的任意区域(纹理过滤),不能像流式解码那样顺序读取
- 固定压缩比:GPU 纹理压缩使用固定大小的块(如 4x4 像素),确保任意位置的访问时间恒定
- 显存限制:游戏可能同时加载数千张纹理,压缩率直接决定能加载多少内容
结果:游戏使用专门的 GPU 纹理压缩格式(BC1-7、ASTC、ETC2),而非 Web 常用的 JPEG/PNG/WebP。
补充:Web 图像优先考虑文件体积与浏览器兼容性,而游戏优先考虑 GPU 绘制性能与 VRAM(显存)占用。JPEG 与 PNG 必须先由 CPU 解码再上传到 GPU,而 GPU 纹理压缩格式可由 GPU 直接读取,无需解码步骤。
- 一般游戏项目中,纹理约占全部资源的 60-80%
- AAA 级作品的纹理数据可达数十 GB,格式选择直接影响下载体积、载入时间与绘制性能
- 选对格式可在维持视觉品质的同时把 VRAM 占用减少 50-75%
GPU 纹理压缩机制 - BCn 系列(DirectX)
BCn(Block Compression)是 DirectX 平台的标准纹理压缩格式族,将纹理分为 4x4 像素块独立压缩。
BCn 格式一览:
- BC1(DXT1):RGB,4 位/像素(8:1 压缩比)。每个 4x4 块存储 2 个端点颜色 + 16 个 2 位索引。适合不需要 Alpha 的漫反射纹理
- BC3(DXT5):RGBA,8 位/像素(4:1)。BC1 的 RGB + 独立的 Alpha 块。适合需要透明度的纹理
- BC4:单通道,4 位/像素。适合高度图、粗糙度图等单通道数据
- BC5:双通道,8 位/像素。适合法线贴图(仅存储 XY,Z 通过计算恢复)
- BC7:RGBA,8 位/像素。质量最高的通用格式,支持多种分区模式。适合高质量纹理
选择指南:漫反射纹理用 BC1/BC7,法线贴图用 BC5,带 Alpha 用 BC3/BC7,单通道数据用 BC4。BC7 质量最好但仅 DX11+ 硬件支持。
补充说明:bpp 是 bits per pixel(每像素位数)的缩写。BC5 最适合法线贴图(Normal Map),它把 XY 两个分量独立高精度压缩,Z 分量由着色器重新计算。BC6H 用于 HDR(Half-Float) RGB 数据,适合环境贴图与 HDR 光照贴图等高动态范围纹理;BC7 提供高品质 RGB/RGBA,是现代游戏彩色纹理的标准。
- 同为 8 位/像素时,BC7 相比 BC1 的 PSNR 提升约 2-4dB,渐变与边缘处的伪影明显减少
- BC7 的编码耗时是 BC1 的 10-50 倍,但解码由 GPU 硬件完成,运行时开销相同
ASTC - 移动端和跨平台标准
ASTC(Adaptive Scalable Texture Compression)是 ARM 开发的纹理压缩格式,支持可变块大小,在移动平台广泛使用。
ASTC 特点:
- 可变块大小:从 4x4 到 12x12,提供从 8 位/像素到 0.89 位/像素的连续压缩率范围
- 统一格式:一种格式覆盖 LDR/HDR、1-4 通道、2D/3D 纹理
- 高质量:在相同压缩率下质量通常优于 BCn 和 ETC2
块大小选择:
- 4x4(8 bpp):最高质量,适合法线贴图和 UI 纹理
- 6x6(3.56 bpp):质量和大小的良好平衡,适合大多数漫反射纹理
- 8x8(2 bpp):较高压缩,适合远景纹理和不太重要的资源
平台支持:iOS(A8+)、Android(大多数现代设备)、macOS(Apple Silicon)。桌面端通过 Vulkan/OpenGL 扩展支持。
补充:ASTC 由 ARM 开发、并由 Khronos Group 完成标准化,在 iOS(A8 以后)、Android(多数 Adreno/Mali GPU)与 Nintendo Switch 上获得广泛支持,已成为移动游戏开发的事实标准。它支持 1-4 个通道,并同时覆盖 LDR(8 位)与 HDR(16 位浮点),因此彩色纹理、法线贴图、遮罩等用途可用同一种格式承载。
- 可变块尺寸:从 4x4 到 12x12 共 14 种块尺寸可选,能按品质需求精细控制
- 5x5(5.12bpp)适合一般角色纹理与小道具,6x6(3.56bpp)适合背景与地形
- Unity 中通过
TextureImporter的设置指定 ASTC 块尺寸,Unreal 则在 Texture Compression Settings 中选择 ASTC 配置 - 在构建流水线中集成
astcenc(ARM 官方编码器)即可进行高品质的离线编码
容器格式 - DDS、KTX2 和 Basis Universal
GPU 纹理压缩数据需要容器格式来存储元数据(尺寸、格式、Mipmap 层级等)。
主要容器格式:
- DDS(DirectDraw Surface):DirectX 生态标准。支持所有 BCn 格式、Mipmap、立方体贴图、纹理数组。Windows/Xbox 游戏的首选
- KTX2(Khronos Texture 2.0):Vulkan/OpenGL 生态标准。支持所有 GPU 格式、Basis Universal 超压缩、Zstandard 额外压缩。跨平台首选
- Basis Universal:中间格式,可在运行时快速转码为目标平台的原生格式(BC1-7、ASTC、ETC2)。一份文件适配所有平台
Basis Universal 的优势:
- 打包时只需存储一份纹理数据,运行时根据 GPU 能力转码
- 转码速度极快(< 1ms/纹理),不影响加载性能
- 显著减少游戏安装包大小(无需为每个平台存储不同格式)
补充:容器格式决定压缩数据如何被打包与解析。DDS(DirectDraw Surface)是 DirectX/Windows 的标准容器,头部仅 128 字节,解析速度快,并可通过 DX10 扩展头支持 BC6H/BC7。KTX2 覆盖 ASTC、ETC2 与 BCn 全系列,并支持 Basis Universal 的超级压缩(Supercompression),最适合跨平台开发。
Basis Universal(.basis)由 Binomial 开发,可在运行时转码:ETC1S 模式先把图像压成 ETC1 兼容的中间表示,再按目标 GPU 转码;UASTC 模式使用 ASTC 4x4 兼容的高品质中间表示,可快速转码为 BC7 或 ASTC。代价是文件体积比各原生格式大 10-25%,但不再需要为每个平台准备单独的文件。
面向 Web 的 glTF 三维模型已把 KTX2 与 Basis Universal 作为标准选项,Three.js 与 Babylon.js 均提供原生支持。
平台特定的最佳格式选择
不同游戏平台的 GPU 支持不同的纹理格式,需要针对性选择。
平台格式推荐:
- PC(Windows/DirectX):BC7(高质量)、BC1/BC3(兼容性)。DX11+ 硬件全面支持 BC7
- PC(Vulkan):BC7 或 ASTC(如果目标硬件支持)
- iOS:ASTC(A8 芯片及以上全面支持)。旧设备用 PVRTC(已过时)
- Android:ASTC(大多数现代设备)、ETC2(广泛兼容的降级方案)
- 主机(PS5/Xbox Series):BC7(最高质量)、BC1-5(节省显存时)
- Web(WebGL/WebGPU):Basis Universal → 运行时转码为设备支持的格式
多平台策略:使用 Basis Universal 作为源格式,构建时或运行时转码为目标平台格式。或在构建流水线中为每个平台生成对应格式的资源包。
补充:如果选择的格式不被目标平台 GPU 硬件支持,就会退化为 CPU 解码,性能大幅下降。因此需要按平台分别指定:
- PC(Windows/DirectX 12):彩色用 BC7、法线贴图用 BC5、HDR 用 BC6H、灰度用 BC4。Linux/macOS 部分环境也支持 ASTC
- PlayStation 5:以 BC7 为基础,使用专有的 GNF 容器格式
- Xbox Series X/S:BC7 加上 Xbox 专有的 BCPack 追加压缩层
- Nintendo Switch:推荐 ASTC,Tegra X1 也支持 BC7
- iOS(A8 以后):以 ASTC 为标准,PVRTC 已不推荐(A15 以后仅推荐 ASTC)
- Android:ASTC(Adreno 4xx 以后、Mali-T760 以后),并按目标 GPU 转换为 BC7/ASTC/ETC2
多平台开发的最佳实践:源资源以未压缩形式(PNG/TGA/EXR)管理,在构建流水线中转换为各平台的最优格式;在 Unity 的 Texture Import Settings 或 Unreal 的 Texture Compression Settings 中定义分平台设置;并把 texconv(DirectX)、astcenc(ARM)、basisu(Basis Universal)集成到 CI/CD 流水线中。
纹理图集与流式加载架构设计
大型游戏需要管理数千张纹理,图集打包和流式加载是性能优化的关键。
纹理图集(Texture Atlas):
- 将多张小纹理打包到一张大纹理中,减少 Draw Call 和状态切换
- 图集大小通常为 2048x2048 或 4096x4096(GPU 最大纹理尺寸限制)
- 需要处理边缘出血(bleeding):在子纹理边缘添加 1-2 像素的填充防止采样时混入相邻纹理
虚拟纹理(Virtual Texturing):
- 将超大纹理(如地形)分割为小块(tiles),仅加载当前可见区域的块
- 类似虚拟内存的概念:逻辑上是一张巨大纹理,物理上只有部分驻留在显存
- Unreal Engine 的 Virtual Texture 和 id Tech 的 MegaTexture 是典型实现
Mipmap 流式加载:
- 先加载低分辨率 Mipmap 快速显示,再异步加载高分辨率层级
- 根据相机距离动态调整驻留的 Mipmap 层级,节省显存
- Unity 的 Texture Streaming 和 UE 的 Texture Streaming 系统
补充:纹理图集与流式加载是减少绘制调用和提升 VRAM 效率的两大手段。图集可减少 GPU 的状态切换(纹理绑定),从而把绘制调用批处理;推荐尺寸一般为 2,048 × 2,048 或 4,096 × 4,096,尺寸不一致时可改用 TextureArray,或把各纹理对齐到 2 的幂。
纹理流式加载(Virtual Texturing)的要点:
- 目的:当整个游戏的纹理无法全部放入显存时(AAA 级作品的纹理数据可达 20-50GB),只动态加载卸载需要的部分
- Mip-Tail Streaming:低分辨率的 mip 层级(mip tail)常驻显存,高分辨率层级按相机距离流式加载
- Sparse/Virtual Textures:使用 DirectX 12 的 Tiled Resources 或 Vulkan 的 Sparse Binding,只把巨大纹理的一部分映射到物理内存
实例:Unreal Engine 5 的 Virtual Texture Streaming 把 16K 纹理切分为 128 × 128 的图块,仅对可见图块进行流式加载,从而在维持观感品质的同时把 VRAM 占用减少 40-60%。