Warum verwenden Tutorials unterschiedliche Ansätze für das OpenGL-Rendering?

43

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

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

Diese beiden Tutorials verwenden völlig unterschiedliche Ansätze, um fast das gleiche Ergebnis zu erzielen. Der erste verwendet Dinge wie glBegin(GL_QUADS). Der zweite verwendet Sachen wie vertexBufferObjectsShader, die auf GLEW basieren. Das Ergebnis ist jedoch dasselbe: Sie erhalten Grundformen.

Warum gibt es diese Unterschiede?

Der erste Ansatz scheint viel einfacher zu verstehen. Was ist der Vorteil des komplizierten zweiten Ansatzes?

reynmar
quelle
4
Es gibt nie nur einen Weg, eine Katze zu häuten.
Philipp
4
@Philipp Ja, aber es gibt richtige und falsche Wege, alte und neue Wege (und wie die folgenden Antworten zeigen, sind die alten und neuen Wege möglicherweise nicht in allen Situationen kompatibel)
Andrew Hill
3
Es gibt keine richtigen und falschen Wege, nur schlechte und bessere Wege (in verschiedenen Dimensionen).
user253751
glBeginund glEndwurden veraltet, weil sie für die aktuellen Grafikarchitekturen extrem ineffizient sind
Alex

Antworten:

77

OpenGL hat vier verschiedene Hauptversionen, wobei die Versionen für mobile Geräte und eingebettete Systeme (OpenGL | ES) und das Web über JavaScript (WebGL) nicht berücksichtigt werden. Genau wie Direct3D 11 eine andere Vorgehensweise als Direct3D 8 hat, hat OpenGL 3 eine andere Vorgehensweise als OpenGL 1. Der große Unterschied ist, dass OpenGL-Versionen meist nur Add-Ons zu den älteren Versionen sind (aber nicht völlig).

Zusätzlich zu den verschiedenen Editionen und Versionen von OpenGL hat das Haupt-OpenGL auch das Konzept von Profilen hinzugefügt. Nämlich das Kompatibilitätsprofil (das die Unterstützung für APIs älterer Versionen aktiviert) und das Kernprofil (das diese alten APIs deaktiviert). Dinge wie glBeginfunktionieren einfach nicht, wenn Sie das Core-Profil verwenden, sondern, wenn Sie das Kompatibilitätsprofil verwenden (das die Standardeinstellung ist).

Als eine weitere große Komplikation werden einige Implementierungen von OpenGL (wie unter anderem von Apple) neuere OpenGL-Funktionen nur dann aktivieren, wenn Sie das Core-Profil verwenden. Dies bedeutet , dass Sie müssen mit älteren APIs stoppen , um zu neueren APIs zu verwenden.

Sie haben dann einige sehr verwirrende Szenarien für Tutorials:

  1. Das Tutorial ist alt und verwendet nur veraltete APIs.
  2. Das Tutorial ist neu und gut geschrieben und verwendet nur Core-kompatible APIs.
  3. Das Lernprogramm ist neu, geht jedoch fälschlicherweise davon aus, dass Sie mit einem Treiber arbeiten, der alle APIs im Kompatibilitätsmodus aktiviert und neue und alte APIs frei mischt.
  4. Das Tutorial bezieht sich auf eine andere Version von OpenGL wie OpenGL | ES, die keine der alten APIs in irgendeiner Version unterstützt.

Dinge wie glBeginsind Teil dessen, was manchmal als Sofortmodus-API bezeichnet wird. Dies ist auch sehr verwirrend, da es in OpenGL keinen beibehaltenen Modus gibt und der "unmittelbare Modus" in Grafiken bereits eine andere Definition hatte. Es ist viel besser, diese APIs nur als OpenGL 1.x-APIs zu bezeichnen, da sie seit OpenGL 2.1 veraltet sind.

Die 1.x-API von OpenGL übermittelte Scheitelpunkte sofort an die Grafik-Pipeline in früheren Zeiten. Dies funktionierte gut, wenn die Geschwindigkeit der Hardware, mit der Scheitelpunkte gerendert wurden, in etwa der Geschwindigkeit der CPU entsprach, die die Scheitelpunktdaten erzeugte. OpenGL hat damals nur die Dreiecksrasterung und nicht viel mehr ausgelagert.

Heutzutage kann die GPU eine große Anzahl von Scheitelpunkten mit sehr hoher Geschwindigkeit durchkauen und gleichzeitig eine erweiterte Scheitelpunkt- und Pixel-Transformation durchführen, und die CPU kann einfach nicht einmal aus der Ferne mithalten. Darüber hinaus wurde die Schnittstelle zwischen der CPU und der GPU auf diesen Geschwindigkeitsunterschied ausgelegt, sodass es nicht einmal mehr möglich ist, Scheitelpunkte einzeln an die GPU zu senden.

