如何构建十二面体并对其边进行纹理化

时间:2015-03-16 15:16:37

标签: unity3d 3d polygons

我想基于十二面体创建一个行星的三维模型,并为每一侧提供不同的纹理,代表该区域的主要景观和其他内容。 (或者,一个分成12个五边形的地球仪会更好,但可能更复杂。)

作为一个游戏项目,我想在Unity3d中完成它,但任何方法(脚本或免费的3D图形工具)都会有所帮助。

我已经研究了柏拉图主体的几何形状,并试图找出如何绘制五边形,然后恰当地调整它们的角度,或者只是为此得到一个现成的解决方案,但到目前为止我还没有找到任何方法。

2 个答案:

答案 0 :(得分:2)

经过一番摆弄后,我确实找到了答案:

第1步:在Blender中创建五角大楼

这实际上非常简单。创建一个圆并将顶点数减少到5.我实际上选择了尺寸为X = 1,Y = 1,Z = 0.1的圆柱体,但对于平面五边形,整体解决方案是相同的。

第2步:将其纳入Unity3d

Unity3d自然导入.blend文件,所以我把五角大楼保存为.blend文件并将其作为资产导入Unity3d。

第3步:将五角大楼放在棍子上。

为了轻松地围绕后十二面体的中心旋转五边形,我在Unity3d中创建了一个尺寸为X = 0.1,Y = 2,Z = 0.1的圆柱体。然后我把五角大楼 - 作为一个儿童对象 - 放在那个圆柱体的确切的末端(我的三维五边形变换Y = 9,75,如果使用扁平五边形,旋转X = 90,则为10)。五角大楼转过身来,我在另一端做了同样的事情。 (变换= -9,75并且旋转X = 90,Z = 180)同时进行两个半部分。

第3步:使用旋转和缩放

所以现在我可以通过X = 63.435(十二面体的两边之间的角度)复制和旋转圆柱体(连接五角大楼)并且Y = 180(因此边缘彼此面对)以使其进入正确的位置。现在我必须找到正确的五角形缩放以缩小差距,恰好在15.275接近。用相应的角度重复五次(y = 180 +/- 72的倍数,五边形两边的角度)。

完成。

答案 1 :(得分:0)

首先创建一些数学助手:

using System;
using UnityEngine;

public static class MathUtils
{

    public static Vector3 MultiplyMatrixAndVector(float[,] m, Vector3 v) 
    {
        return new Vector3(
            v.x * m[0, 0] + v.y * m[0, 1] + v.z * m[0, 2],
            v.x * m[1, 0] + v.y * m[1, 1] + v.z * m[1, 2],
            v.x * m[2, 0] + v.y * m[2, 1] + v.z * m[2, 2]
        );
    }

    public static Vector3 RotateVectorAroundAxis(Vector3 vector, Vector3 a, Vector3 b, double angle)
    {
        var normalizedAxis = (b - a).normalized;
        var x = normalizedAxis.x;
        var y = normalizedAxis.y;
        var z = normalizedAxis.z;
        var translationVector = ProjectPointOnAxis(vector, a, b);
        var c = (float) Math.Cos(angle);
        var s = (float) Math.Sin(angle);
        var rotationMatrix = new float[3, 3];

        rotationMatrix[0, 0] = c + x * x * (1 - c);
        rotationMatrix[0, 1] = x * y * (1 - c) - z * s;
        rotationMatrix[0, 2] = x * z * (1 - c) + y * s;
        rotationMatrix[1, 0] = y * x * (1 - c) + z * s;
        rotationMatrix[1, 1] = c + y * y * (1 - c);
        rotationMatrix[1, 2] = y * z * (1 - c) - x * s;
        rotationMatrix[2, 0] = z * x * (1 - c) - y * s;
        rotationMatrix[2, 1] = z * y * (1 - c) + x * s;
        rotationMatrix[2, 2] = c + z * z * (1 - c);

        return MultiplyMatrixAndVector(rotationMatrix, vector - translationVector) + translationVector;
    }

    public static Vector3 ProjectPointOnAxis(Vector3 vector, Vector3 a, Vector3 b)
    {
        var ba = b - a;
        var lambda = Vector3.Dot(ba, vector - a) / Vector3.Dot(ba, ba);

        return a + lambda * ba;
    }

}

然后创建一个多面体管理器:

using System;
using System.Collections.Generic;
using UnityEngine;

