Richtige Abstraktionsebene für eine 3D-Rendering-Komponente?

8

Ich habe viele Fragen in diesem Bereich gesehen, aber nicht genau diese Frage. Ich entschuldige mich, wenn dies ein Duplikat ist.

Ich mache ein kleines 3D-Spiel. Um ehrlich zu sein, es ist nur ein kleines Hobbyprojekt und wird sich wahrscheinlich nicht als echtes Spiel herausstellen. Ich werde gerne eine schöne Grafikdemo erstellen und etwas über 3D-Rendering und C ++ - Design lernen.

Meine Absicht ist es, direct3d9 zum Rendern zu verwenden, da ich wenig Erfahrung damit habe und es meinen Anforderungen zu entsprechen scheint. Wenn ich als Programmierer jedoch eines gelernt habe, muss ich fragen: " Gibt es einen denkbaren Grund, warum diese Komponente durch eine andere Implementierung ersetzt werden könnte? ". Wenn die Antwort "Ja" lautet, muss ich eine geeignete Abstraktion und Schnittstelle zu dieser Komponente entwerfen . Obwohl ich beabsichtige, d3d9 zu implementieren, muss ich eine 3D-Schnittstelle entwerfen, die für d3d11, opengl ...

Meine Frage ist dann, auf welcher Ebene dies am besten möglich ist. Ich denke, dass eine Schnittstelle in der Lage ist, zu erstellen und später zu zeichnen

  • Scheitelpunktpuffer und Indexpuffer
  • Texturen
  • Vertex- und Pixel- "Shader"
  • Einige Darstellungen des Zeichenzustands (Mischmodi usw.)

Mit anderen Worten, eine Schnittstelle auf relativ niedriger Ebene, auf der mein Code zum Zeichnen eines animierten Modells die Schnittstelle verwendet, um abstrakte Scheitelpunktpuffer usw. zu erhalten. Ich mache mir jedoch Sorgen, dass sie zu niedrig ist, um alle Funktionen, die ich benötige, effizient zu abstrahieren.

Die Alternative besteht darin, dies auf einer höheren Ebene zu tun, auf der die Benutzeroberfläche Objekte, Animationen, Landschaften usw. zeichnen und für jedes System implementieren kann. Das scheint mehr Arbeit zu sein, aber es ist flexibler, denke ich.

Das ist also wirklich meine Frage: Welche Ebene der Benutzeroberfläche funktioniert am besten, wenn ich das Zeichnungssystem abstrahiere?

JohnB
quelle
Möglicherweise wird Ihre Frage nicht vollständig beantwortet, aber ich würde empfehlen, sich die Quellen von DirectX + OpenGL-Engines wie OGRE und Irrlicht anzusehen, um zu sehen, wie sie sie abstrahiert haben.
Die kommunistische Ente

Antworten:

8

Entschuldigung für die Länge dieser Antwort!

"Gibt es einen denkbaren Grund, warum diese Komponente durch eine andere Implementierung ersetzt werden könnte?" Wenn die Antwort "Ja" lautet, muss ich eine geeignete Abstraktion und Schnittstelle zu dieser Komponente entwerfen.

"Jeder denkbare Grund" ist eine viel zu extreme Haltung. Fast jede Funktion konnte auf irgendeine Weise parametrisiert werden, jedes Modul erhielt eine andere Strategie oder Richtlinie und jede Konstante wurde optimiert. Wenn Sie für jede dieser Ebenen eine zusätzliche Abstraktionsebene bereitstellen, schreiben Sie am Ende doppelt so viel Code ohne unmittelbaren Gewinn, nur einen potenziellen Gewinn später, wenn Sie diese Flexibilität jemals benötigen.

