【问题标题】:How to do OpenGL live text-rendering for a GUI?如何为 GUI 进行 OpenGL 实时文本渲染?
【发布时间】:2011-01-05 12:14:35
【问题描述】:

我正在实现基于 OpenGL 构建的 GUI。我遇到了每个 GUI 都会遇到的问题——文本渲染。我知道在 OpenGL 中呈现文本的几种方法,但是,我想知道哪种方法最适合 GUI。

通常在 GUI 中,我们有两种类型的文本——静态的和实时的。静态很容易——我们可以将一个 TTF 渲染到一个纹理上然后忘记它。更让我困扰的是“实时”文本——想象一下控制台,或者多人游戏中的实时聊天。

我想到了几个选项:

  • 没有特殊情况 - 每次文本更改时渲染和加载纹理,请记住仅在实际出现新文本时重新渲染它,并尝试将较大的文本分成小部分(如每个聊天行)。但是,这仍然会让我们陷入困境,例如分数线一直在变化,或者介绍性文字呈现“每个字符”(某些科幻游戏中的打字机风格)
  • quad-per character -- 这似乎也是一个流行的解决方案,您使用 ASCII 表准备纹理并渲染纹理四边形字符。但是,我严重怀疑这种解决方案的效率。我们也欢迎提供如何加快速度的提示。
  • 混合解决方案 -- 但是我不知道如何干净地实现它

因此问题是——如何在 OpenGL 中有效地渲染文本?

如果这有帮助,我正在使用 STL/Boost-heavy C++ 进行编码,并针对 GForce 6 及更高版本的显卡。

【问题讨论】:

  • 不要构建带有所有可能字形的大纹理。虽然对于西方字体来说它简单且“明智”,但如果您尝试将您的应用程序国际化,您很快就会意识到这种方法对于东方字体是不可持续的。唯一可扩展的解决方案是在需要时生成动态文本。
  • 如果您尝试自己渲染单个字形,您将忽略操作系统所知道的关于字距调整、重音符号和组合字符、从右到左的脚本以及其他一些类型布局的怪癖我现在忘记了。使用带有标点符号的 0-9a-zA-Z 文本表可能可以涵盖一些非常常见的情况,但大多数情况下,当您向字体渲染器询问整行或整段时,您会得到更漂亮的类型。

标签: performance opengl text-rendering


【解决方案1】:

EDIT2:Sean Barrett 刚刚发布 Bitmap fonts for C/C++ 3D programmers。

编辑:另一个值得一看的代码宝石是 Font Stash,它利用了 Sean Barrett 的 stb_truetype.h。


您可以创建一个纹理,在其中渲染字体的所有字符。然后,您只需使用正交投影和适当的纹理坐标绘制带纹理的四边形:当您的文本使用不包含太多符号的语言时,这种方法有效:例如英语。字体纹理在应用程序开始时创建一次,然后渲染四边形非常快。

这就是我正在使用的,我相信这是在 OpenGL 中呈现文本的最快方式。一开始,我使用Angelcode's Bitmap Font Generator工具,然后我直接集成FreeType,并在应用程序启动时构建了一个包含所有字形的大纹理。至于提高四边形渲染速度的技巧,我只是将 VBO 用于我的应用程序中的其余几何体。

我很惊讶您对四边形渲染有疑问,而您似乎并不担心在 cpu 上生成纹理,然后上传它,然后绑定它以最终渲染一个矩形的性能,对于每个框架。更改 OpenGL 状态是瓶颈,而不是用于文本的 500 - 1000 个四边形。

此外,请查看 FTGL 库之类的库,该库将整个字体转换为多边形(几何字体)。

【讨论】:

  • 嗯,这就是我在问题 O.o. 中写的。我问的是相对性能问题。
  • 抱歉在写完之前点击了提交
  • +1:我只是想知道“标准解决方案”的性能。更改纹理只会“更改”,而文本的四边形将每帧完成,这就是我想知道的原因。
  • 谢谢!带有 stb_truetype 的 Font-Stash(akrinke 在 github 上的 fork)正是我们的游戏所需要的。它也比 FTGL 快得多。
【解决方案2】:

尝试阅读:http://dmedia.dprogramming.com/?n=Tutorials.TextRendering1。

所描述的方法是使用图形 API 进行文本渲染的典型方法。每个四边形一个字符,以及单个纹理中文本的所有图像数据。如果您可以将整个字符集放入一个纹理中(实际上,取决于纹理的大小,您可能可以容纳多个),那么渲染速度非常快。

