Reputation: 1503
I'm currently developing a small OpenGL game for the Android platform and I wonder if there's an easy way to render text on top of the rendered frame (like a HUD with the player´s score etc). The text would need to use a custom font also.
I've seen an example using a View as an overlay, but I don't know if I want to do that since I might want to port the game to other platforms later.
Any ideas?
Upvotes: 134
Views: 119639
Reputation: 2534
In the OpenGL ES 2.0/3.0 you can also combining OGL View and Android's UI-elements:
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);
...
}
}
Create layout 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>
To update elements from the render thread, can use Handler/Looper.
Upvotes: 1
Reputation: 1261
For static text:
For long text that needs to be updated once in a while:
For a number (formatted 00.0):
In your onDraw event only update the value variable sent to the shader.
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;
}
Above code works for a texture atlas where numbers start from 0 at the 7th column of the 2nd row of the font atlas (texture).
Refer to https://www.shadertoy.com/view/Xl23Dw for demonstration (with wrong texture though)
Upvotes: 1
Reputation: 165
IMHO there are three reasons to use OpenGL ES in a game:
Drawing text is always a problem in game design, because you are drawing things, so you cannot have the look and feel of a common activity, with widgets and so on.
You can use a framework to generate Bitmap fonts from TrueType fonts and render them. All the frameworks I've seen operate the same way: generate the vertex and texture coordinates for the text in draw time. This is not the most efficient use of OpenGL.
The best way is to allocate remote buffers (vertex buffer objects - VBOs) for the vertices and textures early in code, avoiding the lazy memory transfer operations in draw time.
Keep in mind that game players don't like to read text, so you won't write a long dynamically generated text. For labels, you can use static textures, leaving dynamic text for time and score, and both are numeric with a few characters long.
So, my solution is simple:
Draw operations are fast, if you use remote static buffers.
I create an XML file with screen positions (based on screen's diagonal percentage) and textures (static and characters), and then I load this XML before rendering.
To get a high FPS rate, you should avoid generating VBOs at draw time.
Upvotes: 5
Reputation: 18978
I've written a tutorial that expands on the answer posted by JVitela. Basically, it uses the same idea, but instead of rendering each string to a texture, it renders all characters from a font file to a texture and uses that to allow for full dynamic text rendering with no further slowdowns (once the initialization is complete).
The main advantage of my method, compared to the various font atlas generators, is that you can ship small font files (.ttf .otf) with your project instead of having to ship large bitmaps for every font variation and size. It can generate perfect quality fonts at any resolution using only a font file :)
The tutorial includes full code that can be used in any project :)
Upvotes: 39
Reputation: 3443
I have been looking for this for a few hours, this was the first article i came accross and although it has the best answer, the most popular answers i think are off the mark. Certainly for what i needed. weichsel's and shakazed's answers were right on the button but a bit obscured in the articles. To put you right to the project. Here: Just create a new Android project based on existing sample. Choose ApiDemos:
Look under the source folder
ApiDemos/src/com/example/android/apis/graphics/spritetext
And you will find everything you need.
Upvotes: 2
Reputation: 261
Take a look at CBFG
and the Android port of the loading/rendering
code. You should be able to drop the code into your project and use it
straight away.
I have problems with this implementation. It displays only one character, when I try do change size of the font's bitmap (I need special letters) whole draw fails :(
Upvotes: 3
Reputation: 193
According to this link:
http://code.neenbedankt.com/how-to-render-an-android-view-to-a-bitmap
You can render any View to a bitmap. It's probably worth assuming that you can layout a view as you require (including text, images etc.) and then render it to a Bitmap.
Using JVitela's code above you should be able to use that Bitmap as an OpenGL texture.
Upvotes: 9
Reputation: 126
Take a look at CBFG and the Android port of the loading/rendering code. You should be able to drop the code into your project and use it straight away.
CBFG - http://www.codehead.co.uk/cbfg
Android loader - http://www.codehead.co.uk/cbfg/TexFont.java
Upvotes: 8
Reputation: 2512
Rendering text to a texture is simpler than what the Sprite Text demo make it looks like, the basic idea is to use the Canvas class to render to a Bitmap and then pass the Bitmap to an OpenGL texture:
// 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();
Upvotes: 172
Reputation: 6104
The Android SDK doesn't come with any easy way to draw text on OpenGL views. Leaving you with the following options.
Upvotes: 108
Reputation: 1503
I looked at the sprite text example and it looks awfully complicated for such a task, I considered rendering to a texture too, but I'm worried about the performance hit that might cause. I might just have to go with a view instead and worry about porting when it's time to cross that bridge :)
Upvotes: 6
Reputation: 34243
Look at the "Sprite Text" sample in the GLSurfaceView samples.
Upvotes: 4
Reputation: 7317
If you insist on using GL, you could render the text on to textures. Assuming that most of the HUD is relatively static, you shouldn't have to load the textures to texture memory too often.
Upvotes: 3