Apakah mungkin untuk membuat kubus dengan kurang dari 24 simpul

14

Saya memiliki dunia berbasis kubus seperti Minecraft dan saya bertanya-tanya apakah ada cara untuk membangun kubus dengan kurang dari 24 simpul sehingga saya dapat mengurangi penggunaan memori.

Sepertinya tidak mungkin bagi saya karena 2 alasan: normals tidak akan keluar dengan benar dan tekstur per-wajah tidak akan berfungsi.

Apakah ini masalahnya atau saya salah? Mungkin ada beberapa teknologi DX11 baru yang mewah yang dapat membantu?

Sunting: Hanya untuk memperjelas, saya memiliki 2 persyaratan: Saya perlu normals permukaan untuk setiap permukaan kubus untuk melakukan pencahayaan yang tepat dan saya perlu cara untuk mengatasi indeks berbeda dalam array tekstur untuk setiap permukaan kubus

Telanor
sumber
1
Apakah Anda ingin mengurangi penggunaan memori kartu grafis atau penggunaan RAM ?
doppelgreener
1
Data vertex disimpan dalam RAM dan pada GPU sekarang, jadi menguranginya akan mengurangi keduanya.
Telanor

Jawaban:

9

Jika Anda hanya membutuhkan normal per-wajah, dan jika texcoords Anda untuk wajah benar-benar 0/0, 0/1, 1/0, 1/1 (atau mirip dengan tata letak Anda) maka Anda dapat membuat kubus dengan 8 verts dan 30 (strip dengan restart) atau 36 (daftar) indeks. Ambil normals dan texcoords menggunakan pencarian array konstan berdasarkan SV_VertexID di vertex shader Anda.

Melakukan ini berarti Anda bahkan tidak perlu memasukkan texcoords atau normals ke dalam buffer vertex Anda, yang akan memberi Anda lebih banyak penghematan memori.

Lebih jauh, Anda masih bisa mencapai 24 verts per kubus tetapi juga menggunakan instancing. Setiap kubus akan menjadi ukuran tetap dalam buffer verteks Anda (1x1x1) dan Anda akan memiliki faktor penskalaan dan posisi (dengan asumsi kubus Anda tidak berputar, matriks jika mereka lakukan) sebagai data per instance. Dalam kasus non-rotating Anda memiliki biaya sekali pakai dari 24 verts, tetapi kemudian setiap kubus hanya perlu 6 floats untuk sepenuhnya ditentukan. Dalam kasus berputar Anda melihat 16 pelampung, tetapi bahkan itu adalah penghematan besar (Anda lebih cenderung untuk bottleneck sisi-CPU pada transformasi matriks dalam kasus ini - untuk kasus yang tidak berputar membangun matriks dengan cepat di vertex shader Anda - bahkan jika itu dilakukan per-vertex, sangat cepat sehingga Anda bahkan tidak perlu khawatir tentang hal itu).

Untuk tekstur per-wajah, cukup gunakan array tekstur. Anda perlu memastikan bahwa setiap tekstur dalam array memiliki ukuran yang sama, tentu saja, dan Anda masih harus memecah batch Anda saat ini jika array itu sendiri perlu diubah, tetapi jika tidak maka akan melakukan pekerjaan dengan baik. Tambahkan texcoord ketiga ke definisi vertex Anda yang mendefinisikan irisan array yang digunakan untuk setiap wajah.

Anda tidak memerlukan GS dengan ini, dan itu harus berjalan lebih cepat daripada menggunakan satu karena memiliki tahap geometri shader diaktifkan akan membebankan biaya tambahan tambahan itu sendiri.

Saya memiliki kode benchmark di mesin saya yang hanya menggambar sekelompok kubus menggunakan metode ini, dan saya dapat dengan mudah mengunyah lebih dari 300.000 kubus sambil masih membersihkan 60fps, pada GPU yang relatif rendah, dan tanpa melakukan hal lain untuk mengoptimalkan proses . Memang saya bukan pencahayaan atau texturing mereka, tapi saya memang telah mengaktifkan alpha blending, backface culling dinonaktifkan, dan secara keseluruhan itu menyeimbangkan terhadap bagian "tidak melakukan hal lain untuk mengoptimalkan", jadi itu harus memberi Anda ide yang masuk akal dari jenis rata-rata Anda dapat menekan dengan metode ini.

