Je développe actuellement un petit jeu OpenGL pour la Android) et je me demande s’il existe un moyen simple de rendre le texte au-dessus du cadre affiché (comme un HUD avec le score du joueur, etc.). Le texte devrait également utiliser une police personnalisée.
J'ai vu un exemple d'utilisation d'une vue en tant que superposition, mais je ne sais pas si je souhaite le faire, car je souhaiterais peut-être porter le jeu sur d'autres plates-formes ultérieurement.
Des idées?
Le SDK Android ne propose pas de méthode simple pour dessiner du texte sur des vues OpenGL. Il vous laisse les options suivantes.
Le rendu du texte en texture est plus simple que ne le donne la démo Sprite Text. L'idée de base est d'utiliser la classe Canvas pour restituer le rendu dans un bitmap, puis de le transmettre à une texture 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();
J'ai écrit un tutoriel qui développe la réponse envoyée par JVitela . Fondamentalement, il utilise la même idée, mais au lieu de rendre chaque chaîne en une texture, il restitue tous les caractères d'un fichier de polices en une texture et l'utilise pour permettre un rendu dynamique du texte complet sans ralentissement supplémentaire (une fois l'initialisation terminée). .
Le principal avantage de ma méthode, par rapport aux différents générateurs d’atlas de polices, est qu’il est possible d’expédier de petits fichiers de polices (.ttf .otf) avec votre projet, au lieu de devoir envoyer de grandes images bitmap pour chaque variation et taille de police. Il peut générer des polices de qualité parfaite à n'importe quelle résolution en utilisant uniquement un fichier de polices :)
Le tutoriel inclut le code complet qui peut être utilisé dans n'importe quel projet :)
Selon ce lien:
http://code.neenbedankt.com/how-to-render-an-Android-view-to-a-bitmap
Vous pouvez rendre n'importe quelle vue en bitmap. Cela vaut probablement la peine de supposer que vous pouvez mettre en forme une vue selon vos besoins (y compris du texte, des images, etc.), puis la restituer en bitmap.
En utilisant le code de JVitela ci-dessus , vous devriez pouvoir utiliser ce bitmap en tant que texture OpenGL.
Jetez un coup d'œil à CBFG et au port Android du code de chargement/rendu). Vous devriez pouvoir insérer le code dans votre projet et l'utiliser immédiatement.
CBFG - http://www.codehead.co.uk/cbfg
Chargeur Android - http://www.codehead.co.uk/cbfg/TexFont.Java
J'ai regardé l'exemple de texte Sprite et cela a l'air terriblement compliqué pour une telle tâche. J'ai aussi envisagé de rendre une texture, mais je suis inquiet des conséquences que cela pourrait avoir sur les performances. Je devrais peut-être avoir une vue à la place et m'inquiéter du portage lorsqu'il est temps de traverser ce pont :)
Regardez l'exemple "Sprite Text" dans le exemples GLSurfaceView .
À mon humble avis, il y a trois raisons d'utiliser OpenGL ES dans un jeu:
Dessiner du texte est toujours un problème dans la conception de jeux, car vous dessinez des objets, vous ne pouvez donc pas avoir l’aspect d’une activité courante, avec des widgets, etc.
Vous pouvez utiliser un framework pour générer des polices Bitmap à partir de polices TrueType et les restituer. Tous les cadres que j'ai vus fonctionnent de la même manière: générer les coordonnées de sommet et de texture pour le texte en temps d’apparition. Ce n'est pas l'utilisation la plus efficace d'OpenGL.
Le meilleur moyen consiste à allouer des tampons distants (objets de sommets de sommets - VBO) pour les sommets et les textures au début du code, en évitant les opérations de transfert de mémoire paresseux au moment de l'établissement.
Gardez à l'esprit que les joueurs n'aiment pas lire du texte, vous n'écrirez donc pas un texte long généré de manière dynamique. Pour les étiquettes, vous pouvez utiliser des textures statiques, en laissant du texte dynamique pour le temps et le score, et les deux sont numériques avec quelques caractères.
Donc, ma solution est simple:
Les opérations de dessin sont rapides si vous utilisez des tampons statiques distants.
Je crée un fichier XML avec des positions d'écran (en fonction du pourcentage diagonal de l'écran) et des textures (statique et caractères), puis je charge ce fichier XML avant le rendu.
Pour obtenir un débit en FPS élevé, vous devez éviter de générer des VBO au moment de la publication.
Si vous insistez pour utiliser GL, vous pouvez rendre le texte en textures. En supposant que la majeure partie du HUD soit relativement statique, vous ne devriez pas avoir à charger les textures trop souvent dans la mémoire de texture.
Jetez un coup d'œil à CBFG
et au port Android du code de chargement/rendu). Vous devriez pouvoir insérer le code dans votre projet et l'utiliser immédiatement.
J'ai des problèmes avec cette implémentation. Il n'affiche qu'un seul caractère. Lorsque j'essaie de modifier la taille du bitmap de la police (j'ai besoin de lettres spéciales), le tirage au complet échoue :(
Cela faisait quelques heures que je cherchais cela, c’était le premier article que j’ai découvert et bien qu’il ait la meilleure réponse, les réponses les plus populaires, à mon avis, sont erronées. Certainement pour ce dont j'avais besoin. Les réponses de Weichsel et de Shakazed étaient exactes au bouton mais un peu obscurcies dans les articles. Pour vous mettre droit au projet. Ici: Créez simplement un nouveau Android projet basé sur un exemple existant. Choisissez ApiDemos:
Regardez sous le dossier source
ApiDemos/src/com/example/Android/apis/graphics/spritetext
Et vous trouverez tout ce dont vous avez besoin.
Pour texte statique :
Pour un texte long qui doit être mis à jour de temps en temps:
Pour un nombre (formaté 00.0):
Dans votre événement onDraw, ne mettez à jour que la variable de valeur envoyée au 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;
}
Le code ci-dessus fonctionne pour un atlas de texture où les nombres commencent par 0 à la 7ème colonne de la 2e ligne de l’atlas de police (texture).
Reportez-vous à https://www.shadertoy.com/view/Xl23Dw pour obtenir une démonstration (avec une texture incorrecte cependant)