Rysowanie linii o szerokości jednego piksela w przestrzeni 3D


10

Chciałbym narysować linię w przestrzeni 3D, która zawsze ma dokładnie jeden piksel na ekranie, bez względu na to, jak daleko jest od aparatu. (To samo dotyczy pojedynczych punktów).

Wszelkie wskazówki, jak to zrobić?

Odpowiedzi:


24

Linie renderowania - metoda 1 (prymitywy)

W przypadku prostych linii w przestrzeni 3D możesz je narysować za pomocą LineListlub LineStripprymitywu. Oto minimalna ilość kodu, który musisz dodać do pustego projektu XNA, aby narysować linię od (0,0,0) do (0,0, -50). Linia powinna wydawać się mieć mniej więcej tę samą szerokość bez względu na to, gdzie znajduje się kamera.

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

Zasadniczo stworzyłem prosty BasicEffectdo przechowywania transformacji widoku i projekcji i przekazałem dwa wierzchołki (położenie przechowywania i kolor) GraphicsDevice.DrawUserPrimitivesmetodzie, która ma być renderowana jako LineList.

Oczywiście istnieje wiele sposobów na jego optymalizację, z których większość polega na utworzeniu a VertexBufferdo przechowywania wszystkich wierzchołków i grupowaniu jak największej liczby linii w jedno wywołanie Draw, ale nie ma to znaczenia dla pytania.


Punkty renderowania - Metoda 1 (SpriteBatch)

Jeśli chodzi o rysowanie punktów, było to łatwe przy użyciu sprajtów punktowych, ale zostały one usunięte z XNA 4.0 . Istnieje jednak kilka alternatyw. Najłatwiejszym sposobem jest utworzenie białego Texture2Dobiektu 1x1 i renderowanie go przy użyciu SpriteBatchwłaściwej lokalizacji ekranu, którą można łatwo znaleźć za pomocą metody Viewport.Project .

Możesz utworzyć wymagany Texture2Dobiekt w następujący sposób:

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

I renderuj go w miejscu (x, y, z) w następujący sposób:

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

Linie renderowania - metoda 2 (SpriteBatch)

Alternatywnie możesz również rysować linie za SpriteBatchpomocą opisanej tutaj techniki . W takim przypadku musisz po prostu znaleźć współrzędną przestrzenną ekranu dla obu końców linii 3D (jeszcze raz używając Viewport.Project), a następnie narysować regularną linię między nimi.


Punkty renderowania - metoda 2 (mała linia z elementami pierwotnymi)

W komentarzach e-biznes podniósł następujące pytanie:

A co z linią o tym samym punkcie początkowym i końcowym, czy to nie dałoby punktu? A może po prostu byłby niewidoczny?

Spróbowałem i renderowanie LineListprzy użyciu tych samych punktów początkowych i końcowych spowodowało, że nic nie zostało narysowane. Znalazłem sposób na obejście tego, więc opiszę to tutaj dla kompletności.

Sztuczka nie polega na użyciu tych samych punktów początkowych i końcowych, ale na narysowaniu linii tak małej, że po narysowaniu pojawia się ona tylko jako jeden piksel . Aby więc wybrać właściwy punkt końcowy, najpierw rzutowałem światowy punkt kosmiczny na przestrzeń ekranową, przesunąłem go o jeden piksel w przestrzeń ekranową , a na koniec rzutowałem go z powrotem na przestrzeń światową. To jest punkt końcowy twojej linii, aby wyglądała jak kropka. Coś takiego:

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

Następnie renderowane jako normalna linia pierwotna.


Demonstracja

Oto, co narysowałem białą linię w przestrzeni 3D za pomocą prymitywnej listy linii oraz czerwone kropki na obu końcach linii za pomocą tekstury 1x1 i SpriteBatch. Użyty kod jest prawie tym, co napisałem powyżej. Przybliżiłem również, abyś mógł potwierdzić, że mają dokładnie jeden piksel:

wprowadź opis zdjęcia tutaj


A co z linią o tym samym punkcie początkowym i końcowym, czy to nie dałoby punktu? A może po prostu byłby niewidoczny?
aaaaaaaaaaaa

@eBusiness Dobre pytanie! Po prostu spróbowałem i naprawdę nic nie renderuje.
David Gouveia

@eBusiness Właśnie znalazłem sposób, ale myślę, że to trochę głupie. I tak dodam to dla kompletności.
David Gouveia,

2

Jest to oznaczone jako XNA, więc zakładam, że o to pytasz?

Jeśli tak, ten artykuł powinien być pomocny w przypadku wierszy:

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

Oczywiście możesz użyć własnych matryc widoku / projekcji zamiast ich. Zasadniczo, PrimitiveType.LineListczyli LineStripjak narysować linie na poziomie GPU.

Jeśli chodzi o punkty, nie można już używać PrimitiveType.PointList do rysowania punktów w XNA 4.0. Zamiast tego musielibyście robić bardzo małe trójkąty. Ta próbka stanowi dobry punkt odniesienia:

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

W przypadku poprzednich wersji XNA, jeśli używasz jednej z nich, możesz przejść dalej i przeczytać część Point wersji XNA 3.0 artykułu opublikowanego powyżej:

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

Uwaga: link zawiera:

graphics.GraphicsDevice.RenderState.PointSize = 10;

Oczywiście zmień to na 1.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.