OpenGL Ultramodern. Bagian 1



Halo semuanya. Setiap orang yang tahu sedikit tentang topik OpenGL tahu bahwa ada banyak artikel dan kursus tentang topik ini, tetapi banyak yang tidak memengaruhi API modern, dan beberapa dari mereka umumnya berbicara tentang glBegin dan glEnd . Saya akan mencoba untuk membahas beberapa nuansa API baru mulai dari versi 4. Tautan ke bagian kedua artikel

Kali ini saya akan mencoba untuk menulis artikel yang menarik dan informatif, dan apa yang terjadi tergantung pada warga habra yang baik untuk memutuskan. Maafkan saya untuk tata bahasa saya yang buruk (saya akan berterima kasih atas koreksi).

Jika Anda suka, saya akan menulis tentang mengoptimalkan OpenGL dan mengurangi DrawCalls.

Ayo mulai!

Apa yang akan ada di artikel ini - fungsi OpenGL modern
Apa yang tidak akan ada dalam artikel ini - pendekatan modern untuk rendering pada OpenGL

Konten:
  • Akses status langsung
  • Debug
  • Pisahkan Objek Shader
  • Array tekstur
  • Tampilan tekstur
  • Buffer tunggal untuk indeks dan vertex
  • Tessellation dan komputasi warna
  • Pembuatan jalur


DSA (Akses Keadaan Langsung)


Akses Status Langsung - Akses langsung ke status. Alat untuk memodifikasi objek OpenGL tanpa harus membentaknya ke konteks. Ini memungkinkan Anda untuk mengubah keadaan suatu objek dalam konteks lokal tanpa mempengaruhi keadaan global yang dibagikan oleh semua bagian aplikasi. Itu juga membuat API sedikit lebih berorientasi objek, karena fungsi yang mengubah keadaan objek dapat didefinisikan dengan jelas. Inilah yang dikatakan OpenGL Wiki kepada kami.

Seperti yang kita tahu, OpenGL adalah API dengan banyak tombol radio - glActiveTexture , glBindTexture , dll.

Dari sini kami memiliki beberapa masalah :

  • Pemilih dan status saat ini dapat membuat perubahan lebih dalam di kondisi.
  • Mungkin perlu untuk mengikat / mengubah unit aktif untuk mengatur filter untuk tekstur
  • Manajemen negara menjadi bermasalah karena kompleksitas aplikasi tumbuh
  • Keadaan tidak dikenal mengarah ke pengaturan tambahan
  • Upaya untuk menyimpan / mengembalikan keadaan bisa bermasalah

Apa yang ditawarkan kelompok Khronos kepada kami dan bagaimana DSA membantu?

  • Menambahkan fungsi yang bekerja langsung dengan objek / objek
  • Atur filter tekstur untuk objek tekstur yang ditentukan, bukan yang sekarang.
  • Mengikat tekstur ke unit tertentu, bukan ke aktif
  • Menambahkan sejumlah besar fitur baru
  • Mencakup semuanya hingga OpenGL 1.x
  • Menambahkan fitur tambahan.

Secara teori, DSA dapat membantu mengurangi jumlah operasi yang tidak mengubah dan mengubah keadaan menjadi nol ... Tapi itu tidak akurat.

