Ich benutze OpenGL seit einer Weile und habe eine große Anzahl von Tutorials gelesen. Abgesehen von der Tatsache, dass viele von ihnen immer noch die feste Pipeline verwenden, werden in der Regel alle Initialisierungen, Statusänderungen und Zeichnungen in einer Quelldatei gespeichert. Dies ist in Ordnung für den begrenzten Umfang eines Tutorials, aber ich habe Mühe, herauszufinden, wie ich es auf ein vollständiges Spiel hochskalieren kann.
Wie verteilen Sie Ihre OpenGL-Nutzung auf mehrere Dateien? Konzeptionell kann ich die Vorteile einer Rendering-Klasse erkennen, die lediglich das Rendern von Objekten auf dem Bildschirm ermöglicht. Aber wie funktionieren Objekte wie Shader und Lichter? Sollte ich getrennte Klassen für Dinge wie Licht und Schatten haben?
quelle
Antworten:
Ich denke OO OpenGL ist das nicht nötig. Anders ist es, wenn Sie über Shader, Model usw. sprechen.
Grundsätzlich würden Sie zuerst die Game / Engine-Initialisierung (und andere Dinge) durchführen. Anschließend laden Sie Texturen, Modelle und Shader in den Arbeitsspeicher (falls erforderlich) und in Pufferobjekte und laden Shader hoch / kompilieren sie. Danach haben Sie in Ihrer Datenstruktur oder Klasse von Shadern, Modellen , Int- IDs von Shadern, Modellen und Texturpufferobjekten.
Ich denke, die meisten Motoren haben Motorkomponenten und jeder von ihnen hat bestimmte Schnittstellen. Alle von mir untersuchten Engines enthalten Komponenten wie Renderer oder SceneManager oder beides (abhängig von der Komplexität des Spiels / der Engine). Dann können Sie OpenGLRenderer- Klasse und / oder DXRenderer haben, die Renderer-Schnittstelle implementieren. Wenn Sie über SceneManager und Renderer verfügen , können Sie folgende Aktionen ausführen :
Der Renderer würde wahrscheinlich die Zeichenfunktion des Objekts aufrufen, die die Zeichenfunktion jedes Netzes aufruft, aus dem das Netz besteht, und Texturobjekt binden, Shader binden, OpenGL-Zeichenfunktion aufrufen und dann Shader-, Textur- und Objektdatenpufferobjekte nicht verwenden.
HINWEIS: Dies ist nur ein Beispiel. Sie sollten SceneManager genauer studieren und Ihren Anwendungsfall analysieren, um die beste Implementierungsoption zu ermitteln
Sie würden natürlich andere Komponenten der Engine haben, wie MemoryManager , ResourceLoader usw., die sich sowohl um die Video- als auch die RAM-Speichernutzung kümmern, sodass sie nach Bedarf bestimmte Modelle / Shader / Texturen laden / entladen könnten. Zu den Konzepten hierfür gehören Memory Caching, Memory Mapping usw. usw. Zu den einzelnen Komponenten gibt es zahlreiche Details und Konzepte.
Schauen Sie sich eine detailliertere Beschreibung anderer Spiele-Engines an, es gibt viele davon und die Dokumentation ist ziemlich gut verfügbar.
Aber ja, der Unterricht erleichtert das Leben. Sie sollten sie vollständig verwenden und sich an Kapselung, Vererbung, Schnittstellen und weitere interessante Dinge erinnern.
quelle
OpenGL enthält bereits einige 'Object'-Konzepte.
Zum Beispiel kann alles mit einer ID als Objekt durch sein (es gibt auch Dinge, die speziell als "Objekte" bezeichnet werden). Puffer, Texturen, Vertex-Pufferobjekte, Vertex-Array-Objekte, Frame-Pufferobjekte usw. Mit ein wenig Arbeit können Sie Klassen um sie wickeln. Es gibt Ihnen auch eine einfache Möglichkeit, auf veraltete OpenGL-Funktionen zurückzugreifen, wenn Ihr Kontext die Erweiterungen nicht unterstützt. Beispielsweise könnte ein VertexBufferObject auf die Verwendung von glBegin (), glVertex3f () usw. zurückgreifen.
Es gibt einige Möglichkeiten, sich von den herkömmlichen OpenGL-Konzepten zu entfernen. Beispielsweise möchten Sie wahrscheinlich Metadaten zu den Puffern in den Pufferobjekten speichern. Zum Beispiel, wenn der Puffer Eckpunkte speichert. Was ist das Format der Eckpunkte (dh Position, Normalen, Texkoordinaten usw.)? Welche Grundelemente werden verwendet (GL_TRIANGLES, GL_TRIANGLESTRIP usw.), Größeninformationen (wie viele Floats sind gespeichert, wie viele Dreiecke repräsentieren sie usw.). Nur um es einfacher zu machen, sie in die Befehle zum Zeichnen von Arrays zu stecken.
Ich empfehle Ihnen, sich OGLplus anzuschauen . Es sind C ++ - Bindungen für OpenGL.
Auch glxx , das ist allerdings nur für das Laden von Erweiterungen.
Zusätzlich zum Umschließen der OpenGL-API sollten Sie einen etwas höheren Build darüber erstellen.
Zum Beispiel eine Materialmanagerklasse, die für alle Ihre Shader verantwortlich ist, die sie laden und verwenden. Es wäre auch verantwortlich für die Übertragung von Eigenschaften an sie. Auf diese Weise können Sie einfach Folgendes aufrufen: materials.usePhong (); material.setTexture (sometexture); material.setColor (). Dies ermöglicht mehr Flexibilität, da Sie neuere Dinge wie gemeinsame einheitliche Pufferobjekte verwenden können, um nur einen großen Puffer mit allen Eigenschaften, die Ihre Shader verwenden, in einem Block zu haben. Falls dies nicht unterstützt wird, können Sie auf das Hochladen in jedes Shaderprogramm zurückgreifen. Sie können einen großen monolithischen Shader verwenden und mithilfe einheitlicher Routinen zwischen verschiedenen Shader-Modellen wechseln, sofern dies unterstützt wird, oder auf mehrere kleine Shader zurückgreifen.
Sie können auch die Ausgaben in den GLSL-Spezifikationen zum Schreiben Ihres Shader-Codes überprüfen. Zum Beispiel wäre #include unglaublich nützlich und sehr einfach in Ihren Shader-Lade-Code zu implementieren (es gibt auch eine ARB-Erweiterung dafür). Sie können Ihren Code auch im laufenden Betrieb generieren, indem Sie festlegen, welche Erweiterungen unterstützt werden. Verwenden Sie beispielsweise ein gemeinsam genutztes einheitliches Objekt oder greifen Sie auf normale Uniformen zurück.
Schließlich möchten Sie eine übergeordnete Rendering-Pipeline-API, die Szenengraphen, Spezialeffekte (Unschärfe, Glühen) und Dinge, die mehrere Rendering-Durchgänge erfordern, wie Schatten, Beleuchtung und dergleichen, ausführt. Hinzu kommt eine Spiel-API, die nichts mit der Grafik-API zu tun hat, sondern sich nur mit Objekten in einer Welt befasst.
quelle
oglplus::Context
macht die Klasse diese Abhängigkeit sehr gut sichtbar - wäre das ein Problem? Ich glaube, es wird neuen OpenGL-Benutzern helfen, viele Probleme zu vermeiden.In modernem OpenGL können Sie gerenderte Objekte mit verschiedenen Vaos- und Shader-Programmen fast vollständig voneinander trennen. Und selbst die Implementierung eines Objekts kann in viele Abstraktionsschichten unterteilt werden.
Wenn Sie beispielsweise ein Terrain implementieren möchten, können Sie ein TerrainMesh definieren, dessen Konstruktor die Scheitelpunkte und Indizes für das Terrain erstellt, sie in Array-Puffer setzt und - wenn Sie ihm eine Attributposition zuweisen - Ihre Daten schattiert. Es sollte auch wissen, wie es gerendert wird, und es sollte darauf achten, alle Kontextänderungen, die es zum Einrichten des Renderns vorgenommen hat, rückgängig zu machen. Diese Klasse selbst sollte nichts über das Shader-Programm wissen, mit dem sie gerendert wird, und sie sollte auch nichts über andere Objekte in der Szene wissen. Über dieser Klasse können Sie ein Terrain definieren, das den Shader-Code kennt, und dessen Aufgabe es ist, die Verbindung zwischen dem Shader und TerrainMesh herzustellen. Dies sollte bedeuten, Attribute und einheitliche Positionen zu erhalten und Texturen und ähnliches zu laden. Diese Klasse sollte nichts darüber wissen, wie das Terrain implementiert ist, welchen LoD-Algorithmus sie verwendet, sie ist nur für die Schattierung des Terrains verantwortlich. Darüber können Sie die Nicht-OpenGL-Funktionalität wie Verhalten und Kollisionserkennung definieren.
Kommen wir zum Punkt, obwohl OpenGL für die Verwendung auf niedriger Ebene konzipiert ist, können Sie dennoch unabhängige Abstraktionsebenen erstellen, mit denen Sie Anwendungen mit der Größe eines unwirklichen Spiels skalieren können. Die Anzahl der gewünschten / benötigten Ebenen hängt jedoch von der Größe der gewünschten Anwendung ab.
Aber lügen Sie sich nicht wegen dieser Größe an. Versuchen Sie nicht, das Objektmodell von Unity in einer 10-KB-Zeilen-Anwendung nachzuahmen. Das Ergebnis wird eine völlige Katastrophe sein. Bauen Sie die Ebenen schrittweise auf, und erhöhen Sie die Anzahl der Abstraktionsebenen nur dann, wenn dies erforderlich ist.
quelle
ioDoom3 ist wahrscheinlich ein guter Ausgangspunkt, da Sie sich darauf verlassen können, dass Carmack eine gute Codierungspraxis befolgt. Ich glaube auch, dass er Megatexturing in Doom3 nicht verwendet, es ist also relativ einfach als Renderpipe.
quelle