合并子网格/多材质球

该博客探讨了如何在Unity3d中合并包含多个子网格的游戏物体,同时处理超出【0,1】范围的纹理坐标,以避免合并后出现斑马线。文章介绍了两种方案:1) 对于网格贴图坐标在【0,1】范围内的物体,可以合并贴图;2) 对于超出范围的,需要编写定制shader,不合并贴图。博客还提供了用于合并子网格的代码示例。
摘要由CSDN通过智能技术生成
1、对于网格贴图坐标在【0,1】范围内的游戏物体,可以合并贴图。
2、对应网格贴图坐标超出【0,1】范围的游戏物体,合并网格,不合并贴图,需要编写定制纹理数的shader。
          如果也用合并贴图的方式,会在贴图重复使用的接缝处出现斑马线
          (需要在片元着色器将uv转换到【0,1】范围( uv= uv-floor(uv);这个操作会导致 斑马线),再用rect转换到合并后的贴图)。


方案2:对应网格贴图坐标超出【0,1】范围的游戏物体,合并网格,不合并贴图,需要编写定制纹理数的shader。
               如果也用合并贴图的方式,会在贴图重复使用的接缝处出现斑马线
               (需要在片元着色器将uv转换到【0,1】范围( uv= uv-floor(uv);这个操作会导致 斑马线),再用rect转换到合并后的贴图)。
                    斑马线效果:
               
1、合并网格




2、添加网格渲染器,编辑定制贴图数量的shader,创建材质球,指定多个对应贴图。


代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class CombineSubMeshToPrefab : MonoBehaviour {
    [ MenuItem ( "Tools/Combine SubMesh And Create Prefab & C" )]
    public static void CombineSubMesh()  
    {
        GameObject go = Selection .activeGameObject;
        if (go)
        {
            MeshFilter mf = go.GetComponent< MeshFilter >();
            if (mf)
            {
                Mesh mesh = mf.sharedMesh;
                if (mesh)
                {
                    Mesh cMesh = MeshUtil .MergeSubMesh(mesh);
                    if (cMesh)
                    {
                        string meshPath = "Assets/__source/Model/Combined/" +cMesh.name+ ".asset" ;
                        AssetDatabase .DeleteAsset(meshPath);
                        AssetDatabase .CreateAsset(cMesh, meshPath);
                        GameObject cGo = new GameObject ( "CSM_" + go.name);
                        cGo.transform.parent = go.transform;
                        MeshFilter cMf=cGo.AddComponent< MeshFilter >();
                        cMf.sharedMesh = cMesh;
                        string prefabPath = "Assets/__source/Prefab/Combined/" + cGo.name+ ".prefab" ;
                        Object tempPrefab = PrefabUtility .CreateEmptyPrefab(prefabPath);
                        tempPrefab = PrefabUtility .ReplacePrefab(cGo, tempPrefab);
                    }
                }
            }
        }
    }
   
}


using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
public class MeshUtil{
    [MenuItem("Tools/Combine SubMesh & M")]
    public static void CombineSubMesh()
    {
        GameObject go = Selection.activeGameObject;
        if (go)
        {
            MeshFilter mf = go.GetComponent<MeshFilter>();
            if (mf)
            {
                Mesh mesh = mf.sharedMesh;
                if (mesh)
                {
                    Mesh cMesh = MeshUtil.MergeSubMesh(mesh);
                    if (cMesh)
                    {
                        string meshPath = "Assets/__source/Model/Combined/" + cMesh.name + ".asset";
                        AssetDatabase.DeleteAsset(meshPath);
                        AssetDatabase.CreateAsset(cMesh, meshPath);
                    }
                }
            }
        }
    }

