2009-02-06 4 views
6

In base alle informazioni nel capitolo 7 di 3D Programming For Windows (Charles Petzold), ho tentato di scrivere come funzione di supporto che proietta un Point3D in un punto 2D standard che contiene le coordinate dello schermo corrispondenti (x, y) :Proiezione di un punto 3D a una coordinata dello schermo 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); 
} 

Su test tuttavia questa funzione restituisce coordinate dello schermo non corretti (verificata confrontando 2D del mouse coordinate contro una semplice forma 3D). A causa della mia mancanza di esperienza di programmazione 3D, sono confuso sul perché.

La sezione dei commenti del blocco contiene calcoli di ridimensionamento che possono essere essenziali, tuttavia non so come, e il libro continua con MatrixCamera utilizzando XAML. Inizialmente voglio solo ottenere un calcolo di base funzionante indipendentemente da quanto possa essere inefficiente rispetto a Matrici.

Qualcuno può consigliare cosa deve essere aggiunto o modificato?

risposta

2

Poiché coordinate Windows sono z nello schermo (x croce y), userei qualcosa come

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

anziché

screenY = screenY * viewPort.ActualHeight; 

correggere screenY accogliere di Windows.

In alternativa, è possibile utilizzare OpenGL. Quando imposti l'intervallo della vista x/y/z, puoi lasciarlo in unità "native" e lasciare che OpenGL si converta in coordinate dello schermo.

Modifica: Poiché la tua origine è il centro. Vorrei provare

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

Lo schermo + 1.0 converte da [-1.0, 1.0] a [0,0, 2.0]. A quel punto, dividi per 2.0 per ottenere [0.0, 1.0] per il multiplo. Per tenere conto che Windows viene capovolto da Cartesiano y, devi convertire da [1.0, 0.0] (in alto a sinistra a in basso a sinistra), a [0.0, 1.0] (in alto a in basso) sottraendo lo schermo precedente da 1.0. Quindi, è possibile ridimensionare a ActualHeight.

1
  1. Non è chiaro cosa si stia cercando di ottenere con aspectRatio coeff. Se il punto si trova sul bordo del campo visivo, dovrebbe essere sul bordo dello schermo, ma se aspectRatio! = 1 non lo è. Prova ad impostare aspectRatio = 1 e crea una finestra quadrata. Le coordinate sono ancora errate?

  2. ActualWidth e ActualHeight sembrano davvero metà delle dimensioni della finestra, quindi screenX sarà [-ActualWidth; ActualWidth], ma non [0; ActualWidth]. E 'questo quello che vuoi?

0

screenX e screenY dovrebbe essere sempre calcolate rispetto al centro dello schermo ...

0

non vedo una correzione per il fatto che quando si disegna utilizzando l'API di Windows, l'origine è in alto a sinistra angolo dello schermo. Io parto dal presupposto che il sistema di coordinate è

y 
| 
| 
+------x 

Inoltre, è il vostro sistema di coordinate assumendo origine nel centro, per ogni domanda di Scott, o è nell'angolo in basso a sinistra?

Ma, l'API schermo di Windows è

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

Si avrebbe bisogno la coordinata trasformare per andare da cartesiano classico a Windows.

+0

X coordinata, positivo è destra coordinata, positivo è su coordinata z, positivo è fuori schermo (verso di voi) Credo che questo sia il sistema di coordinate della mano destra. L'origine è al centro. L'obiettivo è calcolare le coordinate X e Y come valori compresi tra -1,0 e 1,0, quindi * per larghezza/altezza. – Ash

+0

Siamo spiacenti per la formattazione, i commenti non mi piacciono i ritorni a capo: coordinata X: il positivo è giusto. Coordinata Y: positivo è attivo. Coordinata Z: positivo è fuori dallo schermo (verso di te) – Ash

4

Ho creato e testato con successo un metodo di lavoro utilizzando la libreria di origini Codeplex 3DUtils.

Il lavoro reale viene eseguito nel metodo TryWorldToViewportTransform() da 3DUtils. Questo metodo non funzionerà senza di esso (vedi il link sopra).

Informazioni molto utili sono state trovate anche nell'articolo di Eric Sink: Auto-Zoom.

NB. Potrebbero esserci approcci più affidabili/efficienti, in tal caso, per favore, aggiungili come risposta. Nel frattempo questo è abbastanza buono per i miei bisogni.

/// <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

Questo è probabilmente il metodo migliore. Solo una nota, se il tuo punto è riferito a un modello, e questo modello ha una trasformazione non identificata, il punto deve essere trasformato con quella matrice prima della conversione. –

+0

Questa soluzione mi ha davvero aiutato! Grazie mille. –

2

Questo non affronta l'algoritmo in questione, ma può essere utile per peple imbattersi questa domanda (come ho fatto io).

In .NET 3.5 è possibile utilizzare Visual3D.TransformToAncestor(Visual ancestor). Io lo uso per disegnare un wireframe su una tela sopra la mia finestra 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); 
      } 
     } 
    } 

Questo prende anche in considerazione eventuali trasformazioni sul modello ecc

Vedi anche: http://blogs.msdn.com/wpf3d/archive/2009/05/13/transforming-bounds.aspx