Unity3D静态对象优化系列

 Unity3D对于静态对象优化,一种方式是使用Unity自带的功能比如可以选择Static,这样Unity引擎内部会对其进行优化批处理,无需使用者关心,比较简单,但是还是不够理想。另一种方式是我们用脚本进行控制,用脚本对其进行优化,相对前者效率方面比如在DrawCall上可以减少。接下来我们逐步解析,优化直至达到我们的要求为止,本次作为一个系列来讲解,为了让大家更能透彻的理解。

   我们先看一下,没选择Static和选择Static的比较结果,我们是以三个Cube为例采用同样的材质,如图所示:

 

wKiom1S59PHyXY66AAMT80yKfDw162.jpg

wKioL1S59cGg2s3iAAMTpbxKuA0156.jpg

 

基本上没啥影响,这说明Unity会自动为我们处理的,但是这不是我们需要的结果,接下来我们去优化它,优化的思路就是将这三个对象根据其MeshFilter将其组合成一个大的Mesh,这个Mesh是我们自己生成的,最后将材质赋值给它。接下来我们书写代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using  UnityEngine;
using  System.Collections;
public  class  Combine : MonoBehaviour
{
     void  Start()
     {
         MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter>();
         CombineInstance[] combine =  new  CombineInstance[meshFilters.Length];
         for  ( int  i = 0; i < meshFilters.Length; i++)
         {
             combine[i].mesh = meshFilters[i].sharedMesh;
             combine[i].transform = meshFilters[i].transform.localToWorldMatrix;
             meshFilters[i].gameObject.active =  false ;
         }
         transform.GetComponent<MeshFilter>().mesh =  new  Mesh();
         transform.GetComponent<MeshFilter>().mesh.CombineMeshes(combine);
         transform.gameObject.active =  true ;
         
     }
}

以上是代码实现,那我们如何去绑定到对象上,首先我们需要给对象加一个MeshFilter组件,为了操作方便直接将一个对象拿过来使用.如图所示:

 

wKioL1S5-JTBukYZAAEIl7ySpEA330.jpg

图中画圆圈的地方,接下来我们将我们的脚本挂接到上面去。实现效果如下图:

wKiom1S5-DWjgR_iAAOS6Ft1ZC0137.jpg

相比于我们上图通过Unity自身优化,Draw Call明显减少了,达到了我们的目的,但是还有一个问题,就是用这种方式,优化后,模型位置发生了偏移。这个不是我们想要的。还有以上是相同材质,如果我们换成不同的材质,不加脚本如下图所示:

wKioL1S5-svBw-WtAAcjwlB_mtE063.jpg

那如果我们加上优化脚本,情况如下:

wKioL1S5_TKQL-SBAASmrqUCbHs617.jpg

红色的箱子不见了,所以这么做是不允许的,注意在使用该优化脚本的时候,我们不要选择Static,接下来我们会在系列二中继续解决这个问题。


在系列一中,我们已经知道了问题所在,一个是优化后我们模型位置改变了,另一个是如果是不同的材质的物体一起优化的时候,不同的材质的对象会消失掉,我们在系列二中主要是解决这两个问题:

    接下来我们改进的思路是查找所有的MeshFilter,同时我们根据不同的材质对我们需要优化的对象进行分离。这就需要我们定义两个链表:

1
2
         ArrayList materials =  new  ArrayList();
         ArrayList combineInstanceArrays =  new  ArrayList();

下面我们开始遍历我们需要优化对象的MeshFilter和MeshRenderer,因为材质是与MeshRenderder相关联的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
         foreach  (GameObject obj  in  Objects)
         {
             if  (!obj)
                 continue ;
 
             MeshFilter[] meshFilters = obj.GetComponentsInChildren<MeshFilter>();
 
             foreach  (MeshFilter meshFilter  in  meshFilters)
             {
                 MeshRenderer meshRenderer = meshFilter.GetComponent<MeshRenderer>();
 
                 if  (!meshRenderer)
                 {
                     Debug.LogError( "MeshFilter does not have a coresponding MeshRenderer." );
                     continue ;
                 }
                 if  (meshRenderer.materials.Length != meshFilter.sharedMesh.subMeshCount)
                 {
                     Debug.LogError( "Mismatch between material count and submesh count. Is this the correct MeshRenderer?" );
                     continue ;
                 }
 
                 for  ( int  s = 0; s < meshFilter.sharedMesh.subMeshCount; s++)
                 {
                     int  materialArrayIndex = 0;
                     for  (materialArrayIndex = 0; materialArrayIndex < materials.Count; materialArrayIndex++)
                     {
                         if  (materials[materialArrayIndex] == meshRenderer.sharedMaterials[s])
                             break ;
                     }
 
                     if  (materialArrayIndex == materials.Count)
                     {
                         materials.Add(meshRenderer.sharedMaterials[s]);
                         combineInstanceArrays.Add( new  ArrayList());
                     }
 
                     CombineInstance combineInstance =  new  CombineInstance();
                     combineInstance.transform = meshRenderer.transform.localToWorldMatrix;
                     combineInstance.subMeshIndex = s;
                     combineInstance.mesh = meshFilter.sharedMesh;
                     (combineInstanceArrays[materialArrayIndex]  as  ArrayList).Add(combineInstance);
                 }
             }
         }

