Obwohl OpenGL ES für 3D-Grafiken entwickelt wurde, werden Sie oft die Notwendigkeit finden, visuelle Elemente zu rendern, die 2D sind. OpenGL ist eigentlich recht gut geeignet, um eine flache Welt zu rendern. Mehrere populäre 2D-Bibliotheken, wie cocos2d, verwenden OpenGL als Rendering Engine.

Sprites OpenGL

Die gebräuchlichste Art des 2D-Renderings ist das Text-Rendering. Die OpenGL-API ist zu nah an Metal dran, um Text als besonders wichtig zu erachten, aber es ist einfachm eine vorgenerierte Glyphe mit einem texturierten Quad zu rendern und das ist der Ansatz, den wir in diesem Kapitel verfolgen werden.

Hinweis: Die Berechnung der Punkte entlang des Umrisses einer Glyphe kann überraschend komplex sein. Das TrueType-Dateiformat spezifiziert beispielsweise eine eindeutige Programmiersprache – komplett mit Schleifen und if-Anweisungen – nur zum Zweck der Anpassung der Kurven in einer Glyphe.

In diesem Kapitel werden wir nicht versuchen, über Kern-Algorithmen, Ligaturen oder Zeilenumbrüche hinwegzusehen. Einfaches Textlayout ist gut genug für unsere Zwecke. (Schauen Sie sich die beliebte Pango-Bibliothek an, wenn Sie eine voll ausgestattete Layout-Engine benötigen.)

Ein weiteres gängiges 2D-Konzept ist das Sprite, ein ziemlich allgemeiner Begriff für jede Bitmap, die zu einer Szene zusammengesetzt wird. Sprites enthalten oft transparente Bereiche, so dass ihr Texturformat Alpha enthält. Sprites sind oft in irgendeiner Weise animiert. Es gibt zwei Möglichkeiten, ein Sprite zu animieren: seine Bildschirmposition kann sich ändern (man stelle sich einen springenden Ball vor) oder sein Quellbild kann sich ändern (man stelle sich einen sich drehenden Ball vor).

Das iPhone unterstützt zwei Erweiterungen zu OpenGL ES 1.1, die das Rendern von Sprites erleichtern: GL_OES_draw-texture und GL_OES_point_sprite. Wir werden diese beiden Erweiterungen im Laufe des Kapitels gut nutzen und das Kapitel mit einer lustigen Beispielanwendung abschließen, die ein Federsystem mit Sprites rendert.

Text-Rendering 101: Zeichnen eines FPS-Zählers.

Anstatt das Rendern von Texten mit einer weiteren albernen Spielzeuganwendung zu demonstrieren, sollten wir zur Abwechslung etwas Nützliches tun. Das Überlagern eines Frames-pro-Sekunde-Zählers in einer Ecke des iPhone-Bildschirms bietet eine schnelle und einfache Möglichkeit, die Grafikleistung zu bewerten.

Hinweis: Für anspruchsvolle Laufzeitanalysen der Grafikleistung stellt Apple ein ausgezeichnetes kostenloses Tool mit der Bezeichnung Instruments zur Verfügung, das wir in einem weiteren Kapitel behandeln werden.

Bevor Sie einen Anwendungscode schreiben, müssen Sie ein Bild erzeugen, das Bitmaps für die Zahlen Null bis Neun enthält.

Sie haben wahrscheinlich schon erraten, dass Sie den Begrenzungsrahmen jeder Glyphe abspeichern müssen, um die entsprechenden Texturkoordinaten zu berechnen. Wenn Sie noch ein wenig mehr darüber nachdenken, werden Sie feststellen, dass ein bloßer Begrenzungsrahmen nicht ausreicht. Wenn Sie einen Satz auf liniertem Papier schreiben, erstrecken sich einige Teile der Buchstaben unterhalb der Grundlinie, wie z.B. die Unterlänge der Kleinbuchstaben p. Erschwerend kommt hinzu, dass sich die Begrenzungsfehler der Glyphen im Zielbild überlappen können.

Es stellt sich heraus, dass die Zuordnung eines bestimmten Satzes von Glyphenmetriken zu jedem Zeichen genügend Informationen liefert, um ein ausreichend gutes Textlayout zu erreichen.

Zusammenfassend sind die vier Glyphenmetriken wie folgt:

  • Bearing vector: 2D-Vektor, der den Offset zur Stiftposition beschreibt.
  • Advance Vector: 2D-Vektor, der beschreibt, wie man den Stift nach dem Rendern der aktuellen Glyphe zur nächsten Position bewegt. Die y-Komponente ist bei lateinischen Alphabeten immer Null.
  • Width: Die horizontale Länge der Glyphen.
  • Height: Die vertikale Länge der Glyphen.

Nutzen Sie diese Metriken für den Pseudocode für einen einfachen Text-Layout-Algorithmus.

Copy to Clipboard

Erzeugen einer Glyphentextur mit Python.

Bevor wir irgendeinen Anwendungscode schreiben, müssen wir einen Weg wählen, um eine Glyphentextur und einen Satz von Metriken für jede Glyphe zu erzeugen.

