在OpenGL ES中绘制文本

sha*_*zed 126 android opengl-es text-rendering

我正在为Android平台开发一个小型的OpenGL游戏,我想知道是否有一种简单的方法可以在渲染帧的顶部渲染文本(比如带有玩家得分的HUD等).文本也需要使用自定义字体.

我已经看过使用View作为叠加层的示例,但我不知道是否要这样做,因为我可能希望稍后将游戏移植到其他平台.

有任何想法吗?

JVi*_*ela 164

将文本渲染到纹理比Sprite Text演示使其看起来更简单,基本思想是使用Canvas类渲染到Bitmap,然后将Bitmap传递给OpenGL纹理:

// Create an empty, mutable bitmap
Bitmap bitmap = Bitmap.createBitmap(256, 256, Bitmap.Config.ARGB_4444);
// get a canvas to paint over the bitmap
Canvas canvas = new Canvas(bitmap);
bitmap.eraseColor(0);

// get a background image from resources
// note the image format must match the bitmap format
Drawable background = context.getResources().getDrawable(R.drawable.background);
background.setBounds(0, 0, 256, 256);
background.draw(canvas); // draw the background to our bitmap

// Draw the text
Paint textPaint = new Paint();
textPaint.setTextSize(32);
textPaint.setAntiAlias(true);
textPaint.setARGB(0xff, 0x00, 0x00, 0x00);
// draw the text centered
canvas.drawText("Hello World", 16,112, textPaint);

//Generate one texture pointer...
gl.glGenTextures(1, textures, 0);
//...and bind it to our array
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);

//Create Nearest Filtered Texture
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

//Different possible texture parameters, e.g. GL10.GL_CLAMP_TO_EDGE
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);

//Use the Android GLUtils to specify a two-dimensional texture image from our bitmap
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);

//Clean up
bitmap.recycle();
Run Code Online (Sandbox Code Playgroud)

  • 这是非常慢的,它会在游戏中为fps杀死fps,因为它总是在变化(得分等),但是对于半静态的东西(玩家名称,等级名称等)它会很好. (9认同)
  • 这有助于我在我的应用程序上显示小fps计数器进行调试,谢谢! (5认同)
  • 我想请注意,这个答案中的代码可能只是一个演示,并没有得到优化!请以您自己的方式进行优化/缓存. (3认同)
  • 当您加载其他纹理然后将它们放在一起形成单词或数字时,您可以使用它来将所有字母和数字生成为纹理.然后它将没有任何其他gl纹理效率低. (2认同)

Dav*_*ave 101

Android SDK没有任何简单的方法在OpenGL视图上绘制文本.离开你有以下选择.

  1. 在SurfaceView上放置TextView.这是缓慢而糟糕的,但却是最直接的方法.
  2. 将常见字符串渲染为纹理,并简单地绘制这些纹理.这是迄今为止最简单,最快速,但最不灵活的.
  3. 基于精灵滚动自己的文本渲染代码.如果2不是一个选项,可能是第二个最佳选择.让你的脚湿润的一个好方法,但请注意,虽然看起来很简单(和基本功能),但是当你添加更多功能(纹理对齐,处理换行符,可变宽度字体等)时,它会变得更难,更具挑战性. ) - 如果你采取这条路线,尽量让它变得简单!
  4. 使用现成的/开源库.如果你在谷歌上搜索有一些,那么棘手的一点就是让它们集成并运行.但至少,一旦你这样做,你将拥有他们提供的所有灵活性和成熟度.

  • JVitela的答案更好.这就是我目前正在使用的.你从标准的android vier + canvas切换到opengl的原因是(其中包括)速度.在opengl上添加一个文本框就是否定了这一点. (4认同)
  • 我决定在我的GLView上添加一个视图,它可能不是最有效的方法,但视图不经常更新,而且它让我可以灵活地添加我喜欢的任何字体.感谢所有的答复! (3认同)
  • 或者请参阅下面的JVitela的答案,以获得更具编程性的方法来实现这一目标. (2认同)

fre*_*dom 35

我写了一个教程,扩展了JVitela发布的答案.基本上,它使用相同的想法,但不是将每个字符串渲染为纹理,而是将所有字符从字体文件呈现为纹理,并使用它来允许完整的动态文本呈现而不会进一步减速(初始化完成后) .

与各种字体图集生成器相比,我的方法的主要优点是,您可以随项目一起发送小字体文件(.ttf .otf),而不必为每个字体变体和大小发送大型位图.只使用字体文件,它可以在任何分辨率下生成完美品质的字体:)

教程包含可在任何项目中使用的完整代码:)


JWG*_*WGS 8

根据这个链接:

http://code.neenbedankt.com/how-to-render-an-android-view-to-a-bitmap

您可以将任何视图渲染到位图.可能值得假设您可以根据需要布局视图(包括文本,图像等),然后将其渲染为位图.

使用上面的JVitela代码,您应该能够将该Bitmap用作OpenGL纹理.


小智 7

看一下CBFG和加载/渲染代码的Android端口.您应该能够将代码放入项目中并立即使用它.

CBFG - http://www.codehead.co.uk/cbfg

Android加载程序 - http://www.codehead.co.uk/cbfg/TexFont.java


sha*_*zed 6

我查看了精灵文本示例,它看起来非常复杂,我考虑渲染到纹理,但我担心可能导致的性能损失.我可能只需要使用视图而担心移植时间是跨越那座桥:)


小智 5

恕我直言,在游戏中使用 OpenGL ES 有以下三个原因:

  1. 通过使用开放标准避免移动平台之间的差异;
  2. 对渲染过程有更多的控制;
  3. 受益于 GPU 并行处理;

绘制文本始终是游戏设计中的一个问题,因为您正在绘制东西,因此您无法拥有常见活动(例如小部件等)的外观和感觉。

您可以使用框架从 TrueType 字体生成位图字体并渲染它们。我见过的所有框架都以相同的方式运行:在绘制时生成文本的顶点和纹理坐标。这并不是 OpenGL 最有效的使用方式。

最好的方法是在代码的早期为顶点和纹理分配远程缓冲区(顶点缓冲区对象 - VBO),避免绘制时的惰性内存传输操作。

请记住,游戏玩家不喜欢阅读文本,因此您不会编写长的动态生成的文本。对于标签,您可以使用静态纹理,保留时间和分数的动态文本,两者都是带有几个字符长的数字。

所以,我的解决方案很简单:

  1. 为常见标签和警告创建纹理;
  2. 为数字 0-9、“:”、“+”和“-”创建纹理。每个角色都有一个纹理;
  3. 为屏幕中的所有位置生成远程 VBO。我可以在这些位置渲染静态或动态文本,但 VBO 是静态的;
  4. 仅生成一个纹理 VBO,因为文本始终以一种方式渲染;
  5. 在绘制时,我渲染静态文本;
  6. 对于动态文本,我可以查看 VBO 位置,获取字符纹理并一次绘制一个字符。

如果您使用远程静态缓冲区,绘制操作会很快。

我创建一个包含屏幕位置(基于屏幕对角线百分比)和纹理(静态和字符)的 XML 文件,然后在渲染之前加载此 XML。

为了获得高 FPS 速率,您应该避免在绘制时生成 VBO。