纹理打包工具 TexturePacker

在OpenGL中,纹理是不断被创建然后绘制,绑定纹理是相对昂贵的,所以将许多小图像统一存储在一张较大的图片上一次性绑定纹理,然后多次绘制它的一小部分。将纹理打包为一个大图片可以使用工具 TexturePacker, 不过在 libGDX 中,提供了一个gdx-tools工具,这里可以使用这个工具来打包纹理。

安装

使用gdx-tools,只需要引入 gdx-tools 这个工具jar包即可。

Gradle

dependencies {
    ...
		api "com.badlogicgames.gdx:gdx-tools:$gdxVersion"
}

Maven

<dependencies>
		<dependency>
			<groupId>com.badlogicgames.gdx</groupId>
			<artifactId>gdx-tools</artifactId>
			<version>${gdx-version}</version>
		</dependency>
</dependencies>

使用

  1. 直接写一个main方法调用
import com.badlogic.gdx.tools.texturepacker.TexturePacker;
public class MyPacker {
	public static void main (String[] args) throws Exception {
		String inputDir = "E:\\workspace\\game\\block\\block-game\\android\\assets\\source_image";
		String outputDir = "E:\\workspace\\game\\block\\block-game\\android\\assets\\handle_images";
		String packFileName = "blocks";
		TexturePacker.process(inputDir, outputDir, packFileName);
	}
}

inputDir: 需要打包的图片文件夹

outputDir: 打包后生成的xxx.atlas xxx.png等保存的文件夹

packFileName: 打包生成的文件名

打包完成后就可以使用 TextureAtlas 类在应用程序中轻松引用资源了。

  1. 使用 GUI 带图形界面

具体清查看 https://github.com/crashinvaders/gdx-texture-packer-gui

  1. jar 包运行
java -cp gdx-tools.jar com.badlogic.gdx.tools.texturepacker.TexturePacker inputDir [outputDir] [packFileName]

目录结构

TexturePacker 可以一次打包 inputDir 路径中的所有图像。给定一个目录,它递归地扫描所有图像文件。对于 TexturePacker 遇到的每个图片目录,它都将图片打包到一个更大的纹理中,称为页面。如果一个目录中的图像不适合单个页面的最大大小,则将使用多个页面。

同一目录中的图像放在同一组页面上。如果所有的图片都放在一个页面上,那么就不应该使用子目录,因为在一个页面上应用程序只会执行一个纹理绑定。否则,可以使用子目录隔离相关的图像,以最小化纹理绑定。例如,在游戏中,可能希望将所有的 “游戏game” 图像放在与 “暂停菜单 pause menu” 图像不同的目录中,因为这两组图像是连续绘制的: 所有游戏图像都绘制(一个绑定) ,然后在顶部绘制暂停菜单(另一个绑定)。如果图像在一个目录中,导致多于一个页面,每个页面可以包含游戏和暂停菜单图像的组合。这将导致多个纹理绑定到渲染游戏和暂停菜单,而不是只有一个。

子目录还可以用于对具有相关纹理设置的图像进行分组。像运行时内存格式(RGBA,RGB 等)和过滤(最近的,线性等)等设置是每纹理。每个纹理需要不同设置的图片需要放在单独的页面上,所以应该放在单独的子目录中。

若要使用子目录进行组织,而不必为每个子目录输出一组页面,可以参考 combineSubdirectories 设置。为了避免子目录路径被用于 atlas 文件中的图像名称中,可以参考 delandentpaths 设置。

配置

image.png

image.png

每个打包目录可以放一个 “pack.json” 文件,它是 TexturePacker.Settings 的 JSON 表示形式,用来设置打包配置。 每个子目录从其父目录继承所有设置。子目录中设置的任何设置都会覆盖父目录中设置的设置。如下是整个默认的 json 示例:

{
	pot: true,
	paddingX: 2,
	paddingY: 2,
	bleed: true,
	bleedIterations: 2,
	edgePadding: true,
	duplicatePadding: false,
	rotation: false,
	minWidth: 16,
	minHeight: 16,
	maxWidth: 1024,
	maxHeight: 1024,
	square: false,
	stripWhitespaceX: false,
	stripWhitespaceY: false,
	alphaThreshold: 0,
	filterMin: Nearest,
	filterMag: Nearest,
	wrapX: ClampToEdge,
	wrapY: ClampToEdge,
	format: RGBA8888,
	alias: true,
	outputFormat: png,
	jpegQuality: 0.9,
	ignoreBlankImages: true,
	fast: false,
	debug: false,
	combineSubdirectories: false,
	flattenPaths: false,
	premultiplyAlpha: false,
	useIndexes: true,
	limitMemory: true,
	grid: false,
	scale: [ 1 ],
	scaleSuffix: [ "" ],
	scaleResampling: [ bicubic ]
}

注意,这是 libgdx 的自定义的JSON 格式,不是严格的json文件格式,因此 key 的双引号可以不用写。

