Comme je l'ai dit dans le premier chapitre,
il faut obtenir un Device Context (DC), puis un Rendering Context (RC) afin
d'initialiser OpenGL. Et pour cela, nous avons besoin de maîtriser quelque
peu la gestion de fenêtres dans Windows. Nous allons donc voir comment
créer une fenêtre et récupérer les infos nécessaires
à l'initialisation.
Il y a en fait deux moyens d'obtenir un DC : soit en se créant sa petite
fenêtre en Win32, soit en utilisant les MFC. Ici, je ne vais parler que
de Win32, car c'est beaucoup plus simple et pratique que les MFC, qui sont très
lourdes et chiantes à utiliser. Mais si vous savez gérer les MFC
et compris comment récupérer un DC et un RC, alors vous ne devriez
pas avoir de problème à intégrer OpenGL dans vos programmes
utilisant les MFC.
Autre chose : j'utilise Ms Visual C++, que je trouve plus pratique pour ce genre de choses. Les procédures
spécifiques au compilateur, du genre inclure les .lib, seront donc expliquées
pour ce logiciel, et pas pour le Borland C++ Builder. Mais le code restera bien
sûr le même quelque soit le compilateur.
Enfin, sachez que la touche F1 est votre amie : à chaque fois que je
vous présente une nouvelle fonction, étudiez-la plus en détail
avec l'aide de votre compliateur, car je ne peux pas tout vous dire sur tout,
ou je n'aurais jamais fini. Servez-vous en donc le plus possible, et si jamais
vous avez encore des doutes, envoyez-moi
vos questions.
Pour les newbies de VC++, créez d'abord
un nouveau projet par File>New>Project>Win32 Application, puis un nouveau
fichier C++ par File>New>File>C/C++ Source File.
Bon, commencont par le commencement, à
savoir les #include. Il y en a 3 obligatoires, sans lesquels votre programme
ne pourra pas marcher. Les voici :
#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
gl.h et glu.h servent bien évidemment
à utiliser les fonctions OpenGL. Quand à windows.h, il est utilisé
pour créer les fenêtres, et vous n'en avez normalement pas besoin
si vous utilisez glut.h. Les header OpenGL sont généralement dans
le répertoire \include\gl de votre compilateur.
Mais ce n'est pas tout : les headers se rapportent à des fonctions qui
sont définies dans les opengl32.dll et glu32.dll : il faut donc les lier
à votre projet. Pour ce faire, avec VC++, allez dans Project>Settings>Links,
et incluez opengl32.lib et glu32.lib dans la liste Object/library modules.
Ensuite, il faut savoir que dans un programme
Win32, la fonction de départ n'est plus main mais WinMain. Créez
donc la première fonction de votre programme :
int WINAPI WinMain( |
HINSTANCE hInstance, |
|
HINSTANCE hPrevInstance, |
LPSTR lpCmdLine, |
int nCmdShow) |
{ |
|
La valeur de retour est la même que pour
la fonction main() classique. Voyons maintenant ce que veulent bien dire ces
paramètres bizarres :
- hInstance est une sorte de handle pour votre programme :
comme Windows est multitâche, il a besoin de savoir quel programme lui
dit quoi. Et c'est à travers cette variable que Windows sait à
qui il a affaire : c'est en quelque sorte la carte d'identité du programme.
C'est pourquoi il est conseillé de la sauvegarder dans une variable
globale, ce que nous allons faire :
HInst = hInstance;
//HInst est un variable globale de type HINSTANCE
- hPervInstance ne sert plus à rien, il est conservé
pour des raisons de compaptibilité avec les programmes 16 bits
- lpCmdLine est un pointeur sur la ligne de commande, pour
récupérer les paramètres éventuels
- nCmdShow est le style initial de la fenêtre : normale,
réduite...etc. Il ne nous sert pas à grand chose pour le moment
Hé bien puisque nous avons notre fonction principale,
pourquoi ne pas créer tout de suite notre fenêtre ? Allons-y :
HWND OpenGLWindow = CreateWindow |
( |
"BUTTON", |
//Classe de la fenêtre |
|
"Fenêtre OpenGL", |
//Nom de la fenêtre |
WS_VISIBLE | WS_BORDER, |
//Caractéristiques |
0, |
//Position x |
0, |
//Position y |
640, |
//Largeur |
480, |
//Hauteur |
0, |
//Handle de la fenêtre même |
0, |
//Identifiant de la fenêtre fille |
HInst, |
//HINSTANCE du programme |
NULL |
/*Chaine de caractères envoyée
en paramètre lors de la création
de la fenêtre*/ |
); |
|
if (!OpenGLWindow) exit(1); |
Je sais que les puristes vont m'incendier, mais je trouve que
c'est plus simple de commencer comme ca. Etudions maintenant cette fonction
CreateWindow :
- Elle renvoie une valeur de type HWND, c'est-à-dire
un handle pour la fenêtre (tiens, on se rapproche du DC). Il s'apparente
au HINSTANCE pour le programme, sauf que là c'est pour une fenêtre.
Vous pouvez aussi le sauvegarder en tant que variable globale. S'il est nul,
il y a eu erreur.
- Le premier paramètre correspond à la classe
de la fenêtre : c'est elle qui définit presque toutes ses propriétés.
Ici, nous avons pris une classe préexistante, pour simplifer
l'explication, mais plus tard nous allons devoir en créer une nous-mêmes.
Je disais que les puristes allaient m'incendier car en fait on n'utilise jamais
ce genre de classe prédéfinie, parce que ca sert à rien,
puisqu'on ne peut même pas définir la fonction WinProc. En fait
leur seul utilité est d'apprendre à utiliser CreateWindow.
- Les caractéristiques de la fenêtre peuvent
être multiples. Faites F1 pour obtenir toutes les options possibles
- Les 4 paramètres suivant parlent d'eux-mêmes
- Les 2 d'après ne servent que dans le cas d'applications
à plusieurs fenêtres, donc on s'en fout
- Et enfin le dernier paramètre permet de passer des
arguments à la fonction WinProc avec le message WM_CREATE. Je sais,
vous ne comprenez pas, mais bient vous saurez.
Ca y est, la fenêtre est créée. Le programme
est fini ? Pas tout à fait. Car il faut gérer les interactions
avec Windows. Et pour cela il faut savoir comment le programme communique avec
Windows : en fait, ce n'est pas Windows qui transmet au programme les messages
le concernant, mais plutt le programme qui doit demander à Windows
s'il doit faire quelque chose, grâce à la fonction PeekMessage.
En quelque sorte, le programme va voir dans son casier s'il n'y a pas des messages
pour lui via PeekMessage,
et s'il y en a, il les récupère avec GetMessage,
puis les distribue aux membres de son équipe (les fenêtres), via
DispatchMessage.
C'est très imagé mais au moins c'est clair et je suis sūr
de m'être fait comprendre. Voilà donc le code à écrire
après la création de la fenêtre :
MSG msg; |
do |
{ |
|
|
while (PeekMessage(&msg,OpenGLWindow,0,0,PM_NOREMOVE)) |
{ |
if(!GetMessage(&msg,OpenGLWindow,0,0))
exit(0);
DispatchMessage(&msg);
|
} |
} |
|
while(1); |
Lorsque le message WM_QUIT est envoyé au programme, cela
signifie qu'il doit fermer et que tout est fini. Si c'est ce message que récupère
GetMessage, il renvoie 0 : cela explique le if(!GetMessage(&msg,OpenGLWindow,0,0))
exit(0); C'est la seule condition pour qu'un programme
quitte. Lorsque le message est récupéré par GetMessage,
il est supprimé de la pile des messages (le 'casier' de tout-à-l'heure),
à une exception près : le message WM_PAINT n'est supprimé
que lorsqu'il a été traité. Pour l'instant nous n'avons
pas besoin de savoir ce que msg contient, puisque le seul message qui nous importe
est WM_QUIT, et il est géré par GetMessage.
Peut-être ces histoires de messages vous semblent-elles un peu obscures,
mais ne vous inquiétez pas, les rares taches d'ombres vont bientôt
s'éclaircir.
Mais finissons d'abord notre programme par les lignes qui suivent
:
return 0; //(pour la forme)
}
|
Et voilà ! Le premier programme d'exemple est terminé
et vous pouvez le lancer : vous verrez un zoli bouton avec "Fenêtre
OpenGL" marqué dessus, et vous pourrez même cliquer dessus,
et alors là : RIEN ! Hé oui ! Il ne se passe rien quand on
clique sur le bouton ! Ben oui, c'est là le problème : ca
sert à rien d'utiliser la classe prédéfinie, puisqu'on
peut même pas lui dire ce qu'on veut qu'il se passe quand on clique
sur le bouton. |
Il va donc falloir qu'on définisse notre propre classe
de fenêtre. C'est d'ailleurs toujours comme ce qu'on fait. Stoppez le
programme et recopiez donc le code suivant juste avant l'appel à CreateWindow
:
WNDCLASS WindowClass = |
{ |
0, |
//Style |
|
WinProc, |
//Procédure pour la gestion des messages |
0, |
//octets supplémentaires
à allouer à la classe |
0, |
//octets supplémentaires à allouer
à la fenêtre |
HInst, |
//Handle du programme |
0, |
//Icne |
0, |
//Curseur |
0, |
//Couleur d'arrière-plan |
NULL, |
//Pointeur sur le menu associé à
la classe |
"La classe!" |
//Nom de la classe fenêtre |
}; |
|
if (!RegisterClass(&WindowClass))
exit(1);
|
Ca y est : nous avons défini notre classe de fenêtre,
qui s'appelle "La classe!". On peut donc remplacer "BUTTON"
par "La classe!" dans la procédure CreateWindow. Mais si vous
essayez de lancer le programme tel quel, le compilateur va vous renvoyer une
erreur : en effet, dans la déclaration de la classe, nous lui passons
comme fonction de gestion des messages une fonction que nous avons nommé
WinProc (nous aurions aussi bien pu l'appeler Salutcavaouimoicavaettoicavaouicavaettoiohmoitusaiscava,
mais je trouve que WinProc est plus approprié), mais cette fonction n'existe
pas. Il faut donc la créer :
LRESULT CALLBACK WinProc( |
|
HWND hwnd, |
//Handle de la fenêtre |
UINT uMsg, |
//Message |
WPARAM wParam, |
//Paramètre word |
LPARAM lParam ) |
//Paramètre long |
|
{ |
|
|
return DefWindowProc(
hwnd,
uMsg,
wParam,
lParam
); |
//Lance la procédure par défaut |
} |
|
Voici donc la procédure si mystérieuse : un truc
tout con en fait, qui ne fait que passer le relais à une autre procédure
déjà définie. Et puis si vous êtes perspicace, vous
auriez pu me dire "Mais alors, on aurait pu tout connement mettre DefWindowProc
à la place de WinProc !". Hé ben en fait...oui. Mais maintenant,
on va personnaliser un peu cette fonction, histoire de s'amuser.
D'abord, vous aurez srement remarqué que le fait
d'appuyer sur Alt+F4 ferme la fenêtre, mais pas le programme : il faut
l'arrêter manuellement avec le compilateur ou Ctrl+Alt+Del. En effet,
le fait d'envoyer le message "Ferme la fenêtre" ne provoque
pas chez DefWindowProc l'envoi du message "Quitte le programme" :
nous allons donc remplacer le contenu de WinProc par :
if (uMsg==WM_CLOSE) {PostQuitMessage(0);return
0;}
else return DefWindowProc(hwnd,uMsg,wParam,lParam);
Ca y est ! nous avons enfin notre propre fonction de gestion de messages à
nous, qui sait fermer le programme. Et vous remarquerez que les fameux messages
évoqués plus haut (WM_PAINT,WM_QUIT,etc...) sont contenus dans
la variable uMsg, transmise à WinProc, et que PostQuitMessage dit au
programme de se fermer sans autre forme de procès.
Bon et bien maintenant, si nous intégrions OpenGL à notre fenêtre,
pour voir ? C'est vrai, depuis le début du tutorial, on a toujours pas
vu de DC ou de RC, juste un HWND, qui ma foi faisait penser à un DC de
par sa définition. Je ne vous mentirais pas plus longtemps : le DC est
en effet directement tiré du HWND. En fait, il suffit de le récupérer
grâce à la fonction GetDC. "Hé bien on n'a qu'à
le faire alors !" Ok, ok on va le faire...mais o ? après
la création de la fenêtre, dans le WinMain ? Oui, pourquoi pas,
mais ca ferait un petit peu bidouille, non ? Alors que lorsque l'on appelle
CreateWindow, celle-ci exécute justement WinProc une seule fois, avec
comme message WM_CREATE... C'est trop beau : tant qu'à faire, on va en
profiter pour récupérer notre DC ! Etoffons donc notre chère
WinProc :
switch(uMsg) |
{ |
|
|
case WM_CLOSE:
|
ReleaseDC(hwnd,DC);
PostQuitMessage(0);
break;
|
//Libère le DC et ferme le programme |
case WM_CREATE:
|
DC=GetDC(hwnd);
break;
|
//Récupère le DC |
default:
|
return DefWindowProc(
hwnd,
uMsg,
wParam,
lParam);
break;
|
//Sinon, fait le truc habituel |
} |
|
return 0; |
Sans oublier de rajouter
HDC DC;
dans les déclaration de variables globales. Et voilà, nous avons
notre DC ! Lancez le programme et vous verrez ce que ca change : ... rien. Car
l'initialisation d'OpenGL n'est même pas commencée. Il nous faut
encore récupérer le Rendering Context, et le définir comme
sortie OpenGL courante, en faisant
RC = wglCreateContext(DC); //RC est une variable
globale de type HGLRC
if (!RC) SendMessage(hwnd,WM_CLOSE,0,0);
wglMakeCurrent(DC, RC);
après le GetDC du WM_CREATE, et en rajoutant
wglMakeCurrent(NULL, NULL);
if (RC) wglDeleteContext(RC);
avant le ReleaseDC du WM_CLOSE.
Voilà, normalement ca devrait marcher. Mais ca marche pas. Pourquoi ?
Et bien tout simplement parce qu'on a pas paramétré le Device
Context. En effet, il faudrait d'abord lui dire en quelle résolution
on veut être, et puis si on utilise le double buffering, et puis qu'on
veut lui associer un RC. Bref, il faut paramétrer ce qu'on appelle le
PixelFormat (cad en gros le format d'affichage). Créons donc une petite
fonction qui va nous faire tout ca :
void SetupPixelFormat(HDC hDC) |
{ |
|
|
PIXELFORMATDESCRIPTOR pfd =
|
{ |
|
|
sizeof(PIXELFORMATDESCRIPTOR), |
//taille du descripteur de format |
1, |
//version |
PFD_SUPPORT_OPENGL |
PFD_DRAW_TO_WINDOW |
PFD_DOUBLEBUFFER, |
//Propriété |
PFD_TYPE_RGBA, |
//Mode de couleurs |
16, |
//Bits de couleur |
0, 0, 0, 0, 0, 0, |
//Paramètres des couleurs |
0,0, |
//Paramètres alpha |
0,0, 0, 0, 0, |
//Paramètres du buffer d'accumulation |
32, |
//Bits de profondeur |
0, |
//Bits du buffer stencil |
0, |
//Nombre de buffers auxiliaires |
0, |
//ignoré (obsolète) |
0, |
//réservé/code> |
0, |
//ignoré (obsolète) |
0, |
//Couleur de transparence |
0 |
//Ignoré (obsolète) |
|
}; |
|
|
|
int pixelFormat; |
pixelFormat = ChoosePixelFormat(hDC,
&pfd); |
if (!pixelFormat) |
{ |
|
|
MessageBox
(
WindowFromDC(hDC),
"Mode graphique non supporté",
"Problème",
MB_ICONERROR | MB_OK
);
exit(1);
|
/*Vérifie si un PixelFormat du type
demandé existe*/ |
} |
|
if (!SetPixelFormat(hDC, pixelFormat,
&pfd)) |
{ |
|
|
|
MessageBox
(
WindowFromDC(hDC),
"Mode graphique non supporté",
"Problème",
MB_ICONERROR | MB_OK
);
exit(1);
|
/*Applique le PixelFormat. Arrête si
erreur*/ |
} |
|
} |
|
Je me fais chier à décrire cette fonction, mais en fait il n'y
a pas à en dire beaucoup plus. Les paramètres sont toujours les
mêmes, sauf peut-être le nombre de bits de couleur et de profondeur.
A part ca, le reste ne sert pas à grand chose, mais c'est nécessaire.
Contentez-vous de recopier la fonction, et interrogez votre compilateur ou moi
si quelque chose vous gêne. Mais je vous rassure, vous n'avez pas grand
chose à savoir sur ces paramètres.
Tout ce qu'il reste à faire, c'est d'inclure un appel à cette
fonction après la récupération du DC et avant celle du
RC (sans cela wglCreateContext
vous renverra une erreur). Et voilà ! Là c'est réellement
fini, et votre fonction WinProc doit ressembler à ceci :
LRESULT CALLBACK WinProc(HWND
hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam ) |
{ |
|
|
switch(uMsg) |
{ |
|
|
case WM_CLOSE: |
wglMakeCurrent(NULL, NULL);
if (RC) wglDeleteContext(RC);
ReleaseDC(hwnd,DC);
PostQuitMessage(0);
break;
|
//Libère RC et DC et ferme le programme |
case WM_CREATE: |
DC=GetDC(hwnd);
SetupPixelFormat(DC);
RC = wglCreateContext(DC);
if (!RC) SendMessage(hwnd,WM_CLOSE,0,0);
wglMakeCurrent(DC, RC);
break;
|
//Récupère le DC et le RC |
default: |
return DefWindowProc(hwnd,
uMsg,
wParam,
lParam);
break;
|
//Sinon, fait le truc habituel |
|
} |
|
return 0; |
} |
|
Si vous lancez le programme maintenant, vous ne verrez cependant pas de changement
par rapport à avant, tout simplement parce que le message WM_PAINT
est encore géré par DefWindowProc.
Mais sachez qu'OpenGL est bien lié à cette fenêtre, et que
le pas entre la fenêtre vide et un rendu OpenGL est très mince.
Pour le découvrir, passez à la deuxiême partie de ce tutorial
!
| |
Dans la premiè partie, je vous ai appris à créer
votre fenêtre par CreateWindow() ,
à associer la sortie OpenGL à cette fenêtre par GetDC() ,
SetupPixelFormat() , wglCreateContext()
et wglMakeCurrent() , et à gérer
les messages envoyés à cette fenêtre par une fonction que
nous avons appelé WinProc() .
Je vais maintenant finir de vous expliquer comment initialiser OpenGL lui-même,
maintenant que la fenêtre est bien créée.
Reprenez donc votre programme de la dernière fois, et modifiez votre
fonction WinProc() de façon
à ce qu'elle ressemble à ça :
LRESULT CALLBACK WinProc(HWND
hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam ) |
{ |
|
|
switch(uMsg) |
{ |
|
|
case WM_CLOSE: |
wglMakeCurrent(NULL, NULL);
if (RC) wglDeleteContext(RC);
ReleaseDC(hwnd,DC);
PostQuitMessage(0);
break;
|
//Libère RC et DC et ferme le programme |
case WM_CREATE: |
DC=GetDC(hwnd);
SetupPixelFormat(DC);
RC = wglCreateContext(DC);
if (!RC) SendMessage(hwnd,WM_CLOSE,0,0);
wglMakeCurrent(DC, RC);
InitGL();
break;
|
//Récupère le DC et le RC |
case WM_SIZE: |
Reshape(
LOWORD(lParam),
HIWORD(lParam)
);
break;
|
//Met à jour les paramètres
OGL |
case WM_PAINT: |
Draw();
break;
|
//Exécute le rendu |
default: |
return DefWindowProc(
hwnd,
uMsg,
wParam,
lParam
);
break;
|
//Sinon, fait le truc habituel |
|
} |
|
return 0; |
} |
|
Nous avons rajouté un appel à 3 fonctions que nous allons écrire
un peu plus loin : Draw() , Reshape()
et InitGL() . Nous reparlerons beaucoup
de Draw() et InitGL()
dans les prochains tutoriaux. Mais parlons d'abord de Reshape()
: voici ce qu'il faut écrire à l'intérieur :
void Reshape(int width, int height) |
{ |
|
|
glViewport(0,0,width,height); |
|
glMatrixMode(GL_PROJECTION); |
|
glLoadIdentity(); |
|
gluPerspective(45,float(width)/float(height),0.1,100);
|
} |
|
glViewport() informe d'abord OpenGL
sur la taille de la zone de sortie
- Ensuite
glMatrixMode() charge
la matrice de projection. Une matrice ? quésako ? En fait, c'est un
tableau sur lequel on peut effectuer des opérations mathématiques
spécifiques. La matrice de projection sert à définir
comment les coordonnées 3D sont transformées en coordonnées
2D sur l'écran. La matrice modèle et vue (GL_MODELVIEW )
définit la position du repère de coordonnées dans le
monde. C'est sur cette matrice que l'on effectuent des opérations qui
se traduisent par des rotations, des translations ou des homotéties.
La matrice texture permet de définir l'aspect des textures plaquées
sur les faces. On peut aussi lui faire subir des rotations, etc, qui se répercutent
sur l'affichage. Les matrices sont donc des outils très puissant de
modélisation.
glLoadIdentity() fait un reset
de la matrice (il la réinitialise) pour pouvoir effectuer des modifications
sur celle-ci
- Et enfin gluPerspective modifie la matrice courante (c'est-à-dire
ici la matrice de projection) pour qu'OpenGL transforme les coordonnées
3D au moment du rendu en coordonnées 2D par rapport à l'écran,
de façon à ce qu'on ait 'impression de regarder à travers
un objectif de 45 de focale, de largeur width et de hauteur height, et
dont le clipping va de 0.1 à 100 (attention la valeur du clipping near - ici 0.1 - doit être strictement supérieure à 0).
Cette fonction Reshape() est appelée
à chaque fois que la fenêtre est redimensionnée, et notemment
à sa création. Grâce à cette fonction, OpenGL est
totalement initialisé et est parfaitement près à dessiner.
Mais alors qu'est-ce que vient faire InitGL()
? Et bien en fait on va mettre dans cette fonction toutes les fonctions optionnelles
destinées à paramétrer le rendu : activation du test de
profondeur, définition des lumières, textures, transparence...
Donc pour l'instant, comme on n'a rien à y mettre, on va quand même
la créer, mais en la laissant vide :
Et maintenant, la fontion Draw() :
c'est la fonction qui sera appelée à chaque fois que la fenêtre
sera rafraîchie, et donc c'est là-dedans qu'on va placer toute
la sauce qui va afficher à l'écran de magnifiques images. La fonction
Draw() commencera toujours par glClear()
(sauf effet visuel particulier) et finira toujours par SwapBuffers(DC) .
Mais trêve de bavardages, la voici :
void Draw() |
{ |
|
} |
glClear
(
GL_COLOR_BUFFER_BIT |
GL_DEPTH_BUFFER_BIT
); |
//Efface le frame buffer et le Z-buffer |
glMatrixMode(GL_MODELVIEW); |
//Choisit la matrice MODELVIEW |
glLoadIdentity(); |
//Réinitialise la matrice |
|
//Placer ici tout le code de transformation
et de dessin |
|
SwapBuffers(DC);
|
//Echange les 2 frame buffers |
|
glClear() efface les buffers passés
en paramètre. Ici, nous n'utilisons que le frame buffer (c'est-à-dire
le buffer correspondant à l'image affiché à l'écran)
et le z-buffer (utilisé pour les tests de profondeur). Pour l'instant,
nous n'utiliseront pas le z-buffer, mais comme plus tard on l'utilisera toujours,
autant vous montrer tout de suite comment on s'en sert.
- Ensuite on charge la matrice modèle/vue et on la réinitialise
pour pouvoir par la suite effectuer toutes les transformations 3D nécessaires
(rotations, translations...).
- Après vient tout le code de dessin et de transformation, que l'on
remplira au fur et à mesure des tutorials
- Et enfin la fonction
SwapBuffers(DC)
échange les deux frame buffers : celui sur lequel on travaillait est
affiché à l'écran tandis que celui qui était à
l'écran est caché et servira pour le prochain rendu.
Vous pouvez dès maintenant essayer ce programme : vous y verrez un magnifique
écran noir, car pour l'instant rien n'est dessiné dans notre écran
Mais pour ne pas vous laisser sur votre fin, voici quelques lignes à
insérer dans Draw() entre glLoadIdentity()
et SwapBuffers() pour vous montrer
que ca marche vraiment :
gluLookAt(0,0,-10,0,0,0,0,1,0); |
glBegin(GL_TRIANGLES); |
glVertex2i(0,1);
glVertex2i(-1,0);
glVertex2i(1,0);
|
glEnd(); |
Je vous laisse découvrir ce que ca donne (pour les impatients, allez
voir dans le tut glut).
Et voila ! Vous avez réellement dessiné en OpenGL, rien qu'avec
un petit tutorial de rien du tout ! C'est-y pas génial ? Avouez que c'est
pas compliqué. Tout ce que vous avez besoin de savoir pour programmer
en OpenGL sous Windows, c'est créer une fenêtre, savoir récupérer
et gérer les messages qui lui sont transmis, et récupérer
un Device Context et un Rendering Context pour initialiser le PixelFormat. Et
en plus, vous n'avez même pas besoin de connaître par coeur comment
le faire ! Et si vous trouvez ca déjà trop dur, alors je ne vous
laisse même pas imaginer ce que ce serait si vous vous mettiez à
Direct3D.
Antoche | |