Wenn du im Grunde nur einen Wrapper schreibst, also ähnliche Funktionen anbietest wie die darunterliegende Schnittstelle und das meiste einfach so weiter reichst, hast du noch sehr wenig erreicht. Es kann trotzdem schon viel Code sein, aber wirklich viel helfen muss der nicht.
Meiner Meinung nach lernt man Softwaredesign am aller besten, wenn man es ohne irgendwelche Tipps ausprobiert, mitten im Projekt hängen bleibt und von vorne anfangen muss. Denn viele Probleme versteht man erst dann wirklich, wenn man sie selber einmal hatte. Und obwohl es viele gute Guidelines gibt, kann man sie doch auch oft falsch anwenden, wenn man nicht das Problem verstanden hat, das sie lösen sollen. Natürlich verschlingt dieser Trial & Error Ansatz eine Menge Zeit. Aber besser wird man eben in erster Linie, indem man viel Zeit investiert.
Ich hatte beispielsweise früher eine 3D-Modell Klasse die im Grunde in einzelnes Modell (ein Haus, einen Menschen...) repräsentiert hat. Sie konnte eine Datei laden, hat über einen Manager die Texturen verwaltet und hatte Methoden zum Rendern. Jedes Spielobjekt hatte dann ein 3D Modell und es einmal pro Frame gerendert. Das passte sehr schön in mein Programmiermodell, jedes Objekt updated sich einmal pro Frame (Position+=Geschwindigkeit usw.) und zeichnet sich einmal pro Frame.
Man konnte auch unter der Haube jede Menge optimieren. Ich hatte Manager die jedes Modell und jede Textur nur einmal laden. Und View-Frustum-Culling hatte ich auch.
Aber eine Sache ging nicht: Eine wichtige Grundregel bei 3D Grafik ist, dass Kontextwechsel teuer sind. 100 Modelle mit 100 verschiedenen Texturen zu rendern ist viel langsamer, als 100 Modelle mit der selben Textur zu rendern. Man möchte Texturwechsel, Shaderwechsel und überhaupt alle Wechsel so gut es geht reduzieren.
Nun, jetzt hatte jedes Spielobjekt aber ein 3D Modell, und jedes 3D Modell konnte verschiedene Submodels haben. Die Modelle kannten sich untereinander nicht, also konnte ich dort nichts optimieren.
Also habe ich den Render-Part komplett umgeschrieben. Jetzt werden Modelle von einer Rendererklasse verwaltet, die in jedem Frame die Modelle nach ihrer eigenen Sortierung rendert (und nicht mehr in der Reihenfolge in der die Rendermethode der Spielobjekte aufgerufen wird). Spielobjekte setzen jetzt nur noch Renderparameter wie die Position und Rotation, tätigen aber keinen Renderaufruf mehr. Und die Rendererklasse kann intern alles schön nach Shadern und Texturen sortieren und nur dann Dinge ändern, wenn sie auch wirklich geändert werden müssen. Und dann habe ich spaßeshalber mal die Optimierung abgeschaltet und wie früher für jedes Submodel alle Texturen und Shader neu gesetzt und die Geschwindigkeit verglichen. Und von da an wusste ich, dass sich das umstrukturieren gelohnt hat
Insgesamt kannst du dir sicherlich eine Menge Tipps und Inspiration holen. Aber egal wie viel du liest, lernen tut man es am Ende doch durch die praktische Anwendung. Du wirst immer Designentscheidungen bereuen und das ein oder andere mal Dinge umbauen. Von daher würde ich dir raten, einfach deine eigenen Erfahrungen zu machen. Und auch manchmal einfach mit schlechten Entscheidungen zu leben, denn du wirst immer neue und bessere Ideen haben und wenn du sie jedesmal umsetzt kann ich dir garantieren, dass du niemals irgendetwas fertiges in deinen Händen halten wirst.