openGL font rendering beschleunigen
-
Hallo,
ich hab einen kleinen Fontrenderer, der praktisch aus dem berühmten Besipielcode der "nehe" Tutorials hervorgegangen ist. Der ist nur ein bißchen "OO-fiziert" worden.
Er erzeugt aus jeweils einem FreeType Glyphen eine Textur, und klebt später zum Rendern eines Schriftzugs für jeden Buchstaben die entsprechende Textur an die richtige Stelle.
Das funktioniert gut, ist aber relativ langsam. Durch Profiling habe ich herausgefunden, dass glBindTexture die meiste Zeit frisst.
Daher müssten stattdessen alle Buchstaben auf eine große Textur geklebt werden, und beim Rendern einfach der entsprechende Ausschnitt der Textur, der den Buchstaben enthält, benutzt werden. So spart man sich das separate glBindTexture für jeden einzelnen Character.
Der Code zum erzeugen der einzelnen Texturen sieht so aus:
Zu dem Zeitpunkt steht die aus Freetype gewonnene Textur in "expanded data"glBindTexture(GL_TEXTURE_2D, tex_base[(int)ch]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expanded_data ); glNewList(list_base+ch,GL_COMPILE); glBindTexture(GL_TEXTURE_2D, tex_base[(int)ch]); glPushMatrix(); glTranslatef(bitmap_glyph->left,0,0); glTranslatef(0,bitmap_glyph->top-bitmap.rows,0); float x = bitmap.width / static_cast<float>(width); float y = bitmap.rows / static_cast<float>(height); glBegin(GL_QUADS); glTexCoord2d(0,y); glVertex2f(0,0); glTexCoord2d(0,0); glVertex2f(0,bitmap.rows); glTexCoord2d(x,0); glVertex2f(bitmap.width,bitmap.rows); glTexCoord2d(x,y); glVertex2f(bitmap.width,0); glEnd(); glPopMatrix(); glTranslatef(face->glyph->advance.x >> 6 ,0,0); glEndList();
und gerendert wird so:
glPushAttrib(GL_LIST_BIT | GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TRANSFORM_BIT); glListBase(list_base); float modelview_matrix[16]; glGetFloatv(GL_MODELVIEW_MATRIX, modelview_matrix); glPushMatrix(); glLoadIdentity(); glTranslatef(x,y,0); glMultMatrixf(modelview_matrix); glCallLists(text.size(), GL_UNSIGNED_BYTE, text.c_str()); glPopMatrix(); glPopAttrib();
bei jedem Aufruf von glCallLists führt er strlen()-viele glBindTextures durch, und die will ich loswerden.
Daher meine Frage:
wie kopiere ich mehrere Texturen (Glyphen) in eine Textur? glSubImage2d?wie erreiche ich beim rendern, dass nicht die gesamte Textur, sondern nur der Bereich mit dem richtigen Buchstaben verwendet wird?
Gruß,
PhilippNACHTRAG:
Da ausschließlich eine Fontgröße und ausschließlich monospaced fonts verwendet werden, können width und height als konstant angesehen werden.
-
DisplayListen sind deprecated seit 10 Jahren. Da wirst nicht viel an Performance rausholen können, da moderne Treiber da eigentlich nichts mehr optimieren.
Am besten du verwendest mal wenigstens OpenGL2 oder gar 3/4.
Pack alle Glyphen auf eine Textur und erzeuge daraus deine Sprites.Ich benutze dafür BMFont von AngelCode und lade dann nur die Textur bzw das Control-File das mir die Glpyhen beschreibt.
GL_QUADS ist genauso deprecated in OpenGL3+.
Ich denke moderne Treiber wandeln das eh nur in 2 Polygone um.Tutorial für modernes OpenGL:
http://arcsynthesis.org/gltut/wie kopiere ich mehrere Texturen (Glyphen) in eine Textur? glSubImage2d?
wie erreiche ich beim rendern, dass nicht die gesamte Textur, sondern nur der Bereich mit dem richtigen Buchstaben verwendet wird?
Das würde ich nicht tun. Seperiere dein FontRendering. Lad die Textur und render alles an Fonts, wenn so möglich.
Texturwechsel sind mit das teuerste nach Shaderwechsel.
-
PhilippM schrieb:
Das funktioniert gut, ist aber relativ langsam.
wie langsam?
wie kopiere ich mehrere Texturen (Glyphen) in eine Textur? glSubImage2d?
ich glaube glCopyTexSubImage2D oder so.
wie erreiche ich beim rendern, dass nicht die gesamte Textur, sondern nur der Bereich mit dem richtigen Buchstaben verwendet wird?
glTexCoord2d(X+0,Y+y); glVertex2f(0,0); glTexCoord2d(X+0,Y+0); glVertex2f(0,bitmap.rows); glTexCoord2d(X+x,Y+0); glVertex2f(bitmap.width,bitmap.rows); glTexCoord2d(X+x,Y+y); glVertex2f(bitmap.width,0);
du brauchst das nicht in displaylisten zu stecken, wenn du die font textur einmal bindest und dann einfach alle texte so ausgibst (also glvertex2f...), bist du schneller als wenn du pro liste matritzen pusht, umstellst und popst.
und ich glaube wenn du es erstmal in einer textur hast, sollte die sache schnell genug sein und du musst nicht dein ganzes system auf eine andere api(version) umstellen wegen fonts.
-
Ich habe es jetzt so gemacht, dass ich zunächst die gesamte Font auf eine große Textur rendere, und dann wie von rapso vorgeschlagen, die Textur nur einmal binde und den Quatsch mit der Matrixmultiplikation in Displaylisten weglasse.
Funktioniert sehr anständig und ist "schnell genug" für meine derzeitigen Zwecke.
Nichtsdestoweniger würde mich interessieren, was man denn heute besser machen könnte, und habe mich daher mal bei Amazon durch die Bücher zum Thema openGL3/4 geklickt.
Leider gibt es bei praktisch jedem Buch einen 1-Sterne Kommentar von irgendjemandem, der sich darüber beschwert, im Buch würden veraltete Techniken gelehrt.Das offizielle Khronos-Buch soll auch erst nächstes Jahr im Mai rauskommen.
Gibt's sonst einen heißen Tipp, welches Buch (bitte nur toter Baum, keine online-Tutorials oder Ebooks) zum erlernen moderner openGL-Techniken geeignet ist?
Philipp
-
http://arcsynthesis.org/gltut/
Auch wenn du kein Onlinetutorial willst, aber das ist derzeit die beste Quelle im Netz dafür.Ansonsten gibts noch die OpenGL Superbible, aber leider nur auf englisch und die ist leider diesmal nicht sehr gut geworden. Trotzdem ein Anfang. Es fehlen im Buch die Basics, es wird was vorgefertigtes verwendet.
Leider.
-
Habe mal in das verlinkte Tutorial reingeschaut.
Verstehe ich das richtig das aus
glBegin(GL_QUADS); glVertex2f(0,0); //... usw. glEnd();
glDrawArrays(GL_QUADS)
auf einem array von Punkten wird?
Dann würde mich noch interessieren, was aus folgendem Code wird:
glNewList(gllist, GL_COMPILE); glLineWidth(1.6); glBegin(GL_LINE_LOOP); glVertex2i(0, -4); glVertex2i(4, 0); glVertex2i(0, +4); glVertex2i(-4, 0); glEnd(); glEndList();
Macht man daraus ein Vertexshader-Programm?
Philipp
-
Richtig, du machst ein Array mit Punkten und sagst OpenGL wie es gezeichnet wird.
Zum zweiten: Es gibt keine Display-Listen mehr. Die sind schon seit 10 Jahren deprecated.
-
Okay, aber es muss doch ein Equivalent von "Funktionsaufruf auf der Grafikkarte" geben. So wie man eben bei einer Displayliste quasi eine Funktion definiert, die man später mit glcalllist() aufruft.
In meinem konkreten Fall gibt es eine Menge Punkte, die in Form dieser Raute gezeichnet werden sollen. Bisher habe ich eine Displayliste, die eine Raute zeichnet. Wenn ich also an der Position stehe, wo ich die Raute hinhaben möchte, rufe ich die liste auf.
Wie sieht das equivalent dazu in modernem openGL auf? Hartkodiert jedesmal glDrawArrays aufrufen? Oder kann man sich einen Shader programmieren, der vertices als Rauten zeichnet?
Philipp
-
Es gibt keine Rauten als Zeichenfunktion. Und eben auch keine vorgefertigten Objekte. Du gibst deine Punkte in OpenGL ins Array und sagst OpenGL wie er sie verbinden soll. Eben als GL_POLYGON oder GL_LINE etc.
Daraus entstehen dann deine Objekte. Transformation, Texturing und Farben etc wird alles im Shader gemacht.
Wenn du ein "Objekt" an mehreren Stellen haben willst, dann benutze instancing. Wobei das bei nur ein paar Punkten nicht nötig ist. Hängt alles davon ab was man macht.
Les das Tutorial, ich bin schlecht im erklären :D.
Vllt kann ja dot hier nochmal ausholen.
-
PhilippM schrieb:
Oder kann man sich einen Shader programmieren, der vertices als Rauten zeichnet?
Jap, der GeometryShader ist dein bester Freund was das angeht
Ich hab das im Moment so gelöst, dass ich einen (im Prinzip statischen) Buffer hab in dem die Abmessungen und Texturkoordinaten für jede Glyphe stehen.
In jedem Frame wird dann eine Pointlist, deren Vertices Position und Index der zu zeichnenden Glyphen enthalten, aus einem dynamischen Buffer gerendert und der GeometryShader erzeugt dann an den jeweiligen Koordinaten entsprechende Quads.