Unity模型合并调研(1)

目录

一、模型合并研究

1.基本合并

2.按材质合并

3.单个模型顶点数不能超过65535

3.1.限制单个模型顶点数

3.2.使用UInt32 IndexFormat

4.模型分离

4.1.增加模型

4.2.减少模型

5.打包后运行崩溃问题

6.协程

7.保存合并后模型

7.1 托预设

7.2 存放到子场景中

7.3 文件保存

8.性能测试

二.底层Mesh合并研究

1.创建Mesh

2.修改Mesh

3.合并Mesh

三.插件


一、模型合并研究

1.基本合并

Read/Write Enabled 必须设置为true

参考1:https://blog.csdn.net/ecidevilin/article/details/52490960

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//[RequireComponent(typeof(MeshRenderer),typeof(MeshFilter))]
public class MeshCombiner : MonoBehaviour
{
    public GameObject source;

    public GameObject target;

    // Start is called before the first frame update
    void Start()
    {
        Combine();
    }

    [ContextMenu("Combine")]

    private void Combine(){

        if(source==null){
            source=this.gameObject;
        }
        if(target==null){
            target=this.gameObject;
        }

        MeshFilter[] meshFilters=source.GetComponentsInChildren<MeshFilter>();
        CombineInstance[] combines=new CombineInstance[meshFilters.Length];
        Material[] mats=new Material[meshFilters.Length];
        Matrix4x4 matrix=source.transform.worldToLocalMatrix;
        for(int i=0;i<meshFilters.Length;i++)
        {
            MeshFilter mf=meshFilters[i];
            MeshRenderer mr=mf.GetComponent<MeshRenderer>();
            if(mr==null)continue;
            combines[i].mesh=mf.sharedMesh;
            combines[i].transform=matrix*mf.transform.localToWorldMatrix;
            //combines[i].transform=mf.transform;
            mr.enabled=false;
            mats[i]=mr.sharedMaterial;
        }
        //source.gameObject.SetActive(false);

        MeshFilter meshFilter=target.GetComponent<MeshFilter>();
        Mesh mesh=new Mesh();
        mesh.name=source.name+"_Combined";
        meshFilter.mesh=mesh;
        mesh.CombineMeshes(combines,false);//合并网格
        MeshRenderer meshRenderer=target.GetComponent<MeshRenderer>();
        meshRenderer.sharedMaterials=mats;
        meshRenderer.enabled=true;

        Collider collider=target.GetComponent<Collider>();
        if(collider!=null){
            GameObject.DestroyImmediate(collider);
            MeshCollider meshCollider=target.AddComponent<MeshCollider>();
            meshCollider.sharedMesh=mesh;
        }
        target.transform.position=source.transform.position;
        
    }
}

奇怪的是,运行后执行的结果是模型都没有显示,手动在Edit/Run状态下点击脚本菜单则可以,运行后点击Button也不行。

我需要的是运行时动态合并,而不是事先在Editor中合并。

官方文档:

using UnityEngine;
using System.Collections;

// Copy meshes from children into the parent's Mesh.
// CombineInstance stores the list of meshes.  These are combined
// and assigned to the attached Mesh.

