Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Schwarzefee

Treue Seele

  • »Schwarzefee« ist der Autor dieses Themas

Beiträge: 155

Wohnort: Ost-Sachsen

Beruf: Programmierer

  • Private Nachricht senden

1

14.09.2014, 20:01

[OpenGL] FreeType 2 Text Rendering

Hi,

ich möchte gern mit OpenGL und FreeType 2 Text rendern (ohne andere Bibliotheken).
Bevor ich anfange, Code zu schreiben, möchte ich fragen, was die beste Herangehensweise ist.

Beim googlen habe ich verschiedene Ansätze gefunden:
- Man hat für jede Glyphe eine eigene Texture
- Man benutzt einen Font-Texture-Atlas
- Man erstellt für jeden Text eine eigene Texture.

Was ist aus eurer Profi-Sicht die beste, ressourcenschohnenste Herangehensweise?

Ich denke, für jede Glyphe ne eigene Texture zu erstellen wird wohl (zumindest bei langen Texten) eine sehr lange Renderzeit benötigen, da ja für jeden Buchstaben eine neue Texture gebunden werden muss + ein DrawCall.
Bleibt noch der Font-Atlas und die Text-Texture.
Gibt es da einen großen Unterschied, oder ist egal weche Technik man verwendet?

Oder gibt es sogar Andere, bessere Herangehensweisen?


Gruß

Evrey

Treue Seele

Beiträge: 245

Beruf: Weltherrscher

  • Private Nachricht senden

2

14.09.2014, 21:38

Das Speicherschonendste ist es, den Font als Textur zu nutzen. Deine Glyphen sind dann quasi "Tiles" mit dem Font als "Tileset". Du kannst solche Glyphen zur schnelleren Abrufbarkeit zwischenspeichern (z.B. mit std::map) und größeren Text als Vertex-Array umbasteln. Zumindest ist das der Weg, den ich gerade probiere. Schau doch einfach mal, wie SFML oder so das lösen. Sind ja Open Source.

C-/C++-Quelltext

1
2
3
4
int main(int _argc, char** _argv) noexcept {
  asm volatile("lock cmpxchg8b %eax");
  return 0;
} // ::main
(Dieses kleine Biest vermochte einst x86-Prozessoren lahm zu legen.)

=> Und er blogt unter Hackish.Codes D:

3

15.09.2014, 08:37

Ich mache es so:
http://zfx.info/viewtopic.php?f=11&t=3557

FreetypeGL besteht nur aus einer handvoll Dateien, die du einfach deinem Projekt hinzufügen kannst. Wenn du es so machst wie ich, und das Rendern selber schreibst, sind es sogar nur noch halb so viele.
An FTGL mag ich, dass es meiner Meinung nach sehr schöne und sehr effiziente Glyphen-Atlanten erzeugt. Wenn du darauf verzichten willst, wirst du eine Menge Arbeit darein investieren müssen um ähnlich gute Ergebnisse zu bekommen.

Von der Geschwindigkeit würde ich meine Lösung auch recht weit oben einstufen. Ich erstelle halt pro Text den ich anzeige einen Vertex-Buffer, dadurch dass die einzelnen Vierecke für die Glyphen nur exakt so groß sind wie sie sein müssen, wird auch die Anzahl der gerenderten Pixel minimiert. Außerdem bietet es sich natürlich an, alle Texte zentral zu verwalten, denn so muss man nur einmal den Shader setzen und auch nur eine einzige Textur (solange man nicht sehr viele verschiedene Schriftarten in sehr vielen verschiedenen Größen benutzt kommt man nämlich locker mit einem einzigen Atlas fürs gesamte Programm aus). Dazu noch so Mini-Optimierungen wie "leere Texte überhaupt nicht zeichnen" und "Vertexbuffer nur aktualisieren, wenn sich der Text wirklich verändert hat" und man sollte schon ziemlich gut dastehen. Ich habe es jetzt nicht gemessen, aber mir fällt auf anhieb nicht ein, wie man es noch sehr viel effizienter machen könnte.

Der gepostete Code ist halt noch nicht wirklich etabliert, es fehlen noch ein paar Kleinigkeiten wie Text-Farbe ändern, die ich irgendwann noch einbauen will. Aber er ist ja auch nur zum lernen gedacht. Oh und ich benutze noch keine Indexbuffer, das will ich noch irgendwann einbauen - halte ich aber ehrlich gesagt für nicht sooo wichtig, die Dreiecke die ich für Text brauche sind um Größenordnungen weniger als die für den Rest der Szene.

