在 OpenGL ES 中绘制文本
-
20-09-2019 - |
题
我目前正在为 Android 平台开发一个小型 OpenGL 游戏,我想知道是否有一种简单的方法可以在渲染帧之上渲染文本(例如带有玩家分数的 HUD 等)。文本还需要使用自定义字体。
我见过一个使用视图作为叠加层的示例,但我不知道是否要这样做,因为我稍后可能想将游戏移植到其他平台。
有任何想法吗?
解决方案
Android SDK 没有提供任何在 OpenGL 视图上绘制文本的简单方法。给您留下以下选择。
- 将 TextView 放置在 SurfaceView 上。 这是缓慢且糟糕的,但却是最直接的方法。
- 将常见字符串渲染为纹理,然后简单地绘制这些纹理。 这是迄今为止最简单、最快的,但最不灵活。
- 基于精灵编写您自己的文本渲染代码。 如果 2 不是一个选项,可能是第二个最佳选择。这是一个入门的好方法,但请注意,虽然它看起来很简单(基本功能也很简单),但随着您添加更多功能(纹理对齐、处理换行符、可变宽度字体等),它会变得更加困难和更具挑战性。 ) - 如果您选择这条路线,请使其尽可能简单!
- 使用现成的/开源库。 如果你在谷歌上搜索的话,就会有一些这样的工具,棘手的地方是让它们集成并运行。但至少,一旦你这样做了,你将拥有他们提供的所有灵活性和成熟度。
其他提示
渲染文本的质地比雪碧文本演示做什么样子,其基本思想是使用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, 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();
根据该链接:
HTTP://code.neenbedankt的.com /如何渲染的-AN-机器人视图到一个位图
您可以渲染的任何视图以位图。这可能是值得假设你可以布局视图根据您的需要(包括文字,图片等),然后将其渲染为位图。
使用上面的你应该能够 JVitela代码使用该位图作为一个OpenGL纹理。
看看CBFG和装载/呈现代码的Android的端口。您应该能够将代码放到你的项目,并用它立竿见影。
CBFG - http://www.codehead.co.uk/cbfg
Android的装载机 - http://www.codehead.co.uk/cbfg/TexFont.java
我看着精灵文本示例,它看起来对这样的任务非常复杂,我认为渲染到纹理太多,但我担心对性能的影响可能会导致。 我可能只是有一个观点去代替而发愁时移植它的时间到桥头:)
恕我直言,在游戏中使用 OpenGL ES 有以下三个原因:
- 通过使用开放标准避免移动平台之间的差异;
- 对渲染过程有更多的控制;
- 受益于 GPU 并行处理;
绘制文本始终是游戏设计中的一个问题,因为您正在绘制东西,因此您无法拥有常见活动(例如小部件等)的外观和感觉。
您可以使用框架从 TrueType 字体生成位图字体并渲染它们。我见过的所有框架都以相同的方式运行:在绘制时生成文本的顶点和纹理坐标。这并不是 OpenGL 最有效的使用方式。
最好的方法是在代码的早期为顶点和纹理分配远程缓冲区(顶点缓冲区对象 - VBO),避免绘制时的惰性内存传输操作。
请记住,游戏玩家不喜欢阅读文本,因此您不会编写长的动态生成的文本。对于标签,您可以使用静态纹理,保留时间和分数的动态文本,两者都是带有几个字符长的数字。
所以,我的解决方案很简单:
- 为常见标签和警告创建纹理;
- 为数字 0-9、“:”、“+”和“-”创建纹理。每个角色都有一个纹理;
- 为屏幕中的所有位置生成远程 VBO。我可以在这些位置渲染静态或动态文本,但 VBO 是静态的;
- 仅生成一个纹理 VBO,因为文本始终以一种方式渲染;
- 在绘制时,我渲染静态文本;
- 对于动态文本,我可以查看 VBO 位置,获取字符纹理并一次绘制一个字符。
如果您使用远程静态缓冲区,绘制操作会很快。
我创建一个包含屏幕位置(基于屏幕对角线百分比)和纹理(静态和字符)的 XML 文件,然后在渲染之前加载此 XML。
为了获得高 FPS 速率,您应该避免在绘制时生成 VBO。
如果你坚持要用GL,你可以渲染到纹理的文本。假定大多数HUD的是相对静态的,不应该有过于频繁加载纹理纹理存储器。
我一直在寻找这几个小时,这是我来的第一篇文章翻过,虽然它有最好的答案,最流行的答案,我认为是没谱。当然,我需要的东西。 weichsel的和shakazed的答案是正确的按钮,但在文章有点模糊。 把你的权利的项目。这里: 只要创建基于现有样本一个新的Android项目。选择ApiDemos:
查找源文件夹下
ApiDemos/src/com/example/android/apis/graphics/spritetext
和你会发现你所需要的一切。
为了 静态文本:
- 使用 PC 上使用的所有单词生成图像(例如使用 GIMP)。
- 将其加载为纹理并将其用作平面的材质。
为了 长文本 需要时不时地更新一下:
- 让 android 在位图画布上绘图(JVitela 的解决方案)。
- 将其加载为飞机的材料。
- 为每个单词使用不同的纹理坐标。
为了 一个号码 (格式为 00.0):
- 生成包含所有数字和一个点的图像。
- 将其加载为飞机的材料。
- 使用下面的着色器。
在 onDraw 事件中仅更新发送到着色器的值变量。
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 用于演示(尽管纹理错误)