Unity中动态建立Mesh

什么是Mesh?

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

原理

即动态建立一个Mesh,设置三角形和顶点数据,而后赋值给MeshFilter(增长mesh属性),经过MeshRenderer(增长材质并渲染出Mesh)绘制出来数组

理论基础:

一、左手坐标系和右手坐标系ide

咱们的三维坐标系,在3dmax里是右手坐标系,而在Unity里是左手坐标系。函数

左手坐标系和右手坐标系的区别 http://www.cnblogs.com/mythou/p/3327046.htmlspa

二、三边面如何组成四边面3d

如图,左边是Unity里的左手坐标系,右边是在此坐标系里生成的一个面以及它的各个点坐标。code

012和230这两个三边面就组成了一个四边面。orm

若是我问这个四边面有几个顶点,想必你们都会回答4个,其实是6个,012和230这是6个顶点,不一样面的顶点不公用。htm

要组成2个三边面能够有不少种顺序,例如012和320、012和03二、023和012等等等blog

可是咱们通常都是按照4个点的顺序来画2个三边面组成四边面,因此可选的只有【012和230、230和012】,以及【032和2十、210和032】这两大类

这两类画法有什么区别呢?细心的童鞋应该已经发现,这两种方式前者是逆时针,后者是顺时针。

这种循环的方向会致使面的法线方向不一样,而这个法线方向会决定这个面的朝向。

咱们要肯定这个法线方向其实很简单,上面说了,Unity里是左手坐标系,拿出左手,伸直,拇指与其余四个指头垂直,而后四指弯曲,指尖朝向循环的方向,拇指就指向法线的方向。

由此咱们得出结论,要想生成正确的面(法线指向咱们),咱们只能用【032和2十、210和032】

这里须要注意的一点是,咱们肯定4个点的循环方向,和生成三边面时的循环方向无关,只要生成三边面时,用到的前4个点的index顺序没错就好了。

Mesh的组成部分

1.vertices(顶点数据数组Vector3[])

2.triangles(三角形顶点索引数组,int[])

3.normals(法线向量数组,Vector3[])

4.uv(纹理坐标数组,Vector2[])

顶点坐标:顶点坐标数组存放Mesh的每一个顶点的空间坐标,假设某mesh有n个顶点,则vertex的size为n

法线:法线数组存放mesh每一个顶点的法线,大小与顶点坐标对应,normal[i]对应顶点vertex[i]的法线

      法线详解:

         法线就是垂直于面的一条线,它有方向,没有大小。

         法线的方向就是面朝外的方向。好比咱们如今盯着显示器看,从显示器的正中心会有一条法线垂直于屏幕指向咱们。

         法线向外的面就是正面,相反的就是背面,通常来说,从正面看才能看到面,背面看面是看不到的。

纹理坐标:它定义了图片上每一个点的位置的信息. 这些点与3D模型是相互联系的, 以决定表面纹理贴图的位置. UV就是将图像上每个点精确对应到模型物体的表面. uv[i]对应vertex[i]

三角形序列:每一个mesh都由多个三角面组成,而三角面的三个点就是顶点坐标里的点,三角形的数组的size = 三角形个数 * 3

      三边面和四边面:

        三边面就是三条边组成的面,四边面就是四条边组成的面。

        三边面在三维空间中是不可扭曲的,而四边面在三维空间中能够扭曲。因此Unity里只支持三边面。其余支持四边面的软件例如3dmax在导出fbx的时候,会把四边面转换成三边面。

 

建立一个立方体

1.定顶点坐标

通常咱们会以立方体几何中心为坐标原点。

代码:

 1         //顶点数组
 2         Vector3[] _vertices = 
 3         {
 4             // front
 5             new Vector3(-5.0f, 10.0f, -5.0f),
 6             new Vector3(-5.0f, 0.0f, -5.0f),
 7             new Vector3(5.0f, 0.0f, -5.0f),
 8             new Vector3(5.0f, 10.0f, -5.0f),
 9 
10 
11             // left  
12             new Vector3(-5.0f, 10.0f, -5.0f),
13             new Vector3(-5.0f, 0.0f, -5.0f),
14             new Vector3(-5.0f, 0.0f, 5.0f),//
15             new Vector3(-5.0f, 10.0f, 5.0f),
16 
17             // back
18             new Vector3(-5.0f, 10.0f, 5.0f),
19             new Vector3(-5.0f, 0.0f, 5.0f),
20             new Vector3(5.0f, 0.0f, 5.0f),
21             new Vector3(5.0f, 10.0f, 5.0f),
22 
23 
24             // right  
25             new Vector3(5.0f, 10.0f, 5.0f),
26             new Vector3(5.0f, 0.0f, 5.0f),
27             new Vector3(5.0f, 0.0f, -5.0f),
28             new Vector3(5.0f, 10.0f, -5.0f),
29 
30 
31             // Top
32             new Vector3(-5.0f, 10.0f, 5.0f),
33             new Vector3(5.0f, 10.0f, 5.0f),
34             new Vector3(5.0f, 10.0f, -5.0f),
35             new Vector3(-5.0f, 10.0f, -5.0f),
36 
37            // Bottom
38             new Vector3(-5.0f, 0.0f, 5.0f),
39             new Vector3(5.0f, 0.0f, 5.0f),
40             new Vector3(5.0f, 0.0f, -5.0f),
41             new Vector3(-5.0f, 0.0f, -5.0f),
42 
43         };