关键是纹理绑定是你需要最小化的操作。如果您可以使用单个纹理渲染所有文本,那么您需要在屏幕上放置多少文本都无关紧要。即使您必须多次切换纹理(不同的字体、不同的字体属性 [粗体、下划线等]),性能仍然应该不错。文本性能对于 HUD 可能更为重要,但在 GUI 中则不那么重要。

【讨论】:

  • +1:这确实是一个非常有趣的链接!如果我可以将所有字体放入单个纹理中,则可以取消纹理切换。目前允许的纹理尺寸都很大,所以我认为这是可以实现的。
【解决方案3】:

您可以将文本渲染为三角面,并避免使用纹理。 这样可以在文本缩放时避免边缘不清晰。 我创作了一种低多边形 ASCII 字体,它是开源的,可在 my github 获得。

另一种在文本上具有锐利边缘的方法是SDF font rendering,但这会受到一些伪影的影响。

【讨论】:

    【解决方案4】:
    【解决方案5】:

    对于大多数情况,显示列表(仅在文本更改时更新)处理的每个四字符都足够了。

    我使用 XLoadQueryFont、glGenLists、glXUseXFont、glCallLists 将它与 X11 字体一起使用 您有一组描述每个字符的显示列表。

    glCallLists 函数接受一个 char * 参数作为您的文本,它可以嵌入到显示列表中。

    所以你最终只需要一次调用来显示块文本。

    G. Pakosz 建议的字体纹理与此类似,但您可以计算自己的“按字符”显示列表。

    您的第一个选项会很慢,因为它需要在每次文本更改时基于处理器进行渲染。

    【讨论】:

    • 我知道这是“教科书”方法。
    【解决方案6】:

    只能使用这两个函数:

    void renderstring2d(char string[], float r, float g, float b, float x, float y)
    {
        glColor3f(r, g, b);
    
        glRasterPos2f(x, y);
        for(unsigned int i = 0; i &lt strlen(string); i++)
            glutBitmapCharacter(GLUT_BITMAP_9_BY_15, string[i]);
    }
    
    void renderstring3d(char string[], float r, float g, float b, float x, float y, float z)
    {
        glDisable(GL_LIGHTING);
        glColor3f(r, g, b);
    
        glRasterPos3f(x, y, z);
        for(unsigned int i = 0; i &lt strlen(string); i++)
            glutBitmapCharacter(GLUT_BITMAP_9_BY_15, string[i]);
    }

    那么当您渲染 2D 文本时,不要忘记重置模型视图和投影矩阵。

    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    
    // Render text and quads
    
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();

    您不需要使用这些函数渲染到四边形,因为这些函数直接渲染到帧缓冲区。

    如果这对您不起作用,请检查一下。 http://www.opengl.org/resources/faq/technical/fonts.htm

    它谈到了使用 glBitmap、glDrawPixels 和 alpha 混合渲染文本。

    【讨论】:

    • 不,谢谢,我不想要这样的减速 ;)。当然它会直接渲染到帧缓冲区,但请花点时间考虑一下您在卡和本地内存之间传输每帧的数据量。更不用说这个解决方案不支持自定义字体。
    • 好吧,我已经在 3D 游戏引擎中为我自己的 gui 使用了这些功能,当我打开或关闭 gui 时,帧速率没有明显变化。此外,它传递的位图对于每个字符只有 135 像素,这比创建纹理并在每次要渲染字符时将整个纹理传递到显卡的时间要少得多。
    • @Ned 如果我使用的是 SDL 而不是 GLUT,我该怎么做?
    • 我自己从未使用过 SDL,但我可以指出一个非常相似的问题。
    【解决方案7】:

    这很棘手,特别是如果您想使用亚像素字体渲染技术。看看ClanLib SDK。它使用批处理渲染器在单个 OpenGL 调用中渲染整个屏幕的文本。由于它有一个基于 zlib 的许可证,如果你不想使用 SDK 本身,你可以提取它的代码

    【讨论】:

      猜你喜欢
      • 2012-05-02
      • 1970-01-01
      • 1970-01-01
      • 2013-11-16
      • 2015-07-08
      • 2014-04-15
      • 1970-01-01
      • 2018-09-05
      • 1970-01-01
      相关资源
      最近更新 更多