Ich stand neulich mal wieder vor dem altbekannten Problem, Text rendern zu müssen. Da OpenGL ja nur eine reine Grafikbibliothek ist, bringt es von Haus aus keine Möglichkeiten dazu mit, also selbst wenn man die niedrigsten Ansprüche hat, die man sich vorstellen kann, kommt man nicht darum herum, sich eine eigene Lösung zu suchen (ich benutze übrigens OpenGl 4.3). Das habe ich also gemacht und habe eine Lösung gefunden, die mir recht gut gefallen hat, weswegen ich sie mit euch teilen möchte.
Letztendlich habe ich doch einiges selber programmiert, aber das ganze ist an einem freien Tag zu schaffen und auch nicht wirklich kompliziert.
Recherchiert man im Internet findet man schnell eine reihe an Bibliotheken, die Texte mit OpenGL rendern können. Ich wollte gerne etwas einfaches, was ich relativ schnell umsetzen kann und das nicht zu viele Abhängigkeiten hat. Nach einigem hin und her habe ich mich dann schließlich für FreeTypeGl entschieden:
Schöne Webseite: https://code.google.com/p/freetype-gl/
Aktuelle Webseite: https://github.com/rougier/freetype-gl
Wie man am Namen schon erkennt, setzt dieses Projekt auf die populäre FreeType Bibliothek auf: http://www.freetype.org/
Das ist Grundlage zu fast allem Font-Rendering und man braucht schon einen guten Grund, sich dagegen zu entscheiden.
Jetzt hat man natürlich schon zwei neue Abhängigkeiten, aber FreeType lässt sich sehr problemlos kompilieren und hat selber keine externen Abhängigkeiten und FreeTypeGl besteht nur aus einer handvoll Dateien, die man seinem Projekt hinzufügen kann. Der Aufwand hält sich also in Grenzen.
FreetypeGl kommt auch mit einer Reihe an Demoanwendungen und kann auch direkt rendern, da ich es aber in meinen Renderer integrieren wollte, Benutze ich nur die Funktionen um Bitmapfonts zu erzeugen und auf die Parameter zuzugreifen. FtGl kann verschiedene Schriftarten mit verschiedenen Größen ziemlich effizient in eine Textur packen, wie man im Beispiel sieht.
Code: Alles auswählen
m_Atlas = ftgl::texture_atlas_new(AtlasSize.x, AtlasSize.y, 1);
const auto CommonCharacters = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789äÄüÜöÖß?.,-#/\!\"%&()=' :;<>{}[]@€+-*";
auto NewFont = ftgl::texture_font_new_from_file(m_Atlas, Size, Filename.c_str());
ftgl::texture_font_load_glyphs(NewFont, CommonCharacters);
Code: Alles auswählen
float LineHeight = m_Font->height;
vec2 CursorPosition(0, LineHeight);
for(auto i = 0u; i < wtext.length(); ++i)
{
auto c = wtext[i];
if('\n' == c)
{
CursorPosition.x = 0;
CursorPosition.y += LineHeight;
continue;
}
auto glyph = ftgl::texture_font_get_glyph(m_Font, c);
//Kerning
if(i > 0)
CursorPosition.x += ftgl::texture_glyph_get_kerning(glyph, wtext[i - 1]);
//TP=TopLeft, BR=BottomRight
//Position
auto PosTL = CursorPosition + vec2(glyph->offset_x, -glyph->offset_y);
auto PosBR = PosTL + vec2(glyph->width, glyph->height);
//TexCoord
auto TexTL = vec2(glyph->s0, glyph->t0);
auto TexBR = vec2(glyph->s1, glyph->t1);
//TODO: Vertexe erstellen:
}
Bei mir sieht es derzeit so aus, dass ich beim Starten alle benötigten ttf-Dateien lade und in einen großen Atlas packe. Ich habe eine eigene Text-Objekt Klasse, die einen Vertexbuffer erstellt und verwaltet. Gerendert wird alles über den Text-Manager der dann nur einmal den Shader und die Textur setzen muss und alle Vertexbuffer nacheinander rendern kann - es sollte also ziemlich performant sein, zumindest so schnell, das man bei normaler Benutzung keine Gedanken daran verschwenden muss.
Am Schluss noch der gesamt Code, so wie ich ihn derzeit verwende. Freigegeben als public domain, weil wirklich keine clevere Idee darin steckt. Er verwendet eine eigene Shader und Exception Klasse, die sich aber trivial ersetzen lassen sollte. glm ist auch drin, was ich bei OpenGL sehr empfehlen kann, ansonsten lässt sich die vec2-Klasse aber auch trivial ersetzen. Die Shader sind mit drin, man will vermutlich später noch ne Funktion einbauen um die Textfarbe zu ändern, etwa über eine Uniform.
So, ich hoffe das hilft eines Tages irgendjemanden der mal Texte rendern möchte :D