动态Torus网格生成中角度不正确

时间:2018-10-31 11:27:14

标签: c# unity3d math geometry

我有一个用于根据角度动态生成圆环段的代码(我基本上是从ProceduralPrimitives那里获取的,并做了一些更改以使用输入角度而不是创建完整的圆环):

public class DynamicTorusSegment : MonoBehaviour
{
    [Range(0, 360)] public float CurrentAngle;

    [SerializeField] private int _maxSegments = 20;
    [SerializeField] private float _radius1 = 1f;
    [SerializeField] private float _radius2 = 0.3f;
    [SerializeField] private int _nbSides = 5;

    // For checking if the Angle was changed since the last frame
    private float _lastAngle;
    private const float TwoPi = Mathf.PI * 2f;

    // Update is called once per frame
    private void Update()
    {
        if (Mathf.Approximately(CurrentAngle, _lastAngle)) return;

        _lastAngle = CurrentAngle;
        GenerateTorus(CurrentAngle);
    }

    private void GenerateTorus(float angle)
    {
        var meshFilter = GetComponent<MeshFilter>();
        var mesh = meshFilter.mesh;
        mesh.Clear();

        var segmentsCount = (int)(_maxSegments * angle / 360);
        if (!Mathf.Approximately(angle, 0))
        {
            segmentsCount = Mathf.Clamp(segmentsCount, 2, _maxSegments);
        }


        #region Vertices        
        var vertices = new Vector3[(segmentsCount + 1) * (_nbSides + 1)];

        for (var segment = 0; segment < segmentsCount; segment++)
        {
            var currentSegment = segment == segmentsCount ? 0 : segment;

            var t1 = (float)currentSegment / segmentsCount * TwoPi / 360 * angle;
            var r1 = new Vector3(Mathf.Cos(t1) * _radius1, 0f, Mathf.Sin(t1) * _radius1);

            for (var side = 0; side <= _nbSides; side++)
            {
                var currentSide = side == _nbSides ? 0 : side;

                var t2 = (float)currentSide / _nbSides * TwoPi;
                var r2 = Quaternion.AngleAxis(-t1 * Mathf.Rad2Deg, Vector3.up) * new Vector3(Mathf.Sin(t2) * _radius2, Mathf.Cos(t2) * _radius2);

                vertices[side + segment * (_nbSides + 1)] = r1 + r2;
            }
        }
        #endregion

        #region Normales        
        var normals = new Vector3[vertices.Length];
        for (var segment = 0; segment < segmentsCount; segment++)
        {
            var currentSegment = segment == segmentsCount ? 0 : segment;

            var t1 = (float)currentSegment / segmentsCount * TwoPi / 360 * angle;
            var r1 = new Vector3(Mathf.Cos(t1) * _radius1, 0f, Mathf.Sin(t1) * _radius1);

            for (var side = 0; side <= _nbSides; side++)
            {
                normals[side + segment * (_nbSides + 1)] = (vertices[side + segment * (_nbSides + 1)] - r1).normalized;
            }
        }
        #endregion

        #region UVs
        var uvs = new Vector2[vertices.Length];
        for (var segment = 0; segment < segmentsCount; segment++)
        {
            for (var side = 0; side <= _nbSides; side++)
            {
                uvs[side + segment * (_nbSides + 1)] = new Vector2((float)segment / segmentsCount, (float)side / _nbSides);
            }
        }
        #endregion

        #region Triangles
        var faceCount = vertices.Length;
        var triangleCount = faceCount * 2;
        var indexesCount = triangleCount * 3;
        var triangles = new int[indexesCount];

        var i = 0;
        for (var segment = 0; segment < segmentsCount - 1; segment++)
        {
            for (var side = 0; side <= _nbSides - 1; side++)
            {
                var current = side + segment * (_nbSides + 1);
                var next = side + (segment < (segmentsCount) ? (segment + 1) * (_nbSides + 1) : 0);

                if (i >= triangles.Length - 6) continue;

                triangles[i++] = current;
                triangles[i++] = next;
                triangles[i++] = next + 1;

                triangles[i++] = current;
                triangles[i++] = next + 1;
                triangles[i++] = current + 1;
            }
        }
        #endregion

        mesh.vertices = vertices;
        mesh.normals = normals;
        mesh.uv = uvs;
        mesh.triangles = triangles;

        mesh.RecalculateBounds();
    }
}

这基本上可以正常工作,并根据CurrentAngle

创建Torus细分

enter image description here

但是您可能已经注意到我的代码/数学出了点问题。

主要问题是:圆环在360°上不完整。

enter image description here

但是当我进一步测试它时,我注意到实际上已经在例如90°也不正确:

enter image description here

因此,为了获得闭合的圆环,我必须使用378°至379°之间的某个角度。因此似乎总有缺少19°的偏移..但这是从哪里来的呢? (它似乎也适用于小角度,所以我也有一个angle < 19°的分段)

有人在这里看到我在做什么错吗?


更新

19°的数目实际上似乎取决于(360° / _maxSegments ) = 18,5°(在我的示例中,_maxSegments是20),实际上取决于_maxSegments的值而变小或变大。 / p>

1 个答案:

答案 0 :(得分:3)

从原始代码中提取两行:

Vector3[] vertices = new Vector3[(nbRadSeg+1) * (nbSides+1)];

for( int seg = 0; seg <= nbRadSeg; seg++ )
                      ^^

...以及您自己的代码中的相应行:

var vertices = new Vector3[(segmentsCount + 1) * (_nbSides + 1)];

for (var segment = 0; segment < segmentsCount; segment++)
                              ^

请注意,第二个循环条件是<,而不是应为的<=。这意味着您将跳过最后一段。

在您的示例中,由于线段的角度跨度为〜18.5°,因此您观察到的〜19°系统差异不是巧合。

请注意,法线,UV和三角形生成循环也会发生相同的问题。