OpenGL Moderne

Tutoriel 11 : texte 2D

Dans ce tutoriel, on va apprendre à afficher un texte en 2D par-dessus le contenu 3D. Ici, ce sera un simple chronomètre.

Commentez Donner une note à l'article (5)

Article lu   fois.

Les deux auteur et traducteur

Site personnel

Traducteur : Profil ProSite personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

Navigation

Tutoriel précédent : la transparence

 

Sommaire

 

Tutoriel suivant : extensions OpenGL

I. Introduction

Dans ce tutoriel, on va apprendre à afficher un texte en 2D par-dessus le contenu 3D. Ici, ce sera un simple chronomètre :

Un chronomètre au-dessus de Suzanne

II. Les fonctions

On va implémenter cette simple interface (dans common/text2D.h) :

 
Sélectionnez
void initText2D(const char * texturePath); 
void printText2D(const char * text, int x, int y, int size); 
void cleanupText2D();

Afin que le code fonctionne, que ce soit en 640 x 480 ou 1080p, x et y seront des coordonnées dans l'espace [0-800][0-600]. Le vertex shader adaptera les coordonnées pour correspondre à la taille de l'écran.

Regardez common/text2D.cpp pour voir l'implémentation complète.

III. La texture

initText2D lit simplement une texture et un couple de shaders. Il n'y a rien de spécial là-dedans, mais voyez à quoi ressemble la texture :

Texture de police

Cette texture a été générée avec CBFG, un des nombreux outils pouvant générer une texture à partir d'une police de caractères. Puis la texture a été importée dans Paint.NET où j'ai ajouté un fond rouge (seulement pour des raisons de visualisation : là où c'est rouge, cela devrait être transparent).

Le but de printText2D sera donc de générer des rectangles avec les positions écran appropriées et les coordonnées de texture.

IV. L'affichage

On doit remplir ces tampons :

 
Sélectionnez
std::vector<glm::vec2> vertices; 
std::vector<glm::vec2> UVs;

Pour chaque caractère, on calcule les coordonnées des quatre sommets définissant le rectangle et on ajoute les deux triangles :

 
Sélectionnez
for ( unsigned int i=0 ; i<length ; i++ ){ 
 
    glm::vec2 vertex_up_left    = glm::vec2( x+i*size     , y+size ); 
    glm::vec2 vertex_up_right   = glm::vec2( x+i*size+size, y+size ); 
    glm::vec2 vertex_down_right = glm::vec2( x+i*size+size, y      ); 
    glm::vec2 vertex_down_left  = glm::vec2( x+i*size     , y      ); 
 
    vertices.push_back(vertex_up_left   ); 
    vertices.push_back(vertex_down_left ); 
    vertices.push_back(vertex_up_right  ); 
 
    vertices.push_back(vertex_down_right); 
    vertices.push_back(vertex_up_right); 
    vertices.push_back(vertex_down_left);

Maintenant les coordonnées UV. La coordonnée du coin supérieur gauche est calculée comme suit :

 
Sélectionnez
char character = text[i]; 
float uv_x = (character%16)/16.0f; 
float uv_y = (character/16)/16.0f;

Cela fonctionne (en quelque sorte - voyez ci-dessus) car le code ASCII pour A est 65.

65 % 16 = 1, donc A est sur la colonne numéro 1 (on commence à 0 !).

65 / 16 = 4, donc A est sur la ligne numéro 4 (c'est une division entière, donc ce n'est pas 4.0625 comme cela devrait l'être).

Les deux sont divisés par 16.0 pour rentrer dans l'échelle [0.0 - 1.0] nécessaire aux textures OpenGL.

Et maintenant, on doit faire une chose très proche de ce que nous faisions, mais pour les coordonnées de texture :

 
Sélectionnez
   glm::vec2 uv_up_left    = glm::vec2( uv_x           , 1.0f - uv_y ); 
   glm::vec2 uv_up_right   = glm::vec2( uv_x+1.0f/16.0f, 1.0f - uv_y ); 
   glm::vec2 uv_down_right = glm::vec2( uv_x+1.0f/16.0f, 1.0f - (uv_y + 1.0f/16.0f) ); 
   glm::vec2 uv_down_left  = glm::vec2( uv_x           , 1.0f - (uv_y + 1.0f/16.0f) ); 
 
   UVs.push_back(uv_up_left   ); 
   UVs.push_back(uv_down_left ); 
   UVs.push_back(uv_up_right  ); 
 
   UVs.push_back(uv_down_right); 
   UVs.push_back(uv_up_right); 
   UVs.push_back(uv_down_left); 
}

Le reste est habituel : liaison des tampons, remplissage, sélection du shader program, liaison de la texture, activation/liaison/configuration des attributs de sommets, activation du mélange et appel de la fonction glDrawArrays. Hourra ! Vous avez fini.

Les coordonnées sont générées sur l'échelle [0-800][0-600]. En d'autres mots, il n'y a PAS BESOIN de matrices ici. Le vertex shader doit juste les passer à l'échelle [-1,1][-1,1] avec une simple opération mathématique (cela aurait aussi pu être fait en C++).

 
Sélectionnez
void main(){ 
 
    // position de sortie du sommet, dans l'espace de découpage
    // map [0..800][0..600] to [-1..1][-1..1] 
    vec2 vertexPosition_homoneneousspace = vertexPosition_screenspace - vec2(400,300); // [0..800][0..600] -> [-400..400][-300..300] 
    vertexPosition_homoneneousspace /= vec2(400,300); 
    gl_Position =  vec4(vertexPosition_homoneneousspace,0,1); 
 
    // coordonnées UV du sommet. Pas d'espace de coordonnées spécifique pour celles-ci. 
    UV = vertexUV; 
}

Le fragment shader ne fait que très peu de choses :

 
Sélectionnez
void main(){ 
    color = texture( myTextureSampler, UV ); 
}

D'ailleurs, n'utilisez pas ce code en production, car il ne gère que l'alphabet latin. Ou alors ne vendez rien en Inde, Chine, Japon (ou même Allemagne, car il n'y a pas de ß dans cette image). Cette texture marchera principalement en France (remarquez la présence des é, à, ç, etc.) car elle a été générée avec ma locale. Et soyez attentionné lors de l'adaptation du code des autres tutoriels ou lorsque vous avez recours à des bibliothèques, la plupart d'entre eux utilisent OpenGL 2 qui n'est pas compatible. Malheureusement, je ne connais pas de bibliothèque qui gère UTF-8.

À propos de cela, vous devez lire The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!) (le minimum absolu que chaque développeur de logiciels devrait absolument connaître sur l'Unicode et les ensembles de caractères (pas d'excuse !) de Joel Spolsky.

Regardez aussi l'article de Valve si vous avez besoin de grand texte.

V. Remerciements

Cet article est une traduction autorisée dont le texte original peut être trouvé sur opengl-tutorial.org.

Navigation

Tutoriel précédent : la transparence

 

Sommaire

 

Tutoriel suivant : extensions OpenGL

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2014 opengl-tutorial.org. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.