Alle GL-Treiber müssen emulieren, glBeginindem sie intern einen Scheitelpunktpuffer zuweisen, die mit glVertexin diesen Puffer gesendeten Scheitelpunkte einfügen und dann den gesamten Puffer in einem einzelnen Zeichenaufruf senden, wenn dieser glEndaufgerufen wird. Der Aufwand für diese Funktionen ist weitaus größer als wenn Sie den Vertex-Puffer selbst aktualisiert haben. Aus diesem Grund wird in einigen Dokumentationen (aus Versehen!) Der Vertex-Puffer als "Optimierung" bezeichnet (dies ist keine Optimierung; dies ist der einzige Weg, dies tatsächlich zu tun) mit der GPU sprechen).

Es gibt verschiedene andere APIs, die in OpenGL im Laufe der Jahre veraltet oder veraltet sind. Die sogenannte Festfunktionspipeline ist ein weiteres solches Stück. In einigen Dokumentationen wird diese Pipeline möglicherweise noch verwendet oder mit der programmierbaren Pipeline gemischt. Die Pipeline mit festen Funktionen stammt aus früheren Zeiten, als Grafikkarten die gesamte Mathematik fest codierten, die zum Rendern von 3D-Szenen verwendet wurde, und die OpenGL-API darauf beschränkt war, einige Konfigurationswerte für diese Mathematik festzulegen. Heutzutage hat die Hardware sehr wenig hartcodierte Mathematik und führt (genau wie Ihre CPU) stattdessen vom Benutzer bereitgestellte Programme (oft als Shader bezeichnet) aus.

Wieder müssen die Treiber die alte API emulieren, da die Funktionen mit festen Funktionen auf der Hardware einfach nicht mehr vorhanden sind. Dies bedeutet, dass in den Treiber eine Reihe von Kompatibilitätsshadern eingebettet sind, die die alte Mathematik aus den Tagen mit festen Funktionen ausführen, die verwendet wird, wenn Sie keine eigenen Shader bereitstellen. Die alten OpenGL-Funktionen, die diesen alten Status mit festen Funktionen ändern (wie die alte OpenGL-Beleuchtungs-API), verwenden derzeit moderne OpenGL-Funktionen wie einheitliche Puffer, um diese Werte den Kompatibilitätsshadern des Treibers zuzuführen.

Treiber, die Kompatibilität unterstützen, müssen viel hinter den Kulissen arbeiten, um herauszufinden, wann Sie diese veralteten Funktionen verwenden, und um sicherzustellen, dass Sie sie reibungslos mit modernen Funktionen kombinieren können, was den Aufwand erhöht und den Treiber erheblich verkompliziert. Dies ist einer der Gründe, warum Sie von einigen Treibern dazu gezwungen werden, das Core-Profil auf neuere Funktionen umzustellen. Dies vereinfacht die Einbindung der Treiber erheblich, da die alte und die neue API, die gleichzeitig verwendet werden, nicht unterstützt werden müssen.

In vielen Dokumentationen wird möglicherweise empfohlen, mit den alten APIs zu beginnen, da diese einfacher zu handhaben sind. Direct3D löste dieses Problem für Anfänger, indem es eine Begleitbibliothek ( DirectX Tool Kit ) anbot , die einfachere Zeichnungs-APIs und vorab geschriebene Shader bietet, die mit der Verwendung von Direct3D 11 frei gemischt werden können, wenn Ihr Fachwissen wächst. Die breitere OpenGL-Community hat sich leider größtenteils an das Kompatibilitätsprofil für Anfänger gehalten, was problematisch ist, da es wieder Systeme gibt, mit denen Sie alte OpenGL-APIs nicht mit den neueren mischen können. Es gibt inoffizielle Bibliotheken und Tools für ein einfacheres Rendern in der neuen OpenGL mit unterschiedlichen Funktionen und Anwendungsfällen und Sprachen ( MonoGame zum Beispiel für .NET-Benutzer), aber nichts, was offiziell gebilligt oder weitestgehend vereinbart wurde.

Die Dokumentation, die Sie finden, ist möglicherweise nicht für OpenGL, sondern für eine der anderen ähnlichen APIs. OpenGL | ES 1.x hatte ein Rendering mit festen Funktionen, verfügte jedoch nicht über die OpenGL 1.x-APIs für die Übermittlung von Scheitelpunkten. OpenGL | ES 2.x + und WebGL 1+ haben überhaupt keine Funktionen mit festen Funktionen, und für diese APIs gibt es keine Abwärtskompatibilitätsmodi.

Diese APIs sehen dem OpenGL-Hauptprogramm sehr ähnlich. Sie sind nicht ganz kompatibel, aber es gibt offizielle Erweiterungen für OpenGL, die einige (nicht alle) Treiber unterstützen, um mit OpenGL | ES (auf dem WebGL basiert) kompatibel zu werden. Weil die Dinge vorher nicht verwirrend genug waren.

