Unity中的网格与材质球合并

Wesley13
• 阅读 656

https://blog.csdn.net/dardgen2015/article/details/51517860

很多时候我们需要把具有相同shader的材质球合并,从而减少drawcall的产生。

比如九龙战里面,一个人物带有10个部位,10个部位各自来自不同的fbx文件,加上身体,就有11个材质球,占上11个drawcall。如果主城里面跑着10个角色,光人物就占了110个drawcall!所以这种时候材质球合并是必须的。

(下图为九龙战里面的多部位换装效果)

Unity中的网格与材质球合并

材质球合并,分以下几步走,首先我们讨论普通的MeshRenderer的材质球合并,然后再讨论SkinnedMeshRenderer的材质球合并。

普通的MeshRenderer的材质球合并:

1.合并所有材质球所携带的贴图,新建一个材质球,并把合并好的贴图赋予新的材质球。

2.记录下每个被合并的贴图所处于新贴图的Rect,用一个Rect[]数组存下来。

3.合并网格,并把需要合并的各个网格的uv,根据第2步得到的Rect[]刷一遍。

4.把新的材质球赋予合并好的网格,此时就只占有1个drawcall了。

下面是关键代码:

  1. void CombineMesh()

  2. {

  3. MeshFilter[] mfChildren = GetComponentsInChildren();

  4. CombineInstance[] combine = new CombineInstance[mfChildren.Length];

  5. MeshRenderer[] mrChildren = GetComponentsInChildren();

  6. Material[] materials = new Material[mrChildren.Length];

  7. MeshRenderer mrSelf = gameObject.AddComponent();

  8. MeshFilter mfSelf = gameObject.AddComponent();

  9. Texture2D[] textures = new Texture2D[mrChildren.Length];

  10. for (int i = 0; i < mrChildren.Length; i++)

  11. {

  12. if (mrChildren[i].transform == transform)

  13. {

  14. continue;

  15. }

  16. materials[i] = mrChildren[i].sharedMaterial;

  17. Texture2D tx = materials[i].GetTexture( "_MainTex") as Texture2D;

  18. Texture2D tx2D = new Texture2D(tx.width, tx.height, TextureFormat.ARGB32, false);

  19. tx2D.SetPixels(tx.GetPixels( 0, 0, tx.width, tx.height));

  20. tx2D.Apply();

  21. textures[i] = tx2D;

  22. }

  23. Material materialNew = new Material(materials[0].shader);

  24. materialNew.CopyPropertiesFromMaterial(materials[ 0]);

  25. mrSelf.sharedMaterial = materialNew;

  26. Texture2D texture = new Texture2D(1024, 1024);

  27. materialNew.SetTexture( "_MainTex", texture);

  28. Rect[] rects = texture.PackTextures(textures, 10, 1024);

  29. for (int i = 0; i < mfChildren.Length; i++)

  30. {

  31. if (mfChildren[i].transform == transform)

  32. {

  33. continue;

  34. }

  35. Rect rect = rects[i];

  36. Mesh meshCombine = mfChildren[i].mesh;

  37. Vector2[] uvs = new Vector2[meshCombine.uv.Length];

  38. //把网格的uv根据贴图的rect刷一遍

  39. for (int j = 0; j < uvs.Length; j++)

  40. {

  41. uvs[j].x = rect.x + meshCombine.uv[j].x * rect.width;

  42. uvs[j].y = rect.y + meshCombine.uv[j].y * rect.height;

  43. }

  44. meshCombine.uv = uvs;

  45. combine[i].mesh = meshCombine;

  46. combine[i].transform = mfChildren[i].transform.localToWorldMatrix;

  47. mfChildren[i].gameObject.SetActive( false);

  48. }

  49. Mesh newMesh = new Mesh();

  50. newMesh.CombineMeshes(combine, true,true);//合并网格

  51. mfSelf.mesh = newMesh;

  52. }

合并前的drawcall:

Unity中的网格与材质球合并

合并后的drawcall:

Unity中的网格与材质球合并

合并好的网格:

Unity中的网格与材质球合并

合并好的贴图:

Unity中的网格与材质球合并

下面我们将讨论SkinnedMeshRenderer的合并。

SkinnedMeshRenderer比MeshRenderer稍微麻烦一点,因为SkinnedMeshRenderer要处理bones。

以下是步骤:

1.合并所有材质球所携带的贴图,新建一个材质球,并把合并好的贴图赋予新的材质球。

2.记录下每个被合并的贴图所处于新贴图的Rect,用一个Rect[]数组存下来。

3.记录下需要合并的SkinnedMeshRenderer的bones。

4.合并网格,并把需要合并的各个网格的uv,根据第2步得到的Rect[]刷一遍。

5.把合并好的网格赋予新的SkinnedMeshRenderer,并把第3步记录下的bones赋予新的SkinnedMeshRenderer。

6.把新的材质球赋予合并好的网格,此时就只占有1个drawcall了。

