将3D点投影到2D屏幕坐标
基于3D Programming for Windows(Charles Petzold)第7章中的信息,我试图编写一个辅助函数,将Point3D投影到包含相应屏幕坐标(x,y)的标准2D点:
public Point Point3DToScreen2D(Point3D point3D,Viewport3D viewPort ) { double screenX = 0d, screenY = 0d; // Camera is defined in XAML as: // // // PerspectiveCamera cam = viewPort.Camera as PerspectiveCamera; // Translate input point using camera position double inputX = point3D.X - cam.Position.X; double inputY = point3D.Y - cam.Position.Y; double inputZ = point3D.Z - cam.Position.Z; double aspectRatio = viewPort.ActualWidth / viewPort.ActualHeight; // Apply projection to X and Y screenX = inputX / (-inputZ * Math.Tan(cam.FieldOfView / 2)); screenY = (inputY * aspectRatio) / (-inputZ * Math.Tan(cam.FieldOfView / 2)); // Convert to screen coordinates screenX = screenX * viewPort.ActualWidth; screenY = screenY * viewPort.ActualHeight; // Additional, currently unused, projection scaling factors /* double xScale = 1 / Math.Tan(Math.PI * cam.FieldOfView / 360); double yScale = aspectRatio * xScale; double zFar = cam.FarPlaneDistance; double zNear = cam.NearPlaneDistance; double zScale = zFar == Double.PositiveInfinity ? -1 : zFar / (zNear - zFar); double zOffset = zNear * zScale; */ return new Point(screenX, screenY); }
然而,在测试时,此函数返回不正确的屏幕坐标(通过将2D鼠标坐标与简单的3D形状进行比较来检查)。 由于我缺乏3D编程经验,我很困惑为什么。
块注释部分包含可能必不可少的缩放计算,但是我不确定如何,并且本书继续使用XAML的MatrixCamera。 最初我只是希望得到一个基本的计算工作,无论与Matrices相比效率如何。
任何人都可以建议需要添加或更改的内容吗?
由于Windows坐标是z进入屏幕(x交叉y),我会使用类似的东西
screenY = viewPort.ActualHeight * (1 - screenY);
代替
screenY = screenY * viewPort.ActualHeight;
纠正screenY以适应Windows。
或者,您可以使用OpenGL。 设置视口x / y / z范围时,可以将其保留为“本机”单位,然后让OpenGL转换为屏幕坐标。
编辑:因为你的起源是中心。 我会尝试
screenX = viewPort.ActualWidth * (screenX + 1.0) / 2.0 screenY = viewPort.ActualHeight * (1.0 - ((screenY + 1.0) / 2.0))
屏幕+ 1.0从[-1.0,1.0]转换为[0.0,2.0]。 此时,除以2.0得到乘法的[0.0,1.0]。 为了解释从笛卡尔y翻转的Windows y,通过从1.0减去前一个屏幕,从[1.0,0.0](左上角到左下角)转换为[0.0,1.0](从大到小)。 然后,您可以缩放到ActualHeight。
我已经使用3DUtils Codeplex源库创建并成功测试了一种工作方法。
真正的工作是在3DUtils的TryWorldToViewportTransform()方法中执行的。 没有它,此方法将无法工作(请参阅上面的链接)。
Eric Sink的文章中也发现了非常有用的信息: 自动缩放 。
NB。 可能有更可靠/有效的方法,如果是这样,请添加它们作为答案。 与此同时,这对我的需求来说已经足够了。
/// /// Takes a 3D point and returns the corresponding 2D point (X,Y) within the viewport. /// Requires the 3DUtils project available at https://www.codeplex.com/Wiki/View.aspx?ProjectName=3DTools /// /// A point in 3D space /// An instance of Viewport3D /// The corresponding 2D point or null if it could not be calculated public Point? Point3DToScreen2D(Point3D point3D, Viewport3D viewPort) { bool bOK = false; // We need a Viewport3DVisual but we only have a Viewport3D. Viewport3DVisual vpv =VisualTreeHelper.GetParent(viewPort.Children[0]) as Viewport3DVisual; // Get the world to viewport transform matrix Matrix3D m = MathUtils.TryWorldToViewportTransform(vpv, out bOK); if (bOK) { // Transform the 3D point to 2D Point3D transformedPoint = m.Transform(point3D); Point screen2DPoint = new Point(transformedPoint.X, transformedPoint.Y); return new Nullable(screen2DPoint); } else { return null; } }
这并没有解决有问题的算法,但它可能对于遇到这个问题很有用(正如我所做的那样)。
在.NET 3.5中,您可以使用Visual3D.TransformToAncestor(Visual ancestor) 。 我使用它在我的3D视口上的canvas上绘制线框:
void CompositionTarget_Rendering(object sender, EventArgs e) { UpdateWireframe(); } void UpdateWireframe() { GeometryModel3D model = cube.Content as GeometryModel3D; canvas.Children.Clear(); if (model != null) { GeneralTransform3DTo2D transform = cube.TransformToAncestor(viewport); MeshGeometry3D geometry = model.Geometry as MeshGeometry3D; for (int i = 0; i < geometry.TriangleIndices.Count;) { Polygon p = new Polygon(); p.Stroke = Brushes.Blue; p.StrokeThickness = 0.25; p.Points.Add(transform.Transform(geometry.Positions[geometry.TriangleIndices[i++]])); p.Points.Add(transform.Transform(geometry.Positions[geometry.TriangleIndices[i++]])); p.Points.Add(transform.Transform(geometry.Positions[geometry.TriangleIndices[i++]])); canvas.Children.Add(p); } } }
这也考虑了模型上的任何变换等。
另见: http : //blogs.msdn.com/wpf3d/archive/2009/05/13/transforming-bounds.aspx
-
目前还不清楚你想用aspectRatio coeff实现什么。 如果该点位于视野的边缘,那么它应该位于屏幕的边缘,但是如果aspectRatio!= 1则不是。 尝试设置aspectRatio = 1并使窗口平方。 坐标是否仍然不正确?
-
ActualWidth
和ActualHeight
似乎真的是窗口大小的一半,所以screenX将是[-ActualWidth; ActualWidth],但不是[0; ActualWidth的。 那是你要的吗?
screenX和screenY应该相对于屏幕中心进行计算…
我没有看到使用Windows API绘图时,原点位于屏幕左上角的事实。 我假设您的坐标系是
y | | +------x
另外,根据斯科特的问题,您的坐标系是否位于中心,还是位于左下角?
但是,Windows屏幕API是
+-------x | | | y
你需要坐标转换从经典的笛卡儿到Windows。
上述就是C#学习教程:将3D点投影到2D屏幕坐标分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注---计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/1043092.html