3

如何在 3D 中创建一个具有已知中心点、半径的圆,并且它位于与 WPF 中的线(矢量)垂直的平面上?

4

2 回答 2

4

以下是基于我之前发布的评论的示例。

首先我们定义一个函数来生成圆的模型:

    /// <summary>
    /// Generates a model of a Circle given specified parameters
    /// </summary>
    /// <param name="radius">Radius of circle</param>
    /// <param name="normal">Vector normal to circle's plane</param>
    /// <param name="center">Center position of the circle</param>
    /// <param name="resolution">Number of slices to iterate the circumference of the circle</param>
    /// <returns>A GeometryModel3D representation of the circle</returns>
    private GeometryModel3D GetCircleModel(double radius, Vector3D normal, Point3D center, int resolution)
    {
        var mod = new GeometryModel3D();
        var geo = new MeshGeometry3D();

        // Generate the circle in the XZ-plane
        // Add the center first
        geo.Positions.Add(new Point3D(0, 0, 0));

        // Iterate from angle 0 to 2*PI
        double t = 2 * Math.PI / resolution;
        for (int i = 0; i < resolution; i++)
        {
            geo.Positions.Add(new Point3D(radius * Math.Cos(t * i), 0, -radius * Math.Sin(t * i)));
        }

        // Add points to MeshGeoemtry3D
        for (int i = 0; i < resolution; i++)
        {
            var a = 0;
            var b = i + 1;
            var c = (i < (resolution - 1)) ? i + 2 : 1;

            geo.TriangleIndices.Add(a);
            geo.TriangleIndices.Add(b);
            geo.TriangleIndices.Add(c);
        }

        mod.Geometry = geo;

        // Create transforms
        var trn = new Transform3DGroup();
        // Up Vector (normal for XZ-plane)
        var up = new Vector3D(0, 1, 0);
        // Set normal length to 1
        normal.Normalize();
        var axis = Vector3D.CrossProduct(up, normal); // Cross product is rotation axis
        var angle = Vector3D.AngleBetween(up, normal); // Angle to rotate
        trn.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(axis, angle)));
        trn.Children.Add(new TranslateTransform3D(new Vector3D(center.X, center.Y, center.Z)));

        mod.Transform = trn;

        return mod;
    }

设置我们的 ViewPort3D:

    <Grid Background="Black">
        <Viewport3D Name="mainViewPort">
            <Viewport3D.Camera>
                <PerspectiveCamera NearPlaneDistance="0.1" FarPlaneDistance="100" UpDirection="0,1,0" Position="0,0,10" 
                               LookDirection="0,0,-1" FieldOfView="60" />
            </Viewport3D.Camera>
            <ModelVisual3D>
                <ModelVisual3D.Content>
                    <Model3DGroup>
                        <AmbientLight Color="#40FFFFFF" />
                        <DirectionalLight Color="#FFFFFF" Direction="1,-1,-1" />
                    </Model3DGroup>
                </ModelVisual3D.Content>
            </ModelVisual3D>
        </Viewport3D>
    </Grid>

然后进行测试:

    private void AddCircleModel()
    {
        var mod = GetCircleModel(1.5, new Vector3D(0, 1, 1), new Point3D(0, -1, 0), 20);
        mod.Material = new DiffuseMaterial(Brushes.Silver);
        var vis = new ModelVisual3D() { Content = mod };
        mainViewPort.Children.Add(vis);
    }

加载您的窗口,打电话AddCircleModel();并享受。调整视图/参数,让您随心所欲。

于 2013-01-07T14:42:52.883 回答
0

旧线程,但仍然,如果有人遇到。“newb”答案会起作用,但它是相当懒惰的数学和耗时的 wize,原因如下:

  • 有 2 个 for 循环,真正需要 1 个。
  • 在以 (0,0,0) 为中心的 (x,z) 平面上构建圆后使用变换是多余的,因为您知道数学和向量约束。

我建议你以这种方式实现它(我做了它,所以它也适用于椭圆,只需将 uSize = vSize 设置为圆,u 和 v 真的只需要不平行即可工作,但出于网格质量目的,我强迫他们垂直):

public static Mesh CreateEllipse3D(Vector3 center, Vector3 normal, Vector3 u, 
                                   Vector3 v, float uSize, float vSize, int 
                                   subdiv = 30)
    {
        if (Math.Abs(Vector3.Dot(u, v)) != 0)
        {
            Debug.LogError("Vectors u and v must be orthogonals");
            return new Mesh();
        }
        var mesh = new Mesh();
        var vertices = new List<Vector3>();
        var normals = new List<Vector3>();
        var triangles = new List<int>();
        vertices.Add(center);
        float t = 2 * Mathf.PI / subdiv;
        int a = 0, b, c;
        for (int i = 0; i < subdiv; i++)
        {
            vertices.Add(center + u.normalized * uSize * Mathf.Cos(t * i) - 
            v.normalized * vSize * Mathf.Sin(t * i));
            
            b = i + 1;
            c = (i < (subdiv - 1)) ? i + 2 : 1;

            triangles.Add(a);
            triangles.Add(b);
            triangles.Add(c);

            normals.Add(normal);
            normals.Add(normal);
            normals.Add(normal);
        }

        mesh.vertices = vertices.ToArray();
        mesh.triangles = triangles.ToArray();
        mesh.normals = normals.ToArray();
        return mesh;
    }
于 2021-09-15T10:01:39.053 回答