在unity制作自定义时,经常会遇到自定义妆容等问题,美术会提供大量的眉毛/胡子/腮红等贴图,来供用户选择。
美术给出的眉毛的小贴图如下:
在用户选用不同的胡子眉毛,可以将选定的小贴图和皮肤base贴图进行融合,得到完整的Character贴图。
Method1:CPU端逐像素根据alpha通道进行叠加。
public void MergeTexture_(Texture2D tt1, Texture2D tt2, int offsetX, int offsetY) { Texture2D newTex = new Texture2D(tt1.width, tt1.height, TextureFormat.ARGB32, false); newTex.SetPixels(tt1.GetPixels()); for (int x = 0; x < tt2.width; x++) { for (int y = 0; y < tt2.height; y++) { var PixelColorFore = tt2.GetPixel(x, y) * tt2.GetPixel(x, y).a; var newY = tt1.height - offsetY - tt2.height + y; var PixelColorBack = tt1.GetPixel(x + offsetX, newY) * tt1.GetPixel(x + offsetX, newY).a; newTex.SetPixel(x + offsetX, newY, PixelColorFore+ PixelColorBack); } } newTex.Apply(); System.IO.File.WriteAllBytes(Application.dataPath + "/" + tt1.name + ".png", newTex.EncodeToPNG()); GameObject.Find("obj001").GetComponent<Renderer>().material.mainTexture = newTex; }
这里注意下,由于需要对Texture进行读取像素,因此需要将相应的Texture设置为Read/Write Enabled,否则会报错。
逐像素操作可以用unity内置函数改为块操作,经过测试,能大概少一半的耗时
1 public void MergeTexture(Texture2D tt1, Texture2D tt2, int offsetX, int offsetY) 2 { 3 4 Texture2D newTex= new Texture2D(tt1.width, tt1.height, TextureFormat.ARGB32, false); 5 6 newTex.SetPixels(tt1.GetPixels()); 7 Color32[] colors = tt2.GetPixels32(); 8 // tt1. 9 newTex.SetPixels32(offsetX, tt1.height - offsetY - tt2.height,tt2.width,tt2.height, colors,0); 10 newTex.Apply(); 11 GameObject.Find("obj001").GetComponent<Renderer>().material.mainTexture = newTex; 12 }
上述方案基本上是在CPU端进行的,实际上可以调用Unity的底层绘制接口在GPU上进行操作,主要是利用RenderTexture和Graphics.DrawTexture()进行操作
public Texture2D texture; //Starting image. public Texture2D stampTexture; //Texture to Graphics.Drawtexture on my RenderTexture. public float posX = 256f; //Position the DrawTexture command while testing. public float posY = 256f; //Position the DrawTexture command while testing. RenderTexture rt; //RenderTexture to use as buffer. void Start() { rt = new RenderTexture(1024, 1024, 32); //Create RenderTexture 512x512 pixels in size. GameObject.Find("obj001").GetComponent<Renderer>().material.mainTexture = rt; //Assign my RenderTexure to be the main texture of my object. RenderTexture.active = rt; Graphics.Blit(texture, rt); //Blit my starting texture to my RenderTexture. RenderTexture.active = rt; //Set my RenderTexture active so DrawTexture will draw to it. GL.PushMatrix(); //Saves both projection and modelview matrices to the matrix stack. GL.LoadPixelMatrix(0, 1024, 1024, 0); //Setup a matrix for pixel-correct rendering. //Draw my stampTexture on my RenderTexture positioned by posX and posY. Graphics.DrawTexture(new Rect(posX, posY, stampTexture.width, stampTexture.height), stampTexture); Texture2D png = new Texture2D(rt.width, rt.height, TextureFormat.ARGB32, false); png.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0); System.IO.File.WriteAllBytes(Application.dataPath + "/" + "nihao.png", png.EncodeToPNG()); GL.PopMatrix(); //Restores both projection and modelview matrices off the top of the matrix stack. RenderTexture.active = null; //De-activate my RenderTexture.