unity中Mesh網格程式設計

js0907發表於2020-12-24

在這裡插入圖片描述
上圖是效果
一.關於mesh的意義
有了mesh網格,物體才能被渲染出來。
(1)mesh中包含頂點,
mesh.vertices
(2)頂點對應的uv(一張圖的uv左下角00,右上角11)
mesh.uv
(3)指定每個三角面對應的頂點位置(順時針方向culling back:預設不繪製背面)
mesh.triangles
(4)切線資訊
mesh.tangents
(5)法線資訊
mesh.normals
還有其它就不再說了,這裡主要是 頂點“veticles”,“uv”, 三角面“triangles”!!!

二.一個物體的正常渲染流程:
頂點座標 -》頂點對應的UV座標-》三角面對應的頂點位置
三.設定頂點的座標和順序可以多種多樣,但是條條大路通羅馬
例如:一個長方體:
設定頂點座標的方式:從上往下 從左往右 等

怎麼從0到1繪製一個簡單的長方形有很多教程,解釋的很清楚mesh程式設計入門精講
有兩個函式注意一下:
(1)重置法線垂直於當前的三角面
mesh.RecaculateNormals()
(2)將當前mesh,拆分成3個submesh
mesh.submeshCount = 3
mesh.SetTriangles(trianglesX, 0);
mesh.SetTriangles(trianglesY, 1);
mesh.SetTriangles(trianglesZ, 2);
並且 MeshRender中的Materials中的 第一個material對應渲染第一個submesh;第二個material對應渲染第二個;。。。。

四介紹繪製一個立方體的思路:
(1)計算頂點的個數:

//三個面的交點
        int conerVerticles = 8;
        // 兩個面的交點
        int edgeVerticles = (xSize + ySize + zSize - 3) * 4;
        // 上中下 三個面的點 (共6個面,所以要乘以2)
        int faceVerticles = ((xSize - 1) * (ySize - 1) + (xSize - 1) * (zSize - 1) + (ySize - 1) * (zSize - 1)) * 2;
        // 總頂點數量
        int totalVerticles = conerVerticles + edgeVerticles + faceVerticles;
        Vector3[] veticles = new Vector3[totalVerticles];

(2)在形成三角形的時需要用到三角形頂點的個數:
有n個四邊形,那麼總的三角形頂點數目: 4*n

 		//一共多少四邊形
        int quads = (xSize * zSize + xSize * ySize + zSize * ySize) * 2;
        // 三角面的頂點數
        int totalCount = quads * 6;
        int[] triangles = new int[totalCount];

(3)計算下一個triangles的索引:

    // 返回值是 :下一個triangles的索引
    static int SetQuad(int[]triangles,int t,int v00,int v10,int v01,int v11)
    {
        triangles[t] = v00;
        triangles[t + 1] = triangles[t+4] = v01;
        triangles[t + 2] = triangles[t+3] = v10;
        triangles[t + 5] = v11;
        return t + 6;
    }

(4)其它的就沒有可講的了,都是如何拼接三角形形成mesh。順序和方法比較多。。思想都是一樣的。
下面附詳細程式碼:

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

[RequireComponent(typeof(MeshFilter),  typeof(MeshRenderer))]
public class SplittingMesh : MonoBehaviour
{
    public int xSize, ySize, zSize;
    public int Roundness;

    private Mesh mesh;
    private Vector3[] vertices;
    private Vector3[] normals;
    private Color32[] CubeUV;