Was ich nicht einschätzen kann, ist wieso du keine externe Bibliothek benutzen willst. Wenn du alles selber machen willst, ok, aber wenn du nur keine Lust auf weitere Abhängigkeiten oder Einarbeitungszeit hast würde ich FreeTypeGl trotzdem benutzen, da beides minimal ist (siehe verlinkten Text).
Lieber dumm fragen, als dumm bleiben!

Schwarzefee

Treue Seele

  • »Schwarzefee« ist der Autor dieses Themas

Beiträge: 155

Wohnort: Ost-Sachsen

Beruf: Programmierer

  • Private Nachricht senden

4

15.09.2014, 10:45

Hi,

danke für die Antworten.

Wenn ich das richtig sehe, nutzt ihr beide also einen Font Atlas.



Gruß

Schrompf

Alter Hase

Beiträge: 1 470

Wohnort: Dresden

Beruf: Softwareentwickler

  • Private Nachricht senden

5

15.09.2014, 11:23

Jupp. Allgemeiner formuliert: ein Textur-Atlas ist immer zu empfehlen, wenn man viele kleine Grafiken rendern will. Mein Font-Renderer basiert z.b. auf meinem Sprite Renderer, eben gerade weil die Aufgabe quasi die selbe ist. Eine Textur pro Glyph wäre tödlich, könnte mit DX10 und Texture Arrays aber wieder funktionieren, auch wenn ich vermute, dass Du bald an die Grenzen der Array-Größe stößt, wenn Du ein paar außerkontinentale Zeichensätze unterstützen willst.

Die einzige andere Möglichkeit wäre, den ganzen Text auf eine Textur auszurendern und diese Textur dann im ganzen auf den Bildschirm zu pinseln. Es gibt vereinzelt Leute, die das machen, und ich kann mir vorstellen, dass das SubPixel-genaue Rendern und AntiAliasing damit etwas einfacher wird - freetype macht das dann für Dich. Aber die Mühen, alle Texte auf dem Bildschirm und deren Ressourcen zu tracken, würde ich mir nicht machen. Mit einem Texturatlas und einem dynamischen VertexBuffer kommst Du stressfrei an die Performance ran.
Häuptling von Dreamworlds. Baut aktuell an nichts konkretem, weil das Vollzeitangestelltenverhältnis ihn fest im Griff hat. Baut daneben nur noch sehr selten an der Open Asset Import Library mit.

6

15.09.2014, 13:08

Da hätte ich dann noch eine Frage: Lohnen sich dynamische Buffer wirklich?
Ich bin mir gerade nicht ganz sicher, was intern passiert. Aber sagen wir man hat ein Buffer der in jedem Frame geändert wird, dann wird er immer neu zu Grafikkarte geschickt und dort nicht gespeichert, was die Speicherverwaltung auf der Grafikkarte einfacher macht und die ganze Sache deshalb etwas schneller.
Aber was passiert, wenn man ihn nicht in jedem Frame aktualisiert? Bleiben die Daten dann im RAM und werden tortzdem jedes mal übertragen? Wie auch immer, es kann eigentlich nicht genau so schnell wie ein statischer Buffer sein, sonst würde man die Unterscheidung ja nicht machen und immer dynamische Buffer verwenden.
Basierend auf der Überlegung, dass sich dynamische Buffer wirklich nur lohnen, wenn man sie ständig ändert benutze ich statische Buffer. Klar, Textanzeigen ändern sich ständig, aber nicht mit jedem Frame. Ich habe eher so anzeigen für Ressourcen, Hitpoints, vielleicht ein Tooltip. Da ist die Chance immer hoch, dass sie einige Sekunden gleich bleiben. Text ändert sich schon alleine deshalb meist nicht per Frame, da man ihn dann ja überhaupt nicht lesen kann.
Lieber dumm fragen, als dumm bleiben!

Schrompf

Alter Hase

Beiträge: 1 470

Wohnort: Dresden

Beruf: Softwareentwickler

  • Private Nachricht senden

7

15.09.2014, 14:18

Gute Frage. Soweit ich weiß, funktionieren dynamische Buffer so:

- Ich will neue Daten schreiben
- Treiber macht für mich einen *neuen* Buffer auf und schreibt da die Daten rein
- ab sofort bekomme ich unter dem alten Handle den neuen Buffer
- aber die GPU rendert mit dem alten Buffer weiter, bis die Renderqueue soweit abgearbeitet ist

Und das würde in der Tat bedeuten, dass man stressfrei alle Ressourcen als dynamisch flaggen kann.

