在OpenGL ES中绘制文本

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

我已经看到一个使用View作为覆盖的例子,但是我不知道我是否想这样做,因为我可能想稍后将游戏移植到其他平台。

有任何想法吗?

Android SDK没有任何简单的方法在OpenGL视图上绘制文本。 留下您以下选项。

  1. 在您的SurfaceView上放置一个TextView。 这是缓慢和不好的,但最直接的方法。
  2. 将常用string渲染为纹理,并简单地绘制这些纹理。 这是迄今为止最简单,最快,但最不灵活的。
  3. 基于精灵滚动你自己的文本渲染代码。 如果2不是一个选项,可能是第二好的select。 一个很好的方式让你的脚湿,但要注意,虽然看起来很简单(和基本function),它会变得更难和更具挑战性,因为你添加更多的function(纹理alignment,处理换行符,可变宽度的字体等)。 ) – 如果你采取这条路线,尽可能简单,你可以逃脱!
  4. 使用现成的/开源的库。 如果你在Google上search,有一些棘手的问题是让他们整合并运行。 但至less,一旦你这样做,你将拥有所有的灵活性和成熟度。

将文本渲染到纹理比Sprite Text演示文件更简单,基本思路是使用Canvas类渲染到位图,然后将位图传递给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, eg 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(); 

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

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

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

看看CBFG和加载/渲染代码的Android端口。 你应该能够把代码放到你的项目中,并马上使用它。

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

Android加载器 – http://www.codehead.co.uk/cbfg/TexFont.java

根据这个链接:

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

您可以将任何视图渲染为位图。 这可能是值得假设的,你可以根据需要布置一个视图(包括文本,图像等),然后将其渲染到一个位图。

使用上面的JVitela代码,你应该可以使用该位图作为OpenGL纹理。

我看了一下精灵文本的例子,这个任务看起来非常复杂,我也考虑渲染纹理,但是我担心可能导致的性能下降。 我可能只需要有一个观点,而不必担心移植的时间跨越桥梁:)

看看GLSurfaceView示例中的“Sprite Text”示例。

如果你坚持使用GL,你可以把文本渲染成纹理。 假设大多数的HUD是相对静态的,你不应该经常加载纹理纹理内存。

看看CBFG和加载/渲染代码的Android端口。 你应该能够把代码放到你的项目中,并马上使用它。

  1. CBFG

  2. Android加载器

我有这个实现的问题。 它只显示一个字符,当我尝试改变字体的位图大小(我需要特殊字母)整个绘制失败:(

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

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

绘图文本在游戏devise中一直是一个问题,因为你正在绘制东西,所以你不能拥有常见活动的外观和感觉,使用小部件等等。

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

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

请记住,游戏玩家不喜欢读文本,所以你不会写一个长的dynamic生成的文本。 对于标签,您可以使用静态纹理,留下dynamic文本的时间和分数,并且都是数字和几个字符长。

所以,我的解决scheme很简单:

  1. 为常见标签和警告创build纹理;
  2. 为数字0-9,“:”,“+”和“ – ”创build纹理。 每个字符一个纹理;
  3. 为屏幕上的所有位置生成远程VBO。 我可以在这个职位上呈现静态或dynamic文本,但是维也纳组织是静态的;
  4. 只生成一个纹理VBO,因为文本总是呈现一种方式;
  5. 在绘制时间,我呈现静态文本;
  6. 对于dynamic文本,我可以偷看VBO的位置,获取字符纹理并绘制它,一次一个字符。

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

我创build一个带有屏幕位置(基于屏幕对angular线百分比)和纹理(静态和字符)的XML文件,然后在渲染之前加载这个XML。

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

我一直在寻找这几个小时,这是我第一篇文章,虽然它有最好的答案,我认为最stream行的答案是不正确的。 当然是为了我所需要的。 weichsel和shakazed的答案是正确的button,但在文章中有点模糊。 把你的权利,以项目。 在这里:只要基于现有的示例创build一个新的Android项目。 selectApiDemos:

在源文件夹下查找

ApiDemos/src/com/example/android/apis/graphics/spritetext

你会发现你需要的一切。

对于静态文本

  • 用您PC上使用的所有单词生成图像(例如使用GIMP)。
  • 将其作为纹理加载,并将其用作飞机的材质。

对于需要稍后更新一次的长文本

  • 让android画一个位图canvas(JVitela的解决scheme)。
  • 加载这个作为飞机的材料。
  • 为每个单词使用不同的纹理坐标。

对于一个数字 (格式为00.0):

  • 用所有数字和点生成图像。
  • 加载这个作为飞机的材料。
  • 使用下面的着色器。
  • 在你的onDraw事件中,只更新发送给着色器的值variables。

     precision highp float; precision highp sampler2D; uniform float uTime; uniform float uValue; uniform vec3 iResolution; varying vec4 v_Color; varying vec2 vTextureCoord; uniform sampler2D s_texture; void main() { vec4 fragColor = vec4(1.0, 0.5, 0.2, 0.5); vec2 uv = vTextureCoord; float devisor = 10.75; float digit; float i; float uCol; float uRow; if (uv.y < 0.45) { if (uv.x > 0.75) { digit = floor(uValue*10.0); digit = digit - floor(digit/10.0)*10.0; i = 48.0 - 32.0 + digit; uRow = floor(i / 10.0); uCol = i - 10.0 * uRow; fragColor = texture2D( s_texture, uv / devisor * 2.0 + vec2((uCol-1.5) / devisor, uRow / devisor) ); } else if (uv.x > 0.5) { uCol = 4.0; uRow = 1.0; fragColor = texture2D( s_texture, uv / devisor * 2.0 + vec2((uCol-1.0) / devisor, uRow / devisor) ); } else if (uv.x > 0.25) { digit = floor(uValue); digit = digit - floor(digit/10.0)*10.0; i = 48.0 - 32.0 + digit; uRow = floor(i / 10.0); uCol = i - 10.0 * uRow; fragColor = texture2D( s_texture, uv / devisor * 2.0 + vec2((uCol-0.5) / devisor, uRow / devisor) ); } else if (uValue >= 10.0) { digit = floor(uValue/10.0); digit = digit - floor(digit/10.0)*10.0; i = 48.0 - 32.0 + digit; uRow = floor(i / 10.0); uCol = i - 10.0 * uRow; fragColor = texture2D( s_texture, uv / devisor * 2.0 + vec2((uCol-0.0) / devisor, uRow / devisor) ); } else { fragColor = vec4(0.0, 0.0, 0.0, 0.0); } } else { fragColor = vec4(0.0, 0.0, 0.0, 0.0); } gl_FragColor = fragColor; } 

以上代码适用于纹理图集,其中数字从字体图集(纹理)的第二行第七列的0开始。

演示(请参考https://www.shadertoy.com/view/Xl23Dw