    void Start()
    {
        GenerateVertices();
       StartCoroutine(GenerateTriangles());
    }
    /// <summary>
    /// 生成頂點
    /// </summary>
    void GenerateVertices()
    {
        GetComponent<MeshFilter>().mesh = mesh = new Mesh();
        mesh.name = "Procedural Cube ";

        int cornerVertices = 8; //八個角 八個頂點
        int edgeVertices = (xSize +ySize +zSize -3)*4; //不考慮頂點公用的話 應該是 (xSize+ySize+zSize +1)*4
        int faceVertices = (
            (xSize-1)*(ySize-1)+
            (xSize-1)*(zSize-1)+
            (ySize-1)*(zSize-1)
         )*2;
        int vCount = cornerVertices + edgeVertices + faceVertices;
        vertices = new Vector3[vCount];
        normals = new Vector3[vertices.Length];
        CubeUV = new Color32[vertices.Length];
            
        int v = 0;
        //賦予前後左右四個面頂點
        for (int y = 0; y <=ySize; y++)
        {
            //前面
            for (int x = 0; x <= xSize; x++)
            {
                SetVertex(v++, x, y, 0);
            }
            //右面 一開始  因為上面已經把最後一個點 公共頂點繪製完成
            for (int z = 1; z <= zSize; z++)
            {
                SetVertex(v++, xSize, y, z);
            }
            //後面
            for (int x = xSize - 1; x >= 0; x--)
            {
                SetVertex(v++, x, y, zSize);
            }
            //左面
            for (int z = zSize - 1; z > 0; z--)
            {
                SetVertex(v++, 0, y, z);

            }
        }

        //賦予上面頂點
        for (int z = 1; z < zSize; z++)
        {
            for (int x = 1; x < xSize; x++)
            {
                SetVertex(v++, x, ySize, z);
            }
        }
        //賦予下面頂點
        for (int z = 1; z < zSize; z++)
        {
            for (int x = 1; x < xSize; x++)
            {
                SetVertex(v++, x, 0, z);
            }
        }
        mesh.vertices = vertices;
        mesh.normals = normals;
        mesh.colors32 = CubeUV;
    }

    /// <summary>
    /// 計算頂點 法線
    /// </summary>
    private void SetVertex(int i, int x , int y, int z)
    {
       Vector3 inner =  vertices[i] = new Vector3(x, y, z);
        if (x <Roundness)
        {
            inner.x = Roundness;
        }
        else if (x > xSize -Roundness)
        {
            inner.x = xSize - Roundness;
        }
        if (y < Roundness)
        {
            inner.y = Roundness;
        }
        else if (y> ySize -Roundness)
        {
            inner.y = ySize - Roundness;
        }
        if (z < Roundness)
        {
            inner.z = Roundness;
        }
        else if (z > zSize - Roundness)
        {
            inner.z = zSize - Roundness;
        }

        normals[i] = (vertices[i] - inner).normalized;
        vertices[i] = inner + normals[i] * Roundness;
        CubeUV[i] = new Color32((byte)x, (byte)y, (byte)z,0);
    }


    IEnumerator GenerateTriangles()
    {
        WaitForSeconds wait = new WaitForSeconds(0.1f);


        int[] trianglesZ = new int[xSize * ySize * 12];
        int[] trianglesX = new int[zSize*ySize*12];
        int[] trianglesY = new int[xSize * zSize * 12];

        int tZ = 0, tX = 0, tY = 0, v = 0;
        int ring = (xSize + zSize) * 2;// 行與行的 頂點偏移量

        for (int y = 0; y < ySize; y++, v++)
        {
            for (int q = 0; q < xSize; q++, v++)
            {
                tZ = SetQuad(trianglesZ, tZ, v, v+1, v+ring, v+ring+1);
            }

            for (int q = 0; q < zSize; q++, v++)
            {
                tX = SetQuad(trianglesX, tX, v, v + 1, v + ring, v + ring + 1);
            }

            for (int q = 0; q < xSize; q++, v++)
            {
                tZ = SetQuad(trianglesZ, tZ, v, v + 1, v + ring, v + ring + 1);
            }

            for (int q = 0; q < zSize-1; q++, v++)
            {
                tX = SetQuad(trianglesX, tX, v, v + 1, v + ring, v + ring + 1);
            }
            tX = SetQuad(trianglesX, tX, v, v  -ring +1, v + ring, v + 1);
        }


        tY = CreateTopFace(  trianglesY, tY, ring);
        tY = CreateBottomFace(trianglesY, tY, ring);

        mesh.subMeshCount = 3;

        mesh.SetTriangles(trianglesZ, 0);
        mesh.SetTriangles(trianglesX, 1);
        mesh.SetTriangles(trianglesY, 2);
        mesh.subMeshCount = 3;
        mesh.SetTriangles(trianglesX, 0);

        yield return wait;
    }

