当做一个地球的时候,可以使用六面体,八面体和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;
}