Wenn Sie sich für die Bereitstellung von Schnittstellen entscheiden, damit jedes dieser Dinge eine Fassade für eine von mehreren verschiedenen Optionen sein kann, müssen Sie entscheiden, wie Sie diese verschiedenen Optionen, einschließlich der Standardeinstellungen, bereitstellen und wann Sie versuchen, dies so einfach wie möglich zu gestalten Als ursprüngliches Objekt haben Sie oft eine ganze zusätzliche Schicht darüber. Dies kann (und tut) in vielen realen "Unternehmen" -Software absurd werden - darf ich demütig vorschlagen, dieses Satirestück zu lesen, das in vielen Fällen leider zu nahe an der Heimat liegt: Warum ich Frameworks hasse .

Das größte Argument dagegen ist die Komplexität. Nehmen wir die (wahrscheinlich falsche) Annahme an, dass es eine gute Schnittstelle gibt, die für zwei 3D-Rendering-APIs gleich gut geeignet ist, und dass jemand Ihnen dies in einer Antwort hier beschreiben kann. Sie würden dann an dieser Schnittstelle arbeiten, die aufgrund ihres Mehrzweckcharakters zweifellos Elemente abdeckt, die DX9 nicht verwendet - zum Beispiel die Vielzahl von API-Erweiterungen, die in OpenGL, aber nicht in DirectX 9 verfügbar sind Eine zusätzliche Komplikation, die das Verständnis und die Wartung Ihres Codes erschwert und Ihren Lernprozess verlangsamt. Es ist schon schlimm genug, wenn Sie eine vorhandene Multi-Plattform-Bibliothek wie SDL verwenden, da die Leute immer wieder auf seltsame Funktionen und Einschränkungen stoßen, die nur existieren, um etwas auf einer Plattform zu reparieren, aber Sie ' Ich muss nicht nur lernen, warum dieser Teil der Benutzeroberfläche vorhanden ist, sondern auch, wie er mit den Teilen, die Sie gerade schreiben, interagieren kann oder nicht. z.B. Am Ende würden Sie abstrakte Objekte schreiben, die Aspekte von OpenGL-Erweiterungen enthalten, die in Ihrem Kern-Rendering-Code erforderlich sind, obwohl Sie diese noch nicht verwenden können.

Wenn Sie jedoch in Zukunft mit der Funktionsweise des DX9-Systems vertraut sind, können Sie die Funktionsweise des OpenGL-Systems untersuchen. Sie werden dann sehen, wie Sie Ihr vorhandenes System anpassen müssen, damit beide Teile funktionieren, und dies mit dem praktischen Vorhandensein Ihres vorhandenen DX9-Codepfads als effektiven Komponententest, um sicherzustellen, dass Ihr Refactoring korrekt ist.

Sie haben Glück, dass Sie mit einem der flüssigsten und formbarsten Materialien arbeiten, die der Technik bekannt sind - dem als Computerdaten gespeicherten Quellcode. Umfassen Sie diesen Segen, indem Sie Änderungen daran vornehmen, wenn Sie wissen, dass Sie sie benötigen, anstatt sie Monate im Voraus zu tun und sich in der Zwischenzeit zu belasten, indem Sie an eine Abstraktion schreiben müssen, von der Sie noch nicht profitieren.

Kylotan
quelle
So fühle ich mich, aber gleichzeitig ist der allgemeine Rat, den ich sehe, dass das Abstrahieren von plattformspezifischem Code für ein Spiel einfach ist und gemacht werden sollte ... Ich weiß nicht, was ich denken soll.
Rei Miyasaka
5

Wenn ich als Programmierer jedoch eines gelernt habe, muss ich fragen: "Gibt es einen denkbaren Grund, warum diese Komponente durch eine andere Implementierung ersetzt werden könnte?". Wenn die Antwort "Ja" lautet, muss ich eine geeignete Abstraktion und Schnittstelle zu dieser Komponente entwerfen .

Dies beantwortet Ihre Frage nicht wirklich, aber aus meiner Erfahrung als Programmierer ist eine vorzeitige Verallgemeinerung genauso schlecht wie eine vorzeitige Optimierung. Sie gewinnen selten etwas und müssen es im Laufe des Projekts pflegen.