Der Einsatz von Quartz ist vielleicht der offensichtlichste Weg, eine Glyphentextur zu erzeugen. Dies kann zur Laufzeit erfolgen, wenn ihre Anwendung zum ersten Mal gestartet wird. Dies kann ihre Startzeit um einen kleinen Betrag verlangsamen, hat aber den Vorteil, dass die Größe des Anwendungspakets verkleinert wird.

Meine Vorliebe ist es, die Glyphentextur als Build-Schritt zu generieren, vor allem, weil es meinen Anwendungscode vereinfacht. Build-Schritte finden in Xcode statt und nicht in der iPhone-Ausführungsumgebung, was ein viel reichhaltigeres Tool-Set auf den Tisch bringt. Dies ist ein perfekter Anwendungsfall für eine Skriptsprache und Python kommt dabei als Erstes in den Sinn.

Hinweis: Es gibt viele Möglichkeiten, eine Glyphentextur zu erzeugen. In diesem Beitrag geben wir ihnen einen Überblick über unsere Favoriten. Nehmen Sie es nur als Beispiele zur Kenntnis.

Da wir Python in einem Build-Schritt verwenden, müssen wir einige nützliche Python-Module für die Bildbearbeitung und Bilderzeugung finden. Zur Zeit ist Python Imaging Library (PIL) das beliebteste Bildbearbeitungsmodul und bietet hervorragende Unterstützung für die Bearbeitung von PNG-Bildern auf niedrigem Niveau. Allerdings reicht es allein nicht aus, da es keinen direkten Zugriff auf die benötigten Glyphenmetriken bietet. Eine weitere beliebte Bibliothek ist Kairo, die über eine gut gepflegte Python-Bindung mit der Bezeichnung pycairo verfügt. Kairo ist robust, schnell und wird als Rendering-Backend in Firefox, Mono und GTK verwendet. Wir verwenden hier PIL.

Python-Module installieren:

Die Kopie von Python, die unter Mac OS X installiert ist, hat diese Module nicht standardmäßig installiert, so dass Sie sich ein wenig vorbereiten müssen, um sie zu installieren.

Installieren Sie zunächst die Python Imaging Library. Zum Zeitpunkt dieses Beitrags war die neueste und am besten funktionierende Version von PIL 1.1.7. Stellen Sie sicher, dass Sie die Version für Python 2.6 herunterladen, um sicherzustellen, dass das Skript funktioniert. Entpacken Sie den Tarball, öffnen Sie das Terminal und öffnen Sie die CD in das oberste Verzeichnis der Quellcode-Distribution. Führen Sie anschließend den folgenden Befehl aus:

Copy to Clipboard

Als nächstes installieren Sie pycairo. Laden Sie den Quellcode von hier herunter. Extrahieren Sie den Tarball, öffnen Sie das Terminal und cd in das oberste Verzeichnis der Quellcode-Distribution. Führen Sie anschließend die folgenden Befehle aus:

Copy to Clipboard

Sie müssen auch Kairo selbst installieren, da pycairo nur ein dünner Wrapper ist. Bauanleitungen und Downloads finden Sie hier.

Anstatt die Glyphentextur als PNG- oder PVR-Datei zu verpacken, sollten wir die Daten in eine C-Header-Datei serialisieren. Da es sich um eine Einkanal-Textur handelt, ist die Datenmenge relativ gering. Die Header-Datei bietet auch einen bequemen Platz zum Speichern der Glyphenmetriken. Wir werden einfach unser Python-Skript spawnen lassen.

PVRTexTool zur Erzeugung der Header-Datei aus dem Bild. Wir werden weiterhin eine PNG-Datei für Vorschauzwecke erzeugen, aber wir werden sie nicht in das Anwendungspaket aufnehmen.

Copy to Clipboard

Hinweis: Damit dies funktioniert, müssen Sie entweder den Speicherort von PVRTexTool in die Umgebungsvariable PATH ihrer Shell eintragen oder PVRTexTool in einen ihrer PATH-Einträge wie /usr/local/bin kopieren. Wenn Sie das Khronos SDK in ihr aktuelles Verzeichnis extrahiert haben, können Sie die Datei kopieren und mit diesen Befehlen markieren:

Copy to Clipboard

