Mengapa tutorial menggunakan pendekatan berbeda untuk rendering OpenGL?

43

http://www.sdltutorials.com/sdl-opengl-tutorial-basics

http://www.opengl-tutorial.org/beginners-tutorials/tutorial-2-the-first-triangle/

Kedua tutorial ini menggunakan pendekatan yang sangat berbeda untuk mendapatkan hasil yang hampir sama. Yang pertama menggunakan hal-hal seperti glBegin(GL_QUADS). Yang kedua menggunakan hal-hal seperti vertexBufferObjects, shader berdasarkan GLEW. Tetapi hasilnya sama: Anda mendapatkan bentuk dasar.

Mengapa perbedaan ini ada?

Pendekatan pertama tampaknya jauh lebih mudah dipahami. Apa keuntungan dari pendekatan kedua yang rumit?

Reynmar
sumber
4
Tidak pernah ada satu cara untuk menguliti kucing.
Philipp
4
@ Pilip Ya, tetapi ada cara yang benar dan cara yang salah, cara lama dan cara baru (dan seperti yang ditunjukkan oleh jawaban di bawah ini, cara lama dan baru mungkin tidak kompatibel dalam semua situasi)
Andrew Hill
3
Tidak ada cara yang benar dan cara yang salah, hanya cara yang lebih buruk dan cara yang lebih baik (dalam beberapa dimensi yang berbeda).
user253751
glBegindan glEndtelah ditinggalkan karena mereka sangat tidak efisien untuk arsitektur grafis saat ini
Alex

Jawaban:

77

OpenGL memiliki empat versi utama yang berbeda, tidak termasuk versi untuk perangkat seluler dan sistem tertanam (OpenGL | ES) dan Web via JavaScript (WebGL). Sama seperti Direct3D 11 memiliki cara yang berbeda dalam melakukan sesuatu daripada Direct3D 8, begitu juga OpenGL 3 memiliki cara yang berbeda dalam melakukan sesuatu daripada OpenGL 1. Perbedaan besar adalah bahwa versi OpenGL sebagian besar hanya add-on ke versi yang lebih lama (tetapi tidak sepenuhnya).

Di atas berbagai edisi dan versi OpenGL, OpenGL utama juga menambahkan konsep profil. Yaitu Profil Kompatibilitas (yang memungkinkan dukungan untuk API dari versi yang lebih lama) dan Profil Inti (yang menonaktifkan API lama itu). Hal-hal seperti glBegintidak berfungsi ketika Anda menggunakan Profil Inti tetapi akan ketika Anda menggunakan Profil Kompatibilitas (yang merupakan default).

Sebagai satu komplikasi besar lebih lanjut, beberapa implementasi OpenGL (seperti Apple, antara lain) hanya akan mengaktifkan fitur OpenGL yang lebih baru ketika Anda menggunakan Core Profile. Ini berarti Anda harus berhenti menggunakan API lama untuk menggunakan API yang lebih baru.

Anda kemudian berakhir dengan beberapa skenario yang sangat membingungkan untuk tutorial:

  1. Tutorialnya sudah tua dan hanya menggunakan API yang sudah tidak digunakan lagi.
  2. Tutorialnya baru dan ditulis dengan baik dan hanya menggunakan API yang kompatibel dengan Core.
  3. Tutorialnya baru tetapi membuat kesalahan dengan menganggap bahwa Anda bekerja dengan driver yang memungkinkan semua API dalam mode Kompatibilitas, dan secara bebas mencampur API baru dan lama.
  4. Tutorialnya adalah untuk edisi OpenGL yang berbeda seperti OpenGL | ES yang sama sekali tidak mendukung API lama, dalam versi apa pun.

Hal-hal seperti glBeginadalah bagian dari apa yang kadang-kadang disebut API mode langsung. Ini juga sangat membingungkan karena tidak ada yang namanya mode dipertahankan dalam OpenGL dan "mode langsung" sudah memiliki definisi yang berbeda dalam grafik. Jauh lebih baik untuk merujuknya sebagai OpenGL 1.x API karena sudah usang sejak OpenGL 2.1.

1.x API OpenGL akan segera mengirimkan simpul ke pipa grafis kembali di masa lalu. Ini bekerja dengan baik ketika kecepatan perangkat keras yang membuat simpul kira-kira setara dengan kecepatan CPU menghasilkan data titik. OpenGL saat itu baru saja mengeluarkan rasterisasi segitiga dan tidak banyak lagi.

Hari-hari ini, GPU dapat mengunyah sejumlah besar simpul pada kecepatan yang sangat tinggi sambil melakukan transformasi verteks dan piksel yang canggih dan CPU bahkan tidak dapat mengikuti. Selain itu, antarmuka antara CPU dan GPU telah dirancang untuk perbedaan kecepatan ini yang berarti bahwa bahkan tidak mungkin untuk mengirimkan simpul ke GPU satu per satu lagi.

