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:
- Le geometric engine, qui va effectuer tous les calculs 3D comme les transformations géometriques, le clipping, et le calcul de la couleur d'éclairage en chaque sommets, et qui finalement à partir de données 3D (des coordonnées de polygones dans l'espace), produit des polygones dans l'espace ecran prêts a être remplis par le raster engine
- le raster engine, qui va effectuer toutes les opérations 2D de remplissage des polygones à l'aide de la texture courante, les informations de couleurs...
- le fait de ne pas pouvoir utiliser simulténement plus d'une texture par polygone
- le fait de ne pas pouvoir effectuer soi-meme le calcul de la couleur du pixel à partir des données du raster engine
- les calculs d'éclairage sont effectués uniquement aux sommets, alors que l'on souhaiterait le faire par point
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:
- GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT
- GL_TEXTURE_CUBE_MAP_NEGATIVE_X_EXT
- GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT
- GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT
- GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT
- GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT
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:
- comme operations vectorielles:
- Produit scalaire
- Somme de vecteurs
- comme operations de couleurs:
- multiplication composante a composante
- somme de couleurs
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 normalLe 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:
- la taille du shader, en appelant glCombinerParameteriNV(GL_NUM_COMBINERS_NV,3) dans le cas ou on veut utiliser 3 etages normaux + le final
- definir les couleurs constantes (1 et 2) en appelant glCombinerParameterfvNV(GL_CONSTANT_COLOR0_NV,couleur)
- d'activer les register combiners par glEnable(GL_REGISTER_COMBINERS_NV)
Utilisation des registers combiners
Un exemple d'application des combinersINFORMATIONS
Auteur:Franck SenegasDernière Mise A Jour:22-8-2001