[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
public class ExampleClass : MonoBehaviour
{
    void Start()
    {
        MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter>();
        CombineInstance[] combine = new CombineInstance[meshFilters.Length];

        int i = 0;
        while (i < meshFilters.Length)
        {
            combine[i].mesh = meshFilters[i].sharedMesh;
            combine[i].transform = meshFilters[i].transform.localToWorldMatrix;
            meshFilters[i].gameObject.SetActive(false);

            i++;
        }
        transform.GetComponent<MeshFilter>().mesh = new Mesh();
        transform.GetComponent<MeshFilter>().mesh.CombineMeshes(combine);
        transform.gameObject.SetActive(true);
    }
}

官方文档也是这样的问题,运行后,没有模型显示了。我的Unity时2020.1.6f

写了个MeshInfo,看看运行后合成的MeshFilter里面的Mesh的信息,发现空的,没有任何点存在。

经过更多的测试,比如写了个CombineInstanceInfo记录CombineInstance的信息,发现了mesh的vertex信息实际上无法提取出来。

[Serializable]
public class CombineInstanceInfo
{
    public Mesh mesh;
    public Matrix4x4 transform;

    public Vector3[] Vertices;
}
            combineInstanceInfos[i]=new CombineInstanceInfo();
            combineInstanceInfos[i].mesh=ms;
            combineInstanceInfos[i].transform=matrix*mf.transform.localToWorldMatrix;
            combineInstanceInfos[i].Vertices=ms.vertices;

问题出在上面的             combineInstanceInfos[i].Vertices=ms.vertices;

然后就知道了是模型必须设置为可以读写的,没有一个资料里写啊。TMD,那你默认可读写啊!或者CombineMeshes里面报个错啊!或者你的实例代码中判断一下啊!

-----------------------------------

2.按材质合并

上面的代码对于模型少的物体还好,我的需求是几万个物体要合并,其中材质大概十几种,按照上面的来,一个模型就有十几万个材质了,无论怎么想,效率都会出问题。

区分一下,合并相同材质的物体,然后再合并不同材质的物体。

甚至有些物体有多个材质的情况下,该怎么办!先不考虑,我这里假设一个物体只有一个材质,很多物体其实就共用了几个材质。

参考:https://www.habrador.com/tutorials/unity-mesh-combining-tutorial/3-combine-meshes-colors/

重构了一个合并的类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Linq;

 [Serializable]
public class CombinedMesh{
    public Mesh mesh;

    public Material[] mats;

    public Material mat;

    private Transform source;
    private List<MeshFilter> meshFilters;

    public CombinedMesh(Transform source,List<MeshFilter> meshFilters, Material mat){
        this.source=source;
        this.meshFilters=meshFilters;
        this.mat=mat;
        DoCombine();
    }

    private void DoCombine(){
        DateTime start=DateTime.Now;
        if(meshFilters==null || meshFilters.Count==0){
            meshFilters=source.GetComponentsInChildren<MeshFilter>().ToList();
        }
        int count=meshFilters.Count;
        CombineInstance[] combines=new CombineInstance[count];
        if(mat==null)
            mats=new Material[count];
        Matrix4x4 matrix=source.worldToLocalMatrix;
        for(int i=0;i<count;i++)
        {
            MeshFilter mf=meshFilters[i];
            MeshRenderer mr=mf.GetComponent<MeshRenderer>();
            if(mr==null)continue;
            Mesh ms=mf.sharedMesh;
            if(ms.isReadable==false){
                Debug.LogError("模型必须设置为 Read/Write Enabled = True !! :"+ms);
                continue;
            }
            combines[i].mesh=ms;
            combines[i].transform=matrix*mf.transform.localToWorldMatrix;
            //combines[i].transform=mf.transform;
            mr.enabled=false;

            if(mat==null)
                mats[i]=mr.sharedMaterial;
        }
        //source.gameObject.SetActive(false);

        mesh=new Mesh();
        mesh.name=source.name+"_Combined";
        bool mergeSubMeshes=mat!=null;
        mesh.CombineMeshes(combines,mergeSubMeshes);//合并网格
        Debug.LogWarning(string.Format("CombinedMesh 数量:{0},用时:{1}",count,(DateTime.Now-start)));
    }

    public void SetRendererAndFilter(GameObject go){
        MeshFilter meshFilter=go.GetComponent<MeshFilter>();
        if(meshFilter==null){
            meshFilter=go.AddComponent<MeshFilter>();
        }
        meshFilter.mesh=this.mesh;
        MeshRenderer meshRenderer=go.GetComponent<MeshRenderer>();
        if(meshRenderer==null){
            meshRenderer=go.AddComponent<MeshRenderer>();
        }

        if(mat!=null){
            meshRenderer.sharedMaterial=mat;
        }
        else
        {
            meshRenderer.sharedMaterials=this.mats;
        }
        
        meshRenderer.enabled=true;
    }
    
    public void SetCollider(GameObject go){
        Collider collider=go.GetComponent<Collider>();
        if(collider!=null){
            GameObject.DestroyImmediate(collider);
        }
        MeshCollider meshCollider=go.AddComponent<MeshCollider>();
        meshCollider.sharedMesh=this.mesh;
    }

    public GameObject CreateNewGo(GameObject source,bool enableCollider,GameObject target){
        if(target==null){
            target=new GameObject();
            target.name=source.name+"_Combined";
        }
        this.SetRendererAndFilter(target);
        if(enableCollider)
            this.SetCollider(target);
        target.transform.position=source.transform.position;//坐标一致,不设置的话,就是按照新的target的坐标来
        return target;
    }
}

原来的合并代码

    [ContextMenu("Combine")]

    public void Combine(){
        
        if(source==null){
            source=this.gameObject;
        }
        MeshFilter[] meshFilters=source.GetComponentsInChildren<MeshFilter>();
        CombinedMesh combinedMesh=new CombinedMesh(source.transform,meshFilters.ToList(),null);
        target=combinedMesh.CreateNewGo(source,true,target);
        target.transform.SetParent(this.transform);
        //target.AddComponent<MeshInfo>();
        Debug.Log("Combine:"+source+"->"+target);
    }

按材质合并的代码

    [ContextMenu("CombineByMaterial")]

    public void CombineByMaterial(){
        target=CombineMaterials(source);
        target.transform.SetParent(this.transform);
    }

    public static GameObject CombineMaterials(GameObject go){
        GameObject goNew=new GameObject();
        goNew.name=go.name+"_Combined";
        Dictionary<Material,List<MeshFilter>> mat2Filters=GetMatFilters(go);
        foreach(var item in mat2Filters)
        {
            Material material=item.Key;
            List<MeshFilter> list=item.Value;

            CombinedMesh combinedMesh=new CombinedMesh(go.transform,list,material);
            GameObject matGo=combinedMesh.CreateNewGo(go,true,null);
            matGo.name=material.name;
            matGo.transform.SetParent(goNew.transform);
        }
        return goNew;
    }

按材质合并后再合并,正式使用应该用这个

    [ContextMenu("CombineEx")]

    public void CombineEx(){
        GameObject goNew=CombineMaterials(source);

        CombinedMesh combinedMesh=new CombinedMesh(goNew.transform,null,null);
        target=combinedMesh.CreateNewGo(goNew,true,target);
        target.transform.SetParent(this.transform);
        target.name=source.name+"_Combined";
        GameObject.DestroyImmediate(goNew);
    }

3.单个模型顶点数不能超过65535

ArgumentException: The number of vertices in the combined mesh (174816) exceeds the maximum supported vertex count (65535) of the UInt16 index format. Consider using the UInt32 IndexFormat for the combined mesh to increase the max vertex count.

3.1.限制单个模型顶点数

参考:https://www.habrador.com/tutorials/unity-mesh-combining-tutorial/3-combine-meshes-colors/

加了个数量限制,结果又多了一层子物体的类的定义了。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Linq;

 [Serializable]
public class MeshPartInfo{
    public Mesh mesh;

    public Material[] mats;
}

 [Serializable]
public class CombinedMesh{
    //public Mesh mesh;

    public List<MeshPartInfo> meshes=new List<MeshPartInfo>();

    public Material mat;

    private Transform source;
    private List<MeshFilter> meshFilters;

    public int VertexCount=0;

    public int MaxVertex=65535;

    public CombinedMesh(Transform source,List<MeshFilter> mfs, Material mat){
        this.source=source;

        if(mfs==null || mfs.Count==0){
            this.meshFilters=source.GetComponentsInChildren<MeshFilter>().ToList();
        }
        else{
            this.meshFilters=new List<MeshFilter>(mfs);
        }
        this.mat=mat;
        //SimpleCombine();

        //Debug.LogError("CombinedMesh Read/Write Enabled = True !! :"+ms);
    }

    private MeshPartInfo InnerDoCombine(List<MeshFilter> mfs,int id){

        int count=mfs.Count;
        if(count==0){
            Debug.LogError("meshFiltersToCombined.Count == 0");
            return null;
        }
        CombineInstance[] combines=new CombineInstance[count];
        Material[] mats=null;
        if(mat==null)
            mats=new Material[count];
        Matrix4x4 matrix=source.worldToLocalMatrix;
        for(int i=0;i<count;i++)
        {
            MeshFilter mf=mfs[i];
            MeshRenderer mr=mf.GetComponent<MeshRenderer>();
            if(mr==null)continue;
            Mesh ms=mf.sharedMesh;
            if(ms.isReadable==false){
                Debug.LogError("模型必须设置为 Read/Write Enabled = True !! :"+ms);
                continue;
            }
            combines[i].mesh=ms;
            combines[i].transform=matrix*mf.transform.localToWorldMatrix;
            //combines[i].transform=mf.transform;
            mr.enabled=false;

            if(mat==null)
                mats[i]=mr.sharedMaterial;
        }
        //source.gameObject.SetActive(false);

        Mesh newMesh=new Mesh();
        newMesh.name=source.name+"_Combined"+id;
        if(mat!=null){
            newMesh.name=mat.name+"_Combined"+id;
        }
        bool mergeSubMeshes=mat!=null;
        newMesh.CombineMeshes(combines,mergeSubMeshes);//合并网格

        MeshPartInfo info=new MeshPartInfo();
        info.mesh=newMesh;
        info.mats=mats;
        return info;
    }

    public void DoCombine(bool logTime){

        List<List<MeshFilter>> allList=new List<List<MeshFilter>>();
        List<MeshFilter> list=new List<MeshFilter>();
        allList.Add(list);

        VertexCount=0;
        int vcSum=0;
        int count=meshFilters.Count;
        for(int i=0;i<count;i++)
        {
            MeshFilter mf=meshFilters[i];
            Mesh ms=mf.sharedMesh;
            //Debug.Log(string.Format("DoCombine[{0}/{1}]:{2}",i,count,VertexCount));
            int vc=ms.vertexCount;
            VertexCount+=vc;

            if(vcSum+vc>MaxVertex)
            {
                vcSum=vc;
                list=new List<MeshFilter>();
                list.Add(mf);
                allList.Add(list);
            }
            else{
                vcSum+=vc;
                list.Add(mf);
            }
        }
        //Debug.Log(string.Format("DoCombine[{0}/{1}]:{2}",i,count,VertexCount));
        for(int i=0;i<allList.Count;i++)
        {
            List<MeshFilter> mfs=allList[i];
            var newMesh=InnerDoCombine(mfs,i);
            meshes.Add(newMesh);
        }

        DateTime start=DateTime.Now;
        Debug.LogWarning(string.Format("CombinedMesh Mesh数量:{0} 子模型数:{1},VertexCount:{2},Mat:{3},用时:{4}ms",count,allList.Count,VertexCount,mat,(DateTime.Now-start).TotalMilliseconds));
    }

    public void SetRendererAndFilter(GameObject go,MeshPartInfo ms){
        MeshFilter meshFilter=go.GetComponent<MeshFilter>();
        if(meshFilter==null){
            meshFilter=go.AddComponent<MeshFilter>();
        }
        meshFilter.mesh=ms.mesh;
        MeshRenderer meshRenderer=go.GetComponent<MeshRenderer>();
        if(meshRenderer==null){
            meshRenderer=go.AddComponent<MeshRenderer>();
        }

        if(mat!=null){
            meshRenderer.sharedMaterial=mat;
        }
        else
        {
            meshRenderer.sharedMaterials=ms.mats;
        }
        
        meshRenderer.enabled=true;
    }
    
    public void SetCollider(GameObject go,MeshPartInfo ms){
        Collider collider=go.GetComponent<Collider>();
        if(collider!=null){
            GameObject.DestroyImmediate(collider);
        }
        MeshCollider meshCollider=go.AddComponent<MeshCollider>();
        meshCollider.sharedMesh=ms.mesh;
    }

    public GameObject CreateNewGo(GameObject source,bool enableCollider,GameObject target){
        if(target==null){
            target=new GameObject();
            target.name=source.name+"_Combined";
        }
        if(meshes.Count==1){
            this.SetRendererAndFilter(target,meshes[0]);
            if(enableCollider)
                this.SetCollider(target,meshes[0]);
        }
        else{
            for(int i=0;i<meshes.Count;i++){
                var info=meshes[i];
                GameObject subObj=new GameObject();
                subObj.name=info.mesh.name;
                subObj.transform.SetParent(target.transform);

                this.SetRendererAndFilter(subObj,info);
                if(enableCollider)
                    this.SetCollider(subObj,info);
            }
        }
        
        target.transform.position=source.transform.position;//坐标一致,不设置的话,就是按照新的target的坐标来
        return target;
    }


}

public static class MeshCombineHelper
{

    public static GameObject SimpleCombine(GameObject source,GameObject target){
        
        CombinedMesh combinedMesh=new CombinedMesh(source.transform,null,null);
        combinedMesh.DoCombine(true);
        target=combinedMesh.CreateNewGo(source,true,target);
        //target.AddComponent<MeshInfo>();
        Debug.Log("Combine:"+source+"->"+target);
        return target;
    }
    
    public static GameObject CombineMaterials(GameObject go,out int count){
        GameObject goNew=new GameObject();
        goNew.name=go.name+"_Combined";
        Dictionary<Material,List<MeshFilter>> mat2Filters=GetMatFilters(go,out count);
        foreach(var item in mat2Filters)
        {
            Material material=item.Key;
            List<MeshFilter> list=item.Value;

            CombinedMesh combinedMesh=new CombinedMesh(go.transform,list,material);
            combinedMesh.DoCombine(true);
            GameObject matGo=combinedMesh.CreateNewGo(go,true,null);
            matGo.name=material.name;
            matGo.transform.SetParent(goNew.transform);
        }
        return goNew;
    }
   
    public static Dictionary<Material,List<MeshFilter>> GetMatFilters(GameObject go,out int count){
        DateTime start=DateTime.Now;
        Dictionary<Material,List<MeshFilter>> mat2Filters=new Dictionary<Material, List<MeshFilter>>();
        MeshRenderer[] renderers=go.GetComponentsInChildren<MeshRenderer>();
        count=renderers.Length;
        for(int i=0;i<renderers.Length;i++){
            MeshRenderer renderer=renderers[i];
            if(!mat2Filters.ContainsKey(renderer.sharedMaterial)){
                mat2Filters.Add(renderer.sharedMaterial,new List<MeshFilter>());
            }
            List<MeshFilter> list=mat2Filters[renderer.sharedMaterial];
            MeshFilter filter=renderer.GetComponent<MeshFilter>();
            list.Add(filter);
        }
        Debug.LogWarning(string.Format("GetMatFilters Mat数量:{0},Mesh数量:{1},用时:{2}ms",mat2Filters.Count,count,(DateTime.Now-start).TotalMilliseconds));
        return mat2Filters;
    }

    public static GameObject Combine(GameObject source){
        DateTime start=DateTime.Now;
        int count=0;
        GameObject goNew=CombineMaterials(source,out count);//按材质合并
        CombinedMesh combinedMesh=new CombinedMesh(goNew.transform,null,null);
        combinedMesh.DoCombine(false);
        GameObject target=combinedMesh.CreateNewGo(goNew,true,null);
        target.name=source.name+"_Combined";
        GameObject.DestroyImmediate(goNew);
        Debug.LogWarning(string.Format("CombinedMesh 数量:{0},用时:{1}ms",count,(DateTime.Now-start).TotalMilliseconds));
        return target;
    }
}

发现实际项目中,合并一个建筑模型,需要4s。时间有些慢。

3.2.使用UInt32 IndexFormat

和上面的结合,做一些配置,可以选择是否要限制顶点数量

    public int MaxVertex=65535;

    public UnityEngine.Rendering.IndexFormat indexFormat=UnityEngine.Rendering.IndexFormat.UInt16;

    public bool NoLimit=false;

    // Start is called before the first frame update
    void Start()
    {
        if(Auto)
            Combine();

        if(NoLimit){
            indexFormat=UnityEngine.Rendering.IndexFormat.UInt32;
            MaxVertex=int.MaxValue;
        }
        else{
            if(MaxVertex>65535){
                indexFormat=UnityEngine.Rendering.IndexFormat.UInt32;
            }
        }
        CombinedMesh.indexFormat=indexFormat;
        CombinedMesh.MaxVertex=MaxVertex;
    }

4.模型分离

参考:https://www.habrador.com/tutorials/unity-mesh-combining-tutorial/5-add-remove-trees/

它里面好像是处理Mesh的顶点,我的想法是增加,就是和合并后的模型再合并,或者直接重新合并;减少,就是重新合并模型。

4.1.增加模型

   public bool AutoAdd;

    public List<GameObject> mergedObjs=new List<GameObject>();

    public GameObject mergedObjRoot=null;

    // Update is called once per frame
    void Update()
    {
        if(Input.GetMouseButtonUp(0)){
            Debug.Log("Click");
            Ray ray=Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            if(Physics.Raycast(ray,out hit))
            {
                GameObject go=hit.collider.gameObject;
                Debug.Log("Hit:"+go);
                if(AutoAdd){
                    if(!mergedObjs.Contains(go)){
                        if(mergedObjRoot==null){
                            mergedObjRoot=new GameObject();
                            mergedObjRoot.name="mergedObjRoot";
                        }

                        mergedObjs.Add(go);
                        go.transform.SetParent(mergedObjRoot.transform);

                        target=MeshCombineHelper.SimpleCombine(mergedObjRoot,target);
                        target.transform.SetParent(this.transform);
                        //target.AddComponent<MeshInfo>();
                        Debug.Log("Combine:"+source+"->"+target);
                    }
                }
            }
        }
    }

4.2.减少模型

1.查找独立模型对应的合并后的模型

2.减掉该模型并重新合并

    public static Dictionary<GameObject,CombinedMesh> go2ms=new Dictionary<GameObject, CombinedMesh>();
    public static void AddGo(GameObject gameObject,CombinedMesh mesh){
        if(go2ms.ContainsKey(gameObject)){
            go2ms[gameObject]=mesh;
        }
        else{
            go2ms.Add(gameObject,mesh);
        }
        
    }

    public static CombinedMesh GetMesh(GameObject gameObject){
        if(go2ms.ContainsKey(gameObject)){
            return go2ms[gameObject];
        }
        else{
            return null;
        }
    }

    public static void RemveGo(GameObject gameObject){
        CombinedMesh mesh=GetMesh(gameObject);
        if(mesh!=null){
            Debug.Log("mesh:"+mesh.name);
            Debug.Log("mesh.meshFilters:"+mesh.meshFilters.Count);
            MeshFilter[] meshFilters=gameObject.GetComponentsInChildren<MeshFilter>();
            foreach(var mf in meshFilters){
                mesh.meshFilters.Remove(mf);
            }
            MeshRenderer[] meshRenderers=gameObject.GetComponentsInChildren<MeshRenderer>();
            foreach(var mr in meshRenderers){
                mr.enabled=true;
                mr.material.SetColor("_BaseColor",Color.red);
            }
            Debug.Log("mesh.meshFilters:"+mesh.meshFilters.Count);
            mesh.Refresh();//重新合并
        }
        else{
            Debug.LogError("未找到CombinedMesh:"+gameObject);
        }
    }
    public GameObject Refresh(){
        this.DoCombine(true);
        GameObject target=this.CreateNewGo(false,NewGo);
        return target;
    }

5.打包后运行崩溃问题

打包后,运行exe,合并模型,会崩溃。

模型数量少的一般不会崩溃,数量多的话则容易崩溃。

从日志来看,是使用了非法内存地址。

算法代码应该没问题的。

6.协程

为了处理上面的问题,考虑使用携程,在各个循环的地方增加yield return,减少cpu、内存的压力。

协程代码写好后,测试,当然合并用时更多了,整个流程比较流畅了,比较不会卡,但是还是会崩溃。

而且崩溃的问题一般出现在第二个合并的模型,比如A,B,C模型,合并A,没问题,再合并B,崩溃。合并B,没问题,再合并A,崩溃。合并A、B,没问题,再合并C,崩溃。合并A、B、C,崩溃。

7.保存合并后模型

7.1 托预设

不行,托了预设,但是再拖到项目中,没有模型,说明预设文件没有保存模型信息

7.2 存放到子场景中

将合并后的模型存放到一个新建的场景中,保存场景文件,之后加载该场景。可以。

7.3 文件保存

暂时没时间研究

8.性能测试

合并就是为了提高帧率嘛,记录一下具体的参数信息,Unity的Editor下运行,使用一个SuperFPS测试帧率。

二.底层Mesh合并研究

我上面的那些代码都是基于CombineMeshes这个函数的包装,可以说没有任何"核心技术"的,接下来研究一下Mesh的底层

1.创建Mesh

2.修改Mesh

3.合并Mesh

 

三.插件

1.MeshBaker

插件网址:https://assetstore.unity.com/packages/tools/modeling/mesh-baker-5017

插件使用:

我看这个插件的代码,它里面并没有使用到CombineMeshes。

它这个插件倒是自己写的合并Mesh,但是用起来怎么都不方便,我只要Mesh合并,它总是要考虑材质的问题,它能够合并不同材质的贴图的。但是我的项目其实不需要合并材质,不同的材质大部分情况下也不会合并或者相互影响。

举例来说我有十几个模型,4个材质ABCD,用它的合并材质功能,合并成了3个材质,材质还丢了一个。

 

--------------------------------------------------------------------------------------

动态网格合并器:https://assetstore.unity.com/packages/tools/game-toolkits/runtime-mesh-baker-90510#reviews

其他:https://assetstore.unity.com/packages/tools/utilities/simple-mesh-combine-8748

https://assetstore.unity.com/packages/tools/utilities/batch-few-mesh-combiner-and-material-merger-170448

https://assetstore.unity.com/packages/tools/modeling/super-combiner-92129#reviews

生成LOD:AutomaticLOD&MeshSimplify

https://assetstore.unity.com/packages/tools/modeling/mantis-lod-editor-professional-edition-37086
https://assetstore.unity.com/packages/tools/game-toolkits/advanced-tools-mega-pack-43671

https://assetstore.unity.com/packages/tools/modeling/mesh-simplify-43658

 

三.插件应用

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值