Zeichnen einer ein Pixel breiten Linie im 3D-Raum

10

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?

danbystrom
quelle

Antworten:

24

Rendern von Linien - Methode 1 (Grundelemente)

Für einfache Linien im 3D-Raum können Sie sie mit einem LineListoder einem LineStripGrundelement 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 BasicEffectDarstellung meiner Ansichts- und Projektionstransformationen erstellt und zwei Scheitelpunkte (Speicherposition und Farbe) an die GraphicsDevice.DrawUserPrimitivesMethode übergeben, die als gerendert werden soll LineList.

Natürlich gibt es viele Möglichkeiten, es zu optimieren. Die meisten davon umfassen die Erstellung eines VertexBufferzum 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- Texture2DObjekt zu erstellen und es SpriteBatchan der richtigen Bildschirmposition zu rendern , die Sie mit der Viewport.Project-Methode leicht finden können .

Sie können das gewünschte Texture2DObjekt 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 SpriteBatchmit 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 LineListmit 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:

Geben Sie hier die Bildbeschreibung ein

David Gouveia
quelle
Was ist mit einer Linie mit demselben Start- und Endpunkt? Würde das nicht einen Punkt ergeben? Oder wäre es einfach unsichtbar?
aaaaaaaaaaaa
@eBusiness Gute Frage! Ich habe es nur ausprobiert, und tatsächlich wird nichts gerendert.
David Gouveia
@eBusiness Ich habe gerade einen Weg gefunden, aber ich finde es ein bisschen albern. Ich werde es der Vollständigkeit halber trotzdem hinzufügen.
David Gouveia
2

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.LineListoder LineStripist 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:

graphics.GraphicsDevice.RenderState.PointSize = 10;

Ändern Sie das offensichtlich in 1.


quelle