1.Mesh、MeshFilter、MeshRenderer关系整理
在Unity3D中创建一个Cube,在Inspector可以看到其中含有MeshFilter、MeshRenderer组件。
MeshFilter含有一个Public成员 Mesh。
在Mesh中存储着三维模型的数据:vertices(顶点数据数组Vector3[])、triangles(三角形顶点索引数组,int[])、normals(法线向量数组,Vector3[])、uv(纹理坐标数组,Vector2[])。
2.使用Mesh创建一个Cube
创建一个脚本dyn3d.cs,成为Main Camera的组件,点击运行即可看到动态生成的Cube
1 using UnityEngine;
2 using System.Collections;
3
4 public class dyn3d : MonoBehaviour {
5
6 // Use this for initialization
7 void Start () {
8
9 CreateCube();
10 }
11
12 // Update is called once per frame
13 void Update () {
14
15 }
16
17 void CreateCube()
18 {
19
20 GameObject obj=new GameObject("cube");
21 MeshFilter mf=obj.AddComponent<MeshFilter>();
22 MeshRenderer mr=obj.AddComponent<MeshRenderer>();
23
24
25 Vector3[] vertices=new Vector3[24];
26 int[] triangles=new int[36];
27
28 //forward
29 vertices[0].Set(0.5f,-0.5f,0.5f);
30 vertices[1].Set(-0.5f,-0.5f,0.5f);
31 vertices[2].Set(0.5f,0.5f,0.5f);
32 vertices[3].Set(-0.5f,0.5f,0.5f);
33 //back
34 vertices[4].Set(vertices[2].x,vertices[2].y,-0.5f);
35 vertices[5].Set(vertices[3].x,vertices[3].y,-0.5f);
36 vertices[6].Set(vertices[0].x,vertices[0].y,-0.5f);
37 vertices[7].Set(vertices[1].x,vertices[1].y,-0.5f);
38 //up
39 vertices[8]=vertices[2];
40 vertices[9]=vertices[3];
41 vertices[10]=vertices[4];
42 vertices[11]=vertices[5];
43 //down
44 vertices[12].Set(vertices[10].x,-0.5f,vertices[10].z);
45 vertices[13].Set(vertices[11].x,-0.5f,vertices[11].z);
46 vertices[14].Set(vertices[8].x,-0.5f,vertices[8].z);
47 vertices[15].Set(vertices[9].x,-0.5f,vertices[9].z);
48 //right
49 vertices[16]=vertices[6];
50 vertices[17]=vertices[0];
51 vertices[18]=vertices[4];
52 vertices[19]=vertices[2];
53 //left
54 vertices[20].Set(-0.5f,vertices[18].y,vertices[18].z);
55 vertices[21].Set(-0.5f,vertices[19].y,vertices[19].z);
56 vertices[22].Set(-0.5f,vertices[16].y,vertices[16].z);
57 vertices[23].Set(-0.5f,vertices[17].y,vertices[17].z);
58
59 int currentCount=0;
60 for(int i=0;i<24;i=i+4)
61 {
62 triangles[currentCount++]=i;
63 triangles[currentCount++]=i+3;
64 triangles[currentCount++]=i+1;
65
66 triangles[currentCount++]=i;
67 triangles[currentCount++]=i+2;
68 triangles[currentCount++]=i+3;
69
70 }
71
72 mf.mesh.vertices=vertices;
73 mf.mesh.triangles=triangles;
74
75 }
76 }
在这里定义vertices数组存储顶点坐标信息,定义triangles数组存储三角形顶点索引。但是这个Mesh不包含normals和uv信息,所以显示出来的正方体是无纹理贴图,并且不能反应环境光照的。
3.冗余的顶点坐标
正方体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份存储。(如果你需要创建其它模型,需要根据实际情况决定顶点坐标的冗余度。实质上顶点坐标的冗余正是方便了法线坐标、纹理坐标的存取。)
4.三角形的渲染
准则:三角形有两面,正面可见,背面不可见。三角形的渲染顺序与三角形的正面法线呈左手螺旋定则。
这就决定了,如果我们需要渲染如下一个正方形面,那么就需要保证组成这个正方形的两个小三角形的正面法线都是指向屏幕外的。
我在程序中的顶点顺序为,三角形1: 0->3->1,三角形2: 0->2->3 。
1 int currentCount=0;
2 for(int i=0;i<24;i=i+4)
3 {
4 //三角形1
5 triangles[currentCount++]=i;
6 triangles[currentCount++]=i+3;
7 triangles[currentCount++]=i+1;
8 //三角形2
9 triangles[currentCount++]=i;
10 triangles[currentCount++]=i+2;
11 triangles[currentCount++]=i+3;
12 }
这段代码保证了正方体的6个面,12个三角形的都正确被渲染(正面法线朝外)。