Semua driver GL harus ditiru glBegindengan mengalokasikan secara internal buffer verteks, menempatkan simpul-simpul yang dikirimkan bersama glVertexke buffer ini, dan kemudian mengirimkan seluruh buffer itu dalam panggilan draw tunggal saat glEnddipanggil. Overhead dari fungsi-fungsi ini jauh lebih besar daripada jika Anda baru saja memperbarui buffer vertex sendiri, itulah sebabnya beberapa dokumentasi akan (sangat keliru!) Menyebut buffer vertex sebagai "sebuah optimasi" (ini bukan optimasi; itu satu-satunya cara untuk benar-benar bicara dengan GPU).

Ada berbagai API lain yang telah usang atau usang di OpenGL selama bertahun-tahun. Yang disebut pipeline fungsi-tetap adalah bagian yang lain. Beberapa dokumentasi mungkin masih menggunakan pipa ini atau bergaul dengan pipa yang dapat diprogram. Pipeline fungsi tetap berasal dari masa lalu ketika kartu grafis mengkodekan semua matematika yang digunakan untuk membuat adegan 3D dan OpenGL API terbatas pada pengaturan beberapa nilai konfigurasi untuk matematika itu. Saat ini perangkat keras memiliki matematika hard-code yang sangat sedikit dan (seperti halnya CPU Anda) menjalankan program yang disediakan pengguna (sering disebut shader).

Sekali lagi driver harus meniru API lama karena fitur fungsi tetap tidak ada lagi pada perangkat keras. Ini berarti bahwa driver memiliki banyak shader kompatibilitas yang tertanam di dalamnya yang menjalankan matematika lama dari hari-hari fungsi tetap yang digunakan ketika Anda tidak memasok shader Anda sendiri. Fungsi OpenGL lama yang mengubah keadaan fungsi tetap lama (seperti API pencahayaan OpenGL lama) sebenarnya menggunakan fitur OpenGL modern seperti buffer seragam untuk mengumpankan nilai-nilai ini ke shaders kompatibilitas driver.

Driver yang mendukung kompatibilitas harus melakukan banyak pekerjaan di belakang layar hanya untuk mencari tahu ketika Anda menggunakan fitur usang ini dan memastikan bahwa Anda dapat menggabungkannya dengan fitur modern dengan lancar, yang menambah biaya overhead dan sangat menyulitkan pengemudi. Ini adalah salah satu alasan bahwa beberapa driver memaksa Anda untuk mengaktifkan Profil Inti untuk mendapatkan fitur yang lebih baru; itu sangat menyederhanakan internal driver mereka dengan tidak harus mendukung API lama dan baru yang digunakan secara bersamaan.

Banyak dokumentasi mungkin menyarankan Anda mulai dengan API lama hanya karena lebih mudah untuk memulai. Direct3D memecahkan masalah ini untuk pemula dengan menawarkan perpustakaan pendamping ( DirectX Tool Kit ) yang menyediakan API gambar yang lebih sederhana dan shader yang telah ditulis sebelumnya yang dapat secara bebas dicampur dengan penggunaan Direct3D 11 saat keahlian Anda tumbuh. Komunitas OpenGL yang lebih luas sebagian besar terjebak dengan Profil Kompatibilitas untuk pemula, sayangnya, yang bermasalah karena lagi-lagi ada sistem yang tidak memungkinkan Anda mencampur API OpenGL lama dengan yang lebih baru. Ada pustaka dan alat tidak resmi untuk rendering yang lebih sederhana pada OpenGL baru dengan berbagai tingkat fitur dan kasus penggunaan target dan bahasa ( MonoGame untuk pengguna .NET misalnya), tetapi tidak ada yang secara resmi disetujui atau disetujui secara luas.

Dokumentasi yang Anda temukan mungkin bukan untuk OpenGL tetapi mungkin untuk salah satu API serupa lainnya. OpenGL | ES 1.x memiliki fungsi tetap rendering tetapi tidak memiliki OpenGL 1.x API untuk pengiriman vertex. OpenGL | ES 2.x + dan WebGL 1+ tidak memiliki fitur fungsi tetap sama sekali dan tidak ada mode kompatibilitas mundur untuk API tersebut.

API ini terlihat sangat mirip dengan OpenGL utama; mereka tidak cukup kompatibel, tetapi ada ekstensi resmi untuk OpenGL yang didukung oleh beberapa (tidak semua) driver agar kompatibel dengan OpenGL | ES (yang menjadi dasar WebGL). Karena semuanya tidak cukup membingungkan sebelumnya.

