Dibujar una línea de un píxel de ancho en el espacio 3D

10

Me gustaría dibujar una línea en el espacio 3D que siempre tenga exactamente un píxel de ancho en la pantalla, sin importar qué tan lejos esté de la cámara. (Y lo mismo para los puntos individuales también).

¿Alguna pista sobre cómo podría hacer eso?

danbystrom
fuente

Respuestas:

24

Líneas de renderizado - Método 1 (primitivas)

Para líneas simples en el espacio 3D, puede dibujarlas usando a LineListo LineStripprimitivo. Aquí está la cantidad mínima de código que debe agregar a un proyecto XNA vacío para que dibuje una línea de (0,0,0) a (0,0, -50). La línea debería tener aproximadamente el mismo ancho sin importar dónde se encuentre la cámara.

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

Básicamente, creé un simple BasicEffectpara mantener mi vista y las transformaciones de proyección, y pasé dos vértices (almacenando la posición y el color) al GraphicsDevice.DrawUserPrimitivesmétodo que se representará como a LineList.

Por supuesto, hay muchas formas de optimizarlo, la mayoría de las cuales implican la creación de un VertexBufferpara almacenar todos los vértices y agrupar tantas líneas como sea posible en una sola llamada de Draw, pero eso es irrelevante para la pregunta.


Puntos de renderizado - Método 1 (SpriteBatch)

En cuanto a los puntos de dibujo, esto solía ser fácil usando sprites de puntos, pero se eliminaron de XNA 4.0 . Sin embargo, hay algunas alternativas. La forma más fácil es crear un Texture2Dobjeto blanco 1x1 y renderizarlo SpriteBatchen la ubicación correcta de la pantalla, que puede encontrar fácilmente con el método Viewport.Project .

Puede crear el Texture2Dobjeto requerido de esta manera:

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

Y renderízalo en una ubicación (x, y, z) como esta:

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

Líneas de renderizado - Método 2 (SpriteBatch)

Alternativamente, también puede dibujar líneas SpriteBatchusando la técnica descrita aquí . En este caso, simplemente necesitaría encontrar la coordenada del espacio de la pantalla para ambos extremos de la línea 3D (una vez más usando Viewport.Project) y luego dibujar una línea regular entre ellos.


Puntos de renderizado - Método 2 (línea pequeña con primitivas)

En los comentarios, eBusiness planteó la siguiente pregunta:

¿Qué pasa con una línea con el mismo punto inicial y final, no produciría un punto? ¿O simplemente sería invisible?

Lo intenté y renderizar LineListusando los mismos puntos de inicio y final no resultó en nada . Sin embargo, encontré una forma de evitarlo, así que lo describiré aquí para completarlo.

El truco no es utilizar los mismos puntos de inicio y final, sino dibujar una línea tan pequeña que solo aparece como un píxel cuando se dibuja. Entonces, para elegir el punto final correcto, primero proyecté el punto del espacio mundial en el espacio de la pantalla, lo moví a la derecha un píxel en el espacio de la pantalla y finalmente lo proyecté nuevamente en el espacio mundial. Ese es el punto final de su línea para que parezca un punto. Algo como esto:

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

Seguido por representarlo como una línea normal primitiva.


Demostración

Esto es lo que obtuve dibujando una línea blanca en el espacio 3D usando una lista de líneas primitiva, y puntos rojos en ambos extremos de la línea usando una textura 1x1 y SpriteBatch. El código utilizado es más o menos lo que escribí anteriormente. También he ampliado para que pueda confirmar que tienen exactamente un píxel de ancho:

ingrese la descripción de la imagen aquí

David Gouveia
fuente
¿Qué pasa con una línea con el mismo punto inicial y final, no produciría un punto? ¿O simplemente sería invisible?
aaaaaaaaaaaa
@eBusiness ¡Buena pregunta! Solo lo intenté, y de hecho no muestra nada.
David Gouveia
@eBusiness Sin embargo, acabo de encontrar una manera, pero creo que es un poco tonto. Lo agregaré para completar de todos modos.
David Gouveia
2

Esto está etiquetado como XNA, así que supongo que eso es lo que estás preguntando.

Si es así, este artículo debería ser útil para las líneas:

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

Por supuesto, puede usar sus propias matrices de vista / proyección en lugar de las suyas. Básicamente, PrimitiveType.LineListo LineStripes cómo dibujar líneas a nivel de GPU.

En cuanto a los puntos, ya no puede usar PrimitiveType.PointList para dibujar puntos más en XNA 4.0. En cambio, tendrías que hacer triángulos muy pequeños. Esta muestra proporciona una buena línea de base:

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

Para versiones anteriores de XNA, si está usando una de ellas, puede seguir leyendo la parte Point de la versión XNA 3.0 del artículo publicado anteriormente:

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

Tenga en cuenta que el enlace tiene:

graphics.GraphicsDevice.RenderState.PointSize = 10;

Obviamente cambia eso a 1.


fuente