Sean Middleditch
quelle
4
+1 Fantastische Antwort! Wenn Sie ein paar dieser inoffiziellen Bibliotheken und Tools für das einfache Rendern auf dem neuen OpenGL erwähnen könnten, wäre das großartig :)
Mehrdad
2
Geniale Antwort. Ich hatte damals die gleichen Probleme mit DirectX - viel einfacher als mit OpenGL, aber der Sprung vom Beibehaltungs- / Sofortmodus zum Shader war gewaltig. Zum Glück hat die Dokumentation viel geholfen (im Gegensatz zu OpenGL, zumindest für mich), aber der Anfang von "Wie kann ich überhaupt Licht" war verrückt: D
Luaan
Ich bin der Autor von opengl-tutorial.org und stimme Sean zu. Die API wurde auf diese Weise hauptsächlich aus Leistungsgründen entwickelt.
Calvin1602
Sehr gute Informationen zum Thema ..
Reynmar
1
@Mehrdad: Ich kann mich an nichts auf meinem Kopf erinnern. Es gibt Bibliotheken wie SDL2 oder SFML, die vereinfachtes 2D-Rendering, verschiedene Szenendiagrammbibliotheken, MonoGame für C # usw. hinzufügen, aber mir ist derzeit nichts direkt Gleichwertiges zu Direct TK bekannt, wenn ich darüber nachdenke. Wird den Beitrag bearbeiten, da "viele" eine große, fette Lüge sein können. :)
Sean Middleditch
9

Der Hauptunterschied ist, wie aktuell die Strategien sind. Der im ersten Tutorial verwendete Sofortmodus:

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

Ist veraltet und wird in neueren Versionen nicht unterstützt.

Die Verwendung von Vertex-Puffern und Shadern ist die aktuelle Methode zum Rendern mit OpenGL. Es mag komplizierter erscheinen, ist aber wesentlich leistungsfähiger. Darüber hinaus werden die Unterschiede größtenteils abstrahiert, sobald Sie den unterstützenden Code haben, der das OpenGL-Zeug einschließt.

MichaelHouse
quelle
2

Nur um den anderen hervorragenden Antworten etwas mehr Kontext hinzuzufügen.

Der Sofortmodus, wie er im ersten Link beschrieben wurde, ist, wie andere bereits gesagt haben, Legacy-Code aus den frühesten Versionen von OpenGL (1.1). Es wurde verwendet, als GPUs nur Dreieck-Rasterizer waren und die Idee von programmierbaren Pipelines nicht existierte. Wenn Sie sich den Quellcode einiger früherer hardwarebeschleunigter Spiele wie GLQuake und Quake 2 ansehen, werden Sie feststellen, dass der Sofortmodus verwendet wird. In einfachen Worten, die CPU sendet nacheinander Anweisungen für Scheitelpunkte an die GPU, um mit dem Zeichnen von Dreiecken auf dem Bildschirm zu beginnen. Für den Datensatz hat GL_QUADS das gleiche Ergebnis wie GL_TRIANGLES, außer dass die GPU diese Quads im laufenden Betrieb selbst in Dreiecke verwandeln muss.

Modernes (3.2+) OpenGL verfolgt einen anderen Ansatz. Die Vertex-Daten werden für den schnellen Zugriff im GPU-Speicher zwischengespeichert. Anschließend können Sie Zeichnungsanweisungen entweder mit glDrawArrays oder glDrawElements senden. Sie haben auch die programmierbare Pipeline (glUseProgram), mit der Sie anpassen können, wie die GPU die Scheitelpunkte positioniert und färbt.

Es gibt einige Gründe, warum der Sofortmodus veraltet ist, vor allem wegen der Leistung. Wie Sean in seiner Antwort sagte, können heutzutage GPUs die Daten schneller zerkleinern als die CPU sie hochladen kann, so dass Sie die GPU-Leistung beeinträchtigen würden. Für jeden von Ihnen getätigten Aufruf von OpenGL ist ein geringer Aufwand erforderlich, der jedoch winzig ist. Wenn Sie jedoch in jedem Frame Zehntausende von Anrufen tätigen, beginnt die Stapelung. Einfach ausgedrückt, um ein texturiertes Modell im Sofortmodus zu zeichnen, benötigen Sie mindestens 2 Aufrufe pro Scheitelpunkt (glTexCoord2f und glVertex3f) pro Frame. Mit modernem OpenGL verwenden Sie zu Beginn einige Aufrufe, um die Daten zu puffern. Anschließend können Sie das gesamte Modell unabhängig von der Anzahl der darin enthaltenen Scheitelpunkte mit nur wenigen Aufrufen zum Binden des Scheitelpunktarrayobjekts zeichnen und einige Attributzeiger aktivieren dann ein einzelner Aufruf von glDrawElements oder glDrawArrays.

Welche Technik ist richtig? Nun, das hängt davon ab, was Sie versuchen. Ein einfaches 2D-Spiel, das keine ausgefallenen Nachbearbeitungstechniken oder Shader erfordert, funktioniert im Sofortmodus einwandfrei und es wird wahrscheinlich einfacher sein, den Code zu schreiben. Ein moderneres 3D-Spiel würde sich jedoch wirklich schwer tun, und wenn Sie GLSL (Shader Language) lernen möchten, sollten Sie die moderne Technik auf jeden Fall erlernen.

Neoptolemus
quelle