这里有人会有疑问,正方体6个面,每一个面由2个三角形组成,因此共须要36个三角形顶点索引。可是正方体只有8个顶点,为何须要24个顶点坐标数据呢?

答案是:Unity3D的Mesh.triangles是三角形索引数组,不只依靠这个索引值索引三角形顶点坐标,并且索引纹理坐标,索引法线向量。即正方体的每一个顶点都参与了3个平面,而这3个平面的法线向量是不一样的,该顶点在渲染这3个平面的时候须要索引到不一样的法线向量。而因为顶点坐标和法线向量是由同一个索引值triangles[Index]取得的,例如,有三个点在vertices中索引到的顶点都为(0,0,0),可是在normals中索引到的法向量值各不相同。这就决定了在正方体中一个顶点,须要有3份存储。(若是你须要建立其它模型,须要根据实际状况决定顶点坐标的冗余度。实质上顶点坐标的冗余正是方便了法线坐标、纹理坐标的存取。),通常不共点。还有就是Unity中是左手坐标系,必定记好,由于在绘制三角面时很重要。

2.三角面索引

     //索引数组
        int[] _triangles =
        {
          //front
          2,1,0,
          0,3,2,
          //left
          4,5,6,
          4,6,7,
          //back
          9,11,8,
          9,10,11,
          //right
          12,13,14,
          12,14,15,
          ////up
          //16,17,18,
          //16,18,19,
          ////buttom
          //21,23,22,
          //21,20,23,

          //不可跳跃设置索引值(不然会提示一些索引超出边界顶点   15直接20不可,要连续15-16)
          17,19,18,
          17,16,19,
        };

这里设置的原则时外面被渲染里面剔除掉,顺时针构建(注意里外面的区别),还要注意的一个点,如上所写,好比我想生成5个面,那你的索引值也要是连续的,不可16直接蹦到20。这里立法体面的绘制顺序是(即绘制三角面的面与上面顶点顺序要一致)设置顶点的顺序

3.UV坐标

代码:

 1      //UV数组
 2         Vector2[] uvs =
 3         {
 4             // Front
 5             new Vector2(1.0f, 0.0f),
 6             new Vector2(1.0f, 1.0f),
 7             new Vector2(1.0f, 0.0f),
 8             new Vector2(0.0f, 0.0f),
 9 
10             
11             // Left
12             new Vector2(1.0f, 1.0f),
13             new Vector2(0.0f, 1.0f),
14             new Vector2(0.0f, 0.0f),
15             new Vector2(1.0f, 0.0f),
16 
17             
18             // Back
19             new Vector2(1.0f, 0.0f),
20             new Vector2(1.0f, 1.0f),
21             new Vector2(1.0f, 0.0f),
22             new Vector2(0.0f, 0.0f),
23 
24             
25             // Right
26             new Vector2(1.0f, 1.0f),
27             new Vector2(0.0f, 1.0f),
28             new Vector2(0.0f, 0.0f),
29             new Vector2(1.0f, 0.0f),
30 
31             //// Top
32             //new Vector2(0.0f, 0.0f),
33             //new Vector2(1.0f, 0.0f),
34             //new Vector2(1.0f, 1.0f),
35             //new Vector2(0.0f, 1.0f),
36 
37 
38             // Bottom
39             new Vector2(0.0f, 0.0f),
40             new Vector2(1.0f, 0.0f),
41             new Vector2(1.0f, 1.0f),
42             new Vector2(0.0f, 1.0f),
43 
44         };

UV坐标从左上角开始(想象摄像机在立方体内部去判断),

开始的即(0,0),通常是在0-1之间,一些比较大的面为防止纹理被拉伸马赛克,咱们会重复贴纹理,会有大于1的状况,这里的点要与顶点坐标一一对应。重复贴纹理时须要将重复帖的贴图的Wrap Mode设为Repeat(重复)。
即:
4.构建mesh
代码:
 1         Mesh mesh = new Mesh()
 2         {
 3             vertices = _vertices,
 4             uv = uvs,
 5             triangles = _triangles,
 6         };
 7 
 8         //从新计算网格的法线
 9         //在修改完顶点后,一般会更新法线来反映新的变化。法线是根据共享的顶点计算出来的。
