Sprite-Blatttexturen, die Kanten benachbarter Texturen aufnehmen

10

Ich habe eine benutzerdefinierte Sprite-Routine (openGL 2.0), die ein einfaches Sprite-Blatt verwendet (meine Texturen sind horizontal nebeneinander angeordnet).

Hier ist zum Beispiel ein Test-Sprite-Blatt mit zwei einfachen Texturen:

Geben Sie hier die Bildbeschreibung ein

Beim Erstellen meines openGL-Sprite-Objekts muss ich nun die Gesamtzahl der Frames in seinem Atlas angeben und beim Zeichnen angeben, welchen Frame ich zeichnen möchte.

Es wird dann herausgefunden, woher die Textur stammt:

Teilen Sie die erforderliche Bildnummer durch die Gesamtzahl der Bilder (um die linke Koordinate zu erhalten).

Tauchen Sie dann 1 um die Gesamtzahl der Frames und addieren Sie das Ergebnis zur oben berechneten linken Koordinate.

Das scheint zu funktionieren, aber manchmal bekomme ich Probleme. Sagen wir zum Beispiel, ich möchte das X unten zeichnen und ich bekomme ...........

Geben Sie hier die Bildbeschreibung ein

Ich habe davon gehört, zwischen jede Textur eine "Polsterung" von 1 Pixel zu setzen, aber könnte jemand genau erklären, wie das funktioniert? Ich meine, wenn ich das mache, wird es sicherlich die Berechnungen für das Erhalten der Textur abwerfen.

Wenn ich die Polsterung einfach in die aufgenommene Textur einbeziehe (damit das Sprite mit einem leeren Rand gezeichnet wird), führt dies sicherlich zu Problemen bei der Kollisionserkennung? (dh Sprites scheinen zu kollidieren, wenn Begrenzungsrahmen verwendet werden, wenn die transparenten Teile kollidieren).

Würde mich freuen, wenn jemand erklären könnte.

BungleBonce
quelle
Verwenden Sie GL_NEARESToder GL_LINEARzum Rendern der Textur?
MichaelHouse
Ich benutze GL_Linear @ Byte56
BungleBonce

Antworten:

15

Das Problem bei der Verwendung von Texturatlanten und benachbarten Texeln, die auslaufen, hängt mit der Funktionsweise der linearen Texturfilterung zusammen.

Für jeden Punkt in der Textur, der nicht genau in der Mitte eines Texels abgetastet wird, werden bei der linearen Abtastung 4 benachbarte Texel abgetastet und der Wert an der von Ihnen angeforderten Stelle als gewichteter Durchschnitt (basierend auf dem Abstand vom Abtastpunkt) aller 4 berechnet Proben.

Hier ist eine schöne Visualisierung des Problems:

  

Da Sie so etwas wie GL_CLAMP_TO_EDGEin einem Texturatlas nicht verwenden können , müssen Sie um den Rand jeder Textur Randtexel erstellen. Diese Randtexel verhindern, dass benachbarte Samples aus völlig unterschiedlichen Texturen im Atlas das Bild durch die oben erläuterte gewichtete Interpolation verändern.

Beachten Sie, dass Sie bei Verwendung der anisotropen Filterung möglicherweise die Breite des Rahmens erhöhen müssen. Dies liegt daran, dass anisotrope Filterung die Größe der Probenumgebung bei extremen Winkeln erhöht.


Um zu veranschaulichen, was ich mit einem Rand um den Rand jeder Textur meine, betrachten Sie die verschiedenen in OpenGL verfügbaren Umbruchmodi. Achten Sie besonders darauf CLAMP TO EDGE.

  http://lucera-project.com/blog/wp-content/uploads/2010/06/wrap.png

Obwohl es einen Modus namens "Clamp to Border" gibt, ist dies eigentlich nicht das, woran wir interessiert sind. In diesem Modus können Sie eine einzelne Farbe definieren, die als Rand um Ihre Textur für alle Texturkoordinaten verwendet werden soll, die außerhalb der normalisierten [0.0 -1,0] Bereich.

Was wir wollen, ist das Verhalten zu replizieren CLAMP_TO_EDGE, bei dem jede Texturkoordinate außerhalb des richtigen Bereichs für die (Unter-) Textur den Wert des letzten Texelzentrums in der Richtung erhält, in der es außerhalb der Grenzen lag. Da Sie fast die vollständige Kontrolle darüber haben Die Texturkoordinaten in einem Atlas-System. Das einzige Szenario, in dem sich (effektive) Texturkoordinaten auf einen Ort außerhalb Ihrer Textur beziehen, ist der gewichtete Durchschnittsschritt der Texturfilterung.

Wir wissen, dass GL_LINEARdie 4 nächsten Nachbarn wie im obigen Diagramm dargestellt abgetastet werden, daher benötigen wir nur einen 1-Texel-Rand. Wenn Sie eine anisotrope Filterung verwenden, benötigen Sie möglicherweise einen breiteren Texelrand, da dies unter bestimmten Bedingungen die Größe der Probenumgebung erhöht.

Hier ist ein Beispiel für eine Textur, die den Rand deutlicher darstellt. Für Ihre Zwecke können Sie den Rand jedoch 1 Texel oder 2 Texel breit machen.

  