Sekarang saya akan membahas beberapa fungsi baru secara singkat, saya tidak akan membahas parameter secara rinci, saya akan meninggalkan tautan di wiki.

  • glCreateTextures menggantikan glGenTextures + glBindTexture (inisialisasi).
    Itu:
    glGenTextures(1, &name); glBindTexture(GL_TEXTURE_2D, name); 
    Itu menjadi:
     glCreateTextures(GL_TEXTURE_2D, 1, &name); 
  • glTextureParameterX setara dengan glTexParameterX
     glGenTextures(1, &name); glBindTexture(GL_TEXTURE_2D, name); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); 
    Sekarang kita akan menulis seperti ini:
     glCreateTextures(GL_TEXTURE_2D, 1, &name); glTextureParameteri(name, GL_TEXTURE_WRAP_S, GL_CLAMP); glTextureParameteri(name, GL_TEXTURE_WRAP_T, GL_CLAMP); glTextureParameteri(name, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTextureParameteri(name, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTextureStorage2D(name, 1, GL_RGBA8, width, height); glTextureSubImage2D(name, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); 
  • glBindTextureUnit ganti glActiveTexture + glBindTexture
    Inilah cara kami melakukannya:
     glActiveTexture(GL_TEXTURE0 + 3); glBindTexture(GL_TEXTURE_2D, name); 
    Sekarang:
     glBindTextureUnit(3, name); 

Perubahan juga memengaruhi glTextureImage , tidak lagi digunakan, dan inilah alasannya:

glTexImage agak tidak aman, sangat mudah untuk mendapatkan tekstur yang tidak valid, karena fungsi tidak memeriksa nilai ketika dipanggil, driver melakukan ini saat menggambar. GlTexStorage ditambahkan untuk menggantikannya .

glTexStorage menyediakan cara untuk membuat tekstur dengan pemeriksaan dilakukan selama panggilan, yang meminimalkan kesalahan. Gudang tekstur diselesaikan oleh sebagian besar, jika tidak semua, masalah yang disebabkan oleh tekstur yang bisa berubah, meskipun tekstur yang tidak dapat diubah lebih dapat diandalkan.

Perubahan juga memengaruhi buffer bingkai:


Ini tidak semua fitur yang diubah. Baris berikutnya adalah fungsi untuk buffer:


Berikut adalah daftar apa yang sekarang termasuk dalam dukungan DSA:

  • Objek array Vertex
  • Objek Framebuffer
  • Objek program
  • Benda penyangga
  • Tumpukan matriks
  • Banyak hal yang usang

Debug


Sejak versi 4.3, fungsionalitas baru telah ditambahkan untuk debug, menurut saya sangat berguna dan nyaman. Sekarang OpenGL akan memanggil panggilan balik kami untuk kesalahan dan pesan debug, tingkat yang dapat kita sesuaikan.



Kita hanya perlu memanggil dua fungsi untuk mengaktifkan: glEnable & glDebugMessageCallback , tidak ada tempat yang lebih mudah.

 glEnable(GL_DEBUG_OUTPUT); glDebugMessageCallback(message_callback, nullptr); 

Sekarang kita akan menulis fungsi panggilan balik untuk mendapatkan pesan:

 void callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, GLchar const* message, void const* user_param) { auto source_str = [source]() -> std::string { switch (source) { case GL_DEBUG_SOURCE_API: return "API"; case GL_DEBUG_SOURCE_WINDOW_SYSTEM: return "WINDOW SYSTEM"; case GL_DEBUG_SOURCE_SHADER_COMPILER: return "SHADER COMPILER"; case GL_DEBUG_SOURCE_THIRD_PARTY: return "THIRD PARTY"; case GL_DEBUG_SOURCE_APPLICATION: return "APPLICATION"; case GL_DEBUG_SOURCE_OTHER: return "OTHER"; default: return "UNKNOWN"; } }(); auto type_str = [type]() { switch (type) { case GL_DEBUG_TYPE_ERROR: return "ERROR"; case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: return "DEPRECATED_BEHAVIOR"; case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: return "UNDEFINED_BEHAVIOR"; case GL_DEBUG_TYPE_PORTABILITY: return "PORTABILITY"; case GL_DEBUG_TYPE_PERFORMANCE: return "PERFORMANCE"; case GL_DEBUG_TYPE_MARKER: return "MARKER"; case GL_DEBUG_TYPE_OTHER: return "OTHER"; default: return "UNKNOWN"; } }(); auto severity_str = [severity]() { switch (severity) { case GL_DEBUG_SEVERITY_NOTIFICATION: return "NOTIFICATION"; case GL_DEBUG_SEVERITY_LOW: return "LOW"; case GL_DEBUG_SEVERITY_MEDIUM: return "MEDIUM"; case GL_DEBUG_SEVERITY_HIGH: return "HIGH"; default: return "UNKNOWN"; } }(); std::cout << source_str << ", " << type_str << ", " << severity_str << ", " << id << ": " << message << std::endl; } 

Kami juga dapat mengkonfigurasi filter menggunakan glDebugMessageControl . Filter dapat bekerja dalam mode penyaringan berdasarkan sumber / tipe / kepentingan atau serangkaian pesan menggunakan pengidentifikasi mereka.

Saring pesan dalam lingkup tertentu:

 glPushDebugGroup( GL_DEBUG_SOURCE_APPLICATION, DEPTH_FILL_ID, 11, โ€œDepth Fillโ€); //  Render_Depth_Only_Pass(); //  glPopDebugGroup(); //  

Akan sangat berguna untuk menonaktifkan panggilan asinkron sehingga kita dapat menentukan urutan panggilan fungsi, serta menemukan tempat kesalahan pada stack ketika melakukan debugging. Ini dilakukan cukup sederhana:

 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); 

