Textures
Jusqu’ici, les surfaces étaient simplement coloriées, ce qui ne permet pas d’avoir des rendus très réalistes. Les textures améliore considérablement le rendu. Le principe est de remplir une surface avec une image bitmap correspondant à une photo de la surface.
Un texture est appliquée à une surface de la manière suivante :
· d’abord il faut indiquer quelle image il va falloir appliquer (ce qui revient à donner un pointeur vers l’image).· ensuite, avant d’entrer chaque point (vertex) de la surface, il faut préciser où se situe ce point dans la texture, c’est le même principe qu’avant avec glColor. Cela revient en quelque sorte à mettre des épingles sur la texture
OpenGL se charge ensuite d’appliquer la texture, en faisant correspondre les vertex avec les « épingles ».
Dans ce cours, les routines de chargement d’images en mémoire ne seront pas expliquées. Ce dont à besoin OpenGL, c’est d’une image non compressée chargée sous la forme d’un tableau en mémoire. Il existe de nombreuses routines toutes faites pour charger les principaux formats d’images (BMP, PCX, GIF, PNG…).
2.1. Spécifier l’image
Pour spécifier la texture à utiliser, appeler :
void glTexImage2D( GLenum target, GLint level, GLint components, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels )Avec :
Target |
GL_TEXTUTE_2D |
Level |
Indique le niveau de mipmapping, expliqué plus loin dans ce cours |
Components |
Nombre de composantes dans la texture (1, 2, 3 ou 4). Généralement 3 (pour RGB) ou 4 (pour RGBA). |
Width |
Largeur de l’image. Doit être 2n + 2 (dépend de bordure). |
Height |
Hauteur de l’image. Doit être 2m + 2 (dépend de bordure). |
Bordure |
Indique la largeur de la bordure : 0 ou 1 |
Format |
Codage des couleurs : GL_COLOR_INDEX, GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA, GL_RGB, GL_RGBA, GL_LUMINANCE, ou GL_LUMINANCE_ALPHA |
Type |
Type du tableau : GL_UNSIGNED_BYTE, GL_BYTE, GL_BITMAP, GL_UNSIGNED_SHORT, GL_SHORT, GL_UNSIGNED_INT, GL_INT, ou GL_FLOAT |
Pixel |
Pointeur vers l’image. |
2.2. Les « épingles »
La première chose à faire, c’est d’activer les textures :
glEnable(GL_TEXTURE_2D) ;
Pour poser une épingle, avant un vertex il faut appeler :
void glTexCoord2f( GLfloat s, GLfloat t )
ou l’une de ses variantes.
Les coordonnées (s, t) indiquent la correspondance entre
le prochain vertex entré et la texture. En OpenGL, le repère d’une
texture fait toujours [0..1] sur [0..1], mais s et t peuvent sortir
des ces intervalles, l’effet dépend alors du paramètrage choisi.
Lorsque s et t sont compris entre 0 et 1, OpenGL va positionner les épingles directement sur la texture. Vous pourrez ainsi utiliser seulement une petite partie de celle-ci. Si la surface sur laquelle vous posez la texture est trés grande, il va étirer la texture pour la placer sur votre surface.- A l'inverse, si t et s (ou seulement un seul) sont supérieurs à 1, la texture va être répétée.
La texture appliquée sur la base du temple est répétée plusieurs fois.
La base a une longueur de 40 unités sur 4. Les valeurs de s et t sont respectivement 2 et 20
(les valeurs suivantes sont généralement intéressantes : s = 1/2 * largeur et t = 1/2 * hauteur)2.3. Paramétrage
2.3.1. Application de la texture
Les paramètres d’application d’une texture sont :
Nom |
Type |
Valeur possibles |
Signification |
GL_TEXTURE_WRAP_S |
Entier |
GL_CLAMP, GL_CLAMP_TO_EDGE, GL_REPEAT |
Indique ce qui se passe si une coordonnée s sort de l’intervalle [0..1]. Soit la texture est répètée (mosaïque), soit seulement le bord est répété. |
GL_TEXTURE_WRAP_T |
Entier |
Idem |
Idem pout t |
GL_TEXTURE_MIN_FILTER |
Entier |
NEAREST, LINEAR, NEAREST_MIPMAP_NEAREST, NEAREST_MIPMAP_LINEAR, LINEAR_MIPMAP_NEAREST, LINEAR_MIPMAP_LINEAR |
Indique la qualité de l’agrandissement d’une texture. LINEAR est meilleur que NEAREST, mais plus lent. |
GL_TEXTURE_MAG_FILTER |
Entier |
NEAREST, LINEAR |
Idem en réduction |
Il existe d’autres paramètres, mais ceux-ci sont de loin les plus importants. Pour changer un paramètre, il faut appeler :
void glTexParameteri(GLenum target, GLenum pname, GLint param )
avec target = GL_TEXTURE_2D, pname l’un des noms de paramètres, et param la nouvelle valeur.
2.3.2. glColor
La couleur courante (spécifiée par glColor), joue un rôle différent suivant les paramètres passés à :
void glTexEnvi( GLenum target, GLenum pname, Glint param )avec target = GL_TEXTURE_ENV, pname = GL_TEXTURE_ENV_MODE, et param = GL_REPLACE, GL_MODULATE, GL_DECAL, ou GL_BLEND.
Il y a beaucoup de cas suivant le nombre de composantes de la texture. Dans le cas de 3 ou 4 composantes (RGB ou RGBA), les modes les plus courants sont :
· GL_REPLACE, la couleur courante n’intervient pas· GL_MODULATE, la couleur courante multiplie la texture. Il est alors possible de changer très facilement l’intensité d’une texture, en dessinant d'abord la surface en blanc, par exemple, lequel va être atténué selon la position de la lumière, puis en plaquant la texture. L'intensité de cette dernière va varier selon l'éclairage...
![]() Ici, c'est plutôt sombre |
![]() Mais, en changeant la position de la lumière, cela devient beaucoup plus clair |
Voici un cube texturé. Pour simplifier (et garantir la portabilité), le format d’image est ici le « Raw », c’est-à-dire l’image seule. Paint Shop Pro gère très bien ce format. Le problème est que la taille de l’image n’est pas inclus dans ce fichier. Mon image à une taille de 128*128 et est au format RGB (24 bits = 3 octets).
void init()
{
GLfloat Blanc[] = {1.0f, 1.0f, 1.0f, 1.0f};
//Taille = 128 * 128. RGB 3 octets
char buffer[128*128*3];
FILE *f = fopen("herbe.raw", "rb");
if (f)
{
fread(buffer, 128*128*3, 1, f);
fclose(f);
printf("texture chargée");
//On spécifie qu'elle texture, sa taille, son type ...
glTexImage2D(GL_TEXTURE_2D,0,3, 128, 128, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer);
//cela définit la façon dont la texture est appliquée. LINEAR est le meilleur, mais le + lent.
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//On répète la texture si s et t sortent des bornes [0,1]
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
//ouf, ca y est !!!
glEnable(GL_TEXTURE_2D);
}else printf("Il y a un problème de fichier!!!!\n");
cube = glGenLists(1);
glNewList(cube, GL_COMPILE);
glBegin(GL_QUADS);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Blanc);
glNormal3d(0.0,0.0,1.0);
//on spécifie la position des épingles (on tourne : une seule coordonnée change à la fois)
glTexCoord2d(0,0);
glVertex3d(-1, 1, 1);
glTexCoord2d(0,1);
glVertex3d(-1, -1, 1);
glTexCoord2d(1,1);
glVertex3d( 1, -1, 1);
glTexCoord2d(1,0);
glVertex3d( 1, 1, 1);
glNormal3d(-1.0,0.0,0.0);
//idem glTexCoord2d(0,0);
glVertex3d( -1, 1, 1);
glTexCoord2d(0,1);
glVertex3d( -1, 1, -1);
glTexCoord2d(1,1);
glVertex3d( -1, -1, -1);
glTexCoord2d(1,0);
glVertex3d( -1, -1, 1);
//idem
glNormal3d(1.0,0.0,0.0);
glTexCoord2d(0,0);
glVertex3d( 1, 1, 1);
glTexCoord2d(0,1);
glVertex3d( 1, -1, 1);
glTexCoord2d(1,1);
glVertex3d( 1, -1, -1);
glTexCoord2d(1,0);
glVertex3d( 1, 1, -1);
glNormal3d(0.0,1.0,0.0);
//idem glTexCoord2d(0,0);
glVertex3d( -1, 1, 1);
glTexCoord2d(0,1);
glVertex3d( 1, 1, 1);
glTexCoord2d(1,1);
glVertex3d( 1, 1, -1);
glTexCoord2d(1,0);
glVertex3d( -1, 1, -1);
glNormal3d(0.0,-1.0,0.0);
//idem
glTexCoord2d(0,0);
glVertex3d( -1, -1, 1);
glTexCoord2d(0,1);
glVertex3d( -1, -1, -1);
glTexCoord2d(1,1);
glVertex3d( 1, -1, -1);
glTexCoord2d(1,0);
glVertex3d( 1, -1, 1);
glNormal3d(0.0,0.0,-1.0);
//idem
glTexCoord2d(0,0);
glVertex3d( 1, 1, -1);
glTexCoord2d(1,0);
glVertex3d( 1, -1, -1);
glTexCoord2d(1,1);
glVertex3d( -1, -1, -1);
glTexCoord2d(0,1);
glVertex3d( -1, 1, -1);
glEnd();
glEndList();
}
}
Autres
Formats :
Voici 2 fichiers ZIP contenant les sources de petites librairies qui
permettent de charger des fichiers TGA et BMP.
Download TGA.zip
Download BMP.zip
Le MipMapping consiste à avoir la même texture à plusieurs échelles, de façon à limiter les transferts mémoires lors du plaquage de texture (inutile de transférer une texture 256x256 si l’objet dessiné est très loin dans le décor et ne fait plus que du 5x5).
En théorie, le programmeur devrait avoir 3 ou 4 fois la même texture, mais avec une résolution différente. Il faut alors entrer toutes les textures à chaque fois, en changeant le paramètre level de glTextImage2D (sachant que 0 est l’image la plus grande, et les suivantes sont ses réductions). Lorsque la texture est appliqué, OpenGL soit choisi l’image de taille la plus proche de la surface à afficher (NEAREST_MIPMAP), soit fait une sorte de moyenne entre l’image de taille juste au dessus et celle juste en dessous (LINEAR_MIPMAP).
En pratique, il y a une fonction glu :
int gluBuild2DMipmaps(GLenum target, GLint components, GLint width, GLint height, GLenum format, GLenum type, const void *data )
Les paramètres sont les mêmes que pour glTexImage2D, sauf qu’il est cette fois possible d’utiliser des images de dimensions qui ne sont pas des puissances de 2. L’image passée est celle de meilleure qualité (level = 0).
L’intérêt du MipMapping est très limité si les textures
ne sont pas stockées dans la carte vidéo.
Le principe est similaire à celui des listes :
void glGenTextures( GLsizei n, GLuint * textures)
permet de réserver n textures, les numéros sont alors placés dans le tableau pointé par textures. Ensuite le numéro de la texture courante est changé par :
void glBindTexture( GLenum target, GLuint texture)avec target = GL_TEXTURE_2D et texture le numéro de la texture à activer. Ensuite toutes les fonctions faisant intervenir une texture utilisent la texture active.
void glDeleteTextures(GLsizei n, const GLuint * textures )
Pour résumer, une texture est le plus souvent chargée par :
glGenTextures(1, &Texture_num);
glBindTexture(GL_TEXTURE_2D, Texture_num);
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height(), GL_RGB, GL_UNSIGNED_BYTE, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
Ensuite pour activer une texture, avant un glBegin :
glBindTexture(GL_TEXTURE_2D, Texture_num);
Comme il se peut que toutes les textures ne rentrent pas dans la carte vidéo, il est possible de donner des priorités au chargement des textures en mémoire vidéo.
void glPrioritizeTextures( GLsizei n, GLuint * textures, GLclampf * priorities)
avec priorities un tableau de n flottants appartenant à [0..1], 0 étant la priorité la plus faible et 1 la plus forte.