大家在开发端游的时候,我们经常需要通过工具程序手动合并dds图片,意思是为了提高效率,需要我们把一些小的图片合并成一张1024或者2048的大图片。它的图片合成跟NGUI的图集原理是一样的,在这里我们不用NGUI的合并图集,我们手动合并图集。
大家在游戏开发中我们需要讲一些零碎的图片合并成一张大的,比如3D场景中一些物件的材质,Avatar换装的材质等等。
接下来我们用代码实现,我们的实现原理是通过二叉树原理实现,首先我们需要定义AslatsNode节点,这个节点满足我们插入节点,建立二叉树,首先我们需要定义图集的大小,鉴于移动端硬件的限制,我们初步定义为1024,插入节点核心代码如下:
public AtlasNode Insert(Texture2D p_w_picpath, int index) { if (p_w_picpath == null) return null; if (child != null) AtlasNode newNode = child[0].Insert(p_w_picpath, index); if (newNode != null) return newNode; return child[1].Insert(p_w_picpath, index); } else { if (hasImage) return null; if (!ImageFits(p_w_picpath, rc)) return null; if (PerfectFit(p_w_picpath, rc)) { hasImage = true; p_w_picpathRef = p_w_picpath; name = p_w_picpathRef.name; sortIndex = index; return this; } child = new AtlasNode[2]; child[0] = new AtlasNode(); child[1] = new AtlasNode(); float deltaW = rc.width - p_w_picpath.width; float deltaH = rc.height - p_w_picpath.height; if (deltaW > deltaH) { child[0].rc = new Rect(rc.xMin, rc.yMin, p_w_picpath.width, rc.height); child[1].rc = new Rect(rc.xMin + p_w_picpath.width + TEXTURE_PADDING, rc.yMin, rc.width - (p_w_picpath.width + TEXTURE_PADDING), rc.height); } else { child[0].rc = new Rect(rc.xMin, rc.yMin, rc.width, p_w_picpath.height); child[1].rc = new Rect(rc.xMin, rc.yMin + p_w_picpath.height + TEXTURE_PADDING, rc.width, rc.height - (p_w_picpath.height + TEXTURE_PADDING)); } return child[0].Insert(p_w_picpath, index); } }
以上代码实现了图片在二叉树进行图片的插入,接下来我们需要建立二叉树代码如下:
public void Build(Texture2D target) { if (child != null) { if (child[0] != null) { child[0].Build(target); } if (child[1] != null) { child[1].Build(target); } } if (p_w_picpathRef != null) { Color[] data = p_w_picpathRef.GetPixels(0); for (int x = 0; x < p_w_picpathRef.width; ++x) { for (int y = 0; y < p_w_picpathRef.height; ++y) { target.SetPixel(x + (int)rc.x, y + (int)rc.y, data[x + y * p_w_picpathRef.width]); } } if (TEXTURE_PADDING > 0 && BLEED) { for (int y = 0; y < p_w_picpathRef.height; ++y) { int x = p_w_picpathRef.width - 1; target.SetPixel(x + (int)rc.x + TEXTURE_PADDING, y + (int)rc.y, data[x + y * p_w_picpathRef.width]); } for (int x = 0; x < p_w_picpathRef.width; ++x) { int y = p_w_picpathRef.height - 1; target.SetPixel(x + (int)rc.x, y + (int)rc.y + TEXTURE_PADDING, data[x + y * p_w_picpathRef.width]); } } } } }
第三步是创建图集代码如下:
public static Atlas[] CreateAtlas(string name, Texture2D[] textures, Atlas startWith = null) { List<Texture2D> toProcess = new List<Texture2D>(); toProcess.AddRange(textures); int index = toProcess.Count - 1; toProcess.Reverse(); // Because we index backwards List<Atlas> result = new List<Atlas>(); int insertIndex = 0; if (startWith != null) { insertIndex = startWith.root.sortIndex; } while(index >= 0) { Atlas _atlas = startWith; if (_atlas == null) { _atlas = new Atlas(); _atlas.texture = new Texture2D(AtlasSize, AtlasSize, TextureFormat.RGBA32, false); _atlas.root = new AtlasNode(); _atlas.root.rc = new Rect(0, 0, AtlasSize, AtlasSize); } startWith = null; while (index >= 0 && (_atlas.root.Contains(toProcess[index].name) || _atlas.root.Insert(toProcess[index], insertIndex++) != null)) { index -= 1; } result.Add(_atlas); _atlas.root.sortIndex = insertIndex; insertIndex = 0; _atlas = null; } foreach(Atlas atlas in result) { atlas.root.Build(atlas.texture); List<AtlasNode> nodes = new List<AtlasNode>(); atlas.root.GetBounds(ref nodes); nodes.Sort(delegate (AtlasNode x, AtlasNode y) { if (x.sortIndex == y.sortIndex) return 0; if (y.sortIndex > x.sortIndex) return -1; return 1; }); List<Rect> rects = new List<Rect>(); foreach(AtlasNode node in nodes) { Rect normalized = new Rect(node.rc.xMin / atlas.root.rc.width, node.rc.yMin / atlas.root.rc.height, node.rc.width / atlas.root.rc.width, node.rc.height / atlas.root.rc.height); normalized.x += 0.5f / atlas.root.rc.width; normalized.width -= 1.0f / atlas.root.rc.width; normalized.y += 0.5f / atlas.root.rc.height; normalized.height -= 1.0f / atlas.root.rc.height; rects.Add(normalized); } atlas.uvRects = new AtlasDescriptor[rects.Count]; for (int i = 0; i < rects.Count; i++) { atlas.uvRects[i] = new AtlasDescriptor(); atlas.uvRects[i].width = (int)nodes[i].rc.width; atlas.uvRects[i].height = (int)nodes[i].rc.height; atlas.uvRects[i].name = nodes[i].name; atlas.uvRects[i].uvRect = rects[i]; } atlas.root.Clear(); atlas.texture.Apply(false, false); SaveAtlas(atlas, name); atlas.texture.Apply(false, false); } return result.ToArray(); }
最后一步实现就是把我们生成的图集保存一下:
public static void SaveAtlas(Atlas atlas, string name) { if (atlas == null || atlas.texture == null) return; var bytes = atlas.texture.EncodeToPNG(); if (!System.IO.Directory.Exists(Application.dataPath + "/Debug/")) System.IO.Directory.CreateDirectory(Application.dataPath + "/Debug/"); string file = Application.dataPath + "/Debug/" + name + ".png"; System.IO.File.WriteAllBytes(file, bytes); }
图片示例如下:
以上是零散的图片,接下来我们将其合成图集:
转载于:https://blog.51cto.com/jxwgame/1603152