    /// <summary>
    /// 深度复制网格
    /// </summary>
    /// <param name="i"></param>
    /// <returns></returns>
    public static Mesh MeshTrueCopy(Mesh i)
    {
        Mesh o = new Mesh();

        o.name = i.name;
        o.hideFlags = i.hideFlags;
        //顶点 位置 方向
        o.vertices = i.vertices;
        o.normals = i.normals;
        o.tangents = i.tangents;
        //三角面
        o.triangles = i.triangles;
        o.subMeshCount = i.subMeshCount;
        //颜色
        o.uv = i.uv;
        int dCount = 50;
        for(int j=0;j<o.vertexCount;j++)
        {
            if (dCount > 0&&(Mathf.Abs(o.uv[j].x)>1|| Mathf.Abs(o.uv[j].y) > 1))
            {
                dCount--;
                Debug.Log("uv=" + o.uv[j].x+" "+o.uv[j].y);
            }
            if (dCount == 0)
            {
                break;
            }
        }

        o.uv2 = i.uv2;
        o.uv3 = i.uv3;
        o.uv4 = i.uv4;
        o.colors = i.colors;
        o.colors32 = i.colors32;
        //边缘
        o.bounds = i.bounds;
        //骨骼
        o.boneWeights = i.boneWeights;
        o.bindposes = i.bindposes;
        return o;
    }
    /// <summary>
    /// 合并包含多个子网格的网格为只有一个子网格的网格
    /// 同时更新UV
    /// </summary>
    /// <param name="srcMesh"></param>
    /// <returns></returns>
    public static Mesh MergeSubMesh(Mesh srcMesh,Rect[] texRects)
    {
        Mesh tarMesh = new Mesh();
        List<Vector3> tarVertexList = new List<Vector3>();//要构建的目标网格顶点列表
        List<Vector3> tarNormalList = new List<Vector3>();
        List<Vector2> tarUVList = new List<Vector2>();
        List<int> tarTrianges = new List<int>();//要构建的目标网格三角形列表

        List<Vector3> addVertexList = new List<Vector3>();//新添加的顶点列表
        List<Vector3> existInBeforCurrentSubmeshVL = new List<Vector3>();//当前子网格之前出现过的顶点列表
        List<Vector3> sharedInCurrentSubmeshVL = new List<Vector3>();//当前子网格与其他子网格共享的顶点列表

        Vector3[] srcVertexArray = srcMesh.vertices;
        Vector3[] srcNormalArray = srcMesh.normals;
        Vector2[] srcUvArray = srcMesh.uv;
        int[] srcTrianges = srcMesh.triangles;
        int srcSubmeshCount = srcMesh.subMeshCount;

        //初始化
        tarVertexList = initVertexOrNormalList(srcVertexArray);
        tarNormalList = initVertexOrNormalList(srcNormalArray);
        tarUVList = initUvList(srcUvArray);

        List<int> srcBeforCurrentSubmeshTrianges = new List<int>();
        for (int i = 0; i < srcSubmeshCount; i++)
        {
            int[] srcSubmeshTriangeArray = srcMesh.GetTriangles(i);

            Debug.Log("子网格【" + i + "】拥有三角形数量为【" + (srcSubmeshTriangeArray.Length / 3) + "】顶点索引为【" + ShowArrayElements(srcSubmeshTriangeArray) + "】");
            //找出当前子网格与前面的所有子网格共用的顶点列表。
            int[] hadSharedVertexIndexArr = ArrayFindSharedElements(srcBeforCurrentSubmeshTrianges.ToArray(), srcSubmeshTriangeArray);
            Debug.Log("已共用顶点索引【" + ShowArrayElements(hadSharedVertexIndexArr) + "】");
            //复制共享顶点列表,添加顶点
            int[] oldSubmeshTrianges = CopyArray(srcSubmeshTriangeArray);
            int[] newSubmeshTrianges = oldSubmeshTrianges;
            if (hadSharedVertexIndexArr != null && hadSharedVertexIndexArr.Length > 0)
            {
                Debug.Log("添加顶点前:拥有顶点数【" + tarVertexList.Count + "】");
                Dictionary<int, int> vertexIndexRef = new Dictionary<int, int>();
                CopyAndAddVertexs(hadSharedVertexIndexArr, srcUvArray, ref tarVertexList, ref tarNormalList, ref tarUVList, ref vertexIndexRef);
                Debug.Log("添加顶点后:拥有顶点数【" + tarVertexList.Count + "】,旧新顶点对应关系【" + ShowDictionary(vertexIndexRef) + "】");
                //通过旧新顶点索引对应关系更新三角形数组
                newSubmeshTrianges = UpdateTrianges(oldSubmeshTrianges, vertexIndexRef);
                Debug.Log("通过旧新顶点索引对应关系更新后的三角形顶点索引为【" + ShowArrayElements(newSubmeshTrianges) + "】");
            }
            Debug.Log("更新UV前此子网格各顶点的UV值【"+ ShowUVsi(newSubmeshTrianges, tarUVList) + "】");
            UpdateUv(newSubmeshTrianges, texRects[i],ref tarUVList);
            Debug.Log("Rect【"+texRects[i].ToString()+"】更新UV后此子网格各顶点的UV值【" + ShowUVsi(newSubmeshTrianges, tarUVList) + "】");
            //存储本此循环数据,为下一次循环作准备
            srcBeforCurrentSubmeshTrianges = MergeArrayToList(srcBeforCurrentSubmeshTrianges.ToArray(), srcSubmeshTriangeArray);
            tarTrianges = MergeArrayToList(tarTrianges.ToArray(), newSubmeshTrianges);
            Debug.Log("本次【" + i + "】循环结束,未更新三角形顶点索引为【" + ShowListElements(srcBeforCurrentSubmeshTrianges) + "】");
            Debug.Log("本次【" + i + "】循环结束,更新后三角形顶点索引为【" + ShowListElements(tarTrianges) + "】");
        }
        //组装新的网格
        tarMesh.name = "Msm_" + srcMesh.name;
        tarMesh.vertices = tarVertexList.ToArray();
        tarMesh.normals = tarNormalList.ToArray();
        tarMesh.uv = tarUVList.ToArray();
        tarMesh.triangles = tarTrianges.ToArray();
        return tarMesh;
    }
    public static string ShowUVsi(int[] newSubmeshTriange, List<Vector2> tarUVList)
    {
        string rs = "[";
        foreach (int idx in newSubmeshTriange)
        {
            rs+= idx+":(" +tarUVList[idx].x+","+ tarUVList[idx].y+")|";
        }
        return rs+"]";
    }
    public static void UpdateUv(int[] newSubmeshTrianges,Rect texRect,ref List<Vector2> tarUVList)
    {
        List<int> vertexIndexs_noRepeat = ArrayRemoveDuplication(newSubmeshTrianges);
        foreach (int idx in vertexIndexs_noRepeat)
        {
            float x = texRect.x + UVToZeroOne(tarUVList[idx].x) * texRect.width;
            float y = texRect.y + UVToZeroOne(tarUVList[idx].y) * texRect.height;
            if (idx == 0)
            {
                Debug.Log("idx="+idx+"  x="+x+"  old x="+ tarUVList[idx].x+ "  UVToZeroOne=" + UVToZeroOne(tarUVList[idx].x) + " texRect="+texRect.ToString());
            }
            tarUVList[idx] = new Vector2(x,y);
        }
    }
    public static float UVToZeroOne(float i)
    {
        float o = 0;
        o = i - Mathf.Floor(i);
        return o;
    }
    /// <summary>
    /// 合并包含多个子网格的网格为只有一个子网格的网格
    /// </summary>
    /// <param name="srcMesh"></param>
    /// <returns></returns>
    public static Mesh MergeSubMesh(Mesh srcMesh)
    {
        Mesh tarMesh = new Mesh();
        List<Vector3> tarVertexList = new List<Vector3>();//要构建的目标网格顶点列表
        List<Vector3> tarNormalList = new List<Vector3>();
        List<Vector2> tarUVList = new List<Vector2>();
        List<Vector2> tarUV2List = new List<Vector2>();
        List<int> tarTrianges = new List<int>();//要构建的目标网格三角形列表

        List<Vector3> addVertexList = new List<Vector3>();//新添加的顶点列表
        List<Vector3> existInBeforCurrentSubmeshVL = new List<Vector3>();//当前子网格之前出现过的顶点列表
        List<Vector3> sharedInCurrentSubmeshVL = new List<Vector3>();//当前子网格与其他子网格共享的顶点列表

        Vector3[] srcVertexArray = srcMesh.vertices;
        Vector3[] srcNormalArray = srcMesh.normals;
        Vector2[] srcUvArray = srcMesh.uv;
        int[] srcTrianges = srcMesh.triangles;
        int srcSubmeshCount = srcMesh.subMeshCount;

        //初始化
        tarVertexList = initVertexOrNormalList(srcVertexArray);
        tarNormalList = initVertexOrNormalList(srcNormalArray);
        tarUVList = initUvList(srcUvArray);
        tarUV2List = initUvList(tarUVList.ToArray());
        List<int> srcBeforCurrentSubmeshTrianges = new List<int>();
        for (int i = 0; i < srcSubmeshCount; i++)
        {
            int[] srcSubmeshTriangeArray = srcMesh.GetTriangles(i);

            Debug.Log("子网格【"+i+"】拥有三角形数量为【"+(srcSubmeshTriangeArray.Length/3)+"】顶点索引为【"+ ShowArrayElements(srcSubmeshTriangeArray) + "】");
            //找出当前子网格与前面的所有子网格共用的顶点列表。
            int[] hadSharedVertexIndexArr = ArrayFindSharedElements(srcBeforCurrentSubmeshTrianges.ToArray(), srcSubmeshTriangeArray);
            Debug.Log("已共用顶点索引【"+ ShowArrayElements(hadSharedVertexIndexArr) +"】");
            //复制共享顶点列表,添加顶点
            int[] oldSubmeshTrianges = CopyArray(srcSubmeshTriangeArray);
            int[] newSubmeshTrianges = oldSubmeshTrianges;
            if (hadSharedVertexIndexArr != null && hadSharedVertexIndexArr.Length > 0)
            {
                Debug.Log("添加顶点前:拥有顶点数【"+ tarVertexList.Count+ "】");
                Dictionary<int, int> vertexIndexRef = new Dictionary<int, int>();

                CopyAndAddVertexs(i,hadSharedVertexIndexArr, srcUvArray, ref tarVertexList, ref tarNormalList, ref tarUVList,ref tarUV2List, ref vertexIndexRef);
                Debug.Log("添加顶点后:拥有顶点数【" + tarVertexList.Count + "】,旧新顶点对应关系【"+ ShowDictionary(vertexIndexRef)+ "】");
                //通过旧新顶点索引对应关系更新三角形数组
                newSubmeshTrianges = UpdateTrianges(oldSubmeshTrianges, vertexIndexRef);
                Debug.Log("通过旧新顶点索引对应关系更新后的三角形顶点索引为【"+ ShowArrayElements(newSubmeshTrianges)+ "】");
            }
            //根据三角形顶点列表更新UV2,标识顶点来源自哪个子网格
            UdateUv2(i, newSubmeshTrianges, ref tarUV2List);
            //存储本此循环数据,为下一次循环作准备
            srcBeforCurrentSubmeshTrianges = MergeArrayToList(srcBeforCurrentSubmeshTrianges.ToArray(), srcSubmeshTriangeArray);
            tarTrianges = MergeArrayToList(tarTrianges.ToArray(), newSubmeshTrianges);
            Debug.Log("本次【"+i+"】循环结束,未更新三角形顶点索引为【"+ShowListElements(srcBeforCurrentSubmeshTrianges)+"】");
            Debug.Log("本次【" + i + "】循环结束,更新后三角形顶点索引为【" + ShowListElements(tarTrianges) + "】");
        }
        //组装新的网格
        tarMesh.name = "Msm_"+srcMesh.name;
        tarMesh.vertices = tarVertexList.ToArray();
        tarMesh.normals = tarNormalList.ToArray();
        tarMesh.uv = tarUVList.ToArray();
        tarMesh.uv2 = tarUV2List.ToArray();
        tarMesh.triangles = tarTrianges.ToArray();
        return tarMesh;
    }
    /// <summary>
    /// 合并包含多个子网格的网格为只有一个子网格的网格
    /// </summary>
    /// <param name="srcMesh"></param>
    /// <returns></returns>
    public static Mesh MergeSubMeshStoreRect(Mesh srcMesh,Rect[] rects)
    {
        Mesh tarMesh = new Mesh();
        List<Vector3> tarVertexList = new List<Vector3>();//要构建的目标网格顶点列表
        List<Vector3> tarNormalList = new List<Vector3>();
        List<Vector2> tarUVList = new List<Vector2>();
        List<Vector2> tarUV2List = new List<Vector2>();
        List<Vector2> tarUV3List = new List<Vector2>();
        List<int> tarTrianges = new List<int>();//要构建的目标网格三角形列表

        List<Vector3> addVertexList = new List<Vector3>();//新添加的顶点列表
        List<Vector3> existInBeforCurrentSubmeshVL = new List<Vector3>();//当前子网格之前出现过的顶点列表
        List<Vector3> sharedInCurrentSubmeshVL = new List<Vector3>();//当前子网格与其他子网格共享的顶点列表

        Vector3[] srcVertexArray = srcMesh.vertices;
        Vector3[] srcNormalArray = srcMesh.normals;
        Vector2[] srcUvArray = srcMesh.uv;
        int[] srcTrianges = srcMesh.triangles;
        int srcSubmeshCount = srcMesh.subMeshCount;

        //初始化
        tarVertexList = initVertexOrNormalList(srcVertexArray);
        tarNormalList = initVertexOrNormalList(srcNormalArray);
        tarUVList = initUvList(srcUvArray);
        tarUV2List = initUvList(tarUVList.ToArray());
        tarUV3List = initUvList(tarUVList.ToArray());
        List<int> srcBeforCurrentSubmeshTrianges = new List<int>();
        for (int i = 0; i < srcSubmeshCount; i++)
        {
            int[] srcSubmeshTriangeArray = srcMesh.GetTriangles(i);

            Debug.Log("子网格【" + i + "】拥有三角形数量为【" + (srcSubmeshTriangeArray.Length / 3) + "】顶点索引为【" + ShowArrayElements(srcSubmeshTriangeArray) + "】");
            //找出当前子网格与前面的所有子网格共用的顶点列表。
            int[] hadSharedVertexIndexArr = ArrayFindSharedElements(srcBeforCurrentSubmeshTrianges.ToArray(), srcSubmeshTriangeArray);
            Debug.Log("已共用顶点索引【" + ShowArrayElements(hadSharedVertexIndexArr) + "】");
            //复制共享顶点列表,添加顶点
            int[] oldSubmeshTrianges = CopyArray(srcSubmeshTriangeArray);
            int[] newSubmeshTrianges = oldSubmeshTrianges;
            if (hadSharedVertexIndexArr != null && hadSharedVertexIndexArr.Length > 0)
            {
                Debug.Log("添加顶点前:拥有顶点数【" + tarVertexList.Count + "】");
                Dictionary<int, int> vertexIndexRef = new Dictionary<int, int>();

                CopyAndAddVertexs(i, hadSharedVertexIndexArr, srcUvArray, ref tarVertexList, ref tarNormalList, ref tarUVList, ref tarUV2List, ref vertexIndexRef);
                Debug.Log("添加顶点后:拥有顶点数【" + tarVertexList.Count + "】,旧新顶点对应关系【" + ShowDictionary(vertexIndexRef) + "】");
                //通过旧新顶点索引对应关系更新三角形数组
                newSubmeshTrianges = UpdateTrianges(oldSubmeshTrianges, vertexIndexRef);
                Debug.Log("通过旧新顶点索引对应关系更新后的三角形顶点索引为【" + ShowArrayElements(newSubmeshTrianges) + "】");
            }
            //根据三角形顶点列表更新UV2,标识顶点来源自哪个子网格
            //UdateUv2(i, newSubmeshTrianges, ref tarUV2List);
            StoreRectToUV(newSubmeshTrianges, rects[i], ref tarUV2List, ref tarUV3List);
            //存储本此循环数据,为下一次循环作准备
            srcBeforCurrentSubmeshTrianges = MergeArrayToList(srcBeforCurrentSubmeshTrianges.ToArray(), srcSubmeshTriangeArray);
            tarTrianges = MergeArrayToList(tarTrianges.ToArray(), newSubmeshTrianges);
            Debug.Log("本次【" + i + "】循环结束,未更新三角形顶点索引为【" + ShowListElements(srcBeforCurrentSubmeshTrianges) + "】");
            Debug.Log("本次【" + i + "】循环结束,更新后三角形顶点索引为【" + ShowListElements(tarTrianges) + "】");
        }
        //组装新的网格
        tarMesh.name = "Msm_" + srcMesh.name;
        tarMesh.vertices = tarVertexList.ToArray();
        tarMesh.normals = tarNormalList.ToArray();
        tarMesh.uv = tarUVList.ToArray();
        tarMesh.uv2 = tarUV2List.ToArray();
        tarMesh.uv3 = tarUV3List.ToArray();
        tarMesh.triangles = tarTrianges.ToArray();
        return tarMesh;
    }
    public static void StoreRectToUV(int[] newSubmeshTrianges,Rect rect,ref List<Vector2> uv2,ref List<Vector2> uv3)
    {
        if (newSubmeshTrianges != null && newSubmeshTrianges.Length > 0)
        {
            foreach (int vIdx in newSubmeshTrianges)
            {
                uv2[vIdx] = new Vector2(rect.x, rect.y);
                uv3[vIdx] = new Vector2(rect.width, rect.height);
            }
        }
    }
    public static void UdateUv2(int currSubmeshId, int[] newSubmeshTrianges, ref List<Vector2> uv2)
    {
        if (newSubmeshTrianges != null && newSubmeshTrianges.Length > 0)
        {
            foreach (int vIdx in newSubmeshTrianges)
            {
                uv2[vIdx] = new Vector2((float)currSubmeshId,0.0f);
            }
        }
        //Debug.Log("UV2="+ ShowUVs(uv2));
    }
    public static string ShowUVs(List<Vector2> uv2)
    {
        string rs = "";
        for(int i=0;i<uv2.Count; i++)
        {
            rs += i+"["+uv2[i].x+","+uv2[i].y+"] ";
        }
        return rs;
    }
    public static int[] UpdateTrianges(int[] oldTrianges,Dictionary<int,int> idxRef)
    {
        int[] newTrianges = new int[oldTrianges.Length];
        for (int i = 0; i < oldTrianges.Length; i++)
        {
            if (idxRef.ContainsKey(oldTrianges[i]))
            {
                newTrianges[i] = idxRef[oldTrianges[i]];
            }
            else
            {
                newTrianges[i] = oldTrianges[i];
            }
        }
        return newTrianges;
    }
    public static int[] CopyArray(int[] arr)
    {
        int[] o = new int[arr.Length];
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值