public static class PolyhedronManager
{
    public static GameObject CreatePolyhedron(string name)
    {
        var material = Resources.Load("Materials/Cube") as Material;
        var gameObject = new GameObject();

        Mesh mesh;

        switch (name)
        {
            case "hexahedron":
            {
                mesh = CreatePolyhedronMesh(4, Math.PI / 2);
                break;
            }

            case "tetrahedron":
            {
                mesh = CreatePolyhedronMesh(3, Math.Acos(1d / 3));
                break;
            }

            case "octahedron":
            {
                mesh = CreatePolyhedronMesh(3, Math.Acos(-1d / 3));
                break;
            }

            case "icosahedron":
            {
                mesh = CreatePolyhedronMesh(3, Math.Acos(-Math.Sqrt(5) / 3));
                break;
            }

            case "dodecahedron":
            {
                mesh = CreatePolyhedronMesh(5, 2 * Math.Atan2(1 + Math.Sqrt(5), 2));
                break;
            }

            default:
            {
                throw new Exception("Invalid polyhedron name");
            }
        }

        mesh.RecalculateNormals();
        gameObject.AddComponent<MeshRenderer>().material = material;
        gameObject.AddComponent<MeshFilter>().mesh = mesh;
        gameObject.name = char.ToUpper(name[0]) + name.Substring(1);
        gameObject.transform.rotation = Quaternion.Euler(180, 0, 0);

        return gameObject;
    }

    private static List<Vector3> CreatePolygonVertices(int nSides, Vector3 origin, Vector3 normal, Vector3 firstNode, bool shouldUseFirstNode)
    {
        var angle = 2 * Math.PI / nSides;
        var distanceFromCenter = (float) Math.Sqrt(0.5 / (1 - Math.Cos(angle)));
        var actualFirstNode = shouldUseFirstNode ? firstNode : origin + distanceFromCenter * Vector3.right;

        var vertices = new List<Vector3> {actualFirstNode};

        for (var i = 1; i < nSides; i++)
        {
            vertices.Add(MathUtils.RotateVectorAroundAxis(vertices[i - 1], origin, origin + normal, angle));
        }

        return vertices;
    }

    private static Mesh CreatePolyhedronMesh(int nSides, double dihedralAngle)
    {
        var supplementaryAngle = Math.PI - dihedralAngle;
        var origin = Vector3.zero;
        var faces = new List<List<Vector3>> {CreatePolygonVertices(nSides, origin, Vector3.up, origin, false)};
        var centers = new List<Vector3> {origin};

        var facesQueue = new List<List<Vector3>> {faces[0]};
        var centersQueue = new List<Vector3> {centers[0]};

        while (true)
        {
            if (facesQueue.Count == 0)
            {
                break;
            }

            var face = facesQueue[0];
            var center = centersQueue[0];

            facesQueue.RemoveAt(0);
            centersQueue.RemoveAt(0);

            for (var i = 0; i < nSides; i++)
            {
                var a = face[i];
                var b = face[i == nSides - 1 ? 0 : i + 1];
                var dihedralAxis = b - a;
                var pivot = (b + a) / 2;
                var p = pivot - center;
                var nextCenter = pivot + p;
                var rotatedCenter = MathUtils.RotateVectorAroundAxis(nextCenter, a, b, supplementaryAngle);

                var centerAlreadyExists = false;

                centers.ForEach(existingCenter =>
                {    
                    if ((rotatedCenter - existingCenter).sqrMagnitude < 0.01)
                    {
                        centerAlreadyExists = true;
                    }
                });

                if (centerAlreadyExists)
                {
                    continue;
                }

                var normal = Vector3.Cross(p, dihedralAxis);
                var nextFace = CreatePolygonVertices(nSides, nextCenter, normal, a, true)
                    .ConvertAll(new Converter<Vector3, Vector3>(vertex => MathUtils.RotateVectorAroundAxis(vertex, a, b, supplementaryAngle)));

                faces.Add(nextFace);
                centers.Add(rotatedCenter);

                facesQueue.Add(nextFace);
                centersQueue.Add(rotatedCenter);
            }
        }

        var vertices = new List<Vector3>();
        var triangles = new List<int>();
        var c = -1;

        for (var i = 0; i < faces.Count; i++)
        {
            var face = faces[i];
            var center = centers[i];

            c++;
            vertices.Add(center);

            var centerVertexIndex = c;

            for (var j = 0; j < face.Count; j++)
            {
                var vertex = face[j];

                c++;
                vertices.Add(vertex);
                triangles.Add(centerVertexIndex);
                triangles.Add(c);
                triangles.Add(j == face.Count - 1 ? centerVertexIndex + 1 : c + 1);
            }
        }

        return new Mesh
        {
            vertices = vertices.ToArray(), 
            triangles = triangles.ToArray()
        };
    }
}

然后像下面这样调用您的多管管理器:

var polyhedronGameObject = PolyhedronManager.CreatePolyhedron("dodecahedron");

此脚本根据多边形边上的数字和多面体的二面角生成顶点和三角形。为此,它使用“呼吸优先”搜索策略在脸上构造多面体。

希望这会有所帮助。