Ich möchte eine Linie im 3D-Raum zeichnen, die auf dem Bildschirm immer genau ein Pixel breit ist, egal wie weit sie von der Kamera entfernt ist. (Und das Gleiche gilt auch für einzelne Punkte).
Irgendwelche Hinweise, wie ich das machen könnte?
Rendern von Linien - Methode 1 (Grundelemente)
Für einfache Linien im 3D-Raum können Sie sie mit einem LineList
oder einem LineStrip
Grundelement zeichnen . Hier ist die minimale Menge an Code, die Sie einem leeren XNA-Projekt hinzufügen müssen, damit es eine Linie von (0,0,0) bis (0,0, -50) zeichnet. Die Linie sollte ungefähr gleich breit sein, unabhängig davon, wo sich die Kamera befindet.
// Inside your Game class
private BasicEffect basicEffect;
private Vector3 startPoint = new Vector3(0, 0, 0);
private Vector3 endPoint = new Vector3(0, 0, -50);
// Inside your Game.LoadContent method
basicEffect = new BasicEffect(GraphicsDevice);
basicEffect.View = Matrix.CreateLookAt(new Vector3(50, 50, 50), new Vector3(0, 0, 0), Vector3.Up);
basicEffect.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45f), GraphicsDevice.Viewport.AspectRatio, 1f, 1000f);
// Inside your Game.Draw method
basicEffect.CurrentTechnique.Passes[0].Apply();
var vertices = new[] { new VertexPositionColor(startPoint, Color.White), new VertexPositionColor(endPoint, Color.White) };
GraphicsDevice.DrawUserPrimitives(PrimitiveType.LineList, vertices, 0, 1);
Grundsätzlich habe ich eine einfache BasicEffect
Darstellung meiner Ansichts- und Projektionstransformationen erstellt und zwei Scheitelpunkte (Speicherposition und Farbe) an die GraphicsDevice.DrawUserPrimitives
Methode übergeben, die als gerendert werden soll LineList
.
Natürlich gibt es viele Möglichkeiten, es zu optimieren. Die meisten davon umfassen die Erstellung eines VertexBuffer
zum Speichern aller Scheitelpunkte und das Stapeln so vieler Zeilen wie möglich in einem einzelnen Draw-Aufruf, aber das ist für die Frage irrelevant.
Rendering Points - Methode 1 (SpriteBatch)
Das Zeichnen von Punkten war früher mit Punktsprites einfach, wurde jedoch aus XNA 4.0 entfernt . Es gibt jedoch einige Alternativen. Am einfachsten ist es, ein weißes 1x1- Texture2D
Objekt zu erstellen und es SpriteBatch
an der richtigen Bildschirmposition zu rendern , die Sie mit der Viewport.Project-Methode leicht finden können .
Sie können das gewünschte Texture2D
Objekt folgendermaßen erstellen :
Texture2D pixel = new Texture2D(GraphicsDevice, 1, 1);
pixel.SetData(new [] { Color.White });
Und rendern Sie es an der Position (x, y, z) wie folgt:
// Find screen equivalent of 3D location in world
Vector3 worldLocation = new Vector3(0, 0, 50);
Vector3 screenLocation = GraphicsDevice.Viewport.Project(worldLocation, projectionMatrix, viewMatrix, Matrix.Identity);
// Draw our pixel texture there
spriteBatch.Begin();
spriteBatch.Draw(pixel, new Vector2(screenLocation.X, screenLocation.Y), Color.White);
spriteBatch.End();
Zeilen rendern - Methode 2 (SpriteBatch)
Alternativ können Sie SpriteBatch
mit der hier beschriebenen Technik auch Linien zeichnen . In diesem Fall müssen Sie lediglich die Bildschirmraumkoordinate für beide Enden der 3D-Linie finden (erneut verwenden Viewport.Project
) und dann eine reguläre Linie zwischen ihnen zeichnen.
Rendering Points - Methode 2 (kleine Linie mit Grundelementen)
In den Kommentaren stellte eBusiness die folgende Frage:
Was ist mit einer Linie mit demselben Start- und Endpunkt? Würde das nicht einen Punkt ergeben? Oder wäre es einfach unsichtbar?
Ich habe es versucht und das Rendern eines LineList
mit denselben Start- und Endpunkten führte dazu, dass nichts gezeichnet wurde. Ich habe jedoch einen Weg gefunden, um es der Vollständigkeit halber hier zu beschreiben.
Der Trick besteht nicht darin, dieselben Start- und Endpunkte zu verwenden, sondern eine Linie zu zeichnen, die so klein ist, dass sie beim Zeichnen nur als ein Pixel angezeigt wird. Um den richtigen Endpunkt auszuwählen, projizierte ich zuerst den Weltraumpunkt in den Bildschirmbereich, verschob ihn um ein Pixel nach rechts in den Bildschirmbereich und projizierte ihn schließlich zurück in den Weltraum. Das ist der Endpunkt Ihrer Linie, damit sie wie ein Punkt aussieht. Etwas wie das:
Vector3 GetEndPointForDot(Vector3 start)
{
// Convert start point to screen space
Vector3 screenPoint = GraphicsDevice.Viewport.Project(start, projection, view, Matrix.Identity);
// Screen space is defined in pixels so adding (1,0,0) moves it right one pixel
screenPoint += Vector3.Right;
// Finally unproject it back into world space
return GraphicsDevice.Viewport.Unproject(screenPoint, projection, view, Matrix.Identity);
}
Anschließend wird es als normales Linienprimitiv gerendert.
Demonstration
Hier ist, was ich bekommen habe, eine weiße Linie im 3D-Raum mit einem Linienlisten-Grundelement und rote Punkte an beiden Enden der Linie mit einer 1x1-Textur und SpriteBatch zu zeichnen. Der verwendete Code ist so ziemlich das, was ich oben geschrieben habe. Ich habe auch hineingezoomt, damit Sie bestätigen können, dass sie genau ein Pixel breit sind:
Dies ist als XNA gekennzeichnet, also gehe ich davon aus, dass Sie danach fragen?
Wenn ja, sollte dieser Artikel für Zeilen hilfreich sein:
http://msdn.microsoft.com/en-us/library/bb196414(v=xnagamestudio.40).aspx
Natürlich können Sie Ihre eigenen Ansichts- / Projektionsmatrizen anstelle ihrer verwenden. Grundsätzlich
PrimitiveType.LineList
oderLineStrip
ist das Zeichnen von Linien auf GPU-Ebene.Bei Punkten können Sie PrimitiveType.PointList nicht mehr zum Zeichnen von Punkten in XNA 4.0 verwenden. Stattdessen müssten Sie sehr kleine Dreiecke machen. Dieses Beispiel bietet eine gute Basis:
http://create.msdn.com/education/catalog/sample/primitives_3d
Wenn Sie für frühere Versionen von XNA eine dieser Versionen verwenden, können Sie den Punktteil der XNA 3.0-Version des oben genannten Artikels lesen:
http://msdn.microsoft.com/en-us/library/bb196414(v=xnagamestudio.30).aspx
Beachten Sie, dass der Link Folgendes hat:
Ändern Sie das offensichtlich in
1
.quelle