我没有其他选项可以使用OpenGL方法(即glxxx()
方法)。我需要仅使用GL方法来绘制文本。阅读了红皮书之后,我了解到唯一的方式是通过glBitmap()
方法。如果这是唯一可行的方法,那么有没有人能够提供包含所有字符的像素数组信息呢?是否有其他方法可以绘制文本?
为什么它很困难
流行的字体格式,如TrueType和OpenType,都是矢量轮廓格式:它们使用贝塞尔曲线来定义字母的边界。
图片来源。
将这些格式转换为像素数组(光栅化)过于具体,超出了OpenGL的范围,特别是因为OpenGL没有非直线的基本图形(例如参见为什么OpenGL中没有圆形或椭圆形的基本图形?)。
最简单的方法是首先在CPU上自己光栅化字体,然后将像素数组作为纹理传递给OpenGL。
OpenGL非常擅长处理像素数组的纹理。
纹理图集
我们可以为每一帧光栅化字符并重新创建纹理,但这不是很高效,特别是如果字符具有固定大小。
更高效的方法是将您计划使用的所有字符光栅化并压缩到一个单一的纹理中。
然后将其一次性传输到GPU,并使用自定义的UV坐标来选择正确的字符。
这种方法被称为纹理图集,它不仅可以用于纹理,还可以用于其他重复使用的纹理,比如2D游戏中的瓷砖或Web界面图标。透视下的三维几何内的字体
在三维几何体内使用透视(与正交HUD相比)来渲染字体要复杂得多,因为透视可能使字符的某一部分离屏幕更近且更大,从而使统一的CPU离散化(如光栅化、细分)在近处看起来不好。这实际上是一个活跃的研究课题。Valve的2007年有符号距离场论文。之前在:http://www.valvesoftware.com/publications/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf 但是公司们最终都会改变URL。
https://www.youtube.com/watch?v=1b5hIMqz_wM 是Martin Donald(2020年)的一个很好的评论视频,它似乎还提供了一些更具体的使用建议。
距离场是现在流行的技术之一。
以下示例均在Ubuntu 15.10上进行了测试。
由于这是一个复杂的问题,正如之前讨论的那样,大多数示例都很大,会超过此答案的30k字符限制,所以只需克隆相应的Git存储库进行编译。
然而,它们都是完全开源的,所以你可以直接阅读源代码。
FreeType解决方案
FreeType看起来是主要的开源字体光栅化库,因此它允许我们使用TrueType和OpenType字体,是最优雅的解决方案。
https://github.com/rougier/freetype-gl
这是一个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
生成了像这个演示一样漂亮的输出:
libdgx https://github.com/libgdx/libgdx/tree/1.9.2/extensions/gdx-freetype
示例/教程:
其他字体光栅化器
这些似乎不如FreeType好,但可能更轻量级:
Anton的OpenGL 4教程示例26“位图字体”
字体是由作者手动创建并存储在单个.png
文件中。字母以数组形式存储在图像内。
这种方法当然不太通用,而且在国际化方面可能会遇到困难。
构建方式:
make -f Makefile.linux64
opengl-tutorial 第11章 "2D字体"
纹理是从DDS文件生成的。
本教程解释了如何使用CBFG和Paint.Net创建DDS文件。
输出预览:
由于某种原因,Suzanne对我来说消失了,但计时器正常工作: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员工开发。旨在实现可重用性。尚未尝试过。https://github.com/vallentin/glText
单头文件C库,使用嵌入到库中的位图字体。SDL_ttf
来源:https://github.com/cirosantilli/cpp-cheat/blob/d36527fe4977bb9ef4b885b1ec92bd0cd3444a98/sdl/ttf.c
独立于SDL的树上,易于集成。
然而,它不提供纹理图集实现,因此性能会受到限制:如何高效地使用SDL2渲染字体和文本?
相关主题
使用 glutStrokeCharacter(GLUT_STROKE_ROMAN, myCharString)
。
例如:星球大战字幕滚动效果。
#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;
}
使用带有字符的图像作为纹理,并根据您想要的字符绘制该纹理的一部分。您可以使用绘画程序创建该纹理,硬编码它或使用窗口组件来绘制到图像并检索该图像以获得系统字体的精确副本。
无需使用Glut或任何其他扩展,只需基本的OpenGL操作能力即可完成任务。它可以胜任工作,更不用说专业程序员在非常成功的游戏和其他应用程序中已经像这样做了几十年。