(HINWEIS: Der Rand, auf den ich mich beziehe, ist nicht das Schwarz an allen vier Bildrändern, sondern der Bereich, in dem sich das Schachbrettmuster nicht mehr regelmäßig wiederholt.)

Falls Sie sich gefragt haben, hier ist, warum ich immer wieder anisotrope Filterung anspreche. Es ändert die Form der Probenumgebung basierend auf dem Winkel und kann dazu führen, dass mehr als 4 Texel zum Filtern verwendet werden:

  http://www.arcsynthesis.org/gltut/Texturing/ParallelogramDiag.svg

Je größer der von Ihnen verwendete Anisotropiegrad ist, desto wahrscheinlicher ist es, dass Sie sich mit Beispielvierteln befassen müssen, die mehr als 4 Texel enthalten. Ein 2-Texel-Rand sollte für die meisten anisotropen Filtersituationen ausreichend sein.


Last but not least wird hier ein gepackter Texturatlas erstellt, der das GL_CLAMP_TO_EDGEVerhalten bei Vorhandensein eines GL_LINEARTexturfilters repliziert :

( Subtrahiere 1 von X und Y in den schwarzen Koordinaten, ich habe das Bild vor dem Posten nicht Korrektur gelesen. )   

Aufgrund der Randspeicherung erfordert das Speichern von 4 256x256-Texturen in diesem Atlas eine Textur mit den Abmessungen 516x516. Die Ränder sind farbcodiert, je nachdem, wie Sie sie während der Atlaserstellung mit Texeldaten füllen würden:

  • Rot = Durch Texel direkt darunter ersetzen
  • Gelb = Durch Texel direkt darüber ersetzen
  • Grün = Ersetzen Sie durch Texel direkt links
  • Blau = Durch Texel direkt rechts ersetzen

In diesem gepackten Beispiel verwendet jede Textur im Atlas effektiv einen Bereich von 258 x 258 des Atlas. Sie generieren jedoch Texturkoordinaten, die dem sichtbaren Bereich von 256 x 256 zugeordnet sind. Die angrenzenden Texel werden immer nur verwendet, wenn die Texturfilterung an den Rändern der Texturen im Atlas durchgeführt wird und die Art und Weise, wie sie entworfen wurden, das GL_CLAMP_TO_EDGEVerhalten nachahmt .

Falls Sie sich gefragt haben, können Sie andere Arten von Umbruchmodi mit einem ähnlichen Ansatz implementieren. Dies GL_REPEATkann durch Austauschen der Texel für den linken / rechten und oberen / unteren Rand im Texturatlas und ein wenig cleverer Texturkoordinatenmathematik in a implementiert werden Shader. Das ist etwas komplizierter, also mach dir vorerst keine Sorgen. Da Sie nur mit Sprite-Sheets zu tun haben, beschränken Sie sich auf GL_CLAMP_TO_EDGE:)

Andon M. Coleman
quelle
Danke @AndonMColeman, könnten Sie den Rand in einem Diagramm anzeigen, da ich immer noch nicht sicher bin, wie das funktioniert. Bedeutet dies wiederum, dass die Textur, wenn sie auf mein Objekt angewendet wird, den Rand enthält? Ich möchte, dass die eigentliche Textur bis an die Ränder meines Quad reicht - sorry, wenn ich das nicht richtig verstehe - würde mich über weitere Details
freuen
@ user22241 Natürlich funktioniert dieser Rand so, dass das Texel am Rand der Textur dupliziert wird.
Andon M. Coleman
@ user22241: Nein, dieser Rand ist unter normalen Umständen nicht sichtbar. Sie behandeln Ihre gepackten Texturen so, dass sie für die Berechnung der Texturkoordinaten dieselben Abmessungen haben. Wenden Sie einfach einen Versatz an, um den Rand zu überspringen. Der springende Punkt des Rahmens besteht darin, zu verhindern, dass die lineare Texturabtastung Texel überschreitet und abtastet, die zu separaten Bildern in Ihrem Sprite-Blatt gehören. Wenn Sie die Stichprobe des nächsten Nachbarn verwenden, ist nichts davon erforderlich, aber dann erhalten Sie böse Alias-Sprites.
Andon M. Coleman
Genial detaillierte Antwort - danke! Nur eine letzte Sache, wenn ich könnte. Wie würde ich den 'Versatz' berechnen, um ihn meinen Texturkoordinaten hinzuzufügen? Habe ich Recht, wenn ich sage, es wäre einfach 1 / widthOfTexture?
BungleBonce
@ user22241: Ja, der Anfang des Texturbildes wäre + 1 / widthOfTexture in X-Richtung und + 1 / heightOfTexture in Y-Richtung. Sie haben einen Rand, der sich um jede Textur herum erstreckt. Wenn Sie die Texturkoordinaten für die 3. horizontale Textur berechnen möchten, beträgt die gepackte Position für diese Textur tatsächlich +5 Texel (+2 Texel für den Rand der ersten Textur, +2 für den Rand der zweiten Textur und +1 für den Start dieser Textur) zusätzlich zur Breite der anderen 2 Texturen. Es scheint kompliziert, es nur zu schreiben; Ich kann Ihnen bei Bedarf ein Diagramm zeichnen :)
Andon M. Coleman