Dessiner un cube avec une même texture sur toutes ses faces sous OpenGL
But à atteindre
Pour commencer, affichage d'un cube...(très original non ?)... avec une même texture sur toutes les faces de ce cube.
Revenons à OpenGL...Intialisation des textures
Maintenant qu'une solution pour charger une image en mémoire a été donnée, retour à la gestion des textures sous OpenGL !
Tout d'abord, il faut activer la gestion des textures sous OpenGL. Vous l'avez deviné, il s'agit encore d'uiliser glEnable ().
Vous utiliserez le paramètre suivant: GL_TEXTURE_2D, qui laisse entendre qu'il existe des textures 1D mais aussi 3D !
Mais traitons les textures traditionnelles: les 2D !
Paramétrage de la méthode d'agrandissement/rétrécissement de la texture appliquée à un objet
L'un des problèmes qui sera rencontré, concerne la taille de la texture. Cette taille est fixe, or l'objet 3D peut avoir une taille à l'écran qui variera certainement. La texture risque alors d'être soit trop grande ou soit trop petite par rapport à la taille la face de l'objet.
OpenGL pourra soit agrandir, soit diminuer la taille de la texture d'origine à appliquer sur une face. La méthode employée sera paramétrable grâce à la fonction: glTexParameter()
void glTexParameterf(GLenum target,
GLenum pname,
GLfloat param)
ou
void glTexParameteri(GLenum target,
GLenum pname,
GLint param)
Avec target: Qui contient le type de texture que vous allez utiliser ( GL_TEXTURE_1D, GL_TEXTURE_2D et GL_TEXTURE_3D )
pname: Le filtre à appliquer. Deux filtres seront étudiés pour le moment: agrandissement (GL_TEXTURE_MAG_FILTER) ou rétrécissement (GL_TEXTURE_MIN_FILTER)
param: La valeur à appliquer au filtre. Ici pour les 2 filtres indiqués: GL_NEAREST (prendre le point le plus proche) ou GL_LINEAR (moyenne sur les 2x2 pixels les plus proches).
ou
void glTexParameterfv(GLenum target,
GLenum pname,
const GLfloat *params)
void glTexParameteriv(GLenum target,
GLenum pname,
const GLint *params)
avec params, un tableau sur les valeurs.
En sortie, la fonction pourra retourner:
GL_INVALID_ENUM Si target ou pname sont invalides
GL_INVALID_OPERATION si exécuté entre glBegin et glEnd.
Le contrôle
est nécessaire, sinon vous risquez de chercher longtemps avant de comprendre
la raison d'un non affichage de texture !
On ne parlera
plus du nombre de pixels de l'image à "texturer", mais du nombre
de texels.
Chargement des textures dans OpenGL
Après avoir chargé en mémoire une image, il faut indiquer à OpenGL où trouver cette image et comment celle-ci est composée. Il va ainsi pouvoir en faire une copie qui, si tout va bien, sera d'ailleur transférée qu'une seule fois vers la mémoire vidéo.
Vous indiquerez les informations sur l'image via la fonction void glTexImage2D():
- où trouver l'image
- et surtout la taille et la structure de celle-ci
void glTexImage2D(GLenum target,
GLint level,
GLint components,
GLsizei width,
GLsizei height,
GLint border,
GLenum format,
GLenum type,
const GLvoid *pixels)
Avec target qui ne peut prendre que GL_TEXTURE_2D
level qui sera expliqué bientôt, utilisé pour le mipmapping. Pour le moment laissé à 0.
components pour indiquer le type de codage des couleurs (1, 2, 3 ou 4).
width, largeur de l'image (doit être d'une taille de 2^n + 2 * border) (voir GL_MAX_TEXTURE_SIZE pour la taille maxi)
height, hauteur de l'image (2^m + 2 * border) (voir GL_MAX_TEXTURE_SIZE pour la taille maxi)
border doit être 0 ou 1
format, format des pixels de l'image chargée: GL_COLOR_INDEX, GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA, GL_RGB, GL_RGBA, GL_LUMINANCE et GL_LUMINANCE_ALPHA.
type, type de donnée pour le pixel: GL_UNSIGNED_BYTE, GL_BYTE, GL_BITMAP, GL_UNSIGNED_SHORT, GL_SHORT, GL_UNSIGNED_INT, GL_INT, et GL_FLOAT.
pixels, il s'agit du tampon où se trouve l'image chargée.
Les erreurs possibles en retour seront :
GL_INVALID_OPERATION si exécuté entre glBegin et glEnd.
La taille des
textures avec cette fonction doit être dans une puissance de 2. Il existe
une autre fonction évitant cette contrainte (Voir le
mipmapping).
La taille maximale
d'une texture dépend de votre carte graphique (Voir chapitre Dernières
remarques sur les textures).
Appliquer la texture sur des vertice (appliquer les coordonnées UV)
Associer un texel à un vertex
Voilà, le plus gros est fait, reste à afficher la texture:
Ce sera fait via la fonction glTexCoord(). Avant de détailler la fonction, quelques explications:
Cette fonction va permettre de définir un texel à utiliser par défaut. Un texel étant la contraction de "texture element". Il s'agit donc d'un élément de la structure de la texture, Il aura donc des coordonnées dans la texture (appelées coordonnées UV).
Pour utiliser ce texel par défaut, il suffit de définir les coordonnées d'un vertex. Automatiquement, ce texel sera associé à ce vertex.
Si vous définissez un texel pour chacun de vos vertice, OpenGL dessinera automatiquement lors du rendu de l'image tous les texels compris entre les texels definis sur chacuns des vertice. Facile non ? Bon relisez le texte si ce n'est pas claire.
La fonction glTexCoord:
Elle peut être utilisée pour les types 1D, 2D ou 3D et 4D. Il suffit pour cela de le préciser en indiquant glTexCoord1, glTexCoord2, glTexCoord3 ou glTexCoord4 !
Le type des données passées à la fonction sera traditionnellement passé en indiquant d pour double, f pour float, i pour int et s pour short, ce qui donne par exemple
glTexCoord2f pour un type 2D avec des paramètres en float.
Enfin, il sera possible d'ajouter v si vous désirez passer un paramètre sur un tableau contenant les valeurs attendues par la fonction.
Les valeurs attendues sont tout simplement des coordonnées du texel. 1, 2, 3 ou 4 coordonnées suivant que vous utilisez un type 1D, 2D, 3D ou 4D.
Les coordonnées d'un texel
En 2D, on appelle ces coordonnées (u,v). Donc si vous voyez parler de UV sur des sites parlant de 3D, il ne s'agit pas d'ultra-violet !
Voici un exemple de texture:
Le texel en bas à gauche a comme coordonnées (0,0), celui en haut à droite (1,1)
Les texels ont donc des coordonnées en x entre [0, 1] et en y entre [0 et 1]
Ainsi, pour le texel du milieu, ces coordonnées seront (0.5,0.5).
Voici alors un exemple de code pour afficher une face du cube:
glBegin(GL_QUADS);
glColor3f(1.0f,1.0f,1.0f); // Couleur blanche le cube sera ainsi tout le temps
visible quelque soit la couleur émise par la source
de lumière
glNormal3f(0.0,0.0,-1.0);
glTexCoord2i(1,1);glVertex3i( 1, 1, 1); // Face avant
glTexCoord2i(0,1);glVertex3i( -1, 1, 1);
glTexCoord2i(0,0);glVertex3i( -1, -1, 1);
glTexCoord2i(1,0);glVertex3i( 1, -1, 1);
glEnd();
Lors du chargement
d'une image, celle-ci peut-être inversée à l'écran.
En effet, certains formats de fichiers images utilisent le repère habituellement
utilisé pour l'affichage à l'écran. Dans ce cas, le repère
est inversé sur l'axe des ordonnées !
Donc pour faire
l'affichage de la texture, soit vous retournez l'image en mémoire, soit
vous inversez les coordonnées de la texture !
glTexCoord2i(1,0);glVertex3i( 1, 1, 1); // Face avant
glTexCoord2i(0,0);glVertex3i( -1, 1, 1);
glTexCoord2i(0,1);glVertex3i( -1, -1, 1);
glTexCoord2i(1,1);glVertex3i( 1, -1, 1);
Exemple de code
Voici enfin le cube !
A la base, il s'agit du cube utilisé pour traiter la
lumière. Je n'ai donc pas touché à cette partie du
code, mais la caméra se déplace ainsi que le spot: juste devant
le cube (à votre place en somme !). Vous verrez ainsi que les textures
réagissent aussi à la lumière (en fait, le texel va subir
l'éclairage de la même manière que son vertex associé).
L'image utilisée est la suivante:(elle
diffère un peu de celle ci-dessus).
Copiez cette image dans c:\temp où, dans le code, modifiez la variable filename pour pointer sur un tout autre répertoire.
Annexes
OpenGL pour Linux, Windows, Android...