Perlu diingat bahwa tidak aman untuk memanggil OpenGL atau fungsi jendela dalam fungsi panggilan yang dibungkus, serta panggilan balik tidak gratis dan Anda tidak boleh membiarkannya dimasukkan dalam rilis build.
Lebih detail tentang ini dan hal-hal kecil lainnya - gimmick, Anda bisa baca di sini .

SSO (Objek Shader Terpisah)


Setelah OpenGL bekerja sebagai "jalur pipa tetap" - ini berarti pemrosesan yang diprogram sebelumnya diterapkan ke semua data yang ditransfer untuk visualisasi. Langkah selanjutnya adalah "pipeline yang dapat diprogram" - di mana bagian yang dapat diprogram mengimplementasikan shader, ditulis dalam GLSL, program GLSL klasik terdiri dari shader vertex dan fragmen, tetapi dalam OpenGL modern beberapa jenis shader baru ditambahkan, yaitu shader geometri, pembobotan dan perhitungan (tentang mereka saya) Saya akan ceritakan pada bagian selanjutnya).


SSO memungkinkan kami mengubah langkah shader dengan cepat tanpa menautkannya kembali. Membuat dan mengatur pipa perangkat lunak sederhana tanpa debugging adalah sebagai berikut:

 GLuint pipe = GL_NONE; // Create shaders GLuint fprog = glCreateShaderProgramv( GL_FRAGMENT_SHADER, 1, &text); GLuint vprog = glCreateShaderProgramv( GL_VERTEX_SHADER, 1, &text); // Bind pipeline glGenProgramPipelines( 1, &pipe); glBindProgramPipelines( pipe); // Bind shaders glUseProgramStages( pipe, GL_FRAGMENT_SHADER_BIT, fprog); glUseProgramStages( pipe, GL_VERTEX_SHADER_BIT, vprog); 

Seperti yang kita lihat glCreateProgramPipelines menghasilkan deskriptor dan menginisialisasi objek, glCreateShaderProgramv menghasilkan, menginisialisasi, mengkompilasi, dan menautkan program shader menggunakan sumber yang ditentukan, dan glUseProgramStages melampirkan langkah-langkah program ke objek pipa. glBindProgramPipeline - Mengaitkan sebuah pipeline dengan sebuah konteks.

Tapi ada satu peringatan, sekarang parameter input dan output shader harus cocok. Kami dapat mendeklarasikan parameter input / output dalam urutan yang sama, dengan nama yang sama, atau kami membuat lokasi mereka jelas bertepatan dengan bantuan kualifikasi.
Saya merekomendasikan opsi terakhir, ini akan memungkinkan kita untuk mengkonfigurasi antarmuka yang jelas, serta fleksibel sehubungan dengan nama dan pesanan.

Untuk menyediakan antarmuka yang lebih ketat, kita juga perlu mendeklarasikan blok input dan output bawaan yang ingin kita gunakan untuk setiap tahap.

Antarmuka blok bawaan didefinisikan sebagai ( dari wiki ):
Vertex:

 out gl_PerVertex { vec4 gl_Position; float gl_PointSize; float gl_ClipDistance[]; }; 

Kontrol Tesselation:
 out gl_PerVertex { vec4 gl_Position; float gl_PointSize; float gl_ClipDistance[]; } gl_out[]; 

Evaluasi Tesselation:
 out gl_PerVertex { vec4 gl_Position; float gl_PointSize; float gl_ClipDistance[]; }; 

Geometri:
 out gl_PerVertex { vec4 gl_Position; float gl_PointSize; float gl_ClipDistance[]; }; 

Contoh mendeklarasikan ulang modul bawaan dan menggunakan lokasi atribut dalam vertex shader biasa:

 #version 450 out gl_PerVertex { vec4 gl_Position; }; layout (location = 0) in vec3 position; layout (location = 1) in vec3 color; layout (location = 0) out v_out { vec3 color; } v_out; void main() { v_out.color = color; gl_Position = vec4(position, 1.0); } 

Source: https://habr.com/ru/post/id456932/


All Articles