【问题标题】:How to construct a dodecahedron and texture its sides如何构造一个十二面体并为其侧面设置纹理
【发布时间】:2015-03-16 15:16:36
【问题描述】:

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

由于这是一个游戏项目,我想在 Unity3d 中完成,但任何方法(脚本或免费 3d 图形工具)都会有所帮助。

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

【问题讨论】:

  • 听起来您想要一个 3D 建模程序而不是 3D 游戏引擎。看看 Blender(免费)和这个关于 creating a dodecahedron 的问题。
  • 柏拉图式的身体插件似乎不再存在,而斜角解决方案简直超出了我微薄的搅拌机技能。但你的回答为我指明了正确的方向......

标签: unity3d 3d polygons


【解决方案1】:

经过一番折腾,我确实找到了答案:

第 1 步:在 Blender 中创建五边形

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

第 2 步:将其导入 Unity3d

Unity3d 会自然导入 .blend 文件,所以我只是将五边形保存为 .blend 文件,并将其作为 Asset 导入 Unity3d 中。

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

为了方便地围绕后面的十二面体中心旋转五边形,我在 Unity3d 中创建了一个尺寸为 X=0.1、Y=2、Z=0.1 的圆柱体。然后我将五边形 - 作为子对象 - 放在该圆柱体的确切末端(对于我的 3d-五边形,变换 Y=9,75,如果使用扁平五边形并旋转 X=90,则为 10)。我对另一端做了同样的事情,五边形转过身来。 (Transform =-9,75 and Rotate X=90, Z=180)同时做两半。

第 3 步:玩转旋转和缩放

所以现在我可以复制并旋转圆柱体(附有五角大楼)X=63.435(十二面体边之间的角度)和 Y=180(所以边彼此面对),以使其进入正确的位置。现在我必须找到五边形的正确比例来缩小恰好在 15.275 处相当接近的差距。以各自的角度重复五次(y=180 +/- 72 的倍数,五边形的边之间的角度)。

完成。

【讨论】:

    【解决方案2】:

    首先创建一些数学助手:

    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()
            };
        }
    }
    

    然后像这样调用你的 polydron 管理器:

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

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

    希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 2011-06-09
      • 1970-01-01
      • 1970-01-01
      • 2012-08-17
      • 1970-01-01
      • 1970-01-01
      • 2012-07-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多