Menggambar garis lebar satu piksel dalam ruang 3D

10

Saya ingin menggambar garis dalam ruang 3D yang selalu persis selebar satu piksel di layar, tidak peduli seberapa jauh dari kamera itu. (Dan hal yang sama untuk poin tunggal juga).

Adakah petunjuk tentang bagaimana saya bisa melakukan itu?

danbystrom
sumber

Jawaban:

24

Garis Rendering - Metode 1 (Primitif)

Untuk garis-garis sederhana dalam ruang 3D, Anda dapat menggambarnya menggunakan a LineListatau LineStripprimitif. Berikut jumlah minimal kode yang harus Anda tambahkan ke proyek XNA kosong agar dapat menarik garis dari (0,0,0) hingga (0,0, -50). Garis tersebut seharusnya memiliki lebar yang kira-kira sama di mana pun kamera berada.

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

Saya pada dasarnya membuat sederhana BasicEffectuntuk menahan pandangan saya dan transformasi proyeksi, dan melewati dua simpul (menyimpan posisi dan warna) ke GraphicsDevice.DrawUserPrimitivesmetode yang akan diterjemahkan sebagai LineList.

Tentu saja ada banyak cara untuk mengoptimalkannya, yang sebagian besar melibatkan pembuatan a VertexBufferuntuk menyimpan semua simpul, dan mengelompokkan sebanyak mungkin garis ke dalam satu panggilan Draw tunggal tetapi itu tidak relevan dengan pertanyaan.


Poin Rendering - Metode 1 (SpriteBatch)

Adapun untuk menggambar poin, ini dulunya mudah menggunakan sprite titik tetapi mereka dihapus dari XNA 4.0 . Ada beberapa alternatif. Cara termudah adalah membuat Texture2Dobjek putih 1x1 , dan membuatnya menggunakan SpriteBatchdi lokasi layar yang benar, yang dapat Anda temukan dengan mudah menggunakan metode Viewport.Project .

Anda dapat membuat Texture2Dobjek yang diperlukan seperti ini:

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

Dan render di lokasi (x, y, z) seperti ini:

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

Garis Rendering - Metode 2 (SpriteBatch)

Atau, Anda juga dapat menggambar garis menggunakan SpriteBatchmenggunakan teknik yang dijelaskan di sini . Dalam hal ini, Anda hanya perlu menemukan koordinat ruang layar untuk kedua ujung garis 3D (sekali lagi menggunakan Viewport.Project) dan kemudian menggambar garis biasa di antara mereka.


Poin Rendering - Metode 2 (Garis Kecil dengan Primitif)

Dalam komentar, eBusiness mengangkat pertanyaan berikut:

Bagaimana dengan garis dengan titik awal dan akhir yang sama, bukankah itu menghasilkan titik? Atau apakah itu tidak akan terlihat?

Aku mencoba memberikannya dan render sebuah LineListmenggunakan yang sama awal dan akhir mengakibatkan tidak ditarik. Saya menemukan cara mengatasinya jadi saya akan menjelaskannya di sini untuk kelengkapan.

Triknya adalah tidak menggunakan titik awal dan akhir yang sama, melainkan menggambar garis yang sangat kecil, sehingga hanya muncul sebagai satu piksel saat digambar. Jadi, untuk memilih titik akhir yang benar, saya pertama-tama memproyeksikan titik ruang dunia ke ruang layar, memindahkannya satu pixel ke ruang layar , dan akhirnya memproyeksikannya kembali ke ruang dunia. Itulah titik akhir dari garis Anda agar terlihat seperti titik. Sesuatu seperti ini:

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

Diikuti dengan menjadikannya sebagai garis normal primitif.


Demonstrasi

Inilah yang saya dapatkan menggambar garis putih dalam ruang 3D menggunakan daftar garis primitif, dan titik-titik merah di kedua ujung garis menggunakan tekstur 1x1 dan SpriteBatch. Kode yang digunakan cukup banyak seperti yang saya tulis di atas. Saya juga telah memperbesar sehingga Anda dapat mengonfirmasi bahwa mereka persis selebar satu piksel:

masukkan deskripsi gambar di sini

David Gouveia
sumber
Bagaimana dengan garis dengan titik awal dan akhir yang sama, bukankah itu menghasilkan titik? Atau apakah itu tidak akan terlihat?
aaaaaaaaaaaa
@eBusiness Pertanyaan bagus! Saya hanya mencobanya, dan memang tidak menghasilkan apa-apa.
David Gouveia
@ eBusiness Saya baru saja menemukan cara, tapi saya pikir ini agak konyol. Saya akan menambahkannya untuk kelengkapan.
David Gouveia
2

Ini ditandai sebagai XNA jadi saya berasumsi itu yang Anda tanyakan?

Jika demikian, artikel ini akan bermanfaat untuk baris:

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

Tentu saja Anda dapat menggunakan matriks tampilan / proyeksi Anda sendiri, bukan dari matriks mereka. Pada dasarnya, PrimitiveType.LineListatau LineStripbagaimana cara menggambar garis pada level GPU.

Adapun poin, Anda tidak bisa lagi menggunakan PrimitiveType.PointList untuk menggambar poin lagi di XNA 4.0. Sebaliknya Anda harus membuat segitiga yang sangat kecil. Sampel ini memberikan garis dasar yang baik:

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

Untuk versi XNA sebelumnya, jika Anda menggunakan salah satu dari itu, Anda dapat melanjutkan dan membaca bagian Poin dari versi XNA 3.0 dari artikel yang diposting di atas:

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

Perhatikan bahwa tautannya memiliki:

graphics.GraphicsDevice.RenderState.PointSize = 10;

Jelas mengubahnya menjadi 1.


sumber