Maximus Minimus
sumber
1
Saya menggunakan instancing sebelumnya dan bekerja sangat baik untuk di bawah 40k kubus. Tapi saya memiliki setidaknya 256 ribu kubus, dan instancing tidak menanganinya juga. Menggunakan SV_VertexID dengan pencarian terdengar sangat menjanjikan.
Telanor
Seberapa besar buffer per-instance Anda? Mencoba memasukkan semuanya ke dalam buffer besar mungkin akhirnya akan mencekik GPU Anda; Saya biasanya menyimpan buffer per-instance pada ruang yang cukup untuk objek 64k (memisahkan panggilan draw jika perlu), menggunakan pola discard / no-overwrite, dan menulis langsung ke pointer yang dipetakan alih-alih menyalin dari array perantara, yang bekerja sangat baik .
Maximus Minimus
Juga - menjaga agar jumlah peta tetap rendah sangat penting. Peta / tulis satu kubus / unmap 40k kali akan jauh lebih lambat daripada peta / tulis 40k kubus / unmap sekali saja. Jika Anda melakukan yang pertama coba beralih ke yang terakhir.
Maximus Minimus
15

Saya pikir optimasi utama yang dapat Anda buat, didasarkan pada kenyataan bahwa tidak setiap kubus benar-benar membutuhkan semua 24 simpul . Bahkan, satu-satunya kubus yang membutuhkan 24 simpul adalah orang-orang yang mengambang di udara, yang mungkin merupakan kejadian langka.

Secara umum, hanya menghasilkan paha depan untuk wajah yang bersentuhan dengan udara . Ini berarti bahwa jika dua kubus saling bersentuhan, Anda tidak perlu membuat simpul untuk wajah tempat mereka bersentuhan.

Gambar di bawah ini menunjukkan konsep ini, tetapi dalam 2D ​​untuk memudahkan pemahaman. Dalam gambar ada 11 blok yang ditempati (diwakili oleh lingkaran abu-abu yang diisi), yang biasanya membutuhkan 4 x 11 = 44 tepi untuk diwakili (4 karena itu persegi, bukan kubus). Tapi seperti yang Anda lihat, Anda hanya perlu menggambar tepinya saat bersentuhan dengan kotak kosong, yang dalam hal ini, hanya 8 tepi.

masukkan deskripsi gambar di sini

Jika contoh sederhana dalam 2D ​​berhasil mengurangi 44 edge menjadi 8 edge, bayangkan keuntungan di dunia 3D besar ...

Ini adalah pendekatan yang dijelaskan dalam artikel ini , yang saya sarankan Anda baca, meskipun ditujukan untuk OpenGL. Konsepnya harus cukup universal.

Anda mungkin juga dapat menggunakan geometri shader untuk menghasilkan simpul dengan cepat pada GPU, menghilangkan kebutuhan untuk menyimpannya dalam memori, tetapi saya tidak memiliki pengalaman dengan itu, saya juga tidak tahu seberapa baik kinerjanya untuk yang besar dunia.

David Gouveia
sumber
1
Artikel yang sangat menarik. Saya sudah melakukan optimasi tepi, yang tentunya sangat membantu. Saya akan mencoba geometri shaders dan melihat apakah itu berhasil.
Telanor
Apakah maksud Anda, gunakan geometri shader dan produk silang untuk menghitung normals? Saya berpikir untuk menggunakan itu, seperti membuat program terpisah untuk permukaan datar (saya tidak peduli dengan tekstur).
dreta
@dreta Tidak hanya itu, tetapi pergi ke titik di mana Anda hanya mengirimkan centroid kubus sebagai daftar titik, dan semua simpul dipancarkan oleh GS. Mungkin menyandikan beberapa informasi tetangga ke dalam poin sehingga shader hanya menghasilkan wajah yang benar-benar diperlukan, meskipun saya belum memikirkan caranya.
David Gouveia
Jika itu kinerja yang Anda inginkan, jangan gunakan geometri shader ... Hampir pernah. Mereka benar-benar mengacaukan pipa. Jika Anda ingin menghasilkan geometri pada GPU, gunakan compute shader atau openCL
Robert Fraser