下面是针对MeshFilter的处理代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
     {
             MeshFilter meshFilterCombine = gameObject.GetComponent<MeshFilter>();
             if  (!meshFilterCombine)
                 meshFilterCombine = gameObject.AddComponent<MeshFilter>();
 
             Mesh[] meshes =  new  Mesh[materials.Count];
             CombineInstance[] combineInstances =  new  CombineInstance[materials.Count];
 
             for  ( int  m = 0; m < materials.Count; m++)
             {
                 CombineInstance[] combineInstanceArray = (combineInstanceArrays[m]  as  ArrayList).ToArray( typeof (CombineInstance))  as  CombineInstance[];
                 meshes[m] =  new  Mesh();
                 meshes[m].CombineMeshes(combineInstanceArray,  true true );
 
                 combineInstances[m] =  new  CombineInstance();
                 combineInstances[m].mesh = meshes[m];
                 combineInstances[m].subMeshIndex = 0;
             }
 
             meshFilterCombine.sharedMesh =  new  Mesh();
             meshFilterCombine.sharedMesh.CombineMeshes(combineInstances,  false false );
 
             foreach  (Mesh mesh  in  meshes)
             {
                 mesh.Clear();
                 DestroyImmediate(mesh);
             }
         }

将其组合成一个Mesh,接下来也是最后一步创建Mesh Renderer并将材质赋值给它

1
2
3
4
5
6
7
8
9
    {
             MeshRenderer meshRendererCombine = gameObject.GetComponent<MeshRenderer>();
             if  (!meshRendererCombine)
                 meshRendererCombine = gameObject.AddComponent<MeshRenderer>();
 
             Material[] materialsArray = materials.ToArray( typeof (Material))  as  Material[];
             meshRendererCombine.materials = materialsArray;
         }
     }

下面我们看一下优化前后有啥变化,首先我们用四个Cube和一个Sphere,三种材质。效果图如下:

wKioL1S7oeWiYeqRAAHFHA8YuNg634.jpg


下面我们将我们优化的图展示一下:

wKioL1S7omrwXel6AAVMcI0OlAI656.jpg

对比上图我们可以看到Saved by batching减少了,Used Textures 较少了,Render Textures使用的纹理大小也较少了,虽然相对来说Draw Call增加了,但是由于Render Textures少了,我们的优化效果达到了,而且位置也没变,多种材质也没问题了,但是还有个问题,就是我们的Cube有三个是相同的,但是我们看其材质的效果图:

wKioL1S7o2bSHknmAAC14p3vx0w234.jpg

相同的材质写了三次,这说明我们的程序还需要继续去优化,以上代码只要将其组合在一起就可以使用了,我们将在讲台优化系列三中继续优化我们的优化程序。

我们接着系列二的问题继续讲解,系列二中的问题是如果多个材质是相同的,它没有去优化,接下来我们将其优化一下,首先要找到在哪里去优化,我们看下面的for循环语句也是系列二的代码,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
                 for  ( int  s = 0; s < meshFilter.sharedMesh.subMeshCount; s++)
                 {
                     int  materialArrayIndex = 0;
                     for  (materialArrayIndex = 0; materialArrayIndex < materials.Count; materialArrayIndex++)
                     {
                         if  (materials[materialArrayIndex] == meshRenderer.sharedMaterials[s])
                             break ;
                     }
                     if  (materialArrayIndex == materials.Count)
                     {
                         materials.Add(meshRenderer.sharedMaterials[s]);
                         combineInstanceArrays.Add( new  ArrayList());
                     }
                     CombineInstance combineInstance =  new  CombineInstance();
                     combineInstance.transform = meshRenderer.transform.localToWorldMatrix;
                     combineInstance.subMeshIndex = s;
                     combineInstance.mesh = meshFilter.sharedMesh;
                     (combineInstanceArrays[materialArrayIndex]  as  ArrayList).Add(combineInstance);
                 }

这个代码里它没有判断是否有相同的材质,接下来我们将其重新修改一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
                 for  ( int  s = 0; s < meshFilter.sharedMesh.subMeshCount; s++)
                 {
                     int  materialArrayIndex = Contains(materials, meshRenderer.sharedMaterials[s].name);
                     if  (materialArrayIndex == -1)
                     {
                         materials.Add(meshRenderer.sharedMaterials[s]);
                         materialArrayIndex = materials.Count - 1;
                     }
                     combineInstanceArrays.Add( new  ArrayList());
                     CombineInstance combineInstance =  new  CombineInstance();
                     combineInstance.transform = meshRenderer.transform.localToWorldMatrix;
                     combineInstance.subMeshIndex = s;
                     combineInstance.mesh = meshFilter.sharedMesh;
                     (combineInstanceArrays[materialArrayIndex]  as  ArrayList).Add(combineInstance);
                 }

我们看到上面有个函数Contains它就是用于判断是否有相同的材质,如果有就作为一张材质,Contains函数代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
     private  int  Contains(ArrayList searchList,  string  searchName)
 
     {
 
         for  ( int  i = 0; i < searchList.Count; i++)
 
         {
 
             if  (((Material)searchList[i]).name == searchName)
 
             {
 
                 return  i;
 
             }
 
         }
 
         return  -1;
 
     }

作用是用于返回相同材质的索引,效果如下:

wKiom1S9uuXjP3PvAAYPkBx1Gig256.jpg

看上图红色的部分,有两个材质是一样的,材质只显示一个了,将其优化了。整个静态对象的优化就给大家解答完了。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值