Unity Mesh基础知识

  • 什么是Mesh
  • Mesh的组成部分
  • 利用Mesh创建一个Cube
  • 创建Cube
  • 顶点坐标
  • 渲染
  • 转载链接


什么是Mesh

    Mesh是指的模型的网格,3D模型是由多边形拼接而成,而多边形实际上又是由多个三角形拼接而成的。即一个3D模型的表面其实是由多个彼此相连的三角面构成。三维空间中,构成这些三角形的点和边的集合就是Mesh。

Mesh的组成部分

    1.顶点坐标(vertex)
    2.法线(normal)
    3.纹理坐标(uv)
    4.三角形序列(triangle)
    5.MeshFilter组件,用于增加mesh属性
    6.MeshRender组件,增加材质并渲染出来
顶点坐标:顶点坐标数组存放Mesh的每个顶点的空间坐标,假设某mesh有n个顶点,则vertexs的长度为n。
法线:法线就是垂直于面的一条线,它有方向,没有大小。法线数组存放mesh每个顶点的法线,大小与顶点坐标对应,normal[i]对应顶点vertex[i]的法线。法线向外的面就是正面,相反的就是背面,一般来讲,从正面看才能看到面,背面看面是看不到的。
纹理坐标:它定义了图片上每个点的位置的信息. 这些点与3D模型是相互联系的, 以决定表面纹理贴图的位置. UV就是将图像上每一个点精确对应到模型物体的表面. uv[i]对应vertex[i]。
三角形序列:每个mesh都由多个三角面组成,而三角面的三个点就是顶点坐标里的点,三角形的数组的size = 三角形个数 * 3。
MesgFilter:MeshFilter含有一个Public成员 Mesh。在Mesh中存储着三维模型的数据:vertices(顶点数据数组Vector3[])、triangles(三角形顶点索引数组,int[])、normals(法线向量数组,Vector3[])、uv(纹理坐标数组,Vector2[])。

利用Mesh创建一个Cube

创建Cube

using UnityEngine;
using System.Collections;

public class dyn3d : MonoBehaviour {

    // Use this for initialization
    void Start () {

        CreateCube();
    }

    // Update is called once per frame
    void Update () {

    }

    void CreateCube()
    {

        GameObject obj=new GameObject("cube");
        MeshFilter mf=obj.AddComponent<MeshFilter>();
        MeshRenderer mr=obj.AddComponent<MeshRenderer>();


        Vector3[] vertices=new Vector3[24];
        int[] triangles=new int[36];

        //forward
        vertices[0].Set(0.5f,-0.5f,0.5f);
        vertices[1].Set(-0.5f,-0.5f,0.5f);
        vertices[2].Set(0.5f,0.5f,0.5f);
        vertices[3].Set(-0.5f,0.5f,0.5f);
        //back
        vertices[4].Set(vertices[2].x,vertices[2].y,-0.5f);
        vertices[5].Set(vertices[3].x,vertices[3].y,-0.5f);
        vertices[6].Set(vertices[0].x,vertices[0].y,-0.5f);
        vertices[7].Set(vertices[1].x,vertices[1].y,-0.5f);
        //up
        vertices[8]=vertices[2];
        vertices[9]=vertices[3];
        vertices[10]=vertices[4];
        vertices[11]=vertices[5];
        //down
        vertices[12].Set(vertices[10].x,-0.5f,vertices[10].z);
        vertices[13].Set(vertices[11].x,-0.5f,vertices[11].z);
        vertices[14].Set(vertices[8].x,-0.5f,vertices[8].z);
        vertices[15].Set(vertices[9].x,-0.5f,vertices[9].z);
        //right
        vertices[16]=vertices[6];
        vertices[17]=vertices[0];
        vertices[18]=vertices[4];
        vertices[19]=vertices[2];
        //left
        vertices[20].Set(-0.5f,vertices[18].y,vertices[18].z);
        vertices[21].Set(-0.5f,vertices[19].y,vertices[19].z);
        vertices[22].Set(-0.5f,vertices[16].y,vertices[16].z);
        vertices[23].Set(-0.5f,vertices[17].y,vertices[17].z);

        int currentCount=0;
        for(int i=0;i<24;i=i+4)
        {
            triangles[currentCount++]=i;
            triangles[currentCount++]=i+3;
            triangles[currentCount++]=i+1;

            triangles[currentCount++]=i;
            triangles[currentCount++]=i+2;
            triangles[currentCount++]=i+3;

        }

        mf.mesh.vertices=vertices;
        mf.mesh.triangles=triangles;

    }
}

    在这里定义vertices数组存储顶点坐标信息,定义triangles数组存储三角形顶点索引。但是这个Mesh不包含normals和uv信息,所以显示出来的正方体是无纹理贴图,并且不能反应环境光照的。

顶点坐标

    正方体6个面,每个面由2个三角形组成,所以共需要36个三角形顶点索引。但是正方体只有8个顶点,为什么需要24个顶点坐标数据呢?

    答案是:Unity3D的Mesh.triangles是三角形索引数组,不仅依靠这个索引值索引三角形顶点坐标,而且索引纹理坐标,索引法线向量。即正方体的每个顶点都参与了3个平面,而这3个平面的法线向量是不同的,该顶点在渲染这3个平面的时候需要索引到不同的法线向量。而由于顶点坐标和法线向量是由同一个索引值triangles[Index]取得的,例如,根据triangles[0],triangles[14],triangles[17]在vertices中索引到的顶点都为(0.5,-0.5,0.5),但是在normals中索引到的法向量值各不相同。这就决定了在正方体中一个顶点,需要有3份存储。(如果你需要创建其它模型,需要根据实际情况决定顶点坐标的冗余度。实质上顶点坐标的冗余正是方便了法线坐标、纹理坐标的存取。)

渲染

    准则:三角形有两面,正面可见,背面不可见。三角形的渲染顺序与三角形的正面法线呈左手螺旋定则。

    这就决定了,如果我们需要渲染如下一个正方形面,那么就需要保证组成这个正方形的两个小三角形的正面法线都是指向屏幕外的。

Unity中如何让Mesh变成草地 unity mesh_Unity中如何让Mesh变成草地


假设顶点顺序为:三角形1: 0->3->1,三角形2: 0->2->3 。

int currentCount=0;
for(int i=0;i<24;i=i+4)
{
        //三角形1
    triangles[currentCount++]=i;
    triangles[currentCount++]=i+3;
    triangles[currentCount++]=i+1;
    //三角形2
    triangles[currentCount++]=i;
    triangles[currentCount++]=i+2;
    triangles[currentCount++]=i+3;
}