10         //导入到网格有时不共享全部的顶点。例如:一个顶点在一个纹理坐标的接缝处将会被分红两个顶点。
11         //所以这个RecalculateNormals函数将会在纹理坐标接缝处建立一个不光滑的法线。
12         //RecalculateNormals不会自动产生切线,所以bumpmap着色器在调用RecalculateNormals以后不会工做。然而你能够提取你本身的切线。
13         mesh.RecalculateNormals();

给mesh属性赋值。

5.增长MeshFilter组件,网格过滤。以及增长MeshRenderer组件添加材质实现渲染。OK!!!到这基本已经绘制完了,Mesh已经出来了。

  1 using System.Collections;
  2 using System.Collections.Generic;
  3 using UnityEngine;
  4 
  5 public class ShaderBase : MonoBehaviour
  6 {
  7 
  8     void Start()
  9     {
 10         GameObject gameObject = new GameObject("Cube");
 11         gameObject.transform.position = Vector3.zero;
 12  
 13         //顶点数组
 14         Vector3[] _vertices = 
 15         {
 16             // front
 17             new Vector3(-5.0f, 10.0f, -5.0f),
 18             new Vector3(-5.0f, 0.0f, -5.0f),
 19             new Vector3(5.0f, 0.0f, -5.0f),
 20             new Vector3(5.0f, 10.0f, -5.0f),
 21 
 22 
 23             // left  
 24             new Vector3(-5.0f, 10.0f, -5.0f),
 25             new Vector3(-5.0f, 0.0f, -5.0f),
 26             new Vector3(-5.0f, 0.0f, 5.0f),//
 27             new Vector3(-5.0f, 10.0f, 5.0f),
 28 
 29             // back
 30             new Vector3(-5.0f, 10.0f, 5.0f),
 31             new Vector3(-5.0f, 0.0f, 5.0f),
 32             new Vector3(5.0f, 0.0f, 5.0f),
 33             new Vector3(5.0f, 10.0f, 5.0f),
 34 
 35 
 36             // right  
 37             new Vector3(5.0f, 10.0f, 5.0f),
 38             new Vector3(5.0f, 0.0f, 5.0f),
 39             new Vector3(5.0f, 0.0f, -5.0f),
 40             new Vector3(5.0f, 10.0f, -5.0f),
 41 
 42 
 43             // Top
 44             new Vector3(-5.0f, 10.0f, 5.0f),
 45             new Vector3(5.0f, 10.0f, 5.0f),
 46             new Vector3(5.0f, 10.0f, -5.0f),
 47             new Vector3(-5.0f, 10.0f, -5.0f),
 48 
 49            // Bottom
 50             new Vector3(-5.0f, 0.0f, 5.0f),
 51             new Vector3(5.0f, 0.0f, 5.0f),
 52             new Vector3(5.0f, 0.0f, -5.0f),
 53             new Vector3(-5.0f, 0.0f, -5.0f),
 54 
 55         };
 56         //索引数组
 57         int[] _triangles =
 58         {
 59           //front
 60           2,1,0,
 61           0,3,2,
 62           //left
 63           4,5,6,
 64           4,6,7,
 65           //back
 66           9,11,8,
 67           9,10,11,
 68           //right
 69           12,13,14,
 70           12,14,15,
 71           ////up
 72           //16,17,18,
 73           //16,18,19,
 74           ////buttom
 75           //21,23,22,
 76           //21,20,23,
 77 
 78           //不可跳跃设置索引值(不然会提示一些索引超出边界顶点   15直接20不可,要连续15-16)
 79           17,19,18,
 80           17,16,19,
 81         };
 82 
 83         //UV数组
 84         Vector2[] uvs =
 85         {
 86             // Front
 87             new Vector2(1.0f, 0.0f),
 88             new Vector2(1.0f, 1.0f),
 89             new Vector2(1.0f, 0.0f),
 90             new Vector2(0.0f, 0.0f),
 91 
 92             
 93             // Left
 94             new Vector2(1.0f, 1.0f),
 95             new Vector2(0.0f, 1.0f),
 96             new Vector2(0.0f, 0.0f),
 97             new Vector2(1.0f, 0.0f),
 98 
 99             
100             // Back
101             new Vector2(1.0f, 0.0f),
102             new Vector2(1.0f, 1.0f),
103             new Vector2(1.0f, 0.0f),
104             new Vector2(0.0f, 0.0f),
105 
106             
107             // Right
108             new Vector2(1.0f, 1.0f),
109             new Vector2(0.0f, 1.0f),
110             new Vector2(0.0f, 0.0f),
111             new Vector2(1.0f, 0.0f),
112 
113             //// Top
114             //new Vector2(0.0f, 0.0f),
115             //new Vector2(1.0f, 0.0f),
116             //new Vector2(1.0f, 1.0f),
117             //new Vector2(0.0f, 1.0f),
118 
119 
120             // Bottom
121             new Vector2(0.0f, 0.0f),
122             new Vector2(1.0f, 0.0f),
123             new Vector2(1.0f, 1.0f),
124             new Vector2(0.0f, 1.0f),
125 
126         };
127 
128         Mesh mesh = new Mesh()
129         {
130             vertices = _vertices,
131             uv = uvs,
132             triangles = _triangles,
133         };
134 
135         //从新计算网格的法线
136         //在修改完顶点后,一般会更新法线来反映新的变化。法线是根据共享的顶点计算出来的。
137         //导入到网格有时不共享全部的顶点。例如:一个顶点在一个纹理坐标的接缝处将会被分红两个顶点。
138         //所以这个RecalculateNormals函数将会在纹理坐标接缝处建立一个不光滑的法线。
139         //RecalculateNormals不会自动产生切线,所以bumpmap着色器在调用RecalculateNormals以后不会工做。然而你能够提取你本身的切线。
140         mesh.RecalculateNormals();
141         gameObject.AddComponent<MeshFilter>().mesh=mesh;
142         //Material/New Material 1
143         gameObject.AddComponent<MeshRenderer>().material = Resources.Load<Material>("Material/New Material");
144 
145     }
146 
147 }

 这不是上述代码的结果图片,这是动态建立外围盒的图片,作法同样。

 

