Answers:
Android SDK没有提供在OpenGL视图上绘制文本的任何简便方法。为您提供以下选择。
将文本渲染到纹理比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();
我编写了一个教程,扩展了JVitela发布的答案。基本上,它使用相同的想法,但不是将每个字符串呈现为纹理,而是将字体文件中的所有字符呈现为纹理,并使用它来实现完整的动态文本呈现,而不会进一步减速(一旦完成初始化) 。
与各种字体图集生成器相比,我的方法的主要优点是您可以在项目中附带小的字体文件(.ttf .otf),而不必为每种字体变化和大小都附带较大的位图。它可以仅使用字体文件以任何分辨率生成完美质量的字体:)
本教程包含可在任何项目中使用的完整代码:)
根据此链接:
http://code.neenbedankt.com/how-to-render-an-android-view-to-a-bitmap
您可以将任何视图渲染为位图。可能值得假设的是,您可以根据需要布局视图(包括文本,图像等),然后将其渲染为位图。
使用上面的JVitela的代码,您应该能够将该位图用作OpenGL纹理。
看一下CBFG和加载/渲染代码的Android端口。您应该能够将代码放入项目中并立即使用它。
CBFG- http://www.codehead.co.uk/cbfg
Android加载器-http: //www.codehead.co.uk/cbfg/TexFont.java
查看GLSurfaceView示例中的“ Sprite Text”示例。
恕我直言,在游戏中使用OpenGL ES的三个原因:
在游戏设计中,绘制文本始终是一个问题,因为您正在绘制事物,因此您无法使用小部件等来拥有普通活动的外观。
您可以使用框架从TrueType字体生成位图字体并进行渲染。我见过的所有框架都以相同的方式运行:在绘制时为文本生成顶点和纹理坐标。这不是最有效的OpenGL使用。
最好的方法是在代码的早期为顶点和纹理分配远程缓冲区(顶点缓冲区对象-VBO),避免在绘制时进行惰性内存传输操作。
请记住,游戏玩家不喜欢阅读文本,因此您不会写很长的动态生成的文本。对于标签,您可以使用静态纹理,为时间和分数保留动态文本,并且两者均为数字,并带有几个字符。
因此,我的解决方案很简单:
如果使用远程静态缓冲区,则绘制操作很快。
我创建一个具有屏幕位置(基于屏幕的对角线百分比)和纹理(静态和字符)的XML文件,然后在呈现之前加载此XML。
为了获得较高的FPS速率,应避免在绘制时生成VBO。
查看CBFG
加载/渲染代码的Android端口。您应该能够将代码放入项目中并立即使用它。
我对此实施有疑问。它只显示一个字符,当我尝试更改字体的位图大小时(我需要特殊字母),整个绘制失败:(
对于静态文本:
对于需要偶尔更新的长文本:
对于数字(格式为00.0):
在onDraw事件中,仅更新发送到着色器的value变量。
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进行演示(但纹理错误)
在OpenGL ES 2.0 / 3.0中,您还可以结合使用OGL View和Android的UI元素:
public class GameActivity extends AppCompatActivity {
private SurfaceView surfaceView;
@Override
protected void onCreate(Bundle state) {
setContentView(R.layout.activity_gl);
surfaceView = findViewById(R.id.oglView);
surfaceView.init(this.getApplicationContext());
...
}
}
public class SurfaceView extends GLSurfaceView {
private SceneRenderer renderer;
public SurfaceView(Context context) {
super(context);
}
public SurfaceView(Context context, AttributeSet attributes) {
super(context, attributes);
}
public void init(Context context) {
renderer = new SceneRenderer(context);
setRenderer(renderer);
...
}
}
创建布局activity_gl.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
tools:context=".activities.GameActivity">
<com.app.SurfaceView
android:id="@+id/oglView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<TextView ... />
<TextView ... />
<TextView ... />
</androidx.constraintlayout.widget.ConstraintLayout>
要从渲染线程更新元素,可以使用Handler / Looper。