项目中因为需要更换模型的各种贴图(比如眉毛,胡子,睫毛等),需要用到多个贴图,然后在gpu把颜色混合到角色肤色上,来得到我们显示的效果
每个位置之前都是一个贴图混合而成,
这样会有几个问题:
1.每个位置为一个贴图,那么每个贴图都会占用一个TEXCOORD语义,寄存器使用非常多。
2.每张图都只是用到一小部分,浪费非常大的内存空间
3.gpu做了大量的无论计算。因为图片过多,也导致了无谓的带宽过大
基于以上问题,考虑把多个贴图合并后用一个只渲染一个贴图的shader来实现。
合并方式:
1.最开始需要把贴图设置为可读写的,不然是没办法读取贴图的具体像素的。虽然会增加一些内存,但是是可以接受的,大概1/3的内存增加把
2.在换装结束或者刚进游戏时(总之就是重置形象时)把所有贴图合并为一个贴图
3.把材质上的shader换成我们只需渲染一整块贴图的shader
第一步主要是做一个editor工具,方便设置是自己写导入的工具
public class TextureAssetPostProcessor : FusionBaseAssetPostProcessor
{
for (int i = 0; i < filters.Length; i++) {
if (paths[paths.Length - 2].Equals(filterReadables[i])) {
TextureImporter importer = assetImporter as TextureImporter;
importer.isReadable = true;
}
}
}
上面是简化的代码,总的意思是说需要让指定目录的贴图在导入到项目时就设置为可读写
第二步合并的代码大意:
var pixels = tex.GetPixels32();
for (int j = 0; j < pixels.Length; j++)
{
if (pixels[j].a != 0)
{
var newPixel = pixels[j];
var curColors = colors[j];
var dstColors= Color32.Lerp(curColors, newPixel, (float)newPixel.a / 255.0f);
colors[j] = new Color32((byte)(curColors.r > 0 ? (dstColors.r) : newPixel.r),
(byte)(curColors.g > 0 ? (dstColors.g) : newPixel.g),
(byte)(curColors.b > 0 ? (dstColors.b) : newPixel.b),
(byte)(curColors.a > 0 ? (dstColors.a) : newPixel.a));
}
}
这里是获取每个图片的像素,然后插值获取具体的像素上的值。注意的是这里一定要插值,不然如果大家都在统一像素上有像素,后面的贴图上的像素会覆盖前面的
var texture2d = new Texture2D(2048, 2048);
texture2d.SetPixels32(colors);
texture2d.Apply();
最后把贴图设置上相应颜色,注意最后要apply
第三步就是设置具体的shader并把图片附上了
var faceOtherShader = Shader.PropertyToID("_FaceOtherMap");
var hasFaceMap = bodymat.HasProperty(faceOtherShader);
if (hasFaceMap)
{
var texture = bodymat.GetTexture(faceOtherShader);
if (texture != null)
{
Texture2D.Destroy(texture);
texture = null;
}
}
bodymat.shader = AvatarManager.instance.shaderPreloader[6];
bodymat.EnableKeyword("_FACE_COMBINE_TEXTURE");
bodymat.SetTexture(faceOtherShader, texture2d);
具体不细说了。
得到的效果如下:
整体难度不大,记录下来说不定以后能直接用上。