最新:这个立方体,我想底面和侧面贴不一样贴图,如何实现?

使用  mesh.subMeshCount = X;即subMesh,子网格,具体使用以下:

 1   Vector3 contralPos = (maxPos + minPos) / 2;
 2             float boxHight = Mathf.Abs(maxPos.y - minPos.y);
 3             float boxLength = Mathf.Abs(maxPos.x - minPos.x);
 4             float boxWidth = Mathf.Abs(maxPos.z - minPos.z);
 5             vertexPosArray = AddVertexPos(1.2f * boxLength, 1.2f * boxWidth, 1.4f * boxHight);
 6             vertexIndexList = AddVertexIndex();
 7             uvArr = SetUVPos(GetIntValue(boxLength / (textureSizeL * uvNorm)), GetIntValue(boxWidth / (textureSizeL * uvNorm)), GetIntValue(boxHight / (textureSizeW * uvNorm)));
 8             Mesh mesh = new Mesh()
 9             {
10                 vertices = vertexPosArray,
11                 uv = uvArr,
12             };
13             mesh.subMeshCount = 2;
14             mesh.SetTriangles(vertexIndexList[0], 0);
15             mesh.SetTriangles(vertexIndexList[1], 1);
16             mesh.RecalculateNormals();
17             GameObject Box = new GameObject(name);
18             // Box.transform.localPosition = contralPos;
19             Box.transform.localPosition = new Vector3(contralPos.x, minPos.y, contralPos.z);
20             Box.AddComponent<MeshFilter>().mesh = mesh;
21             Material[] materials = new Material[2];
22             materials[0] = new Material(Resources.Load<Material>("Materials/Mine/MinefieldTexture_Side"));
23             materials[1] = new Material(Resources.Load<Material>("Materials/Mine/MinefieldTexture_Buttom"));
24             Box.AddComponent<MeshRenderer>().materials = materials;
 mesh.subMeshCount = 2;
 mesh.SetTriangles(vertexIndexList[0], 0); mesh.SetTriangles(vertexIndexList[1], 1);
这是指定子网格对应的索引集合,在设置索引时,应该这样分开存储:
 1  /// <summary>
 2         /// 添加索引
 3         /// </summary>
 4          private List<int[]> AddVertexIndex()
 5          {
 6             List<int[]> indexList = new List<int[]>();
 7             int[] sideIndexArray =
 8             {
 9                //front
10                2,1,0,
11                2,0,3,
12 
13                //back
14                4,5,6,
15                4,6,7,
16 
17                //left
18                8,10,11,
19                8,9,10,
20 
21                //right
22                13,15,14,
23                13,12,15,
24             };
25             int[] buttomFaceIndexArray =
26             {
27                //buttom
28                17,16,19,
29                17,19,18
30             };
31             indexList.Add(sideIndexArray);
32             indexList.Add(buttomFaceIndexArray);
33 
34             return indexList;
35         }
View Code

即这样完成分开了Mesh,分别使用不一样的材质。

相关文章
相关标签/搜索