2009-02-06 22 views
6

Basado en información en el Capítulo 7 de 3D Programming For Windows (Charles Petzold), he intentado escribir como función auxiliar que proyecta un Point3D a un Punto 2D estándar que contiene las coordenadas de pantalla correspondientes (x, y) :Proyectando un punto 3D a una coordenada de pantalla 2D

public Point Point3DToScreen2D(Point3D point3D,Viewport3D viewPort) 
{ 
    double screenX = 0d, screenY = 0d; 

    // Camera is defined in XAML as: 
    //  <Viewport3D.Camera> 
    //    <PerspectiveCamera Position="0,0,800" LookDirection="0,0,-1" /> 
    //  </Viewport3D.Camera> 

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

en las pruebas sin embargo, esta función devuelve las coordenadas de pantalla incorrectas (comprobado mediante la comparación de las coordenadas 2D de ratón contra una sencilla forma 3D). Debido a mi falta de experiencia en programación en 3D, estoy confundido sobre por qué.

La sección de comentario del bloque contiene cálculos de escalado que pueden ser esenciales, sin embargo no estoy seguro de cómo, y el libro continúa con MatrixCamera utilizando XAML. Inicialmente, solo quiero que funcione un cálculo básico, independientemente de cuán ineficiente sea en comparación con Matrices.

¿Alguien puede aconsejar qué se debe agregar o cambiar?

Respuesta

2

Desde las coordenadas z de Windows están en la pantalla (x cruz y), me gustaría utilizar algo así como

screenY = viewPort.ActualHeight * (1 - screenY); 

en lugar de

screenY = screenY * viewPort.ActualHeight; 

para corregir screenY para acomodar de Windows.

Como alternativa, puede usar OpenGL. Cuando establece el rango de ventana gráfica x/y/z, puede dejarlo en unidades "nativas" y dejar que OpenGL convierta a coordenadas de pantalla.

Editar: Dado que su origen es el centro. Lo intentaría

screenX = viewPort.ActualWidth * (screenX + 1.0)/2.0 
screenY = viewPort.ActualHeight * (1.0 - ((screenY + 1.0)/2.0)) 

La pantalla + 1.0 convierte de [-1.0, 1.0] a [0.0, 2.0]. En ese momento, se divide por 2.0 para obtener [0.0, 1.0] para la multiplicación. Para tener en cuenta Windows y ser volteado de cartesiano y, se convierte de [1.0, 0.0] (superior izquierdo a inferior izquierdo), a [0.0, 1.0] (superior a inferior) al restar la pantalla anterior de 1.0. Luego, puedes escalar a la Altura real.

1
  1. No está claro lo que está tratando de lograr con aspectRatio coeff. Si el punto está en el borde del campo de visión, entonces debe estar en el borde de la pantalla, pero si aspectRatio! = 1 no lo está. Intenta establecer aspectRatio = 1 y haz que la ventana sea cuadrada. ¿Las coordenadas siguen siendo incorrectas?

  2. ActualWidth y ActualHeight parecen ser realmente la mitad del tamaño de la ventana, por lo que screenX será [-ActualWidth; ActualWidth], pero no [0; ActualWidth]. ¿Es eso lo que quieres?

0

screenX y screenY debería estar recibiendo calculan en relación con el centro de la pantalla ...

0

no veo una corrección por el hecho de que cuando se dibuja utilizando la API de Windows, el origen está en la parte superior izquierda esquina de la pantalla. Estoy asumiendo que su sistema de coordenadas es

y 
| 
| 
+------x 

Además, es su sistema de coordenadas origen asumiendo en el centro, por pregunta de Scott, o se trata en la esquina inferior izquierda?

embargo, la API de pantalla de Windows es

+-------x 
| 
| 
| 
y 

Sería necesario transformar la cota para ir desde el clásico cartesiana a Windows.

+0

de coordenadas X, es positivo derecho y coordenada y, positivo está arriba coordenada z, positivo está fuera de la pantalla (hacia usted) Creo que este es el sistema de coordenadas de la derecha. El origen está en el centro. El objetivo es calcular la coordenada X e Y como valores entre -1.0 y 1.0, luego * por widht/height. – Ash

+0

Disculpa por el formateo, a los comentarios no les gustan los retornos de carro: Coordenada X: positivo es correcto. Coordenada Y: positivo está arriba. Coordenada Z: positivo está fuera de la pantalla (hacia usted) – Ash

4

He creado y probado con éxito un método de trabajo utilizando la biblioteca de código fuente 3DUtils Codeplex.

El trabajo real se realiza en el método TryWorldToViewportTransform() de 3DUtils. Este método no funcionará sin él (ver el enlace de arriba).

También se encontró información muy útil en el artículo de Eric Sink: Auto-Zoom.

NB. Puede haber enfoques más confiables/eficientes, de ser así, agréguelos como respuesta. Mientras tanto, esto es lo suficientemente bueno para mis necesidades.

/// <summary> 
    /// Takes a 3D point and returns the corresponding 2D point (X,Y) within the viewport. 
    /// Requires the 3DUtils project available at http://www.codeplex.com/Wiki/View.aspx?ProjectName=3DTools 
    /// </summary> 
    /// <param name="point3D">A point in 3D space</param> 
    /// <param name="viewPort">An instance of Viewport3D</param> 
    /// <returns>The corresponding 2D point or null if it could not be calculated</returns> 
    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<Point>(screen2DPoint); 
     } 
     else 
     { 
      return null; 
     } 
    } 
+0

Este es probablemente el mejor método. Solo una nota, si su punto se refiere a un modelo, y este modelo tiene una transformación no identificada, el punto debe transformarse con esa matriz antes de la conversión. –

+0

¡Esta solución realmente me ayudó! Muchas gracias. –

2

Esto no aborda el algoritmo en cuestión, sino que puede ser útil para peple que cruzan esta pregunta (como yo).

En .NET 3.5 puede usar Visual3D.TransformToAncestor(Visual ancestor). Lo utilizo para dibujar una estructura metálica en una lona en la ventana gráfica 3D:

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

Esto también tiene en cuenta cualquier transformación en el modelo, etc.

Ver también: http://blogs.msdn.com/wpf3d/archive/2009/05/13/transforming-bounds.aspx

Cuestiones relacionadas