Cara paling efisien untuk menggambar titik dengan OpenGL

8

Saya sedang menulis game OpenGL 3D. Akan ada banyak segitiga untuk medan dan objek yang digunakan.

Saya belajar dari panduan resmi OpenGL dan metode pertama yang disajikan adalah memanggil fungsi glVertexsetelah glBeginuntuk setiap simpul yang ingin Anda gambar.

Namun metode ini kedengarannya cukup antik dan tidak efisien ketika Anda harus menggambar ribuan segitiga. Saya kira ada metode untuk menyimpan dalam memori kartu grafis informasi yang tidak berubah di setiap frame sehingga ketika Anda membuat setiap frame informasi itu sudah ada di sana. Sedangkan untuk informasi yang lebih "tidak stabil" mungkin Anda harus mengirim data vertex baru setiap frame sehingga teknik yang berbeda mungkin lebih efisien.

Datang ke pertanyaan saya: Saya ingin penjelasan yang jelas tentang fungsi dan teknik OpenGL paling modern dan efisien yang digunakan untuk mengirim informasi vertex ke perangkat keras dan untuk memberikan instruksi kepada render.

Apa cara terbaik dan alat OpenGL untuk mengirim pesanan untuk membuat data titik yang jarang berubah selama bingkai (saya memikirkan medan dan objek) dan apa cara terbaik untuk mengirim data titik yang banyak berubah selama bingkai?

Marco
sumber
Anda benar, glBegin () dan glEnd () adalah bagian dari rendering mode langsung dan sangat kuno. Sekarang ini orang akan menggunakan misalnya VBO. Bagaimana tepatnya dan apa yang Anda gunakan tergantung pada skenario Anda!
thalador
Saya juga seorang siswa yang mencoba belajar OpenGL. Saya tahu bahwa Anda harus menggunakan Vertex Buffer Objects (VBOs) untuk menggambar vertex lebih cepat. Meskipun saya tidak tahu cukup banyak untuk memberi tahu Anda bagaimana melakukan itu, saya dapat memberi Anda tautan ke buku (online gratis) yang hebat yang bisa memberi Anda. Saya telah menekanya dan membuat segitiga kecil tetapi itu benar-benar kompleks. Arcsintesis Modern OpenGL Tutorial Saya akan melalui tutorial LazyFoo sekarang. Mereka mulai dengan pipeline fungsi tetap glBegin (), glEnd () kemudian pindah ke OpenGL yang lebih modern menggunakan pipa fungsi tetap sebagai fondasi. Saya akan j
benbot

Jawaban:

19

Secara umum, jawabannya adalah "itu tergantung." Cara seseorang mengirim pembaruan partikel agak sedikit berbeda dari cara Anda mengirim serangkaian model berkulit GPU.

Vertex / Indeks Buffer

Dalam arti umum, bagaimanapun, semua hari ini dilakukan dengan VBO (objek vertex buffer ). API mode langsung lama ( glBegin/ glEnd) mungkin hanya diimplementasikan sebagai pembungkus lemak di sekitar sistem buffer vertex internal pengemudi.

Untuk objek statis, buat VBO statis dan isi dengan data titik Anda. Jika ada simpul yang dibagikan (biasanya case), Anda mungkin juga ingin membuat buffer indeks. Keduanya mengurangi kebutuhan untuk mengirim data yang sama lebih dari satu kali untuk mengubah jerat (berpotensi menghemat transfer bandwidth) dan pada pemrosesan (menghemat waktu shading vertex). Perhatikan bahwa Anda menggambar dengan berbagai fungsi saat membuat pengundian yang diindeks vs yang tidak diindeks.

Untuk objek dinamis, lakukan hal yang sama, meskipun dengan set buffer yang dinamis.

Catatan Lanjut

Untuk potongan yang lebih besar seperti medan, Anda mungkin tidak akan memecah jala menjadi beberapa bagian. Membuat GPU membuat seratus juta segitiga ketika hanya dua ratus ribu dari mereka yang terlihat adalah sangat besar, terutama jika itu tidak disortir dan ada banyak permintaan cerukan shader fragmen yang terbuang dan terbuang. Pisahkan mesh menjadi potongan besar dan kemudian hanya membuat orang-orang yang dalam pandangan frustrasi. Ada juga berbagai teknik pemusnahan yang lebih maju yang dapat Anda gunakan untuk menyingkirkan bongkahan-bongkahan yang mungkin berada dalam frustasi tetapi sepenuhnya di belakang bukit atau bangunan atau sesuatu. Mempertahankan hitung mundur panggilan imbang adalah baik, tetapi ada keseimbangan yang bisa didapat (yang harus Anda temukan untuk aplikasi / perangkat keras khusus Anda) antara meminimalkan panggilan draw dan meminimalkan menggambar geometri tersembunyi.

