Utilisation des extensions OpenGL sur une NVidia GeForce (256,2 ou 3)


Rappels sur l'OpenGL

NB: je considère que vous connaissez bien OpenGL 1.1. Pour réviser, ce site permet de se rafraichir la mémoire.
Le pipeline OpenGL se divise en deux moteurs principaux:

Dans la version d'OpenGL 1.1, les limitations sont: Lorsque les constructeurs developpent de nouvelles fonctionnalites sur les cartes videos, ils fournissent au programmeur l'acces a ces fonctionnalites par l'intermediaire d'extensions. Ces extensions s'accedent au sein d'OpenGL detaille comme suit.


Utilisations des extensions

Sous linux (cést le seul unix sous lequel la GeForce tourne), pour utiliser une extension (i.e soit une fonction, soit un flag), il suffit que cette fonction soit définie dans le gl.h et que ca compile. Sous Windows, l'accès est un peu different. Soit vous trouvez un fichier glext.h qui vous permet d'avoir toutes les definitions, sinon il vous faut chercher le profil de la fonction et creer votre glext.h a la main. La démarche consiste à récupérer l' adresse de chaque fonction que l' on veut utiliser. Ceci permet de savoir si la carte dispose de cette extension lors du lancement du programme. Pour ceci, il s'agit de déclarer un pointeur sur ce type de fonction. Pour initialiser votre programme, vous devez avant d'entrer dans votre boucle d'evenements récuperer l'adresse dans le driver de cette fonction par l'appel a la fonction windows wglGetProcAdress("TokenDeLaFonction"). Si le pointeur renvoye n'est pas nul, alors la fonction est utilisable.

Utilisation du multitexture

Puisqu'OpenGL est une machine d'états finis, il faut toujours spécifier à OpenGL quelle est la texture sur laquelle les opérations vont s'appliquer. Pour le multitexture, le principe consiste à spécifier l'unité de texture sur laquelle les opérations de texture s'appliquent. On utilise pour celà la fonction glActiveTextureARB. Avant toute operation liée à la texture, on appelle un glActiveTextureARB(GL_TEXTURE0_ARB) ou GL_TEXTURE1_ARB ou GL_TEXTUREk_ARB (0<k<n) où n est le nombre d'unites de textures disponibles simultanement pour le hard. Dans le cas des GeForce 256 et GeForce 2, ce nombre est 2. Dans le cas de la GeForce 3, ce nombre est 4. (Attention cependant l'utilisation des 4 textures divise le frame rate par 2) Ensuite, pour utiliser plusieurs textures simultanement, il faut pour chaque unité de texture activer l'utilisation de la texture a l'aide de glEnable(GL_TEXTURE_2D) ou glEnable(GL_TEXTURE_CUBE_MAP_EXT) ou glEnable(GL_TEXTURE_1D).
Dans le cas de 2 textures simultanees:

glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(montexid[0]);
glActiveTextureARB(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(montexid[1]);
Ensuite pour definir les coordonnees de textures pour un sommet, (par exemple pour des textures 2D) glMultiTexCoord2fARB(GL_TEXTURE0_ARB,0.,1.);
glMultiTexCoord2fARB(GL_TEXTURE1_ARB,0.5,1.);
glVertex3fv(MonVertex.pos);
Remarque: on n'est pas obligé d'utiliser des textures 2D pour chaque unité de texture. On peut très bien utiliser une cube map et une texture 1D, ou n'importe quelle autre configuration de textures.

Utilisation des cube maps

Un cube map peut se voir comme une table d'indirection permettant d'associer à une direction donnée une valeur RGB(A). Comment les utiliser? En fait, les texture target sont déjà alloués:

NB: il n'y a quún seul contexte de texture pour une cube map. Toutes les opérations de création de texture s'appellent comme d'habitude sauf que le target est un de ces texture id. glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT, ) va donc charger dans la face orientee vers +x la texture 2D passee en parametre de glTexImage2D. Ensuite on effectue les operations souhaitees. On peut utiliser ces cube maps selon 2 modes, reflections et normales.
Pour le mode reflection (Env Map), il faut dire:

glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_REFLECTION_MAP_EXT);
glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_REFLECTION_MAP_EXT);
glTexGeni(GL_R,GL_TEXTURE_GEN_MODE,GL_REFLECTION_MAP_EXT);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glEnable(GL_TEXTURE_GEN_R);
Il faudra ensuite penser que le cube est situe autour de l'objet et que les faces du cube correspondent à l'image de l'environement extérieur vu du centre de l'objet.
Pour le mode Normales (renormalisation), il faut taper:

glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_NORMAL_MAP_EXT);
glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_NORMAL_MAP_EXT);
glTexGeni(GL_R,GL_TEXTURE_GEN_MODE,GL_NORMAL_MAP_EXT);
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glDisable(GL_TEXTURE_GEN_R);
Ce mode est appelé renormalisation car le but principal de son implémentation est de pouvoir renormaliser n'importe quel vecteur. Comme coordonnées de texture, il suffit de donner les coordonnées du vecteur. La couleur renvoyée est la couleur située dans le cube map à l'intersection du vecteur et du cube. Il suffit que les textures soient construites de facon à ce que chaque vecteur issu de l'origine et touchant le cube ait comme couleur lui-meme renorme en RGB. Attention tout de meme, il ne faut pas oublier le glEnable(GL_TEXTURE_CUBE_MAP_EXT) pour que la cube map soit utilisee par gl.

Utilisation des registers combiners

Le mécanisme des registers combiners intervient dans le raster engine. En OpenGL standard, la fonction de mélange des attributs divers (couleur du materiau, couleur de la lumière, couleur de la texture) pour le pixel courant n'est pas reprogrammable (juste une poignée de méthodes entre lesquelles choisir). Le calcul de mélange des attributs est fixe. L'idée de ce genre de "Pixel Shaders" est de permettre au programmeur de redéfinir la fonction de mélange des attributs. Lorsque le raster engine a fini de calculer les données associées au fragment, il execute le pixel shader. Ce pixel shader est en fait un micro-programme executé par le hardware pour chaque pixel a tracer à l'écran, qui prend en entrée toutes les données calculées par le raster pour le pixel. Ce pixel shader est implementé sur NVidia GeForce par les Register combiners. Dans ces Register combiners, on peut effectuer:

Pour de plus amples informations: le document de MJKilgard: A practical and robust approach for bump-mapping with Today'hardware


En fait, les register combiners se présentent comme un micro-programme découpé en plusieurs étages. Ceux-ci sont soit des étages generaux, soit l'étage final. Au sein de chaque étage général, on dispose pour le pixel des valeurs:

NOM DE REGISTRE Valeur initiale Droits d'acces Description
GL_ZERO 0 Lecture seule Une constante
GL_CONSTANT_COLOR0_NV definie par l'application Lecture seule Une valeur constante pour un meme shader
GL_CONSTANT_COLOR1_NV definie par l'application Lecture seule id
GL_FOG selon les parametres de glFogi et du fragment Lecture seule En RGB la couleur du fog, et en alpha son opacite
GL_PRIMARY_COLOR_NV couleur interpolee Lecture/ecriture La couleur du fragment definie par glColor ou glColorMaterial
GL_SECONDARY_COLOR_NV couleur interpolee Lecture/ecriture Une extension OpenGL permet de definir une seconde couleur de materiau
GL_SPARE0_NV indefinie Lecture ecriture Un registre de travail
GL_SPARE1_NV indefinie Lecture ecriture Un registre de travail
GL_TEXTURE0_ARB couleur filtree de l'unite de texture 0 pour le fragment courant Lecture ecriture (attention, on ne modifie pas la valeur de la texture) Une information de texture
GL_TEXTURE1_ARB couleur filtree de l'unite de texture 1 pour le fragment courant Lecture ecriture Une information de texture
GL_TEXTUREi_ARB couleur filtree de l'unite de texture i pour le fragment courant, dans le cas de la GeForce 3, i vaut (0,1,2,3) Lecture ecriture Une information de texture

