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ć?
Linie renderowania - metoda 1 (prymitywy)
W przypadku prostych linii w przestrzeni 3D możesz je narysować za pomocą LineList
lub LineStrip
prymitywu. 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 BasicEffect
do przechowywania transformacji widoku i projekcji i przekazałem dwa wierzchołki (położenie przechowywania i kolor) GraphicsDevice.DrawUserPrimitives
metodzie, 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 VertexBuffer
do 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 Texture2D
obiektu 1x1 i renderowanie go przy użyciu SpriteBatch
właściwej lokalizacji ekranu, którą można łatwo znaleźć za pomocą metody Viewport.Project .
Możesz utworzyć wymagany Texture2D
obiekt 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 SpriteBatch
pomocą 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 LineList
przy 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:
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.LineList
czyliLineStrip
jak 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:
Oczywiście zmień to na
1
.źródło