Multitextring
Cette page (tiree de http://reality.sgi.com/jamesb) est en anglais mais il y a plein d'images. L'exemple est impressionnant (voir la page pour les touches) : Technique/reality.sgi.com/jamesb/multi.exe.zip
Le multitexturing consiste comme son nom l'indique a faire plusieurs
passages par face. La fonction la plus importante est glBlendFunc, deja vue pour la
transparence. Mais si elle s'appelle ainsi et pas glTransparencyFunc, ce n'est pas pour
rien !
Blend signifie ici melanger les couleurs, et il se trouve que la
transparence c'est la meme chose, mais il est possible de melanger les couleurs de
plusieurs facons. Maintenant c'est un autre aspect d'OpenGL qui apparait : des fonctions
puissantes, mais le moindre parametre oublie et l'effet n'est pas du tout ce qu'il etait
prevu.
glBlendFunc prend deux parametres : le 1er pour indiquer la maniere
dont un nouveau point est transforme, le 2eme pour le point deja dans le buffer video.
Ensuite les deux valeurs sont ajoutees (composante par composante, si >1 alors = 1).
Voici la nouvelle fonction Draw de CCube :
void CCube::Draw(float x, float y, float z,
bool multitexturing)
{
glPushMatrix();
glTranslated(m_x - x, m_y - y, m_z - z);
glRotated(m_rx, 1.0, 0.0, 0.0);
glRotated(m_ry, 0.0, 1.0, 0.0);
glRotated(m_rz, 0.0, 0.0, 1.0);
if (multitexturing)
{
GLfloat amplitude =
1.0f;
glColor3f(amplitude,
amplitude, amplitude);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glEnable(GL_BLEND);
glDepthFunc(GL_LEQUAL);
glDepthMask(GL_TRUE);
glDisable(GL_LIGHTING);
glDisable(GL_DITHER);
glShadeModel(GL_FLAT);
glEnable(GL_TEXTURE_2D);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (m_changeable)
DrawCube();
else
glCallList(m_num_list);
glEnable(GL_LIGHTING);
glDisable(GL_TEXTURE_2D);
glDepthFunc(GL_EQUAL);
glDepthMask(GL_FALSE);
glEnable(GL_DITHER);
glBlendFunc(GL_ONE,
GL_ONE);
if (m_changeable)
DrawCube();
else
glCallList(m_num_list);
}
else
if (m_changeable)
DrawCube();
else
glCallList(m_num_list);
glPopMatrix();
}
Changement de lampes dans OnSize :
GLfloat lightZeroPosition[] =
{5.0f, 0.0f, -15.0f, 1.0f};
GLfloat lightZeroColor[] = {0.8f, 1.0f, 0.8f, 1.0f}; /* green-tinted */
GLfloat lightOnePosition[] = {-10.0f, 0.0f, -10.0f, 0.0f};
GLfloat lightOneColor[] = {0.6f, 0.3f, 0.2f, 1.0f}; /* red-tinted */
glLightfv(GL_LIGHT0, GL_POSITION, lightZeroPosition);
glLightfv(GL_LIGHT0, GL_DIFFUSE, lightZeroColor);
glLightfv(GL_LIGHT0, GL_SPECULAR, lightZeroColor);
glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.05f);
glLightfv(GL_LIGHT1, GL_POSITION, lightOnePosition);
glLightfv(GL_LIGHT1, GL_DIFFUSE, lightOneColor);
glLightfv(GL_LIGHT1, GL_SPECULAR, lightOneColor);
glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.05f);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHT1);
GLfloat white[] = {1.0f, 1.0f, 1.0f, 1.0f};
glMaterialfv(GL_FRONT, GL_DIFFUSE, white);
glMaterialfv(GL_FRONT, GL_SPECULAR, white);
glMaterialf(GL_FRONT, GL_SHININESS, 20.0f);
D'abord, ce qu'il aurait fallu rajouter dans l'exemple de texture :
couleur a (1.0, 1.0, 1.0, 1.0) et cette valeur sert a multiplier (GL_MODULATE) la couleur
de la texture (ce sont les valeurs par defaut).
Ensuite activer le blending. Puis mettre comme fonction de test de
profondeur GL_LEQUAL, et autoriser l'ecriture dans le Z-Buffer (une fois encore, ce sont
les valeurs par defaut, mais elles sont changees au deuxieme passage).
L'eclairage est desactive. Ce n'est pas obligatoire, mais le resultat
est meilleur, surtout qu'ici il n'y a plus de lumiere ambiente, on ne voit pas grand
chose.
L'application de texture est activee, et la fonction de blending est
celle deja vue pour la transparence, mais quelques explications :
- GL_SRC_ALPHA : la couleur a appliquer est
multipliee par son facteur alpha (1.0 par defaut)
- GL_ONE_MINUS_ALPHA : signifie "1 moins
le facteur alpha", la couleur destination est multipliee par 1 - le facteur alpha de
la couleur a appliquer. Cela donne bien un effet de transparence : si un point opaque
(alpha = 1.0) vient par dessus, la couleur du dessous est effacee, ce qui revient a la
multiplier par 0. Au contraire si le point est parfaitement transparent (alpha = 0.0), il
n'apparait pas (est multiplie par 0) et celui du dessous reste tel quel (multiplie par 1).
Il y a d'autres possibilites, mais ce sont les plus courantes.
Deuxieme passage : cette fois on met la lumiere, enleve la texture.
Cependant le cube a deja ete dessine. Pour gagner du temps, il suffit de ne dessiner que
les points qui apparaissent, c'est-a-dire ceux qui ont la meme profondeur que le point
precedement affiche (GL_EQUAL). Il ne faut pas modifier le Z-Buffer.
Pour superposer directement le nouveau rendu du cube (cette fois juste
de faces unies), on utilise GL_ONE pour les nouveaux points et les anciens (GL_ONE signife
multiplier par 1).
Pour les lumieres, ce qui est important c'est qu'elle ait un parametre
"specular", c'est un spot lumineux, c'est lui qui donne les reflets.
L'attenuation lineaire diminue l'intensite avec la distance. Pour plus de realite
utliliser GL_QUADRATIC_ATTENUATION, mais tres vite la lumiere n'eclaire plus.
Le spot ne se voit pas bien car l'intensite lumineuse n'est calculee
qu'en chaque vertex : pour voir le spot, il faudrait decouper chaque face du cube en
plusieurs carres (ou triangles...). Un autre moyen est de changer pour une sphere, il y a
d'ailleurs une fonction gluSphere. Pour tester, changer CCube::DrawCube :
GLUquadricObj *obj =
gluNewQuadric();
gluSphere(obj, 7, 20, 20);
gluDeleteQuadric(obj);
Pour qu'elles soient plus belles, passer en glShadeModel(GL_SMOOTH).
Reduire si besoin le nombre de spheres (dans TutorialView.h). Augmenter le nombre de
divisions des spheres.
Un defaut apparait dans la transparence : les spheres sont visibles a
l'interieur de celle de droite mais pas celle de gauche. Ceci est "normal" : le
fait que cela soit la sphere de droite ou de gauche depend de l'ordre d'affichage, mais la
disparation d'une partie des points est due au "depth-test", qui est a
GL_LEQUAL. Lorsque l'on dessine une deuxieme sphere, la premiere est en partie recouverte,
la il n'y a pas de probleme (elle apparait sous la nouvelle), mais lorsque la seconde est
sous la premiere, les points ne passent pas le "depth-test", puisqu'ils sont
plus loin (et donc pas less or equal) : ils disparaissent. Il est possible d'enlever le
test (ou le mettre a GL_ALWAYS), mais alors la fonction de blending n'est plus la bonne.
En mettant GL_ONE en second parametre de glBlendFunc tout est apparent, mais l'effet de
transparence n'est pas terrible, et rien n'est attenue, cela sature (tout est blanc).
Seule solution : afficher les faces dans le bon ordre pour qu'elles se recouvrent
correctement (c'est la seule que je connaisse).
Code source : Tutorial8.zip