如何在 3D 中创建一个具有已知中心点、半径的圆,并且它位于与 WPF 中的线(矢量)垂直的平面上?
问问题
5919 次
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 回答