OpenGL 2D-Scrolling - Optimale Vorgehensweise ?
-
Hi,
ich habe eine Frage bezüglich der Implementierung von 2D-Scrolling. Ich bin gerade an einem Worms-Clone dran, und der funktioniert bisher folgendermaßen.
Beim Initialisieren von OpenGL wird eine große 2048x2048 Texture generiert und
in die Grafikkarte geladen. Diese Texture ist quasi meine gesamte Spiele-Welt.
(Vielleicht kommt da noch später ein Background-Layer rein, aber erstmal soll diese Texture mein gesamtes level darstellen). Zugehörig zu der großen Texture habe ich das ganze nochmal im RAM als Schwarz-Weiss-Map, anhand der ich dann später pixelgenaue Kollisionen für meine Würmer und die Bomben schnell erkennen kann. Jeder der Pixel der Texture ist im RGBA-Format, damit ich später dann einfach den Alphakanal der Texture manipuliern kann, um sie quasi zerstörbar zu machen. (Ist das ein gängiger weg, oder gibt es da eleganteres ?)
Nun zu meinem primären Problem. Wie scrolle ich denn nun am besten in meiner Welt
und das ganze möglichst Performant ? Ich habe 4 Lösungen gefunden, aber irgendwie weiss ich nciht so recht, welche ich davon verwenden sollte, bzw. ob ich überhaupt so verfahren sollte:Weg1:
Ich zeichne einfach einen großen 2048x2048 quad und lege da die gesamte Texture drüber. Mit glTranslate schiebe ich dann diesen Quad hin und her.
Hier hab ich scheinbar die meiste Performance: ca. 390FPS.
cx und cy ist die aktuelle Mausposition in Pixeln.RenderLoop:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glTranslatef(cx, cy, 0); glBegin(GL_QUADS); glTexCoord2f(0,0); glVertex3f(0.0f , 0.0f , 0.0f); glTexCoord2f(1,0); glVertex3f(2048.0f, 0.0f , 0.0f); glTexCoord2f(1,1); glVertex3f(2048.0f, 2048.0f, 0.0f); glTexCoord2f(0,1); glVertex3f(0.0f , 2048.0f, 0.0f); glEnd();
Weg2:
Wie Weg1, nur dass ich das ganze in eine Display-List packe:
Ergebnis. Gibt keinen wirklichen Performance-Boost!!DisplayList:
dlLandscape = glGenLists(1); glNewList(dlLandscape,GL_COMPILE); glBegin(GL_QUADS); glTexCoord2f(0,0); glVertex3f(0.0f , 0.0f , 0.0f); glTexCoord2f(1,0); glVertex3f(2048.0f, 0.0f , 0.0f); glTexCoord2f(1,1); glVertex3f(2048.0f, 2048.0f, 0.0f); glTexCoord2f(0,1); glVertex3f(0.0f , 2048.0f, 0.0f); glEnd(); glEndList();
RenderLoop:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glTranslatef(cx, cy, 0); glCallList(dlLandscape);
Weg 3:
Um eventuell das automatisierte Clipping von OpenGL zu umgehen, ist mir noch folgende Möglichkeit eingefallen.
Ich zeichne einen Quad, der genauso groß wie der Bildschirm ist (1366x768)
und setze dort immer einen passenden Ausschnitt der großen 2048x2048 Textur hinein. Vorteil ist eventuell hier, dass ich kein glTranslate brauche!
Nicht wirklich schneller!RenderLoop:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glBegin(GL_QUADS); glTexCoord2f(cx * (1.0f/2048.0f),cy * (1.0f/2048.0f)); glVertex3f(0.0f , 0.0f , 0.0f); glTexCoord2f(1366.0f/2048.0f+cx * (1.0f/2048.0f),cy * (1.0f/2048.0f)); glVertex3f(1366.0f, 0.0f , 0.0f); glTexCoord2f(1366.0f/2048.0f+cx * (1.0f/2048.0f),768.0f/2048.0f+cy * (1.0f/2048.0f)); glVertex3f(1366.0f,768.0f, 0.0f); glTexCoord2f(cx * 1.0f/2048.0f,768.0f/2048.0f+cy * (1.0f/2048.0f)); glVertex3f(0.0f , 768.0f, 0.0f); glEnd();
Weg 4:
Vielleicht sind kleinere Quads schneller als so ein Riesiger? Man könnte das
auch per kleiner Tile-Engine regeln, wobei ein Tile ein 32x32 Quad ist, und diese Tiles dann kachelförmig angeordnet eine Fläche von 2048x2048 ergeben.
Die einzelnen Tiles(quads) werden dann dementsprechend mit dem passenden Ausschnitt aus der großen 2048x2048 Texture bestückt. Dieses ist scheinbar etwas langsamer als alle anderen Lösungen!RenderLoop:
glBegin(GL_QUADS); for (float y=0; y < 64; y++) { for (float x=0; x < 64; x++) { glTexCoord2f(x*(1.0/64.0) , y*(1.0/64.0)); glVertex3f(0.0f +x*32.0f , 0.0f+y*32.0f , 0.0f); glTexCoord2f(x*(1.0/64.0)+(1.0/64.0), y*(1.0/64.0)); glVertex3f(32.0f+x*32.0f, 0.0f+y*32.0f , 0.0f); glTexCoord2f(x*(1.0/64.0)+(1.0/64.0), y*(1.0/64.0)+(1.0/64.0)); glVertex3f(32.0f+x*32.0f, 32.0f+y*32.0f, 0.0f); glTexCoord2f(x*(1.0/64.0) , y*(1.0/64.0)+(1.0/64.0)); glVertex3f(0.0f+x*32.0f , 32.0f+y*32.0f, 0.0f); } } glEnd();
Welche Variante würdet Ihr bevorzugen bzw. welche ist am sinvollsten bezüglich der Performance? Ist das überhaupt der richtige Weg? Kann es schneller gehen mit 4 x 512er Texturen als mit einer großen 2048x2048 Texture? aber da muss ich ja des öfteren glBindTexture(...) verwenden, und das frisst ja auch recht gut Performance!
Noch etwas:
Um das Terrain später zerstörbar zu machen, könnte ich ja dann in Realtime die Texture-Pixel (Deren Alphakanal) manipulieren. Ein anderer Weg könnte sein, einfach bestimmte Polygone darüber zu rendern mit Alphakanal 0 oder so ähnlich, die dann bestimmte Teile ausblenden/überblenden. Aber da braucht man viele solche polygone bei vielen explosionen im Terrain
Und da könnte Methode 1 besser sein oder ?
vielen Dank und Gruß
SoulVielleicht kann mir jemand hier insgesamt zu diesem Thread mal ein paar Tipps geben, wie ich am besten an die ganze Sache rangehen kann und wie man am besten Scrollen sollte und wie man eventuell zerstörbares Terrain gut hinbekommt. Über jede anregung (Kein Code!) wäre ich sehr dankbar
-
Kennst du diese Seite schon?
http://jnrdev.72dpiarmy.com/en/jnrdev5/Anregungen in Code-Form findest du bestimmt auch bei hedgewars..^^
iirc speichert worms die landschaft als rgb, wobei schwarz bedeutet, dass hier nichts ist. Sollte über color coded transparency (??-blending?) auch recht einfach zu rendern sein (auch mit Hintergrund).
Zum Scrolling will ich jetzt nix sagen, aber 390 auf einem normalen Rechner sind doch genug FPS, oder?
-
Vielen Dank für deine Antwort. De Artikel bzw. generell die Seite ist recht Interessant, werde ich mal durchstöbern.
@ColorcodedTransparency
Ja, entweder mache ich es per Alphatest oder einfach nur mit Blending, obwohl ich glaube, dass ich komplette Blending-Unterstützung nicht wirklich brauche.
(Diesmal mache ich nicht den Fehler, dass ich glaube jegliches Feature brauchen zu müssen und dann niemals fertig zu werden!! *grinns)@Scrolling
390 sind noch ausreichend, mit Vsync brauche ich ja nur 60-100 FPS oder so, aber zu diesem Zeitpunkt habe ich gerade mal nur einen Layer gezeichnet. Ohne Physik, Audio etc. kommt mir das doch recht langsam vor...da kann man doch bestimmt noch mehr rausholen oder? Ich brauche mindestens 4 Layer
-
Ich würde es mit Variante 4 versuchen. Brauchst allerdings nicht so kleine Quads zu nehmen. Ich denke 128 oder 256 ist ein guter Anfang. Da kachelst du die die Textur drauf und achtest darauf, das du jeweils nur die Tiles renderst die auch sichtbar sind. Das sollte eigentlich ziemlich schnell laufen. Wenn du den ganzen Bildschirm füllst, kannst du übrigens auch GL_COLOR_BUFFER_BIT in glClear weglassen. Das bringt nochmal ein Paar FPS
-
Ja bei Variante4 dachte ich mir auch, dass das vielleicht schneller sein könnte,
da kaum was geclippt werden muss, wenn ich da die unsichtbaren Tiles garnicht erst rendere. Überraschenderweise ist das nicht schneller als einen einzigen riesigen Quad zu zeichnen. Das Clippen des riesigen Quads scheint OpenGL hochperformant zu machen.Sollte nicht Weg3 der beste sein? Ein Quad, genau auf den Bildschirm angepasst und nur die Texture immer wieder versetzt zeichnen? Nach meinen Tests ist das aber auch nicht wirklich schneller.
Am besten ich lasse einfach alles erstmal so, und dann, wenn ich wirklich in Performance-Schwierigkeiten komme, denke ich nochmal darüber nach. Dachte da gibt es so einen gängige Lösung, das Scrolling zu generieren, aber hier führen scheinbar viele Wege nach Rom..
-
Vielleicht ist auch die Texturgröße das Problem. Da gibts nämlich eine Beschränkung die via GL_MAX_TEXTURE_SIZE abgefragt werden kann. Ich kann mich auch an gewisse Gerüchte erinnern, das OpenGL unter bestimmten Umständen nicht von der Hardware Beschleunigung gebrauch machen kann, im Zusammenhang mit zu grossen Texturen. (Siehe auch OpenGL FAQ: http://www.opengl.org/resources/faq/technical/texture.htm )
Ich versuche eigentlich immer Texturen mit max. 512x512 zu verwenden. Allerdings entwickel ich auch auf fieser Uralt Hardware und heutzutage mag das wohl alles etwas einfacher geworden sein.