配置项 描述 默认值
pot If true, output pages will have power of two dimensions.(不懂, 感觉是排列方式不同) true
paddingX / paddingY 边距 2
bleed 如果为true, 则根据最接近的非透明像素的 RGB 值设置透明像素的 RGB 值。这可以防止在为透明像素采样 RGB 值时出现过滤伪影。 true
bleedIterations 执行的 bleed 迭代的数量。 2
edgePadding 如果为true,一半的 paddingX 和 paddingY 将被用于包装材质的边缘。 true
duplicatePadding 如果为 true,则将边缘像素复制到填充中。paddingX/paddingY应该大于2 false
rotation 如果为true,TexturePacker 将尝试通过90度旋转图像来实现更有效的打包。在游戏中使用时必须特别注意正确地绘制这些区域。 false
minWidth / maxWidth / minHeight / maxHeight 最大/最小输出宽高 minWidth、minHeight为16,maxWidth、maxHeight为1024
square 如果为 true,则强制输出页面具有相同的宽度和高度。 false
stripWhitespaceX /stripWhitespaceY 如果为true,则输入图像左右/上下边缘的空白像素将被移除。在游戏开发使用中必须特别注意正确地绘制这些区域。 false
alphaThreshold 从0到255。当空白被删除时,低于这个值的 Alpha 值被视为零。 0
filterMin / filterMag 给纹理缩小/放大的过滤器设置 Texture.TextureFilter 枚举类中的类型,默认为Nearest
wrapX / wrapY 纹理在x / y轴方向上的打包设置 ClampToEdge
format 纹理在内存中使用的格式 RGBA8888
alias 如果为true,则两个像素相同的图像将只打包一次 true
outputFormat 图片的输出格式,支持 png 或者 jpg png
jpegQuality 输出为jpg时的输出质量设置 0.9
ignoreBlankImages 如果为true,纹理打包不会添加完全空白的图像区域。 true
fast 如果为true,纹理打包将不会有效率,但执行打包速度更快。 false
debug 如果为 true,则在输出页面上绘制边框线以显示打包图像边界。 false
combineSubdirectories 如果为 true,则将包含设置文件和所有子目录的目录打包,就像它们在同一个目录中一样。子目录中的所有设置文件都被忽略。 false
flattenPaths 如果为true,则打包后使用的图像名称将不会带上子目录前缀。这时每个子目录的图像文件名必须唯一的,不能和其他子目录下的图像文件名重复。 false
premultiplyAlpha 如果为true, 则 RGB 将乘以 alpha。 false
useIndexes 是否使用图像索引 true
limitMemory 如果为true,则在任何给定时间内,内存中只有一个图像,但每个图像将被读取两次。如果为 false,则在打包期间将所有图像保存在内存中,但只读取一次。 true
grid 如果为true,则图像将按顺序以统一的格子排列。 false
scale 打包缩放设置, 为一个数字数组 [1]
scaleSuffix 对于每个缩放图像,设置输出文件后缀。如果省略,多个缩放的文件将以相同的名称输出到每个缩放的子目录。 [ "" ]
scaleResampling 对于每个缩放,用于将源重采样到缩放尺寸的插值类型。nearest, bilinear 或者 bicubic。 [ bicubic ]

部分选项详述

1. 纹理过滤选项 纹理打包使用的过滤器为 Texture.TextureFilter 枚举类中指定的过滤器类型,上面配置的选项filterMin和filterMag的选项如下:

  • Nearest: no filtering, no mipmaps
  • Linear: filtering, no mipmaps
  • MipMap & MipMapLinearLinear: filtering, smooth transition between mipmaps
  • MipMapNearestNearest: no filtering, sharp switching between mipmaps
  • MipMapLinearNearest: filtering, sharp switching between mipmaps
  • MipMapNearestLinear: no filtering, smooth transition between mipmaps

2. NinePatches

如果一个图像文件名在文件扩展名前以 “.9” 结尾,那么它被认为是一个 NinePatches, NinePatches图像必须有一个1px 的透明边框。上边缘和左边缘可以选择有一个连续的黑色像素线,表示分裂的信息,即哪一部分的 NinePatches 将拉伸。底部和右边缘可以选择有一个连续的黑色像素线,表示填充信息,即如何在 NinePatches 顶部的内容应插入。当这个图像被打包时,1px 的边框被移除,分割和填充信息被存储在打包文件中。TextureAtlas 允许使用分割信息为该区域创建 NinePatch 实例。

3. useIndexes(图像索引)

如果图像文件名以下划线结尾,然后是一个数字(例如 animation_23.png) ,则该数字被认为是“索引”,并单独存储。存储图像名称时不使用下划线和索引。TextureAtlas 允许检索所有同名图像的列表,并按索引排序。这在打包动画图像资源时很容易使用,而不会丢失帧的顺序。

TextureAtlas

打包完成后输出目录包含为一个文本的描述文件和一堆图像,使用示例如下:

TextureAtlas atlas;
atlas = new TextureAtlas(Gdx.files.internal("packedimages/pack.atlas"));
AtlasRegion region = atlas.findRegion("imagename");
Sprite sprite = atlas.createSprite("otherimagename");
NinePatch patch = atlas.createPatch("patchimagename");