Und was den Bildschirmtext angeht: ich habe nie erprobt, ob das statische Zusammenstellen aller Vertizes für einen Text schneller wäre. Das dynamische Zusammenstellen ist einfach und schnell genug - ich kriege ein paar hunderttausend Rechtecke mit >60fps auf den Schirm, das reicht mir.
Häuptling von Dreamworlds. Baut aktuell an nichts konkretem, weil das Vollzeitangestelltenverhältnis ihn fest im Griff hat. Baut daneben nur noch sehr selten an der Open Asset Import Library mit.

SlinDev

Treue Seele

Beiträge: 142

Wohnort: Lübeck

Beruf: Programmierer

  • Private Nachricht senden

8

15.09.2014, 15:24

Meint ihr mit statischen und dynamischen Buffern die entsprechenden Flags in glBufferData? Mir wurde erzählt, dass ich DYNAMIC nur benutzen sollte wenn sich der Buffer innerhalb eines Frames mehr als einmal ändert, was ja fast nie passiert und ansonsten ist STATIC schneller. Wobei das dabei gemeinte "schneller" selten relevant sein sollte.
In Rayne rendern wir auch einfach die einzelnen Zeichen in große Texturatlasse und bauen uns Vertexbuffer für die Texte zusammen, die wir dann neu bauen sobald sich etwas am Text ändert.

Schrompf

Alter Hase

Beiträge: 1 470

Wohnort: Dresden

Beruf: Softwareentwickler

  • Private Nachricht senden

9

15.09.2014, 15:48

Bei OpenGL fehlt mir die Erfahrung, da habe ich bisher glNamedBufferDataEXT() mit GL_DYNAMIC_DRAW oder GL_STATIC_DRAW zu diesem Zweck benutzt. Ob das ne Auswirkung hat, weiß ich nicht - OpenGL ist da zwangsweise so überladen und schwammig, dass da alles Mögliche passieren kann.

Ich beziehe mich bei "dynamischen" Buffern auf DirectX9 mit Flag D3DUSAGE_DYNAMIC. DX11 hat diesbezüglich noch konkretere Mittel: D3D11_CPU_ACCESS_WRITE und D3D11_USAGE_DYNAMIC versus D3D11_USAGE_IMMUTABLE.
Häuptling von Dreamworlds. Baut aktuell an nichts konkretem, weil das Vollzeitangestelltenverhältnis ihn fest im Griff hat. Baut daneben nur noch sehr selten an der Open Asset Import Library mit.

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

10

15.09.2014, 15:54

Also ich habe experimentell da immer das mal ausprobiert. Bei mir war hatte sich "STATIC" bei meinen Situationen überall als am schnellsten herausgestellt, was mich selbst ein wenig gewundert hat. Wenn dann hätte ich damit gerechnet, das es keinen Unterschied macht. Aber das kann sich aber natürlich auf einer anderen Grafikkarte anders sein.

Einen Texturatlas finde ich für Text nicht ganz optimal, weil die Zeichen sich in der Größe ja massiv unterscheiden. Das führt zu einer Menge verschwendeten Platz.

Ich verwende bei mir große Texturen und direkt Freetype ohne FreetypeGL. Für das rendern der Texturen verwende ich einfach große Texturen. Ein Feature das ich eingebau hatte, war das dynamische Nachladen von Zeichen. Beim Texturpacking habe ich mich für ähnlichen einfachen Algorithmus wie den entschieden: https://devel.nuclex.org/framework/wiki/RectanglePacking("Cygon")
Nachträglich gesehen weiß ich aber nicht ob ich mit dieser Technik absolut glücklich bin. Es gibt andere Ansätze wie zum Beispiel vektorisiertes Zeichnen mit OpenGL oder etwas, dass sich "Distance Field" nennt. Vorteil der Techniken ist vor allen Dingen, dass man nicht für jede Pixel und eigentlich sogar Subpixelgröße eigene Zeichen in einer Textur ablegen muss und allgemein mehr Flexibilität bei akzeptabler Qualität.

Gutes Rendern von Text ist auf jeden Fall keine einfache Aufgabe, wenn man gewisse Ansprüche hat. Schon alleine die Überraschungen die Unicode noch so bereit hält sind eine mehr als komplexe Aufgabe für sich. Das fängt mit verschieden breite Leerzeichen oder dem bedingten Trennzeichen an und geht bis zum Beispiel BiDi-Klassen oder Combining-Characters... Da es fast unmöglich ist, das alles umzusetzen muss man wissen wann es genug ist.

Werbeanzeige