Navigation▲
I. Introduction▲
De nombreux efforts ont été effectués pour que ces tutoriels soient faciles à compiler et exécuter. Malheureusement, cela signifie aussi que CMake cache la façon de faire pour votre propre projet.
Donc, ce tutoriel va expliquer comment compiler votre propre application C à partir de rien. Mais avant tout, vous avez besoin de quelques connaissances de base sur ce que votre compilateur fait.
Veuillez ne pas sauter les deux premières sections. Si vous lisez ce tutoriel, vous avez probablement besoin de connaître ces choses.
II. Le modèle des applications C▲
II-A. Le préprocesseur▲
On parle ici des #define et #include.
L'étape du préprocesseur est très simple : du copier-coller.
Lorsque le préprocesseur voit le code du fichier MyCode.c suivant :
#include "MyHeader.h"
void
main
(
){
FunctionDefinedInHeader
(
);
}
Il ouvre simplement le fichier MyHeader.h et copie-colle son contenu dans le fichier MyCode.c.
// Début de MyCode.c
// Début de MyHeader.h
#ifndef MYHEADER_H
#define MYHEADER_H
void
FunctionDefinedInHeader
(
); // Déclare la fonction
#endif
// Fin de MyHeader.h
void
main
(
){
FunctionDefinedInHeader
(
); // Utilisation
}
// Fin de MyCode.c
De façon similaire, les #define sont copiées-collées, les #if sont analysés et potentiellement retirés, etc.
À la fin de cette étape, vous obtenez un fichier C++, sans #define, #if, #ifdef, #include, prêt à être compilé.
Par exemple, voici le fichier main.cpp du sixième tutoriel, complètement prétraité dans Visual : fichier prétraité. Attention, c'est un immense fichier ! Mais cela vaut le coup de savoir ce qu'un fichier .cpp simple est réellement pour le compilateur.
II-B. Compilation▲
Le compilateur traduit le code C++ en une représentation que le CPU peut comprendre directement. Par exemple, le code suivant :
int
i=
3
;
int
j=
4
*
i+
2
;
sera traduit en ces opcodes x86 :
mov
dword
ptr
[i],3
mov
eax
,dword
ptr
[i]
lea
ecx
,[eax
*
4
+
2
]
mov
dword
ptr
[j],ecx
Chaque fichier .cpp est compilé séparément et le code binaire résultant est écrit dans des fichiers .o/.obj.
Nous n'avons pas encore l'exécutable : il reste une étape à faire.
II-C. Éditeur de liens▲
L'éditeur de liens prend tout le code binaire (les vôtres, mais aussi celui des bibliothèques externes) et génère l'exécutable final. Quelques notes :
- Une bibliothèque possède l'extension .lib.
- Certaines bibliothèques sont statiques. Cela signifie que les .lib contiennent tous les opcodes x86 nécessaires.
- Certaines bibliothèques sont dynamiques (aussi appelées « bibliothèques partagées »). Cela signifie que le fichier .lib ne contient pas tout le code x86 ; il indique simplement : « Je jure que la fonction Foo, Bar et WhatsNot seront disponibles à l'exécution ».
Lorsque l'éditeur de liens a été exécuté, vous obtenez un exécutable (.exe sous Windows, sans extension sous Unix) :
II-D. Exécution▲
Lorsque vous lancez l'exécutable, le système d'exploitation va ouvrir le .exe et placer les opcodes x86 en mémoire. Comme indiqué précédemment, certains codes ne sont pas disponibles à ce moment-là : le code des bibliothèques dynamiques. Mais l'éditeur de liens a été assez gentil pour indiquer où les trouver : le .exe indique clairement que la fonction glClearColor est implémentée dans le fichier OpenGL32.dll.
Windows va ouvrir la .dll et trouver glClearColor :
Quelques fois, une .dll ne peut être trouvée, sûrement, car l'installation a échoué et le programme ne peut pas s'exécuter.
III. Comment faire X avec l'éditeur Y ?▲
Les instructions de compilation d'une application OpenGL sont séparées des opérations de base suivantes. En voici les raisons :
- premièrement, vous devez faire ce genre de chose tout le temps, donc vous avez intérêt à les connaître correctement ;
- deuxièmement, vous allez connaître ce qui est spécifique à OpenGL de ce qui ne l'est pas.
III-A. Visual Studio▲
III-A-1. Créer un nouveau projet▲
Fichier (File) → Nouveau (New) → Projet (Project) → Projet vide (Empty Project). N'utilisez pas ces étranges assistants. N'utilisez aucune option que vous ne connaissez pas (désactivez MFC, ATL, les entêtes précompilés, stdafx, le fichier main).
III-A-2. Ajouter un fichier source dans un projet▲
Faites un clic droit sur Fichiers sources (Sources Files) → Ajouter nouveau (Add new).
III-A-3. Ajouter un répertoire d'inclusion▲
Faites un clic droit sur projet (Project) → Propriétés du projet (Project Properties) → C++ → Général → Répertoire d'inclusion additionnel (Additional include directories). C'est une liste déroulante, vous pouvez la modifier comme vous le souhaitez.
III-A-4. Lier une bibliothèque▲
Faites un clic droit sur projet (Project) → Propriétés du projet (Project Properties) → Éditeur de liens (Linker) → Entrée (Input) → Dépendances additionnelles (Additional dependencies) : tapez le nom du fichier .lib. Par exemple : opengl32.lib.
Dans les propriétés du projet (Project Properties) → Éditeur de liens (Linker) → Général → répertoires de bibliothèques additionnels (Additional library directories), assurez-vous que le chemin de la bibliothèque ci-dessus est présent.
III-A-5. Compiler, exécuter et déboguer▲
Pour définir le répertoire de travail (là où sont les textures et les shaders) : Propriétés du projet (Project Properties) → Débogage (Debugging) → Répertoire de travail (Working Directory).
Pour exécuter : Maj-F5 ; mais vous ne souhaitez probablement jamais faire cela. Pour déboguer : F5.
Voici une courte liste de raccourcis pour le débogage :
- F9 sur une ligne, ou en faisant un clic gauche sur les numéros de ligne : définit un point d'arrêt. Un point rouge va apparaître ;
- F10 : exécuter la ligne actuelle ;
- F11 : exécuter la ligne actuelle, mais en s'enfoncer dans les fonctions que cette ligne appelle (« Step into ») ;
- Maj-F1 : exécuter jusqu'à la fin de la fonction (« Step out »).
Vous avez aussi de nombreuses fenêtres de débogage : les variables observées, la pile d'appels, les threads…
III-B. QtCreator▲
QtCreator est disponible gratuitement à cette adresse.
III-B-1. Créer un nouveau projet▲
Utilisez un projet vide C ou C++ ; éviter les templates contenant des trucs de Qt.
Utilisez les options par défaut.
III-B-2. Ajouter un fichier source dans un projet▲
Utilisez l'interface, ou ajoutez un fichier dans le .pro :
SOURCES += main.cpp \
other.cpp \
foo.cpp
III-B-3. Ajouter un répertoire d'inclusion▲
Dans le fichier .pro :
INCLUDEPATH += <chemin> \ <autre chemin>
III-B-4. Lier avec une bibliothèque▲
Faites un clic droit sur le projet → Ajout une bibliothèque (Add library)
- Si vous êtes sous Linux et que vous avez installé la bibliothèque à partir de apt-get ou similaire, il y a de fortes chances que la bibliothèque soit enregistrée dans le système. Vous pouvez sélectionner « Paquet système » (System package) et entrer le nom de la bibliothèque (par exemple : libglfw ou glew).
- Sinon, utilisez « Bibliothèque système » (System library). Naviguez jusqu'à atteindre le dossier où vous l'avez compilée.
III-B-5. Compiler, exécuter et déboguer▲
Pour compiler : Ctrl-B ou le marteau dans le coin inférieur gauche.
Pour exécuter : la flèche verte. Vous pouvez définir les arguments du programme et le répertoire de travail dans « Projets » (Projects) → « Paramètres d'exécution » (Run Settings).
Pour déboguer :
- définir un point d'arrêt : cliquez à gauche des numéros de ligne. Un point rouge apparaîtra ;
- F10 : exécuter la ligne actuelle ;
- F11 : exécuter la ligne actuelle, mais en s'enfonçant dans les fonctions que cette ligne appelle (« Step into ») ;
- Maj-F11 : exécuter jusqu'à la fin de la fonction (« Step out »).
Vous avez aussi de nombreuses fenêtres de débogage : les variables observées, la pile d'appels, les threads…
III-C. CMake▲
CMake créera les projets et cela pour pratiquement tous les outils de compilation : Visual, Qt Creator, XCode, make, Code::Blocks, Eclipse, etc., sur n'importe quel système d'exploitation. Cela vous libère de la maintenance de multiples fichiers de projet.
III-C-1. Créer un nouveau projet▲
Créez un fichier CMakeLists.txt et écrivez ceci (adaptez si besoin) :
cmake_minimum_required (VERSION 2.6)
project (le_nom_du_projet)
find_package(OpenGL REQUIRED)
add_executable(le_nom_de_l_executable
tutorial04_colored_cube/tutorial04.cpp
common/shader.cpp
common/shader.hpp
)
Lancez l'interface graphique de CMake, naviguez jusqu'à atteindre votre fichier .txt et sélectionnez votre dossier de compilation. Cliquez sur « Configurer » (Configure) puis « Generer » (Generate). Votre solution va être créée dans le dossier de compilation.
III-C-2. Ajouter un fichier source dans un projet▲
Ajoutez simplement une ligne dans la commande add_executable.
III-C-3. Ajouter des répertoires d'inclusion▲
include_directories(
external/AntTweakBar-1.15/include/
external/glfw-2.7.2/include/
external/glm-0.9.1/
external/glew-1.5.8/include/
.
)
III-C-4. Lier une bibliothèque▲
set(ALL_LIBS
${OPENGL_LIBRARY}
GLFW_272
GLEW_158
ANTTWEAKBAR_151_OGLCORE_GLFW
)
target_link_libraries(tutorial01_first_window
${ALL_LIBS}
)
III-C-5. Compiler, exécuter et déboguer▲
CMake ne fait pas cela. Utilisez votre EDI favori.
III-D. Make▲
Veuillez, s'il vous plaît, ne pas utiliser cela.
III-E. GCC▲
Cela peut être intéressant de compiler un petit projet « à la main » afin de mieux comprendre le flux de travail. Ne le faites pas sur un vrai projet…
Vous pouvez aussi le faire sur Windows avec MinGW.
Compilez chaque fichier .cpp séparément :
g++ -c main.cpp
g++ -c tools.cpp
Comme indiqué précédemment, vous allez obtenir des fichiers main.o et tools.o. Liez-les :
g++ main.o tools.o
un fichier a.out apparaît ; c'est votre exécutable. Exécutez-le :
./a.out
C'est tout !
IV. Compiler votre propre application C▲
Armé de cette connaissance, on peut commencer à compiler soi-même une application OpenGL.
- Téléchargez les dépendances : ici, on utilise GLFW, GLEW et GLM, mais suivant votre projet, vous pouvez avoir besoin d'autre chose. Sauvegardez-les préférablement dans des sous-répertoires de votre projet (par exemple : external/).
- Elles doivent être précompilées pour votre plateforme. Toutefois, GLM n'a pas besoin d'être compilée.
- Créez un nouveau projet avec l'EDI de votre choix.
- Ajoutez un nouveau fichier .cpp dans le projet.
-
Copiez et collez, par exemple, le code suivant (c'est en fait le fichier playground.cpp) :
Sélectionnez#include
<stdio.h>
#include
<stdlib.h>
#include
<GL/glew.h>
#include
<GL/glfw.h>
#include
<glm/glm.hpp>
using
namespace
glm;int
main(void
){
// Initialise GLFW
if
(!
glfwInit() ){
fprintf( stderr,"Failed to initialize GLFW
\n
"
);return
-
1
;}
glfwOpenWindowHint(GLFW_FSAA_SAMPLES,4
); glfwOpenWindowHint(GLFW_WINDOW_NO_RESIZE,GL_TRUE); glfwOpenWindowHint(GLFW_OPENGL_VERSION_MAJOR,3
); glfwOpenWindowHint(GLFW_OPENGL_VERSION_MINOR,3
); glfwOpenWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);// Ouvre une fenêtre et créé son contexte OpenGL
if
(!
glfwOpenWindow(1024
,768
,0
,0
,0
,0
,32
,0
, GLFW_WINDOW ) ){
fprintf( stderr,"Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.
\n
"
); glfwTerminate();return
-
1
;}
// Initialise GLEW
if
(glewInit()!=
GLEW_OK){
fprintf(stderr,"Failed to initialize GLEW
\n
"
);return
-
1
;}
glfwSetWindowTitle("Playground"
);// Assure que l'on capture la touche Échap lorsque pressée
glfwEnable( GLFW_STICKY_KEYS );// Fond bleu foncé
glClearColor(0.0
f,0.0
f,0.3
f,0.0
f);do
{
// Ne dessine rien, à bientôt pour le second tutoriel !
// Alterne les tampons
glfwSwapBuffers();}
// Vérifie si la touche Échap a été pressée ou si la fenêtre a été fermée
while
( glfwGetKey( GLFW_KEY_ESC )!=
GLFW_PRESS&&
glfwGetWindowParam( GLFW_OPENED ) );// Ferme la fenêtre OpenGL et termine GLFW
glfwTerminate();return
0
;}
- Compilez le projet.
Vous allez obtenir de nombreuses erreurs de compilation. On va les analyser une par une.
V. Dépannage▲
Les messages d'erreurs ci-dessous sont pour Visual Studio 2010, mais ils sont plus ou moins proches de ceux de GCC.
V-A. Visual Studio - fatal error C1083 : impossible d'ouvrir le fichier 'GL/glew.h' : Aucun fichier trouvé.▲
(Ou n'importe quel autre fichier.)
Certains entêtes ont des emplacements bizarres. Par exemple, les fichiers à inclure de GLEW sont situés dans external/glew-x.y.z/include. Le compilateur n'a aucune méthode magique pour le deviner, donc vous devez les lui indiquer. Dans les propriétés du projet, ajoutez le chemin approprié dans les options du compilateur (et non pas l'éditeur de liens).
Pour aucune raison vous ne devriez copier les fichiers dans les répertoires du compilateur (Program Files/Visual Studio/…). Techniquement, cela fonctionnera, mais c'est une très mauvaise pratique.
De plus, c'est une bonne chose d'utiliser des chemins relatifs (./external/glew/… au lieu de C:/Users/utilisateur/Downloads/…).
Par exemple, voici ce que contiennent les fichiers de CMake de ces tutoriels :
external/glfw-2.7.2/include
external/glm-0.9.1
external/glew-1.5.8/include
Répétez cela jusqu'à ce que tous les fichiers soient trouvés.
V-B. GCC - fatal error : GL/glew.h : aucun fichier trouvé▲
(ou n'importe quel autre fichier.)
Cela signifie que la bibliothèque n'est pas installée. Si vous êtes chanceux, la bibliothèque est connue et vous n'avez qu'à l'installer. C'est le cas pour GLFW, GLEW et GLM :
sudo apt-get install libglfw-dev libglm-dev libglew1.6-dev
Si ce n'est pas une bibliothèque répandue, regardez la réponse pour Visual Studio ci-dessus.
V-C. Visual Studio - error LNK2019 : symbole non résolu glfwGetWindowParam référencé dans la fonction main▲
(ou n'importe quel autre symbole dans n'importe quelle autre fonction.)
Félicitations ! Vous avez une erreur d'édition de liens. C'est une excellente nouvelle : cela signifie que la compilation a réussi. Plus qu'une dernière étape !
Les fonctions GLFW se trouvent dans une bibliothèque externe. Vous devez indiquer cette bibliothèque à l'éditeur de liens. Ajoutez-la dans les options de l'éditeur de liens. N'oubliez pas d'ajouter le chemin menant à cette bibliothèque.
Par exemple, voici ce que le projet Visual Studio utilise. Les noms sont quelque peu inhabituels, car c'est une compilation personnalisée. De plus, GLM ne nécessite pas d'être compilée ou liée, donc elle n'est pas là.
external\Debug\GLFW_272.lib
external\Debug\GLEW_158.lib
Si vous téléchargez ces bibliothèques à partir de SourceForge (GLFW, GLEW) et les compilez vous-même, vous devez spécifier le chemin adéquat. Par exemple :
C:\Ou\Vous\Avez\Place\La\Bibliotheque\glfw.lib
C:\Ou\Vous\Avez\Place\La\Bibliotheque\glew32.lib
V-D. GCC - main.cpp : référence indéfinie vers 'glfwInit'▲
(ou n'importe quel autre symbole.)
Même réponse que pour Visual Studio ci-dessus.
Sur Linux, GLFW et GLEW (et plein d'autres) sont généralement installées avec apt-get ou autre : sudo apt-get install libglew-dev libglfw-dev (cela peut changer). Lorsque vous faites ceci, les bibliothèques sont copiées dans les répertoires standards du compilateur, donc vous n'avez pas à préciser le chemin. Liez simplement glfw et glew comme présenté dans la première section.
V-E. J'ai tout configuré correctement, mais j'ai toujours une erreur « unresolved external error » !▲
Cela peut être quelque peu difficile à tracer. Il y a plusieurs solutions :
V-E-1. J'ai une erreur durant l'édition des liens avec _imp_glewInit ou d'autres symboles qui commencent par _imp▲
Cela signifie que la bibliothèque (dans ce cas, glew) a été compilée comme bibliothèque statique, mais vous essayez de l'utiliser comme bibliothèque dynamique. Ajoutez simplement la définition pour le préprocesseur suivante dans les options du compilateur (pour votre propre projet, et non celui de glew) :
GLEW_STATIC
V-E-2. J'ai quelques problèmes étranges avec GLFW▲
Peut-être, car GLFW a été compilée comme bibliothèque dynamique, mais vous essayez de l'utiliser comme bibliothèque statique ?
Essayez d'ajouter la définition pour le préprocesseur suivante :
GLFW_DLL
V-E-3. J'ai un autre problème avec l'éditeur de liens ! Aidez-moi, je suis bloqué !▲
Veuillez envoyer un rapport détaillé et un projet complet compressé et on ajoutera des indications.
V-E-4. J'aimerais résoudre cela moi-même. Quelles sont les règles générales ?▲
Admettons que vous êtes l'auteur de la GLFW. Vous souhaitez fournir la fonction glfwInit().
Lors de la compilation comme DLL, vous devez indiquer au compilateur que glfwInit() n'est pas comme n'importe quelle autre fonction dans la DLL ; elle doit être vue par les autres, contrairement à glfwPrivateImplementationMethodNobodyShouldCareAbout(). Cela est fait en déclarant la fonction externe « external » avec GCC, ou « __declspec(dllexport) » avec Visual.
Lorsque vous souhaitez utiliser GLFW, vous devez indiquer au compilateur que la fonction n'est pas réellement disponible : il doit la lier dynamiquement. Cela est fait en déclarant la fonction externe « external » avec GCC ou « __declspec(dllimport) » avec Visual.
Donc vous utilisez une définition pratique #define : GLFWAPI et vous l'utilisez pour déclarer les fonctions :
GLFWAPI int glfwInit(void) ;
- lorsque vous compilez la bibliothèque en DLL, vous définissez #define GLFW_BUILD_DLL. GLFWAPI sera alors défini en __declspec(dllexport) ;
- lorsque vous utilisez GLFW à travers une DLL, vous définissez #define GLFW_DLL. GLFWAPI sera alors défini en __declspec(dllimport) ;
- lorsque vous compilez la bibliothèque statique, GLFWAPI est défini vide.
- Lorsque vous utilisez GLFW en bibliothèque statique, GLFWAPI est défini vide.
Donc la règle est : ces indications doivent être consistantes. Si vous compilez une bibliothèque (n'importe quelle bibliothèque, pas que GLFW) en DLL, utilisez la bonne définition pour le préprocesseur : GLFW_DLL, GLEW_STATIC.
V-F. Mon programme crashe !▲
Il y a de multiples raisons qu'une application C++ OpenGL plante. Voici quelques-unes d'entre elles. Si vous ne connaissez pas la ligne exacte où votre programme plante, apprenez à utiliser un débogueur (voir les raccourcis ci-dessus). Veuillez NE PAS déboguer avec printf().
V-F-1. Je n'atteins même pas le main▲
Cela est certainement dû à des DLL qui n'ont pas pu être trouvées. Essayez d'ouvrir votre application avec Dependency Walker (Windows) ou ldd (Linux ; essayez aussi ceci).
V-F-2. Mon programme crashe sur glfwOpenWindow(), ou toute autre fonction qui crée un contexte OpenGL▲
Plusieurs raisons existent :
- votre GPU ne supporte pas la version OpenGL demandée. Essayez de voir la version supportée avec GPU Caps Viewer ou un outil similaire. Mettez à jour vos pilotes si la version semble trop faible. Les cartes intégrées Intel sur les netbooks sont particulièrement nulles. Utilisez une version d'OpenGL plus basse (2.1 par exemple) et utilisez les extensions si vous manquez de fonctionnalités ;
- votre système d'exploitation ne supporte pas la version OpenGL demandée : Mac OS… même réponse ;
- vous essayez d'utiliser GLEW avec un contexte OpenGL core (c'est-à-dire, sans les choses dépréciées). C'est un bogue de GLEW. Utilisez glewExperimental=true avec glewInit() ou utilisez le mode de compatibilité (c'est-à-dire utilisez GLFW_OPENGL_COMPAT_PROFILE à la place de GLFW_OPENGL_CORE_PROFILE).
V-G. Mon programme crashe sur le premier appel OpenGL, ou sur la première création de tampon▲
Trois raisons sont possibles :
- vous n'avez pas appelé glewInit() APRÈS glfwOpenWindow() ;
-
vous utilisez un profil OpenGL core et vous n'avez pas créé de VAO. Ajoutez le code suivant après glewInit() :
SélectionnezGLuint VertexArrayID; glGenVertexArrays(
1
,&
VertexArrayID); glBindVertexArray(VertexArrayID); -
Vous utilisez la version de GLEW par défaut, qui contient un bogue. Vous ne pouvez utiliser un profil OpenGL core à cause de celui-ci. Utilisez soit glewExperimental=true
- avec glewInit(), ou demandez à GLFW d'utiliser un profil de compatibilité à la place :
glfwOpenWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE);
V-H. Mon programme crashe lorsque j'essaie de charger un fichier▲
Définissez votre répertoire de travail correctement. Lisez le premier tutoriel.
Créez un fichier test.txt et essayez le code suivant :
if
( fopen("test.txt"
, "r"
) ==
NULL
){
printf("I'm probably running my program from a wrong folder"
);
}
UTILISEZ LE DÉBOGUEUR !
Sincèrement ! Ne déboguez pas avec printf() ; utilisez un bon EDI. http://www.dotnetperls.com/debugging est pour le C# mais est aussi valide pour le C++. Cela peut être différent pour XCode ou Qt Creator, mais les concepts restent exactement les mêmes.
V-I. Autre chose ne va pas▲
Veuillez utiliser le forum.
VI. Remerciements▲
Cet article est une traduction autorisée dont le texte original peut être trouvé sur opengl-tutorial.org.