Cairo ist eine recht umfangreiche Bibliothek und sprengt hier den Rahmen. Dennoch wird hier eine kurze Erklärung dazu im folgenden Beispiel gegeben.

  • Erstellen Sie mit Cairo eine 256×32-Bildoberfläche und erstellen Sie dann einen Kontext, der mit der Oberfläche verknüpft ist.
  • Wählen Sie eine TrueType-Schriftdatei aus und wählen Sie dann deren Größe. In diesem Fall wähle ich die Apple Chancery Schriftart aus, die sich in /Library/Fonts befindet.
  • Setzt Cairo`s aktuelle Zeichenfarbe auf weiß.
  • Initialisieren Sie eine Zeichenkette, die wir später an die Header-Datei anhängen. Definieren Sie zunächst einige Strukturen für die Glyphentabelle.
  • Initialisieren Sie die Stiftposition auf (0, 0).
  • Iteration über Glyphen 0 bis 9.
  • Erhalten Sie die Metriken für die Glyphe.
  • Füllen Sie die GlyphPosition-Struktur, die wir in der generierten Header-Datei definieren.
  • Füllen Sie die GlyphMetrics-Struktur, die wir in der generierten Header-Datei definieren.
  • Teilen Sie Cairo mit, dass er die Glyphenformen ausfüllen soll.
  • Verschieben Sie die Stiftposition durch Padding.
  • Speichern Sie die Cairo-Oberfläche in einer PNG-Datei.
  • Laden Sie die PNG Datei in PIL hoch.
  • Verwenden Sie PIL, um nur den Alphakanal zu extrahieren und überschreiben Sie dann die PNG-Datei.
  • Verwenden Sie PVRTexTool, um die Bilddaten in eine C-Header-Datei zu serialisieren. (Zu diesem Zweck wird das PNG nicht mehr benötigt)
  • Fügen Sie die Metrikdaten an die gleiche Header-Datei an, die die Bilddaten definiert.

Wenn Sie möchten, können Sie das Skript zur Nummerngenerierung zu ihrem Xcode-Projekt hinzufügen und es zu einem Build-Schritt machen, ähnlich wie bei der Texturkomprimierung. Der Einfachheit halber haben wir und dafür entschieden, dies nicht in dem Beispielprojekt zu tun, das Sie von der folgenden Website herunterladen können.

Rendern des FPS-Textes.

Nun, da wir die Routinearbeit der Generierung der Glyphentextur hinter uns gelassen haben, können wir zum eigentlichen Rendering-Code übergehen. Ein Frames-pro-Sekunde-Zähler ist viel nützlicher als unsere anderen Spielzeug-Demos, also lassen Sie uns dieses Mal versuchen, den Rendering-Code sehr eigenständig und einfach in jedes Projekt zu integrieren. Wir können dies tun, indem wir eine C++-Klasse erstellen, die vollständig in einer einzigen Header-Datei implementiert ist. Das folgende Beispiel zeigt die Grundzüge dieser Klasse:

Copy to Clipboard
  • Private Methode, die die Vertex- und Texturkoordinaten für eine der Ecken in einem Glyphenrechteck generiert.
  • Glättungsfaktor für den Tiefpassfilter.
  • Exponentiell gewichteter gleitender Mittelwert der Bildrate.
  • Zeitstempel in Nanosekunden des letzten Aufrufs von RenderFps(0)
  • Breite und Höhe des Ansichtsfensters (normalerweise 320×480).
  • Breite und Höhe der Glyphentextur.
  • Die OpenGL-ID des Glyphen-Textur-Objekts.
  • Die OpenGL-ID des Vertex-Pufferobjekts, das für die Darstellung der Glyphen verwendet wird.

Stabilisierung des Zählers mit einem Tiefpassfilter.

Um zu verhindern, dass der FPS-Zähler stark schwankt, verwenden wir einen Tiefpassfilter, ähnlich dem, den wir für den Beschleunigungssendor verwendet haben. Die Anwendung kann eine Konstante berechnen, den sogenannten Glättungsfaktor, der immer zwischen Null und Eins liegt. Hier ist eine Möglichkeit, dies zu tun:

Copy to Clipboard

In der vorherigen Auflistung helfen cutoffFrequency und sampleRate bei der Definition des Begriffs „Rauschen“ im Signal. Für unsere Zwecke ist die Berechnung eines solchen Glättungsfaktors jedoch etwas pedantisch. Pragmatisch gesehen ist es völlig in Ordnung, durch Experimente eine vernünftige Zahl zu finden. Ich finde, dass ein Wert von 0.1 für einen Bildratenzähler gut funktioniert. Ein höherer Glättungsfaktor würde zu einem spastischen Zähler führen.

Ausarbeiten der FpsRenderer-Klasse.

Fahren wir fort und implementieren den Konstruktor der FpsRenderer-Klasse. Es ist verantwortlich für das Laden der Glyphentextur und das Erstellen der leeren VBO für das Rendern von bis zu drei Ziffern.

Copy to Clipboard

Die Klasse FpsRenderer hat nur eine öffentliche Methode. Diese Methode ist für die Aktualisierung des gleitenden Mittelwerts und die Darstellung der Ziffern zuständig. Beachten Sie, dass das Aktualisieren der VBO ein ziemlicher Aufwand ist. Wir werden im nächsten Abschnitt eine viel einfachere Art der Darstellung texturierter Rechtecke demonstrieren.

Copy to Clipboard

Als nächstes müssen wir die private WriteGlyphVertex-Methode implementieren, die die VBO-Daten für eine bestimmte Ecke eines Glyphenrechtecks erzeugt. Er nimmt einen Pointer-to-Float für die Eingabe, schiebt ihn nach dem Schreiben jedes Wertes vor und gibt ihn dann an den Caller zurück.

Copy to Clipboard

Vielen Dank für ihren Besuch.