Sean Middleditch
sumber
4
+1 Jawaban yang fantastis! Jika Anda dapat menyebutkan beberapa pustaka dan alat tidak resmi untuk render sederhana pada OpenGL baru yang akan menjadi luar biasa :)
Mehrdad
2
Jawaban yang brilian Saya memiliki masalah yang sama dengan DirectX pada hari itu - jauh lebih sederhana daripada dengan OpenGL, tetapi lompatan dari mode dipertahankan / langsung ke shader sangat besar. Untungnya, dokumentasi sangat membantu (tidak seperti OpenGL, setidaknya bagi saya), tetapi awal "bagaimana saya menyalakan" adalah gila: D
Luaan
Saya penulis opengl-tutorial.org, dan saya setuju dengan Sean. API berkembang dengan cara ini terutama karena alasan kinerja.
Calvin1602
Informasi yang sangat bagus tentang topik ..
reynmar
1
@Mehrdad: Saya tidak ingat apa-apa dari atas kepala saya; ada pustaka seperti SDL2 atau SFML yang menambahkan rendering 2D yang disederhanakan, berbagai pustaka grafik adegan, MonoGame untuk C #, dll., tetapi saya tidak benar-benar menyadari sesuatu yang secara langsung setara dengan Direct TK sekarang saya memikirkannya. Akan mengedit posting karena mengatakan "banyak" mungkin kebohongan besar. :)
Sean Middleditch
9

Perbedaan utama adalah seberapa mutakhir strategi. Mode langsung yang digunakan dalam tutorial pertama:

glBegin(GL_QUADS);
    glColor3f(1, 0, 0); glVertex3f(0, 0, 0);
    glColor3f(1, 1, 0); glVertex3f(100, 0, 0);
    glColor3f(1, 0, 1); glVertex3f(100, 100, 0);
    glColor3f(1, 1, 1); glVertex3f(0, 100, 0);
glEnd();

Sudah usang dan tidak didukung pada versi yang lebih baru.

Menggunakan buffer vertex dan shader adalah metode rendering saat ini dengan OpenGL. Ini mungkin terlihat lebih rumit, tetapi kinerjanya jauh lebih baik. Selain itu, setelah Anda memiliki kode pendukung Anda yang membungkus hal-hal OpenGL, perbedaannya akan diabstraksi untuk sebagian besar.

MichaelHouse
sumber
2

Hanya dengan menambahkan beberapa konteks ke jawaban luar biasa lainnya.

Mode langsung seperti yang dijelaskan dalam tautan pertama adalah, seperti yang dikatakan orang lain, kode sebelumnya dari OpenGL versi terbaru (1.1). Ini digunakan kembali ketika GPU sedikit lebih dari rasterizers segitiga dan gagasan pipa yang dapat diprogram tidak ada. Jika Anda melihat kode sumber untuk beberapa game akselerasi perangkat keras awal seperti GLQuake dan Quake 2 misalnya, Anda akan melihat mode langsung sedang digunakan. Secara sederhana, CPU mengirimkan instruksi untuk simpul satu per satu ke GPU untuk mulai menggambar segitiga di layar. Sebagai catatan, GL_QUADS memiliki hasil yang sama dengan GL_TRIANGLES, kecuali GPU harus mengubah paha depan menjadi segitiga itu sendiri dengan cepat.

Modern (3.2+) OpenGL mengambil pendekatan yang berbeda. Ini buffer data vertex ke dalam memori GPU untuk akses cepat, dan kemudian Anda dapat mengirim instruksi menggambar baik menggunakan glDrawArrays atau glDrawElements. Anda juga memiliki pipeline yang dapat diprogram (glUseProgram) yang memungkinkan Anda untuk menyesuaikan cara posisi GPU dan warna simpul.

Ada beberapa alasan mengapa mode segera tidak digunakan lagi, alasan utamanya adalah kinerja. Seperti yang Sean katakan dalam jawabannya, saat ini GPU dapat mengolah data lebih cepat dari yang dapat diunggah CPU, sehingga Anda akan menghambat kinerja GPU. Ada overhead kecil yang terlibat untuk setiap panggilan ke OpenGL yang Anda buat, itu sangat kecil tetapi ketika Anda membuat puluhan ribu panggilan setiap frame itu mulai menumpuk. Sederhananya, untuk menggambar model bertekstur menggunakan mode langsung, Anda memerlukan setidaknya 2 panggilan per simpul (glTexCoord2f dan glVertex3f) per bingkai. Dengan OpenGL modern Anda menggunakan beberapa panggilan di awal untuk buffer data, maka Anda dapat menggambar seluruh model, terlepas dari berapa banyak simpul yang dikandungnya, hanya menggunakan beberapa panggilan untuk mengikat objek array verteks, mengaktifkan beberapa pointer atribut dan lalu satu panggilan ke glDrawElements atau glDrawArrays.

Teknik mana yang benar? Yah itu tergantung pada apa yang Anda coba lakukan. Gim 2D sederhana yang tidak memerlukan teknik pasca-pemrosesan mewah atau shader akan bekerja dengan baik menggunakan mode langsung dan mungkin akan lebih mudah untuk menulis kode. Namun, game 3D yang lebih modern akan benar-benar berjuang, dan jika Anda berencana belajar GLSL (bahasa shader), maka pasti pelajari teknik modern.

Neoptolemus
sumber