Salah satu hal utama yang perlu diingat dengan buffer GPU adalah bahwa Anda tidak dapat menulis kepadanya ketika GPU membaca dari itu. Anda perlu memberi tahu pengemudi bahwa tidak apa-apa untuk membuang salinan buffer yang lama (setelah selesai) dan memberi Anda yang baru (jika yang lama sibuk). Tentu saja untuk waktu yang lama tidak ada fungsi OpenGL untuk melakukan ini (sekarang ada InvalidateBufferData untuk GL 4.3 dan beberapa implementasi yang lebih lama sebagai ekstensi). Sebaliknya, ada perilaku non-standar tetapi umum yang diterapkan sebagian besar driver. Lakukan ini untuk membuang buffer sebelum memperbaruinya:

glBindBuffer(GL_ARRAY_BUFFER, my_vbo);
glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_DYNAMIC_DRAW);

Tentu saja berubah GL_ARRAY_BUFFERdan GL_DYNAMIC_DRAWke nilai yang sesuai untuk buffer. Buffer statis tidak akan diperbarui (atau tidak boleh jadi) sehingga Anda tidak perlu khawatir tentang membuang buffer seperti itu.

Perhatikan bahwa mungkin lebih cepat digunakan glBufferDataatau glBufferSubDatalebih cepat glMapBuffer. Itu benar-benar tergantung pada driver dan perangkat keras. Perangkat keras PC generasi sekarang mungkin akan paling cepat dengan glBufferDatatetapi uji untuk memastikan.

Teknik lain adalah menggunakan instancing . Instancing memungkinkan Anda untuk membuat satu panggilan undian yang menarik banyak salinan data dalam buffer vertex / indeks. Jika Anda memiliki, katakanlah, 100 batu identik maka Anda ingin menggambar semuanya dalam sekali jalan daripada membuat 100 undian independen.

Saat membuat instance, Anda harus memasukkan data per-instance ke buffer lain (seperti posisi objek masing-masing individu). Ini bisa berupa buffer seragam ( buffer konstan dalam terminologi D3D) atau buffer tekstur atau atribut vertex per-instance. Sekali lagi, mana yang lebih cepat tergantung. Atribut per-instance mungkin lebih cepat dan pasti jauh lebih mudah untuk digunakan, tetapi banyak implementasi GL umum masih tidak mendukung glBindingAttribDivisorsehingga Anda harus melihat apakah itu tersedia untuk digunakan dan apakah itu benar-benar lebih cepat (beberapa driver yang lebih lama meniru instancing dengan menambahkan buffer dan akhirnya menjadi lebih lambat untuk menggunakan instancing pada mereka daripada membuat panggilan draw independen dan tidak ada cara standar untuk mengetahuinya ... kesenangan menggunakan OpenGL).

Ada juga algoritma untuk optimasi cache vertex , yang merupakan tindakan memesan simpul / indeks di buffer Anda untuk bermain akan dengan cache vertex pada GPU modern. GPU hanya akan menjalankan shader untuk sebuah vertex kemudian men-cache-nya dalam vertex cache tetapi mungkin harus diusir terlalu dini untuk memberi ruang bagi verteks lainnya. (Katakanlah, dua segitiga sama-sama berbagi satu simpul tetapi ada 100 segitiga lain yang ditarik di antara mereka; simpul bersama itu mungkin akan berakhir sia-sia dievaluasi oleh vertex shader dua kali.)

Beberapa fitur ini memerlukan versi GL atau GLES yang cukup baru. GLES2 tidak mendukung penerbitan, misalnya.

Selalu Profil

Sekali lagi, jika Anda peduli dengan kinerja, uji setiap metode yang mungkin dan lihat mana yang lebih cepat untuk aplikasi Anda pada perangkat keras target Anda. Tidak hanya perangkat keras / driver yang berbeda dari produsen yang berbeda akan berbeda, tetapi beberapa kelas perangkat keras juga berbeda secara bawaan. GPU seluler tipikal adalah binatang yang sangat berbeda dari GPU desktop diskrit yang khas. Teknik yang "terbaik" pada satu belum tentu terbaik pada yang lain.

Ketika berbicara tentang kinerja, selalu bersikap skeptis .

Sean Middleditch
sumber