Tracciare una linea larga un pixel nello spazio 3D


10

Mi piacerebbe tracciare una linea nello spazio 3D che sia sempre esattamente larga un pixel sullo schermo, non importa quanto sia lontana dalla fotocamera. (E lo stesso anche per i singoli punti).

Qualche suggerimento su come potrei farlo?

Risposte:


24

Linee di rendering - Metodo 1 (Primitive)

Per linee semplici nello spazio 3D puoi disegnarle usando a LineListo LineStripprimitive. Ecco la quantità minima di codice che è necessario aggiungere a un progetto XNA vuoto per tracciare una linea da (0,0,0) a (0,0, -50). La linea dovrebbe avere all'incirca la stessa larghezza, indipendentemente dalla posizione della telecamera.

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

Fondamentalmente ho creato un semplice BasicEffectper mantenere la mia vista e le trasformazioni di proiezione e ho passato due vertici (memorizzazione della posizione e del colore) al GraphicsDevice.DrawUserPrimitivesmetodo da renderizzare come a LineList.

Naturalmente ci sono molti modi per ottimizzarlo, la maggior parte dei quali comporta la creazione di un VertexBufferarchivio per memorizzare tutti i vertici e il raggruppamento di più righe possibili in una singola chiamata di Draw, ma questo è irrilevante per la domanda.


Punti di rendering - Metodo 1 (SpriteBatch)

Per quanto riguarda i punti di disegno, questo era facile usando gli sprite dei punti ma sono stati rimossi da XNA 4.0 . Ci sono alcune alternative però. Il modo più semplice è creare un Texture2Doggetto bianco 1x1 e renderlo usando SpriteBatchnella posizione corretta dello schermo, che puoi facilmente trovare usando il metodo Viewport.Project .

È possibile creare l' Texture2Doggetto richiesto in questo modo:

Texture2D pixel = new Texture2D(GraphicsDevice, 1, 1);
pixel.SetData(new [] { Color.White });

E renderlo in posizione (x, y, z) in questo modo:

// 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();

Linee di rendering - Metodo 2 (SpriteBatch)

In alternativa, puoi anche disegnare linee usando una SpriteBatchusando la tecnica qui descritta . In questo caso, dovrai semplicemente trovare le coordinate dello spazio dello schermo per entrambe le estremità della linea 3D (usando ancora una volta Viewport.Project) e quindi tracciare una linea regolare tra di loro.


Punti di rendering - Metodo 2 (Linea piccola con primitivi)

Nei commenti, eBusiness ha sollevato la seguente domanda:

Che dire di una linea con lo stesso punto iniziale e finale, non produrrebbe un punto? O sarebbe semplicemente invisibile?

Ho provato e renderlo LineListusando gli stessi punti iniziale e finale non ha portato a disegnare nulla . Ho trovato un modo per aggirarlo, quindi lo descriverò qui per completezza.

Il trucco non è usare gli stessi punti iniziale e finale, ma piuttosto disegnare una linea così piccola da apparire come un solo pixel quando viene disegnata. Quindi, al fine di scegliere il punto finale corretto, per prima cosa ho proiettato il punto dello spazio mondiale nello spazio dello schermo, spostato a destra di un pixel nello spazio dello schermo e infine proiettato di nuovo nello spazio del mondo. Questo è il punto finale della linea per farlo sembrare un punto. Qualcosa come questo:

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

Seguito da renderlo come una primitiva di linea normale.


Dimostrazione

Ecco cosa ho ottenuto disegnando una linea bianca nello spazio 3D usando una primitiva dell'elenco di linee e punti rossi ad entrambe le estremità della linea usando una trama 1x1 e SpriteBatch. Il codice utilizzato è praticamente quello che ho scritto sopra. Ho anche ingrandito in modo da poter confermare che sono esattamente larghi un pixel:

inserisci qui la descrizione dell'immagine


Che dire di una linea con lo stesso punto iniziale e finale, non produrrebbe un punto? O sarebbe semplicemente invisibile?
aaaaaaaaaaaa,

@eBusiness Buona domanda! Ho appena provato, e in effetti non rende nulla.
David Gouveia,

@eBusiness Ho appena trovato un modo, ma penso che sia un po 'sciocco. Lo aggiungerò per completezza comunque.
David Gouveia,

2

Questo è etichettato come XNA, quindi suppongo che sia quello che stai chiedendo?

In tal caso, questo articolo dovrebbe essere utile per le righe:

http://msdn.microsoft.com/en-us/library/bb196414(v=xnagamestudio.40).aspx

Ovviamente puoi usare le tue matrici di visualizzazione / proiezione anziché le loro. Fondamentalmente, PrimitiveType.LineListo LineStripè come disegnare linee a livello di GPU.

Per quanto riguarda i punti, non è più possibile utilizzare PrimitiveType.PointList per disegnare punti in XNA 4.0. Invece dovresti creare triangoli molto piccoli. Questo esempio fornisce una buona base:

http://create.msdn.com/education/catalog/sample/primitives_3d

Per le versioni precedenti di XNA, se si utilizza una di quelle, è possibile andare avanti e leggere la parte Point della versione XNA 3.0 dell'articolo pubblicato sopra:

http://msdn.microsoft.com/en-us/library/bb196414(v=xnagamestudio.30).aspx

Nota che il link ha:

graphics.GraphicsDevice.RenderState.PointSize = 10;

Ovviamente cambiarlo in 1.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.