Sprite是以Texture2d为基础的,所以我们应该首先获取Sprite的texture2d对象,合并,然后生成新的Sprite对象。
可以用画画的方式一层一层的混合,Unity3d新建的Texture2d对象并不是一张RGB为0的图,而是0.8的灰色缓冲区,所以我们需要清空这个颜色,只需要用最底层的texture去填充覆盖,上面的texture在绘制即可。
分别为两个指令,Fill和Draw。
public static void Fill(Texture2D src, Texture2D brush) { src.SetPixels(brush.GetPixels()); }
Draw绘制函数
public static void Draw(Texture2D src, Texture2D brush, Vector2Int pos) { int width = brush.width; int height = brush.height; for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { var destColor = brush.GetPixel(i, j); var srcColor = src.GetPixel(pos.x + i, pos.y + j); if (srcColor.a != 0) { float alpha = srcColor.a + destColor.a; destColor = Color.Lerp(srcColor, destColor, destColor.a); if (alpha >= 1) { destColor.a = 1; } } src.SetPixel(pos.x + i, pos.y + j, destColor); } } }
该函数对旧颜色进行判断,如果旧颜色的的透明度不为0,则使用新颜色的透明度对两个颜色插值,这是为了处理两层交界边缘的半透明区域。但是Lerp同时也会插值alpha,会在旧不透明与新半透明时混合错误,所以这里做了一个判断,如果两个颜色透明度相加大于等于1,那就将Lerp插值错误的alpha补偿回来。
现在已经可以合并多个Texture了,例:
Texture2D t1 = sprite1.texture as Texture2D; Texture2D t2 = sprite2.texutre as Texture2D; Texture2D tex = new Texture2D(t1.width, t1.height, t1.format, false); Fill(tex, t1); Draw(tex, t2, Vector2Int.zero); tex.Apply();
到这里我们已经可以手动设置多张texture合成了。
public static Texture2D ComposeNew(Texture2D[] objs, Vector2Int[] offset) { Texture2D t1 = objs[0]; Texture2D tex = new Texture2D(t1.width, t1.height, t1.format, false); Fill(tex, t1); for (int i = 1; i < objs.Length; i++) { Draw(tex, objs[i], offset[i]); } tex.Apply(); return tex; }
但是对于sprite来说还需要计算它们的偏移。
在ugui中的sprite以Image显示,而在ugui的Canvas中一个单位即一个像素。在绘制的过程中我们要注意一点:Unity的颜色缓冲信息是从图像的左下角开始的,所以我们要算出Image在Canvas中的相对位置以及和填充层的相对位置。
一般情况下ugui的控件中心点pivot为(0.5,0.5),使用fillRect.sizeDelta * fillRect.pivot就可以求出填充层的中心位置,但是从填充层中心开始画就错了,因为是从左下角开始画的,所以这里还需要减掉需要绘制图像的一半:
fillRect.sizeDelta * fillRect.pivot - transRect.sizeDelta / 2
然后我们加上两个对象在Canvas上的相对距离,首先编写控件获取在Canvas上的位置:
private static Vector2 TransformToCanvasLocalPosition(Transform current, Canvas canvas) { var screenPos = canvas.worldCamera.WorldToScreenPoint(current.transform.position); Vector2 localPos; RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.GetComponent<RectTransform>(), screenPos, canvas.worldCamera, out localPos); return localPos; }
最后将上述表达式整合,编写Sprite合成函数:
public static Sprite ComposeNewSpriteFromUImage(Transform[] transforms, Canvas canvas) { Texture2D[] texArr = new Texture2D[transforms.Length]; Vector2Int[] offsetArr = new Vector2Int[transforms.Length]; var first = transforms[0]; for (int i = 0; i < transforms.Length; i++) { var transform = transforms[i]; texArr[i] = transform.GetComponent<Image>().mainTexture as Texture2D; if (i == 0) { offsetArr[i] = new Vector2Int(0, 0); } else { var fillRect = first.GetComponent<RectTransform>(); var transRect = transform.GetComponent<RectTransform>(); var offset = fillRect.sizeDelta * fillRect.pivot - transRect.sizeDelta / 2; offset += TransformToCanvasLocalPosition(transform, canvas) - TransformToCanvasLocalPosition(first, canvas); offsetArr[i] = new Vector2Int((int)offset.x, (int)offset.y); } } var tex = ComposeNew(texArr, offsetArr); return Texture2DUtility.Texture2dToSprite(tex); }
文章评论