当做一个地球的时候,可以使用六面体,八面体和20面体来画一个球体,它们的算法都是相同的,下面是20面体画球体的代码
欧拉定理(欧拉公式) V + F- E = 2 (简单多面体的顶点数 V,棱数 E 和面数 F).
1、这种方法的原理是:
对每一个三角面进行一次
public GameObject GetSphere(int subdivision = 5, float radius = 1.0f) { GameObject go = new GameObject ("Pentagon"); MeshFilter filter = go.AddComponent<MeshFilter> (); Mesh mesh = new Mesh(); filter.mesh = mesh; List<Vector3> vertices = new List<Vector3>() { new Vector3(0.000000f, -1.000000f, 0.000000f), new Vector3(0.723600f, -0.447215f, 0.525720f), new Vector3(-0.276385f, -0.447215f, 0.850640f), new Vector3(-0.894425f, -0.447215f, 0.000000f), new Vector3(-0.276385f, -0.447215f, -0.850640f), new Vector3(0.723600f, -0.447215f, -0.525720f), new Vector3(0.276385f, 0.447215f, 0.850640f), new Vector3(-0.723600f, 0.447215f, 0.525720f), new Vector3(-0.723600f, 0.447215f, -0.525720f), new Vector3(0.276385f, 0.447215f, -0.850640f), new Vector3(0.894425f, 0.447215f, 0.000000f), new Vector3(0.000000f, 1.000000f, 0.000000f), }; int[] triangles = { 0, 1, 2, 1, 0, 5, 0, 2, 3, 0, 3, 4, 0, 4, 5, 1, 5, 10, 2, 1, 6, 3, 2, 7, 4, 3, 8, 5, 4, 9, 1, 10, 6, 2, 6, 7, 3, 7, 8, 4, 8, 9, 5, 9, 10, 6, 10, 11, 7, 6, 11, 8, 7, 11, 9, 8, 11, 10, 9, 11 }; for (int s = 0; s < subdivision; s++) { //这将存储连接中已经存在的所有顶点,因此不存在重复/重叠的顶点 List<VertOnEdge> connected = new List<VertOnEdge>(); //细分后的面数 int[] newTriangles = new int[triangles.Length * 4]; //遍历三角形面数 for (int i = 0; i < triangles.Length / 3; i++) { //获取一个面的三个顶点 int A = triangles[i * 3]; int B = triangles[i * 3 + 1]; int C = triangles[i * 3 + 2]; int ab = -1; int bc = -1; int ca = -1; List<int> connectionToBeRemoved = new List<int>(); //需要删除的连接 for (int j = 0; j < connected.Count; j++) { VertOnEdge voe = connected[j]; if (voe.A == A) { if (voe.B == B) { ab = voe.vertIndex; connectionToBeRemoved.Add(j); } else if (voe.B == C) { ca = voe.vertIndex; connectionToBeRemoved.Add(j); } } else if (voe.A == B) { //check if there is a connection between B and A or C if (voe.B == A) { ab = voe.vertIndex; connectionToBeRemoved.Add(j); } else if (voe.B == C) { bc = voe.vertIndex; connectionToBeRemoved.Add(j); } } else if (voe.A == C) { //check if there is a connection between C and A or B if (voe.B == A) { ca = voe.vertIndex; connectionToBeRemoved.Add(j); } else if (voe.B == B) { bc = voe.vertIndex; connectionToBeRemoved.Add(j); } } } connectionToBeRemoved.Sort(); connectionToBeRemoved.Reverse(); for (int k = 0; k < connectionToBeRemoved.Count; k++) { connected.RemoveAt(connectionToBeRemoved[k]); } //create new vertices and connections that don't exist if (ab == -1) { vertices.Add((vertices[A] + vertices[B]) / 2); ab = vertices.Count - 1; connected.Add(new VertOnEdge(ab, A, B)); } if (bc == -1) { vertices.Add((vertices[B] + vertices[C]) / 2); bc = vertices.Count - 1; connected.Add(new VertOnEdge(bc, B, C)); } if (ca == -1) { vertices.Add((vertices[C] + vertices[A]) / 2); ca = vertices.Count - 1; connected.Add(new VertOnEdge(ca, C, A)); } //4个三角形 12个顶点 int triangleStartingIndex = i * 12; //在写入三角形的时候注意要顺时针写(opengl标准),逆时针写的时候面是反的 newTriangles[triangleStartingIndex] = A; newTriangles[triangleStartingIndex + 1] = ab; newTriangles[triangleStartingIndex + 2] = ca; newTriangles[triangleStartingIndex + 3] = ab; newTriangles[triangleStartingIndex + 4] = B; newTriangles[triangleStartingIndex + 5] = bc; newTriangles[triangleStartingIndex + 6] = bc; newTriangles[triangleStartingIndex + 7] = C; newTriangles[triangleStartingIndex + 8] = ca; newTriangles[triangleStartingIndex + 9] = ab; newTriangles[triangleStartingIndex + 10] = bc; newTriangles[triangleStartingIndex + 11] = ca; } for (int i = 0; i < vertices.Count; i++) { vertices[i] = vertices[i].normalized; } triangles = null; triangles = newTriangles; } mesh.vertices = vertices.ToArray(); mesh.triangles = triangles; mesh.RecalculateNormals(); Material material = new Material (Shader.Find ("Diffuse")); material.SetColor ("_Color", Color.yellow); MeshRenderer renderer = go.AddComponent<MeshRenderer> (); renderer.sharedMaterial = material; return go; }
2、原理:
先对AB和BC这两个面进行N次切分,然后按照顺序获取所有的切分后的顶点,相对于上面的算法优化了切分时的算法,比较推荐
public GameObject GetSphere(int subdivision = 5) { GameObject go = new GameObject("Planet"); MeshFilter filter = go.AddComponent<MeshFilter>(); Mesh mesh = new Mesh(); filter.mesh = mesh; List<Vector3> vertices = new List<Vector3>() { new Vector3(0.000000f, -1.000000f, 0.000000f), new Vector3(0.723600f, -0.447215f, 0.525720f), new Vector3(-0.276385f, -0.447215f, 0.850640f), new Vector3(-0.894425f, -0.447215f, 0.000000f), new Vector3(-0.276385f, -0.447215f, -0.850640f), new Vector3(0.723600f, -0.447215f, -0.525720f), new Vector3(0.276385f, 0.447215f, 0.850640f), new Vector3(-0.723600f, 0.447215f, 0.525720f), new Vector3(-0.723600f, 0.447215f, -0.525720f), new Vector3(0.276385f, 0.447215f, -0.850640f), new Vector3(0.894425f, 0.447215f, 0.000000f), new Vector3(0.000000f, 1.000000f, 0.000000f) }; int[] triangles = { 0, 1, 2, 1, 0, 5, 0, 2, 3, 0, 3, 4, 0, 4, 5, 1, 5, 10, 2, 1, 6, 3, 2, 7, 4, 3, 8, 5, 4, 9, 1, 10, 6, 2, 6, 7, 3, 7, 8, 4, 8, 9, 5, 9, 10, 6, 10, 11, 7, 6, 11, 8, 7, 11, 9, 8, 11, 10, 9, 11 }; if (subdivision <= 0) { subdivision = 0; } int cut = (subdivision + 1); List<Vector3> newvertices = new List<Vector3>(); List<int> trangles = new List<int>(); Dictionary<Vector3,int> verticeidxs = new Dictionary<Vector3, int>(); //对每个三角面进行遍历 for (int i = 0; i < (triangles.Length/3); i++) { //获取三角面的顶点 int index = i * 3; var a = vertices[triangles[index]]; var b = vertices[triangles[index+1]]; var c = vertices[triangles[index+2]]; var ab = (b - a) / cut;// var ac = (c - a) / cut; List<int> newpsindex = new List<int>(); //进行切分的次数 for (int j = 0; j <= cut; j++) { //切分后AB上的向量长度 var abcut = ab * j; // for (int k = 0; k <= (cut- j); k++) { //获取三角形上所有的顶点位置 var pos = a + (abcut + ac * k); //新的顶点的个数 int newindex = newvertices.Count; //如果顶点在三条边上 if (j == 0 || k == 0 || ((j + k) == cut)) { //如果顶点字典中不存在该顶点 if (!verticeidxs.ContainsKey(pos)) { //将该顶点添加到字典和新顶点列表中 newindex = newvertices.Count; newvertices.Add(pos); verticeidxs.Add(pos,newindex); } else { //将字典中的该顶点的下标赋 newindex = verticeidxs[pos]; } } else { newindex = newvertices.Count; newvertices.Add(pos); verticeidxs.Add(pos,newindex); } //添加新的顶点的下标 newpsindex.Add(newindex); } } var s0 = 0;//下标最小数 //组成三角面,对切分次数进行遍历,j代表第几行 for (int j = 0; j < cut; j++) { //下一行的第一个顶点的下标 var s1 = s0 + cut + 1 - j ; //k代表第J行的第K个点 for (int k = 0; k < (cut - j); k++) { //添加每一行的正三角 trangles.Add(newpsindex[s0 + k]); trangles.Add(newpsindex[s1 + k]); trangles.Add(newpsindex[s0 + k + 1]); //添加每一行的倒立的三角 if (j < cut - 1 && k<(cut - j - 1)) { trangles.Add(newpsindex[s0 + k + 1]); trangles.Add(newpsindex[s1 + k]); trangles.Add(newpsindex[s1 + k + 1]); } } s0 = s1; } } //将顶点归一化 for (int i = 0; i < newvertices.Count; i++) { newvertices[i] = newvertices[i].normalized * 1; } vertices = newvertices; MeshRenderer renderer = go.AddComponent<MeshRenderer>(); renderer.material.shader = Shader.Find("Diffuse"); renderer.material.SetColor("_Color",Color.yellow); mesh.vertices = newvertices.ToArray(); mesh.triangles = trangles.ToArray(); return go; }