    /// <summary>
    /// 建立上面
    /// </summary>
    /// <returns></returns>
    int CreateTopFace( int [] trinagles,  int t ,  int ring)
    {
        int v = ring * ySize;
        for (int x = 0; x < xSize-1; x++,v++)
        {
            t = SetQuad( trinagles, t,v,v+1,v+ring-1,v+ring);
        }
        t = SetQuad( trinagles, t,v,v+1,v+ring-1,v+2);

        int vMin = ring * (ySize + 1) - 1;
        int vMid = vMin + 1;
        int vMax = v + 2;

        for (int z = 1; z < zSize-1; z++,vMin--,vMid++,vMax++)
        {
            t = SetQuad(trinagles, t, vMin, vMid, vMin - 1, vMid + xSize - 1);
            for (int x = 1; x < xSize - 1; x++, vMid++)
            {
                t = SetQuad(trinagles, t, vMid, vMid + 1, vMid + xSize - 1, vMid + xSize);
            }

            t = SetQuad(trinagles, t, vMid, vMax, vMid + xSize - 1, vMax + 1);
        }
        //最後一行
        int vTop = vMin - 2;
        t = SetQuad(trinagles, t, vMin, vMid, vTop+1, vTop );
        for (int x = 1; x < xSize-1; x++, vTop--,vMid++)
        {
            t = SetQuad(trinagles, t, vMid, vMid+1, vTop , vTop-1);
        }
        t = SetQuad(trinagles, t, vMid, vTop-2, vTop, vTop - 1);

        return t;
    }

    /// <summary>
    /// 建立底面
    /// </summary>
    /// <param name="trinagles"></param>
    /// <param name="t"></param>
    /// <param name="ring"></param>
    /// <returns></returns>
    int CreateBottomFace(int[] trinagles, int t, int ring)
    {
        int v = 1;
        int vMid = vertices.Length - (xSize - 1) * (zSize - 1);
        int vOriginMid = vMid;

        t = SetQuad(trinagles, t, ring-1, vMid, 0, 1);
        for (int x = 1; x < xSize-1; x++, vMid++,v++)
        {
            t = SetQuad(trinagles, t, vMid, vMid+1, v, v+1);
        }
        t = SetQuad(trinagles, t, vMid, v+2, v, v + 1);

        /
        int vMin = ring - 2;
        int vMax = v + 2;
        vMid++;

        for (int z = 1; z < zSize-1; z++,vMin--,vMax++,vMid++,vOriginMid++)
        {
            t = SetQuad(trinagles, t, vMin, vMid, vMin + 1, vOriginMid);
            for (int x = 1; x < xSize - 1; x++, vMid++, vOriginMid++)
            {
                t = SetQuad(trinagles, t, vMid, vMid + 1, vOriginMid, vOriginMid + 1);
            }
            t = SetQuad(trinagles, t, vMid, vMax + 1, vOriginMid, vMax);
        }

        int vTop = vMin - 1;

        t = SetQuad(trinagles, t, vTop+1, vTop, vTop+2, vOriginMid);
        for (int x = 1; x < xSize-1; x++, vOriginMid++,vTop--)
        {
            t = SetQuad(trinagles, t, vTop, vTop-1,  vOriginMid, vOriginMid+1);
        }

        t = SetQuad(trinagles, t, vTop, vTop - 1, vOriginMid, vTop-2);
        return t;
    }



    /// <summary>
    /// 我們封裝這個 三角形拼接四邊形的方法 返回三角形的下一輪第一個索引  引數v00 是頂點的索引
    /// </summary>
    private static  int SetQuad(int[] triangles,  int i , int v00 , int v10 ,int v01 , int v11  )
    {
        triangles[i] = v00;
        triangles[i + 2] = triangles[i + 3] = v10;
        triangles[i + 1] = triangles[i + 4] = v01;
        triangles[i + 5] = v11;
        return i + 6;
    }


    private void OnDrawGizmos()
    {
        //if (vertices == null) return;

        //for (int i = 0; i < vertices.Length; i++)
        //{
        //    Gizmos.color = Color.black;
        //    Gizmos.DrawSphere( vertices[i] ,0.1f);
        //    Gizmos.color = Color.yellow;
        //    Gizmos.DrawRay(vertices[i],normals[i]);
        //}
    }


}

相關文章