如何仅使用OpenGL方法绘制文本?
我没有select使用除opengl方法以外(即glxxx()方法())。 我只需要使用gl方法绘制文本。 读完红皮书后,我明白,只有通过glBitmap()方法才有可能。 如果这是唯一可能的方法,那么任何人都可以帮助我所有字符的像素数组信息。 有没有其他的方式来绘制文字?
理论
为什么这很难
像TrueType和OpenType这样的stream行字体格式是vector轮廓格式:它们使用贝塞尔曲线来定义字母的边界。
图像源 。
将这些格式转换为像素数组(光栅化)太具体,超出了OpenGL的范围,特别是因为OpenGl没有非直的基元(例如, 为什么OpenGL中没有圆或椭圆原型?
最简单的方法是首先在CPU上自己扫描字体,然后将像素数组作为纹理赋予OpenGL。
OpenGL然后知道如何通过纹理非常好地处理像素数组。
纹理地图集
我们可以为每一帧光栅字符和重新创build纹理,但是这不是很有效,特别是如果字符具有固定的大小。
更有效的方法是将您计划使用的所有字符进行栅格化,并将其填充到单个纹理中。
然后将其传输到GPU一次,并使用它自定义uv坐标纹理select正确的字符。
这种方法被称为https://en.wikipedia.org/wiki/Texture_atlas ,它不仅可以用于纹理,还可以用于其他重复使用的纹理,如2D游戏中的贴图或Web UI图标。
完整纹理的维基百科图片,它本身取自freetype-gl,很好地说明了这一点:
我怀疑优化字符放置到最小的纹理问题是一个NP难题,请参阅: 可以使用什么algorithm将不同大小的矩形以相当优化的方式包装到最小的矩形中?
Web开发中使用了相同的技术来一次传输几个小图像(如图标),但在那里被称为“CSS Sprites”: https : //css-tricks.com/css-sprites/ ,用于隐藏networking的延迟而不是CPU / GPU通信的延迟。
非CPU的栅格方法
也有一些不使用CPU栅格纹理的方法。
CPU rastering很简单,因为它尽可能less地使用GPU,但是我们也开始考虑是否可以进一步使用GPU的效率。
2014年FOSDEMvideohttps://youtu.be/LZis03DXWjE?t=886解释了其他现有的技术:
- tesselation:将字体转换为小三angular形。 GPU非常擅长绘制三angular形。 缺点:
- 生成一束三angular形
- O(n log n)CPU计算三angular形
- 计算着色器上的曲线。 Blinn-Loop在2005年的一篇论文中将这种方法放在地图上。 下行:复杂。 请参阅: 分辨率独立立方贝塞尔在GPU上绘图(Blinn / Loop)
- 直接硬件实现,如OpenVG https://en.wikipedia.org/wiki/OpenVG 。 下行:由于某种原因没有得到广泛的实施。 看到:
- OpenGL,OpenVG。 画文字?
- OpenVG实施?
带有透视的3D几何内的字体
透视(与正交HUD相比)在3D几何内部渲染字体要复杂得多,因为透视可以使angular色的一部分更接近屏幕并且比其他部分更大,使得统一的CPU离散化(例如光栅, tesselation)在closures部分看起来不好。 这实际上是一个活跃的研究课题:
- 什么是在4.1版本的OpenGL中进行文本渲染的最新技术?
- http://www.valvesoftware.com/publications/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf
距离场是现在stream行的技术之一。
实现
下面的例子都是在Ubuntu 15.10上testing的。
因为这是一个前面讨论过的复杂问题,所以大多数例子都很大,并且会炸掉这个答案的30k字符限制,所以只需要克隆相应的Git仓库来编译。
他们都是完全开源的,所以你可以只用RTFS。
FreeType解决scheme
FreeType看起来就像占统治地位的开源字体光栅化库,所以它可以让我们使用TrueType和OpenType字体,使它成为最优雅的解决scheme。
-
https://github.com/rougier/freetype-gl
是一组示例OpenGL和freetype,但是或多或less正在演变成一个图书馆,它会暴露一个体面的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
像这个演示生成漂亮的输出:
-
/ libdgx
示例/教程:
- NEHE教程: http ://nehe.gamedev.net/tutorial/freetype_fonts_in_opengl/24001/
- http://learnopengl.com/#!In-Practice/Text-Rendering提到它,但我找不到可运行的源代码;
- SO问题:
- 使用Freetype2的OpenGL字体渲染
- 文字不能正确渲染 – 使用FreeType2的OpenGL
其他字体栅格化器
这些似乎不如FreeType好,但可能更轻量级:
- https://github.com/nothings/stb/blob/master/stb_truetype.h
- http://www.angelcode.com/products/bmfont/
Anton的OpenGL 4教程示例26“位图字体”
- 教程: http : //antongerdelan.net/opengl/ )
- 来源: https : //github.com/capnramses/antons_opengl_tutorials_book/blob/9a117a649ae4d21d68d2b75af5232021f5957aac/26_bitmap_fonts/main.cpp
字体是由作者手动创build的,并存储在一个.png
文件中。 字母在图像内以数组forms存储。
这种方法当然不是很一般,而且国际化也会有困难。
搭build:
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文件生成的。
本教程解释了如何使用CBFG和Paint.Net创buildDDS文件。
输出预览:
出于某种原因,苏珊娜对我来说是错过了,但时间计数器工作正常: https : //github.com/opengl-tutorials/ogl/issues/15
FreeGLUT
GLUT有glutStrokeCharacter
和FreeGLUT是开源的… https://github.com/dcnieho/FreeGLUT/blob/FG_3_0_0/src/fg_font.c#L255
OpenGLText
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渲染字体和文本
相关的线程
- 如何做一个GUI的OpenGL生活文字渲染?
- OpenGLES 2中的文本/字体渲染(iOS – CoreText?) – 选项和最佳实践?
使用glutStrokeCharacter(GLUT_STROKE_ROMAN, myCharString)
。
一个例子:一个星球大战SCROLLER。
#include <windows.h> #include <string.h> #include <GL\glut.h> #include <iostream.h> #include <fstream.h> GLfloat UpwardsScrollVelocity = -10.0; float view=20.0; char quote[6][80]; int numberOfQuotes=0,i; //********************************************* //* glutIdleFunc(timeTick); * //********************************************* void timeTick(void) { if (UpwardsScrollVelocity< -600) view-=0.000011; if(view < 0) {view=20; UpwardsScrollVelocity = -10.0;} // exit(0); UpwardsScrollVelocity -= 0.015; glutPostRedisplay(); } //********************************************* //* printToConsoleWindow() * //********************************************* void printToConsoleWindow() { int l,lenghOfQuote, i; for( l=0;l<numberOfQuotes;l++) { lenghOfQuote = (int)strlen(quote[l]); for (i = 0; i < lenghOfQuote; i++) { //cout<<quote[l][i]; } //out<<endl; } } //********************************************* //* RenderToDisplay() * //********************************************* void RenderToDisplay() { int l,lenghOfQuote, i; glTranslatef(0.0, -100, UpwardsScrollVelocity); glRotatef(-20, 1.0, 0.0, 0.0); glScalef(0.1, 0.1, 0.1); for( l=0;l<numberOfQuotes;l++) { lenghOfQuote = (int)strlen(quote[l]); glPushMatrix(); glTranslatef(-(lenghOfQuote*37), -(l*200), 0.0); for (i = 0; i < lenghOfQuote; i++) { glColor3f((UpwardsScrollVelocity/10)+300+(l*10),(UpwardsScrollVelocity/10)+300+(l*10),0.0); glutStrokeCharacter(GLUT_STROKE_ROMAN, quote[l][i]); } glPopMatrix(); } } //********************************************* //* glutDisplayFunc(myDisplayFunction); * //********************************************* void myDisplayFunction(void) { glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); gluLookAt(0.0, 30.0, 100.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); RenderToDisplay(); glutSwapBuffers(); } //********************************************* //* glutReshapeFunc(reshape); * //********************************************* void reshape(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60, 1.0, 1.0, 3200); glMatrixMode(GL_MODELVIEW); } //********************************************* //* int main() * //********************************************* int main() { strcpy(quote[0],"Luke, I am your father!."); strcpy(quote[1],"Obi-Wan has taught you well. "); strcpy(quote[2],"The force is strong with this one. "); strcpy(quote[3],"Alert all commands. Calculate every possible destination along their last known trajectory. "); strcpy(quote[4],"The force is with you, young Skywalker, but you are not a Jedi yet."); numberOfQuotes=5; glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(800, 400); glutCreateWindow("StarWars scroller"); glClearColor(0.0, 0.0, 0.0, 1.0); glLineWidth(3); glutDisplayFunc(myDisplayFunction); glutReshapeFunc(reshape); glutIdleFunc(timeTick); glutMainLoop(); return 0; }
在普通的OpenGL中绘制文本不是一个前瞻性的任务。 你可能应该看看这样做的库(通过使用库或作为一个示例实现)。
一些好的起点可能是GLFont , OpenGL Font Survey和NeHe位图字体教程(Windows) 。
请注意,位图不是在字体调查中提到的在OpenGL中实现文本的唯一方法。
本文介绍如何使用各种技术在OpenGL中呈现文本。
只使用opengl,有几种方法:
- 使用glBitmap
- 使用纹理
- 使用显示列表
加载一个带有字符的图像作为纹理,根据你想要的字符来绘制纹理的一部分。 您可以使用绘图程序创build该纹理,对其进行硬编码或使用窗口组件绘制图像并检索该图像以获取系统字体的精确副本。
不需要使用Glut或任何其他扩展,只需要基本的OpenGL可操作性。 它完成了工作,更不用说数十年来由专业程序员在非常成功的游戏和其他应用程序中这样做了。
我认为在OpenGL中绘制文本的最佳解决scheme是纹理字体,我与他们合作了很长时间。 他们是灵活的,快速和漂亮的外观(有一些后例外)。 我使用特殊的程序将字体文件(例如.ttf)转换为纹理,保存为一些内部“字体”格式的文件(我已经开发了基于http://content.gpwiki.org/index的格式和程序;.php / OpenGL:教程:Font_System虽然我的版本离原来的支持Unicode比较远等等)。 当启动主应用程序时,字体从这个“内部”格式加载。 看看上面的链接了解更多信息。
使用这种方法,主应用程序不使用任何像FreeType这样的特殊库,这对我也是不可取的。 正在使用标准的OpenGL函数绘制文本。