如何仅使用 OpenGL 方法绘制文本?

新手上路,请多包涵

我没有使用 OpenGL 方法的选项(即 glxxx() 方法)。我只需要使用 gl 方法绘制文本。看了红皮书才明白,只有通过 glBitmap() 方法才有可能。如果这是唯一可能的方法,那么任何人都可以帮助我了解所有字符的像素阵列信息。有没有其他的方法来绘制文字?

原文由 sathish v 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 993
1 个回答

理论

为什么难

流行的字体格式如 TrueTypeOpenType 是矢量轮廓格式:它们使用 贝塞尔曲线 来定义字母的边界。

图片来源

将这些格式转换为像素数组(光栅化)过于具体且超出了 OpenGL 的范围,特别是因为 OpenGL 没有非直线图元(例如,请参阅 为什么 OpenGL 中没有圆形或椭圆图元?

最简单的方法是首先在 CPU 上自己光栅字体,然后将像素数组作为纹理提供给 OpenGL。

OpenGL 然后知道如何通过纹理很好地处理像素数组。

纹理图集

我们可以为每一帧光栅字符并重新创建纹理,但这不是很有效,特别是如果字符具有固定大小。

更有效的方法是对您计划使用的所有字符进行光栅化,并将它们填充到单个纹理上。

然后将其传输到 GPU 一次,并使用带有自定义 uv 坐标的纹理来选择正确的角色。

这种方法称为 纹理图集,它不仅可以用于纹理,还可以用于其他重复使用的纹理,例如 2D 游戏中的图块或 Web UI 图标。

完整纹理的维基百科图片本身取自 freetype-gl,很好地说明了这一点:

我怀疑将字符放置优化为最小的纹理问题是一个 NP 难题,请参阅: What algorithm can be used for packing the rectangles into the minimum rectangle possible in a相当优化的方式?

在 web 开发中使用相同的技术来一次传输几个小图像(如图标),但在那里它被称为“CSS Sprites”: https ://css-tricks.com/css-sprites/ 并用于隐藏网络的延迟而不是 CPU / GPU 通信的延迟。

非 CPU 光栅方法

还有一些方法不使用 CPU 光栅来处理纹理。

CPU 光栅化很简单,因为它尽可能少地使用 GPU,但我们也开始考虑是否可以进一步使用 GPU 效率。

此 FOSDEM 2014 视频 解释了其他现有技术:

带有透视的 3D 几何体内部的字体

使用透视渲染 3D 几何体内部的字体(与正交 HUD 相比)要复杂得多,因为透视可以使角色的一部分更靠近屏幕并且比另一部分更大,从而实现统一的 CPU 离散化(例如光栅、镶嵌)在近距离看起来很糟糕。这实际上是一个活跃的研究课题:

https://www.youtube.com/watch?v=1b5hIMqz_wM by Martin Donald (2020) 是一个很好的视频评论,它似乎也提供了一些更具体的使用建议。

在此处输入图像描述

距离场 是现在流行的技术之一。

实现

以下示例均在 Ubuntu 15.10 上进行了测试。

因为这是一个前面讨论过的复杂问题,所以大多数示例都很大,并且会破坏此答案的 30k 字符限制,因此只需克隆相应的 Git 存储库进行编译。

然而,它们都是完全开源的,所以你可以只使用 RTFS。

FreeType 解决方案

FreeType 看起来像占主导地位的开源字体光栅化库,因此它允许我们使用 TrueType 和 OpenType 字体,使其成为最优雅的解决方案。

是一组 OpenGL 和 freetype 的示例,但或多或少地演变成一个库,可以做到这一点并公开一个体面的 API。

无论如何,应该已经可以通过复制粘贴一些源代码将其集成到您的项目中。

它提供了开箱即用的纹理图集和距离场技术。

下的演示: https ://github.com/rougier/freetype-gl/tree/master/demos

没有 Debian 软件包,在 Ubuntu 15.10 上编译很痛苦: https ://github.com/rougier/freetype-gl/issues/82#issuecomment-216025527(包装问题,一些上游),但它变得更好截至 16.10。

没有很好的安装方法: https ://github.com/rougier/freetype-gl/issues/115

像这个演示一样生成漂亮的输出:

在此处输入图像描述

示例/教程:

其他字体光栅化器

这些似乎不如 FreeType 好,但可能更轻量级:

Anton 的 OpenGL 4 教程示例 26“位图字体”

  • 教程: http ://antongerdelan.net/opengl/
  • 来源: https ://github.com/capnramses/antons_opengl_tutorials_book/blob/9a117a649ae4d21d68d2b75af5232021f5957aac/26_bitmap_fonts/main.cpp

该字体由作者手动创建并存储在单个 .png 文件中。字母以数组形式存储在图像内。

这种方法当然不是很通用,国际化也会有困难。

构建:

 make -f Makefile.linux64

输出预览:

在此处输入图像描述

opengl-tutorial 第11章“2D字体”

  • 教程: http ://www.opengl-tutorial.org/intermediate-tutorials/tutorial-11-2d-text/
  • 来源: https ://github.com/opengl-tutorials/ogl/blob/71cad106cefef671907ba7791b28b19fa2cc034d/tutorial11_2d_fonts/tutorial11.cpp

纹理是从 DDS 文件 生成的。

本教程解释了如何使用 CBFGPaint.Net 创建 DDS 文件。

输出预览:

在此处输入图像描述

出于某种原因,苏珊娜对我来说不见了,但时间计数器工作正常: https ://github.com/opengl-tutorials/ogl/issues/15

自由GLUT

GLUT有 glutStrokeCharacter 并且FreeGLUT是开源的…… https://github.com/dcnieho/FreeGLUT/blob/FG_3_0_0/src/fg_font.c#L255

OpenGL文本

https://github.com/tlorach/OpenGLText

TrueType 栅格。由 NVIDIA 员工提供。旨在实现可重用性。还没试过。

ARM Mali GLES SDK 示例

http://malideveloper.arm.com/resources/sample-code/simple-text-rendering/ 似乎对 PNG 上的所有字符进行编码,并从那里剪切它们。

SDL_ttf

在此处输入图像描述

来源: https ://github.com/cirosantilli/cpp-cheat/blob/d36527fe4977bb9ef4b885b1ec92bd0cd3444a98/sdl/ttf.c

与 SDL 位于单独的树中,并且易于集成。

但是不提供纹理图集实现,因此性能将受到限制: 如何使用 SDL2 有效地渲染字体和文本?

相关主题

原文由 Ciro Santilli OurBigBook.com 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题