上面红色部分的步骤就是与MeshRenderer不同之处。

下面是关键代码:

  1. void CombineMesh()

  2. {

  3. SkinnedMeshRenderer[] smrs = GetComponentsInChildren();

  4. CombineInstance[] combine = new CombineInstance[smrs.Length];

  5. Material[] materials = new Material[smrs.Length];

  6. Texture2D[] textures = new Texture2D[smrs.Length];

  7. SkinnedMeshRenderer smrCombine = combineMesh.gameObject.AddComponent();

  8. smrCombine.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;

  9. smrCombine.receiveShadows = false;

  10. for (int i = 0; i < smrs.Length; i++)

  11. {

  12. materials[i] = smrs[i].sharedMaterial;

  13. Texture2D tx = materials[i].GetTexture( "_MainTex") as Texture2D;

  14. Texture2D tx2D = new Texture2D(tx.width, tx.height, TextureFormat.ARGB32, false);

  15. tx2D.SetPixels(tx.GetPixels( 0, 0, tx.width, tx.height));

  16. tx2D.Apply();

  17. textures[i] = tx2D;

  18. }

  19. Material materialNew = new Material(materials[0].shader);

  20. materialNew.CopyPropertiesFromMaterial(materials[ 0]);

  21. Texture2D texture = new Texture2D(1024, 1024);

  22. Rect[] rects = texture.PackTextures(textures, 10, 1024);

  23. materialNew.SetTexture( "_MainTex", texture);

  24. List boneTmp = new List();

  25. for (int i = 0; i < smrs.Length; i++)

  26. {

  27. if (smrs[i].transform == transform)

  28. {

  29. continue;

  30. }

  31. Rect rect = rects[i];

  32. Mesh meshCombine = CreatMeshWithMesh(smrs[i].sharedMesh);

  33. Vector2[] uvs = new Vector2[meshCombine.uv.Length];

  34. for (int j = 0; j < uvs.Length; j++)

  35. {

  36. uvs[j].x = rect.x + meshCombine.uv[j].x * rect.width;

  37. uvs[j].y = rect.y + meshCombine.uv[j].y * rect.height;

  38. }

  39. boneTmp.AddRange(smrs[i].bones);

  40. meshCombine.uv = uvs;

  41. combine[i].mesh = meshCombine;

  42. combine[i].transform = smrs[i].transform.localToWorldMatrix;

  43. GameObject.Destroy(smrs[i].gameObject);

  44. }

  45. Mesh newMesh = new Mesh();

  46. newMesh.CombineMeshes(combine, true, true);

  47. smrCombine.bones = boneTmp.ToArray();

  48. smrCombine.rootBone = rootBone;

  49. smrCombine.sharedMesh = newMesh;

  50. smrCombine.sharedMaterial = materialNew;

  51. }

  52. Mesh CreatMeshWithMesh(Mesh mesh)

  53. {

  54. Mesh mTmp = new Mesh();

  55. mTmp.vertices = mesh.vertices;

  56. mTmp.name = mesh.name;

  57. mTmp.uv = mesh.uv;

  58. mTmp.uv2 = mesh.uv2;

  59. mTmp.uv2 = mesh.uv2;

  60. mTmp.bindposes = mesh.bindposes;

  61. mTmp.boneWeights = mesh.boneWeights;

  62. mTmp.bounds = mesh.bounds;

  63. mTmp.colors = mesh.colors;

  64. mTmp.colors32 = mesh.colors32;

  65. mTmp.normals = mesh.normals;

  66. mTmp.subMeshCount = mesh.subMeshCount;

  67. mTmp.tangents = mesh.tangents;

  68. mTmp.triangles = mesh.triangles;

  69. return mTmp;

  70. }

未合并前,有两个网格,占用2drawcall,分别为人物和刀的网格和材质球。

Unity中的网格与材质球合并

合并后,只占用 1 drawcall了,而且AnimationController以及其动画能够正常工作:

Unity中的网格与材质球合并

合并好的网格:

Unity中的网格与材质球合并

合并好的贴图:

Unity中的网格与材质球合并

以上便是合并Mesh和Material的内容,目前只关注实现,还可以进一步优化,有以下几点要注意的:

1.合并的材质球需要使用同一个shader,如果多个材质球使用了不同shader,就要做进一步的出分类处理了。

2.材质球所用的Texture文件的Read/Write Enable选项要打上勾。

项目的github地址:  https://github.com/arBao/MeshMaterialCombine

原创文章,转载请注明出处:   http://blog.csdn.net/dardgen2015/article/details/51517860

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Karen110 Karen110
2年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
2年前
Unity横屏
Android下发现Unity里面的Player设置,并不能完全有效,比如打开了自动旋转,启动的时候还是会横屏,修改XML添加以下代码<applicationandroid:icon"@drawable/ic\_launcher"                    android:label"@string/app\_name"
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
4个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这