Mein Rat ist, D3D9-Clientcode direkt in Ihrem Projekt zu verwenden. Wenn Sie feststellen, dass Sie die Schnittstelle wechseln müssen, erstellen Sie eine Schnittstelle mit allen Dingen, die Sie derzeit verwenden. Programmierer können schrecklich vorhersagen, was sie möglicherweise benötigen, doppelt so in Ihrem Fall, in dem Sie mit dem Material nicht besonders vertraut sind. Auf diese Weise erledigen Sie die geringste Menge an Arbeit.

Siehe auch: http://c2.com/xp/YouArentGonnaNeedIt.html

Tetrad
quelle
Das Problem dabei ist, dass er, wenn er später eine Abstraktion anstreben muss, seinen gesamten Schnittstellencode neu schreiben muss. Und wenn D3D9 in seinem gesamten Projekt global enthalten war, wird es verdammt schwer sein, den gesamten Code zu überarbeiten.
DeadMG
1
Er wird sowieso alles neu schreiben müssen, wenn er einen Anwendungsfall hat, der nicht durch die Annahmen abgedeckt ist, die er jetzt sowieso macht. Refactoring sollte insbesondere für ein Lernprojekt begrüßt werden.
Tetrad
Ich übertreibe den Fall ein bisschen. Auch ich hasse unnötige Abstraktion. Aber in diesem Fall kann ich tatsächlich sagen, dass es eine gute Chance gibt, dass ich dies mit einer anderen Implementierung tun möchte ...
JohnB
4

Hm, es sieht so aus, als hätte ich nicht genug Ruf, um Kommentare zu Antworten zu posten.

Sowohl Kylotan als auch Tetrad gaben ausgezeichnete Antworten. Was ich hinzufügen möchte, ist vorzuschlagen, dass Sie den Rendering-Code von der Spielelogik trennen - dies dient mehreren Zwecken:

  • Ihr Code wird sauberer
  • Es ist einfacher für Sie, Änderungen an Ihrem Renderer vorzunehmen, ohne jedes Mal die Hälfte der Spielelogik neu zu schreiben
  • Auf diese Weise ist es schließlich einfacher, völlig andere Rendering-Module bereitzustellen

Als zusätzlichen Bonus können Sie auf diese Weise Ihre Abstraktion schrittweise erstellen, während Sie an Ihrem Projekt arbeiten. Wie Sie sagen, ist dies ein Lernprojekt. Nehmen Sie es einfach und vereinfachen Sie es, indem Sie Komplexität und Abstraktionen hinzufügen, anstatt große Pläne zu schmieden, da Ihnen wahrscheinlich das Fachwissen fehlt, um es anfangs richtig zu machen (wie Tetrad ebenfalls vorgeschlagen hat).

Gilead
quelle
Oh in der Tat! Aber die Frage ist, auf welcher Ebene trenne ich es? Auf der Ebene des Zeichnens einer ganzen Stadt oder auf der Ebene des Zeichnens eines Dreiecks? Oder wo dazwischen
JohnB
Ein vernünftiger Weg wäre, API-Aufrufe getrennt zu rendern. In Ihrer Analogie geht es wahrscheinlich darum, ein Gebäude oder ein Auto zu zeichnen (wenn man bedenkt, dass Ihre Gebäude und Autos einzelne Objekte sind :)). Ein Beispiel: Sie möchten Ihr Objekt animieren (der Einfachheit halber nehmen wir an, dass jeweils nur eines abgespielt werden kann). Ein Spielobjekt könnte eine Liste der verfügbaren Sequenzen und deren Länge haben und die aktive Animation und ihre Geschwindigkeit einstellen, während der Renderer den Rendering-Teil verwalten würde. Möglicherweise müssen Sie auch Daten vom Renderer zurückholen, z. B. kann sich die Kollisionsform des Objekts während der Animation ändern.
Gilead
1

Sie können keine Abstraktion auf niedriger Ebene über solche Dinge schreiben, da die Details eines Shaders zwischen D3D9, 11, OGL usw. völlig unterschiedlich sind. Es ist einfach unmöglich.