Ensuite on dispose de 8 registres, 4 en RGB, 4 en alpha qui vont permettre de décomposer les opérations. L'idée est de dire que les opérations en RGB vont être indépendantes de celles en alpha. On nomme alors ces registres A,B,C,D. On peut donner à chaque registre une des valeurs des registres précédemment décrits auxquels on a éventuellement appliqué une fonction de conversion. La différence entre les GeForce 256 et 2 par rapport à la GeForce 3 est qu'elles disposent de moins de textures en même temps (2 au lieu de 4). Ces fonctions de conversions sont:
Mapping d'entrée Fonction appliquée Intervalles Explications
GL_UNSIGNED_IDENTITY_NV max(0,x) [0..1]=>[0..1] Presque l'identité (ne pas oublier que les valeurs negatives sont clampées avec ce mecanisme)
GL_SIGNED_INVERT_IDENTITY_NV 1.-min(max(0,x),1) [0..1]=>[1..0] Presque l'inversion sauf qu'on clampe ce qui depasse 1
GL_EXPAND_NORMAL_NV 2*max(0,x)-1 [0..1]=>[-1..1] La fonction de conversion d'une couleur en un vecteur (attention le vecteur 0,0,0 est représenté par 0.5,0.5,0.5)
GL_EXPAND_NEGATE_NV -2*max(0,x)+1 [0..1]=>[1..-1] La conversion d'une couleur en le vecteur opposé
GL_HALF_BIAS_NORMAL_NV max(0,x)-0.5 [0..1]=>[-0.5..0.5] sans commentaire
GL_HALF_BIAS_NEGATE_NV -max(0,x)+0.5 [0..1]=>[0.5..-0.5] sans commentaire
GL_SIGNED_IDENTITY_NV x [-1..1]=>[-1..1] l'identite signee
GL_SIGNED_NEGATE_NV -x [-1..1]=>[1..-1] L'inverse signee


Cette figure montre un exemple d'utilisation d'un étage. Pour se représenter le mécanisme des register combiners, il faut imaginer un processeur. Une partie des registres sont accessibles par le programmeur (voir tableau precedent), une partie des registres sont réservés par le processeur pour réaliser les opérations de calculs intermédiaires (registres A B C D). Pour executer une partie de micro-programme de pixel shader, il faut imaginer l'execution d'une operation dans un processeur. Le processeur va charger dans ses registres de travail les valeurs contenus dans ceux du programmeur, en utilisant un mode d'interprétation particulier de chaque valeur dans le registre de travail. Ensuite, il exécute forcément une opération de multiplication (produit scalaire ou composante a composante) entre A et B, et entre C et D, en parallèle. Ensuite, le programmeur choisit la destination de chaque produit (ou il peut choisir de ne pas les stocker). De plus, une opération d'addition ou de sélection peut s'effectuer sur le résultat des deux produits précédents. La sélection correspond à : si SPARE0.alpha>0.5 alors CD sinon AB. Ensuite on peut stocker le resultat ou ne pas en tenir compte. L'opération finale de scale et bias s'applique à tous les resultats (produits + addition). Tous les résultats ne peuvent stocker que dans les resultats accessibles au programmeur.



Générateurs de code

Le générateur de code d'un étage normal
Le générateur de code de l'étage final
NB:Pour utiliser ces morceaux de code, il ne faut pas oublier de dire a la carte:

Utilisation des registers combiners

Un exemple d'application des combiners

INFORMATIONS

Auteur:Franck Senegas
Dernière Mise A Jour:22-8-2001