Sie können sich die in beispielsweise SFML, SDL, OGRE implementierten Abstraktionen ansehen, wenn Sie sehen möchten, was andere Personen für das Rendern von Abstraktionen ausgewählt haben.

DeadMG
quelle
Es sei denn, Shader sind nicht Teil der Abstraktion.
user253751
1

Ich habe versucht, eine API zu schreiben, die sowohl D3D als auch OGL sauber umschließt. Es ist ein gewaltiges Unterfangen, habe ich gelernt. Viele ihrer Unterschiede können in Einklang gebracht werden. Zum Beispiel Laden von Shadern. Ich kapsle D3D- und OGL-Shader-Code effektiv in meinem eigenen Format. IE: In D3D müssen Sie das Shader-Profil angeben, damit in der Datei selbst das Shader-Profil geschrieben ist. Wenn die gekapselte Shader-Quelle an meine API übergeben wird, ruft sie die entsprechende D3D-Shader-Erstellungsmethode mit dem entsprechenden Shader-Profil und dergleichen auf.

Es gibt jedoch immer noch viele Unterschiede, die ich nicht gelöst habe oder jemals lösen könnte, da ich entschieden habe, dass eine übergeordnete Abstraktion einfacher zu entwerfen und für meine Anforderungen zufriedenstellend ist. Ich arbeite derzeit an etwas ähnlichem wie dem D3DXEffect-Framework. Objekte haben eine Rendertechnik, die Renderdurchläufe enthält, die eine Reihe von Renderzuständen und eine Reihe von Shadern aufweisen und bestimmte Eingaben erfordern. Dadurch kann ich mehr von der API weg abstrahieren.

Sion Sheevok
quelle
1

Ich weiß nicht, warum Leute automatisch zu vorzeitiger Verallgemeinerung / Optimierung springen, wenn Fragen wie diese auftauchen. Es gibt etwas zu sagen, wenn man präventiv ist und erkennt, wann eine Komponente in einem System wahrscheinlich ausgetauscht wird. Alle Antworten zu "Keine Sorge und Code weg, bis Sie ihn ändern müssen" bringen die Menschen in der realen Welt in Schwierigkeiten, da sie später größere Änderungen bewirken oder Code hinterlassen, der anders gestaltet sein sollte als Beginnen Sie mit, wurde aber belassen, weil es "funktioniert". Vorausdenken und wissen, wo man aufhören muss, bevor man überlegt, ist eine Fähigkeit für sich.

Es wäre viel einfacher, Schnittstellen umzugestalten, um Unterschiede zwischen DX / OGL auszugleichen, als eine völlig neue Implementierung (z. B. DX) in ein System einzuführen, das streng für die Arbeit mit OGL codiert ist. Die Code-Abstraktion sollte sowieso auf einer bestimmten Ebene erfolgen, da Sie nicht überall Grafikcode mit Spielelogikcode mischen oder Code wiederholen möchten. Wenn ich an einem Spiel arbeite und mit OpenGL anfange (weil ich Erfahrung damit habe), aber auch weiß, dass ich das Spiel irgendwann auf Xbox / Windows (DX) bekommen möchte, wäre es dumm für mich Berücksichtigen Sie dies nicht beim Entwerfen meiner Grafik-API. Die Idee, etwas so Komplexes wie ein Spiel umzugestalten und neue Grafik-APIs einzuführen, ist mühsam und fehleranfällig.

Sie sollten überlegen, wie Sie Ihre API verwenden möchten, und sie aufbauen, wenn sich Ihre Anforderungen / Kenntnisse während des Entwicklungsprozesses des Spiels ändern. Sie führen keine Abstraktion über OGL / DX auf niedriger Ebene durch, sondern erstellen eine API für sich selbst, um das Laden von Geometrie, das Laden / Kompilieren von Shadern, das Laden von Texturen usw. zu unterstützen.

nenchev
quelle