opengl Tutoriel
Démarrer avec opengl
Recherche…
Remarques
OpenGL est un standard ouvert pour le rendu des graphiques 2D et 3D en utilisant le matériel graphique. OpenGL a été mis en œuvre sur un nombre impressionnant de plates-formes permettant aux applications ciblant OpenGL d'être extrêmement flexibles.
Versions
Version | Date de sortie |
---|---|
1.1 | 1997-03-04 |
1.2 | 1998-03-16 |
1.2.1 | 1998-10-14 |
1.3 | 2001-08-14 |
1.4 | 2002-07-24 |
1,5 | 2003-07-29 |
2.0 | 2004-09-07 |
2.1 | 2006-07-02 |
3.0 | 2008-08-11 |
3.1 | 2009-03-24 |
3.2 | 2009-08-03 |
3.3 | 2010-03-11 |
4.0 | 2010-03-11 |
4.1 | 2010-07-26 |
4.2 | 2011-08-08 |
4.3 | 2012-08-06 |
4.4 | 2013-07-22 |
4.5 | 2014-08-11 |
Obtenir OpenGL
L'une des idées fausses les plus courantes à propos d'OpenGL est qu'il s'agissait d'une bibliothèque pouvant être installée à partir de sources tierces. Cette idée fausse mène à de nombreuses questions sous la forme "comment installer OpenGL" ou "où télécharger le SDK OpenGL".
Ce n'est pas ainsi que OpenGL trouve le chemin dans le système informatique. OpenGL en soi n'est qu'un ensemble de spécifications sur les commandes qu'une implémentation doit suivre. Donc, c'est l'implémentation qui compte. Et pour le moment, les implémentations OpenGL font partie des pilotes GPU. Cela pourrait changer à l'avenir, lorsque la nouvelle interface de programmation GPU permettra de véritablement implémenter OpenGL en tant que bibliothèque, mais pour l'instant, il s'agit d'une API de programmation destinée aux pilotes graphiques.
Lorsque OpenGL a été publié pour la première fois, l'API s'est retrouvée dans le contrat ABI (Application Binary Interface) de Windows, Solaris et Linux (LSB-4 Desktop) en plus de son origine Sun Irix. Apple a suivi et intégré OpenGL si profondément dans MacOS X, que la version OpenGL disponible est étroitement liée à la version de MacOS X installée. Cela a pour effet notable que les environnements de programmation système pour ces systèmes d'exploitation (à savoir la chaîne d'outils du compilateur et de l'éditeur de liens qui cible en natif ces systèmes) doivent également fournir des définitions d'API OpenGL. Il n'est pas nécessaire d'installer un SDK pour OpenGL. Il est techniquement possible de programmer OpenGL sur ces systèmes d’exploitation sans qu’il soit nécessaire d’installer un SDK dédié, en supposant qu’un environnement de construction suivant l’ABI ciblée soit installé.
Un effet secondaire de ces règles ABI strictes est que la version OpenGL exposée via l’interface de liaison est le plus petit dénominateur commun que les programmes exécutés sur la plate-forme cible peuvent espérer être disponible. Par conséquent, les fonctionnalités OpenGL modernes sont accessibles via le mécanisme d'extension, décrit en détail séparément.
Linux
Sous Linux, il est assez courant de compartimenter les packages de développement pour différents aspects du système, afin de pouvoir les mettre à jour individuellement. Dans la plupart des distributions Linux, les fichiers de développement pour OpenGL sont contenus dans un package dédié, qui est généralement une dépendance pour un méta-package de développement d'applications de bureau. L'installation des fichiers de développement OpenGL pour Linux est généralement prise en charge lors de l'installation du / des package (s) de développement de méta.
Microsoft Windows
La bibliothèque de liaisons API opengl32.dll
(nommée ainsi pour les versions 32 bits et 64 bits de Windows) est livrée par défaut avec chaque version de Windows depuis Windows NT-4 et Windows 95B (toutes deux vers 1997). Cependant, cette DLL ne fournit pas d'implémentation OpenGL réelle (hormis un repli de logiciel dont le seul but est de servir de filet de sécurité pour les programmes si aucune autre implémentation OpenGL n'est installée). Cette DLL appartient à Windows et ne doit pas être modifiée ou déplacée! Les versions OpenGL modernes sont livrées dans le cadre du pilote ICD ( Installable Client Driver ) et accessibles via le opengl32.dll
par défaut, préinstallé avec toutes les versions de Windows. Il a été décidé en interne par Microsoft, cependant, que les pilotes graphiques installés via Windows Update n'installeraient / ne mettraient pas à jour un ICD OpenGL. En tant que telles, de nouvelles installations de Windows avec des pilotes installés automatiquement ne prennent pas en charge les fonctionnalités OpenGL modernes. Pour obtenir un ICD OpenGL doté de fonctionnalités modernes, les pilotes graphiques doivent être téléchargés directement à partir du site Web du fournisseur du GPU et installés manuellement.
En ce qui concerne le développement, aucune mesure supplémentaire ne doit être prise en soi. Tous les compilateurs C / C ++ qui suivent les spécifications Windows ABI sont livrés avec des en-têtes et le stub de l'éditeur de liens (opengl32.lib) requis pour créer et lier des exécutables utilisant OpenGL.
Configuration manuelle OpenGL sous Windows
Exemple de code complet inclus à la fin
Composants Windows pour OpenGL
WGL
WGL (peut être prononcé wiggle ) signifie "Windows-GL", comme dans "une interface entre Windows et OpenGL" - un ensemble de fonctions de l'API Windows pour communiquer avec OpenGL. Les fonctions WGL ont un préfixe wgl et ses jetons ont un préfixe WGL_ .
La version OpenGL par défaut prise en charge sur les systèmes Microsoft est la version 1.1. C'est une très vieille version (la plus récente est la 4.5). La manière d'obtenir les versions les plus récentes est de mettre à jour vos pilotes graphiques, mais votre carte graphique doit prendre en charge ces nouvelles versions.
La liste complète des fonctions WGL peut être trouvée ici .
Interface de périphérique graphique (GDI)
GDI (aujourd'hui mis à jour vers GDI +) est une interface de dessin 2D qui vous permet de dessiner dans une fenêtre de Windows. Vous avez besoin de GDI pour initialiser OpenGL et lui permettre d'interagir avec lui (mais n'utilisera pas réellement GDI lui-même).
Dans GDI, chaque fenêtre a un contexte de périphérique (DC) utilisé pour identifier la cible de dessin lors de l'appel de fonctions (vous le transmettez en tant que paramètre). Cependant, OpenGL utilise son propre contexte de rendu (RC) . Donc, DC sera utilisé pour créer RC.
Configuration de base
Créer une fenêtre
Donc, pour faire des choses avec OpenGL, nous avons besoin de RC, et pour obtenir RC, nous avons besoin de DC, et pour obtenir DC, nous avons besoin d'une fenêtre. La création d'une fenêtre à l'aide de l'API Windows nécessite plusieurs étapes. Ceci est une routine de base, donc pour une explication plus détaillée, vous devriez consulter d'autres documentations, car il ne s'agit pas d'utiliser l'API Windows.
Ceci est une installation de Windows, donc Windows.h
doit être inclus et le point d’entrée du programme doit être la procédure WinMain
avec ses paramètres. Le programme doit également être lié à opengl32.dll
et à gdi32.dll
(que vous soyez sur un système 64 ou 32 bits).
Nous devons d'abord décrire notre fenêtre en utilisant la structure WNDCLASS
. Il contient des informations sur la fenêtre que nous voulons créer:
/* REGISTER WINDOW */
WNDCLASS window_class;
// Clear all structure fields to zero first
ZeroMemory(&window_class, sizeof(window_class));
// Define fields we need (others will be zero)
window_class.style = CS_OWNDC;
window_class.lpfnWndProc = window_procedure; // To be introduced later
window_class.hInstance = instance_handle;
window_class.lpszClassName = TEXT("OPENGL_WINDOW");
// Give our class to Windows
RegisterClass(&window_class);
/* *************** */
Pour une explication précise de la signification de chaque champ (et pour une liste complète des champs), consultez la documentation MSDN.
Ensuite, nous pouvons créer une fenêtre à l'aide de CreateWindowEx
. Une fois la fenêtre créée, nous pouvons acquérir son DC:
/* CREATE WINDOW */
HWND window_handle = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
TEXT("OPENGL_WINDOW"),
TEXT("OpenGL window"),
WS_OVERLAPPEDWINDOW,
0, 0,
800, 600,
NULL,
NULL,
instance_handle,
NULL);
HDC dc = GetDC(window_handle);
ShowWindow(window_handle, SW_SHOW);
/* ************* */
Enfin, nous devons créer une boucle de message qui reçoit les événements de fenêtre du système d'exploitation:
/* EVENT PUMP */
MSG msg;
while (true) {
if (PeekMessage(&msg, window_handle, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// draw(); <- there goes your drawing
SwapBuffers(dc); // To be mentioned later
}
/* ********** */
Format de pixel
OpenGL a besoin de connaître certaines informations sur notre fenêtre, telles que le bitness de la couleur, la méthode de mise en mémoire tampon, etc. Pour cela, nous utilisons un format de pixel . Cependant, nous ne pouvons que suggérer au système d'exploitation le type de format de pixel dont nous avons besoin, et le système d'exploitation fournira le format supporté le plus similaire , nous n'avons pas de contrôle direct sur celui-ci. C'est pourquoi on l'appelle uniquement un descripteur .
/* PIXEL FORMAT */
PIXELFORMATDESCRIPTOR descriptor;
// Clear all structure fields to zero first
ZeroMemory(&descriptor, sizeof(descriptor));
// Describe our pixel format
descriptor.nSize = sizeof(descriptor);
descriptor.nVersion = 1;
descriptor.dwFlags = PFD_DRAW_TO_WINDOW | PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL | PFD_GENERIC_ACCELERATED | PFD_DOUBLEBUFFER | PFD_SWAP_LAYER_BUFFERS;
descriptor.iPixelType = PFD_TYPE_RGBA;
descriptor.cColorBits = 32;
descriptor.cRedBits = 8;
descriptor.cGreenBits = 8;
descriptor.cBlueBits = 8;
descriptor.cAlphaBits = 8;
descriptor.cDepthBits = 32;
descriptor.cStencilBits = 8;
// Ask for a similar supported format and set it
int pixel_format = ChoosePixelFormat(dc, &descriptor);
SetPixelFormat(dc, pixel_format, &descriptor);
/* *********************** */
Nous avons activé la double mise en mémoire tampon dans le champ dwFlags
, nous devons donc appeler SwapBuffers
pour voir les choses après le dessin.
Contexte de rendu
Après cela, nous pouvons simplement créer notre contexte de rendu:
/* RENDERING CONTEXT */
HGLRC rc = wglCreateContext(dc);
wglMakeCurrent(dc, rc);
/* ***************** */
Notez qu'un seul thread peut utiliser le RC à la fois. Si vous souhaitez l'utiliser à partir d'un autre thread plus tard, vous devez appeler wglMakeCurrent
pour l'activer à nouveau (cela le désactivera sur le thread actuellement actif, etc.).
Obtenir les fonctions OpenGL
Les fonctions OpenGL sont obtenues en utilisant des pointeurs de fonctions. La procédure générale est la suivante:
- D'une manière ou d'une autre, obtenir des types de pointeurs de fonctions (essentiellement les prototypes de fonctions)
- Déclarez chaque fonction que nous aimerions utiliser (avec son type de pointeur de fonction)
- Obtenir la fonction réelle
Par exemple, considérons glBegin:
// We need to somehow find something that contains something like this,
// as we can't know all the OpenGL function prototypes
typedef void (APIENTRY *PFNGLBEGINPROC)(GLenum);
// After that, we need to declare the function in order to use it
PFNGLBEGINPROC glBegin;
// And finally, we need to somehow make it an actual function
("PFN" signifie "pointeur vers la fonction", suit le nom d'une fonction OpenGL et "PROC" à la fin - c'est le nom habituel du type de pointeur de la fonction OpenGL.)
Voici comment cela se passe sous Windows. Comme mentionné précédemment, Microsoft ne fournit que OpenGL 1.1. Tout d'abord, les types de pointeurs de fonction pour cette version peuvent être trouvés en incluant GL/gl.h
Après cela, nous déclarons toutes les fonctions que nous avons l'intention d'utiliser comme indiqué ci-dessus (le faire dans un fichier d'en-tête et les déclarer "extern" nous permettrait de les utiliser toutes après les avoir chargées une seule fois). Enfin, le chargement des fonctions OpenGL 1.1 se fait en ouvrant la DLL:
HMODULE gl_module = LoadLibrary(TEXT("opengl32.dll"));
/* Load all the functions here */
glBegin = (PFNGLBEGINPROC)GetProcAddress("glBegin");
// ...
/* *************************** */
FreeLibrary(gl_module);
Cependant, nous voulons probablement un peu plus que OpenGL 1.1. Mais Windows ne nous donne pas les prototypes de fonction ou les fonctions exportées pour tout ce qui précède. Les prototypes doivent être acquis à partir du registre OpenGL . Trois fichiers nous intéressent: GL/glext.h
, GL/glcorearb.h
et GL/wglext.h
.
Pour compléter GL/gl.h
fourni par Windows, nous avons besoin de GL/glext.h
. Il contient (comme décrit par la base de registre) "Interface de profil et d’extension de compatibilité OpenGL 1.2 et supérieur" (plus de détails sur les profils et les extensions plus tard, où nous verrons que ce n’est pas une bonne idée d’utiliser ces deux fichiers ).
Les fonctions actuelles doivent être obtenues par wglGetProcAddress
(pas besoin d'ouvrir la DLL pour ce type, elles ne sont pas là, utilisez simplement la fonction). Avec cela, nous pouvons récupérer toutes les fonctions d'OpenGL 1.2 et supérieur (mais pas 1.1). Notez que, pour fonctionner correctement, le contexte de rendu OpenGL doit être créé et mis à jour . Ainsi, par exemple, glClear
:
// Include the header from the OpenGL registry for function pointer types
// Declare the functions, just like before
PFNGLCLEARPROC glClear;
// ...
// Get the function
glClear = (PFNGLCLEARPROC)wglGetProcAddress("glClear");
Nous pouvons en fait créer une procédure get_proc
qui utilise à la fois wglGetProcAddress
et GetProcAddress
:
// Get function pointer
void* get_proc(const char *proc_name)
{
void *proc = (void*)wglGetProcAddress(proc_name);
if (!proc) proc = (void*)GetProcAddress(gl_module, proc_name); // gl_module must be somewhere in reach
return proc;
}
Donc, pour conclure, nous créerions un fichier d'en-tête rempli de déclarations de pointeur de fonction comme ceci:
extern PFNGLCLEARCOLORPROC glClearColor;
extern PFNGLCLEARDEPTHPROC glClearDepth;
extern PFNGLCLEARPROC glClear;
extern PFNGLCLEARBUFFERIVPROC glClearBufferiv;
extern PFNGLCLEARBUFFERFVPROC glClearBufferfv;
// And so on...
Nous pouvons alors créer une procédure comme load_gl_functions
que nous appelons une seule fois et fonctionne comme ceci:
glClearColor = (PFNGLCLEARCOLORPROC)get_proc("glClearColor");
glClearDepth = (PFNGLCLEARDEPTHPROC)get_proc("glClearDepth");
glClear = (PFNGLCLEARPROC)get_proc("glClear");
glClearBufferiv = (PFNGLCLEARBUFFERIVPROC)get_proc("glClearBufferiv");
glClearBufferfv = (PFNGLCLEARBUFFERFVPROC)get_proc("glClearBufferfv");
Et vous êtes tous ensemble! Il suffit d'inclure l'en-tête avec les pointeurs de la fonction et GL loin.
Meilleure configuration
Profils OpenGL
OpenGL est en développement depuis plus de 20 ans et les développeurs ont toujours été stricts quant à la compatibilité en amont (BC) . L'ajout d'une nouvelle fonctionnalité est très difficile à cause de cela. Ainsi, en 2008, il a été séparé en deux "profils". Core et compatibilité . Le profil de base brise le BC en faveur des améliorations de performances et de certaines des nouvelles fonctionnalités. Il supprime même complètement certaines fonctionnalités héritées. Le profil de compatibilité conserve BC avec toutes les versions jusqu'à 1.0, et certaines nouvelles fonctionnalités ne sont pas disponibles. Il ne doit être utilisé que pour les anciens systèmes hérités, toutes les nouvelles applications doivent utiliser le profil de base.
À cause de cela, il y a un problème avec notre configuration de base - il ne fournit que le contexte rétrocompatible avec OpenGL 1.0. Le format de pixel est également limité. Il y a une meilleure approche, en utilisant des extensions.
Extensions OpenGL
Tout ajout à la fonctionnalité d'origine d'OpenGL s'appelle des extensions. En règle générale, ils peuvent soit rendre certaines choses légales qui ne l'étaient pas auparavant, étendre la plage de valeurs des paramètres, étendre GLSL, et même ajouter de nouvelles fonctionnalités.
Il existe trois groupes principaux d’extensions: fournisseur, EXT et ARB. Les extensions de fournisseur proviennent d'un fournisseur spécifique et elles ont une marque spécifique à un fournisseur, comme AMD ou NV. Les extensions EXT sont réalisées par plusieurs fournisseurs travaillant ensemble. Après un certain temps, ils peuvent devenir des extensions ARB, qui sont toutes les extensions officiellement supportées et approuvées par ARB.
Pour acquérir des types de pointeurs de fonctions et des prototypes de fonctions de toutes les extensions et comme mentionné précédemment, tous les types de pointeurs de fonctions d'OpenGL 1.2 et plus , il faut télécharger les fichiers d'en-tête à partir du registre OpenGL . Comme nous l'avons vu, pour les nouvelles applications, il est préférable d'utiliser le profil de base, il serait donc préférable d'inclure GL/glcorearb.h
au lieu de GL/gl.h
et GL/glext.h
(si vous utilisez GL/glcorearb.h
alors don ne comprend pas GL/gl.h
).
Il existe également des extensions pour le WGL, dans GL/wglext.h
. Par exemple, la fonction permettant d'obtenir la liste de toutes les extensions prises en charge est en réalité une extension elle-même, wglGetExtensionsStringARB
(elle retourne une grande chaîne avec une liste de toutes les extensions prises en charge séparées par des espaces).
Obtenir des extensions est également géré via wglGetProcAddress
, nous pouvons donc utiliser notre wrapper comme avant.
Format de pixel avancé et création de contexte
L'extension WGL_ARB_pixel_format
nous permet de créer un format de pixel avancé. Contrairement à avant, nous n'utilisons pas de structure. Au lieu de cela, nous passons la liste des attributs recherchés.
int pixel_format_arb;
UINT pixel_formats_found;
int pixel_attributes[] = {
WGL_SUPPORT_OPENGL_ARB, 1,
WGL_DRAW_TO_WINDOW_ARB, 1,
WGL_DRAW_TO_BITMAP_ARB, 1,
WGL_DOUBLE_BUFFER_ARB, 1,
WGL_SWAP_LAYER_BUFFERS_ARB, 1,
WGL_COLOR_BITS_ARB, 32,
WGL_RED_BITS_ARB, 8,
WGL_GREEN_BITS_ARB, 8,
WGL_BLUE_BITS_ARB, 8,
WGL_ALPHA_BITS_ARB, 8,
WGL_DEPTH_BITS_ARB, 32,
WGL_STENCIL_BITS_ARB, 8,
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
0
};
BOOL result = wglChoosePixelFormatARB(dc, pixel_attributes, NULL, 1, &pixel_format_arb, &pixel_formats_found);
De même, l'extension WGL_ARB_create_context
nous permet de créer un contexte avancé:
GLint context_attributes[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
0
};
HGLRC new_rc = wglCreateContextAttribsARB(dc, 0, context_attributes);
Pour une explication précise des paramètres et des fonctions, consultez la spécification OpenGL.
Pourquoi n'avons-nous pas simplement commencé avec eux? Eh bien, c'est parce que les extensions nous permettent de le faire, et pour obtenir des extensions, nous avons besoin de wglGetProcAddress
, mais cela ne fonctionne qu'avec un contexte valide actif. Par conséquent, avant de pouvoir créer le contexte que nous souhaitons, nous devons déjà avoir un contexte actif, généralement appelé contexte factice .
Cependant, Windows ne permet pas de définir le format de pixel d'une fenêtre plus d'une fois. Pour cette raison, la fenêtre doit être détruite et recréée pour appliquer de nouvelles choses:
wglMakeCurrent(dc, NULL);
wglDeleteContext(rc);
ReleaseDC(window_handle, dc);
DestroyWindow(window_handle);
// Recreate the window...
Exemple de code complet:
/* We want the core profile, so we include GL/glcorearb.h. When including that, then
GL/gl.h should not be included.
If using compatibility profile, the GL/gl.h and GL/glext.h need to be included.
GL/wglext.h gives WGL extensions.
Note that Windows.h needs to be included before them. */
#include <cstdio>
#include <Windows.h>
#include <GL/glcorearb.h>
#include <GL/wglext.h>
LRESULT CALLBACK window_procedure(HWND, UINT, WPARAM, LPARAM);
void* get_proc(const char*);
/* gl_module is for opening the DLL, and the quit flag is here to prevent
quitting when recreating the window (see the window_procedure function) */
HMODULE gl_module;
bool quit = false;
/* OpenGL function declarations. In practice, we would put these in a
separate header file and add "extern" in front, so that we can use them
anywhere after loading them only once. */
PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB;
PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB;
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB;
PFNGLGETSTRINGPROC glGetString;
int WINAPI WinMain(HINSTANCE instance_handle, HINSTANCE prev_instance_handle, PSTR cmd_line, int cmd_show) {
/* REGISTER WINDOW */
WNDCLASS window_class;
// Clear all structure fields to zero first
ZeroMemory(&window_class, sizeof(window_class));
// Define fields we need (others will be zero)
window_class.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
window_class.lpfnWndProc = window_procedure;
window_class.hInstance = instance_handle;
window_class.lpszClassName = TEXT("OPENGL_WINDOW");
// Give our class to Windows
RegisterClass(&window_class);
/* *************** */
/* CREATE WINDOW */
HWND window_handle = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
TEXT("OPENGL_WINDOW"),
TEXT("OpenGL window"),
WS_OVERLAPPEDWINDOW,
0, 0,
800, 600,
NULL,
NULL,
instance_handle,
NULL);
HDC dc = GetDC(window_handle);
ShowWindow(window_handle, SW_SHOW);
/* ************* */
/* PIXEL FORMAT */
PIXELFORMATDESCRIPTOR descriptor;
// Clear all structure fields to zero first
ZeroMemory(&descriptor, sizeof(descriptor));
// Describe our pixel format
descriptor.nSize = sizeof(descriptor);
descriptor.nVersion = 1;
descriptor.dwFlags = PFD_DRAW_TO_WINDOW | PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL | PFD_GENERIC_ACCELERATED | PFD_DOUBLEBUFFER | PFD_SWAP_LAYER_BUFFERS;
descriptor.iPixelType = PFD_TYPE_RGBA;
descriptor.cColorBits = 32;
descriptor.cRedBits = 8;
descriptor.cGreenBits = 8;
descriptor.cBlueBits = 8;
descriptor.cAlphaBits = 8;
descriptor.cDepthBits = 32;
descriptor.cStencilBits = 8;
// Ask for a similar supported format and set it
int pixel_format = ChoosePixelFormat(dc, &descriptor);
SetPixelFormat(dc, pixel_format, &descriptor);
/* *********************** */
/* RENDERING CONTEXT */
HGLRC rc = wglCreateContext(dc);
wglMakeCurrent(dc, rc);
/* ***************** */
/* LOAD FUNCTIONS (should probably be put in a separate procedure) */
gl_module = LoadLibrary(TEXT("opengl32.dll"));
wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)get_proc("wglGetExtensionsStringARB");
wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)get_proc("wglChoosePixelFormatARB");
wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)get_proc("wglCreateContextAttribsARB");
glGetString = (PFNGLGETSTRINGPROC)get_proc("glGetString");
FreeLibrary(gl_module);
/* ************** */
/* PRINT VERSION */
const GLubyte *version = glGetString(GL_VERSION);
printf("%s\n", version);
fflush(stdout);
/* ******* */
/* NEW PIXEL FORMAT*/
int pixel_format_arb;
UINT pixel_formats_found;
int pixel_attributes[] = {
WGL_SUPPORT_OPENGL_ARB, 1,
WGL_DRAW_TO_WINDOW_ARB, 1,
WGL_DRAW_TO_BITMAP_ARB, 1,
WGL_DOUBLE_BUFFER_ARB, 1,
WGL_SWAP_LAYER_BUFFERS_ARB, 1,
WGL_COLOR_BITS_ARB, 32,
WGL_RED_BITS_ARB, 8,
WGL_GREEN_BITS_ARB, 8,
WGL_BLUE_BITS_ARB, 8,
WGL_ALPHA_BITS_ARB, 8,
WGL_DEPTH_BITS_ARB, 32,
WGL_STENCIL_BITS_ARB, 8,
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
0
};
BOOL result = wglChoosePixelFormatARB(dc, pixel_attributes, NULL, 1, &pixel_format_arb, &pixel_formats_found);
if (!result) {
printf("Could not find pixel format\n");
fflush(stdout);
return 0;
}
/* **************** */
/* RECREATE WINDOW */
wglMakeCurrent(dc, NULL);
wglDeleteContext(rc);
ReleaseDC(window_handle, dc);
DestroyWindow(window_handle);
window_handle = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
TEXT("OPENGL_WINDOW"),
TEXT("OpenGL window"),
WS_OVERLAPPEDWINDOW,
0, 0,
800, 600,
NULL,
NULL,
instance_handle,
NULL);
dc = GetDC(window_handle);
ShowWindow(window_handle, SW_SHOW);
/* *************** */
/* NEW CONTEXT */
GLint context_attributes[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
0
};
rc = wglCreateContextAttribsARB(dc, 0, context_attributes);
wglMakeCurrent(dc, rc);
/* *********** */
/* EVENT PUMP */
MSG msg;
while (true) {
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// draw(); <- there goes your drawing
SwapBuffers(dc);
}
/* ********** */
return 0;
}
// Procedure that processes window events
LRESULT CALLBACK window_procedure(HWND window_handle, UINT message, WPARAM param_w, LPARAM param_l)
{
/* When destroying the dummy window, WM_DESTROY message is going to be sent,
but we don't want to quit the application then, and that is controlled by
the quit flag. */
switch(message) {
case WM_DESTROY:
if (!quit) quit = true;
else PostQuitMessage(0);
return 0;
}
return DefWindowProc(window_handle, message, param_w, param_l);
}
/* A procedure for getting OpenGL functions and OpenGL or WGL extensions.
When looking for OpenGL 1.2 and above, or extensions, it uses wglGetProcAddress,
otherwise it falls back to GetProcAddress. */
void* get_proc(const char *proc_name)
{
void *proc = (void*)wglGetProcAddress(proc_name);
if (!proc) proc = (void*)GetProcAddress(gl_module, proc_name);
return proc;
}
Compilé avec g++ GLExample.cpp -lopengl32 -lgdi32
avec MinGW / Cygwin ou cl GLExample.cpp opengl32.lib gdi32.lib user32.lib
avec le compilateur cl GLExample.cpp opengl32.lib gdi32.lib user32.lib
. Assurez-vous toutefois que les en-têtes du registre OpenGL sont dans le chemin d'inclusion. Sinon, utilisez l' -I
pour g++
ou /I
pour cl
afin d'indiquer au compilateur où elles se trouvent.
Créer OpenGL 4.1 avec C ++ et Cocoa
Note: Il y aura des Objective-c dans cet exemple. Dans cet exemple, nous allons créer un wrapper en C ++. Donc, ne vous en faites pas trop.
Commencez par démarrer Xcode et créez un projet.
Et sélectionnez une application Cocoa
Supprimer toutes les sources sauf le fichier Info.plist (votre application ne fonctionnera pas sans elle)
Créez 4 nouveaux fichiers sources: Un fichier Objective-c ++ et un en-tête (j'ai appelé le mien MacApp) Une classe C ++ (j'ai appelé le mien (Application))
En haut à gauche (avec le nom du projet), cliquez dessus et ajoutez les frameworks et bibliothèques liés. Ajouter: OpenGL.Framework AppKit.Framework GLKit.Framework
Votre projet ressemblera probablement à ceci:
NSApplication est la classe principale que vous utilisez lors de la création d'une application MacOS. Il vous permet d'enregistrer des fenêtres et de capturer des événements.
Nous voulons enregistrer (notre propre) fenêtre sur la NSApplication. Commencez par créer dans votre en-tête objective-c ++ une classe objective-c qui hérite de NSWindow et implémente NSApplicationDelegate.
//Mac_App_H
#import <Cocoa/Cocoa.h>
#import "Application.hpp"
#import <memory>
NSApplication* application;
@interface MacApp : NSWindow <NSApplicationDelegate>{
std::shared_ptr<Application> appInstance;
}
@property (nonatomic, retain) NSOpenGLView* glView;
-(void) drawLoop:(NSTimer*) timer;
@end
Nous appelons cela du principal avec
int main(int argc, const char * argv[]) {
MacApp* app;
application = [NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
//create a window with the size of 600 by 600
app = [[MacApp alloc] initWithContentRect:NSMakeRect(0, 0, 600, 600) styleMask:NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask backing:NSBackingStoreBuffered defer:YES];
[application setDelegate:app];
[application run];
}
L'implémentation de notre fenêtre est en fait assez facile. Nous déclarons d'abord avec synthétiser notre vue et ajoutons un booléen global objectif-c lorsque la fenêtre devrait se fermer.
#import "MacApp.h"
@implementation MacApp
@synthesize glView;
BOOL shouldStop = NO;
Maintenant pour le constructeur. Ma préférence est d'utiliser initWithContentRect.
-(id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag{
if(self = [super initWithContentRect:contentRect styleMask:aStyle backing:bufferingType defer:flag]){
//sets the title of the window (Declared in Plist)
[self setTitle:[[NSProcessInfo processInfo] processName]];
//This is pretty important.. OS X starts always with a context that only supports openGL 2.1
//This will ditch the classic OpenGL and initialises openGL 4.1
NSOpenGLPixelFormatAttribute pixelFormatAttributes[] ={
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
NSOpenGLPFAColorSize , 24 ,
NSOpenGLPFAAlphaSize , 8 ,
NSOpenGLPFADoubleBuffer ,
NSOpenGLPFAAccelerated ,
NSOpenGLPFANoRecovery ,
0
};
NSOpenGLPixelFormat* format = [[NSOpenGLPixelFormat alloc]initWithAttributes:pixelFormatAttributes];
//Initialize the view
glView = [[NSOpenGLView alloc]initWithFrame:contentRect pixelFormat:format];
//Set context and attach it to the window
[[glView openGLContext]makeCurrentContext];
//finishing off
[self setContentView:glView];
[glView prepareOpenGL];
[self makeKeyAndOrderFront:self];
[self setAcceptsMouseMovedEvents:YES];
[self makeKeyWindow];
[self setOpaque:YES];
//Start the c++ code
appInstance = std::shared_ptr<Application>(new Application());
}
return self;
}
Bon ... maintenant nous avons en fait une application exécutable .. Vous pourriez voir un écran noir ou scintiller.
Commençons à dessiner un triangle génial. (En c ++)
Mon en-tête d'application
#ifndef Application_hpp
#define Application_hpp
#include <iostream>
#include <OpenGL/gl3.h>
class Application{
private:
GLuint program;
GLuint vao;
public:
Application();
void update();
~Application();
};
#endif /* Application_hpp */
La mise en oeuvre:
Application::Application(){
static const char * vs_source[] =
{
"#version 410 core \n"
" \n"
"void main(void) \n"
"{ \n"
" const vec4 vertices[] = vec4[](vec4( 0.25, -0.25, 0.5, 1.0), \n"
" vec4(-0.25, -0.25, 0.5, 1.0), \n"
" vec4( 0.25, 0.25, 0.5, 1.0)); \n"
" \n"
" gl_Position = vertices[gl_VertexID]; \n"
"} \n"
};
static const char * fs_source[] =
{
"#version 410 core \n"
" \n"
"out vec4 color; \n"
" \n"
"void main(void) \n"
"{ \n"
" color = vec4(0.0, 0.8, 1.0, 1.0); \n"
"} \n"
};
program = glCreateProgram();
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, 1, fs_source, NULL);
glCompileShader(fs);
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs, 1, vs_source, NULL);
glCompileShader(vs);
glAttachShader(program, vs);
glAttachShader(program, fs);
glLinkProgram(program);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
}
void Application::update(){
static const GLfloat green[] = { 0.0f, 0.25f, 0.0f, 1.0f };
glClearBufferfv(GL_COLOR, 0, green);
glUseProgram(program);
glDrawArrays(GL_TRIANGLES, 0, 3);
}
Application::~Application(){
glDeleteVertexArrays(1, &vao);
glDeleteProgram(program);
}
Maintenant, il suffit d'appeler la mise à jour encore et encore (si vous voulez que quelque chose bouge) Implémentez dans votre classe objective-c
-(void) drawLoop:(NSTimer*) timer{
if(shouldStop){
[self close];
return;
}
if([self isVisible]){
appInstance->update();
[glView update];
[[glView openGLContext] flushBuffer];
}
}
Et ajoutez la méthode this dans l'implémentation de votre classe objective-c:
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
[NSTimer scheduledTimerWithTimeInterval:0.000001 target:self selector:@selector(drawLoop:) userInfo:nil repeats:YES];
}
cela appellera la fonction de mise à jour de votre classe c ++ encore et encore (chaque 0,000001 seconde pour être précis)
Pour terminer, nous fermons la fenêtre lorsque le bouton Fermer est enfoncé:
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication{
return YES;
}
- (void)applicationWillTerminate:(NSNotification *)aNotification{
shouldStop = YES;
}
Félicitations, vous avez maintenant une fenêtre impressionnante avec un triangle OpenGL sans framework tiers.
Création de contexte OpenGL Cross Platform (en utilisant SDL2)
Créer une fenêtre avec un contexte OpenGL (extension par GLEW ):
#define GLEW_STATIC #include <GL/glew.h> #include <SDL2/SDL.h> int main(int argc, char* argv[]) { SDL_Init(SDL_INIT_VIDEO); /* Initialises Video Subsystem in SDL */ /* Setting up OpenGL version and profile details for context creation */ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); /* A 800x600 window. Pretty! */ SDL_Window* window = SDL_CreateWindow ( "SDL Context", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 600, SDL_WINDOW_OPENGL ); /* Creating OpenGL Context */ SDL_GLContext gl_context = SDL_GL_CreateContext(window); /* Loading Extensions */ glewExperimental = GL_TRUE; glewInit(); /* The following code is for error checking. * If OpenGL has initialised properly, this should print 1. * Remove it in production code. */ GLuint vertex_buffer; glGenBuffers(1, &vertex_buffer); printf("%u\n", vertex_buffer); /* Error checking ends here */ /* Main Loop */ SDL_Event window_event; while(1) { if (SDL_PollEvent(&window_event)) { if (window_event.type == SDL_QUIT) { /* If user is exiting the application */ break; } } /* Swap the front and back buffer for flicker-free rendering */ SDL_GL_SwapWindow(window); } /* Freeing Memory */ glDeleteBuffers(1, &vertex_buffer); SDL_GL_DeleteContext(gl_context); SDL_Quit(); return 0; }
Installez Modern OpenGL 4.1 sur macOS (Xcode, GLFW et GLEW)
1. Installez GLFW
La première étape consiste à créer une fenêtre OpenGL. GLFW est une bibliothèque multi-plateforme Open Source pour créer des fenêtres avec OpenGL, pour installer GLFW en premier téléchargeant ses fichiers depuis www.glfw.org
Extraire le dossier GLFW et son contenu ressemblera à ceci
Téléchargez et installez CMake pour construire GLFW. Allez sur www.cmake.org/download/ , téléchargez CMake et installez pour MAC OS X
Si Xcode n'est pas installé. Téléchargez et installez Xcode à partir du Mac App Store.
Créer un nouveau dossier Construire dans le dossier GLFW
Ouvrez CMake, cliquez sur le bouton Parcourir la source pour sélectionner le dossier GLFW (assurez-vous que CMakeLists.txt) se trouve dans ce dossier. Après cela, cliquez sur le bouton Browse Build et sélectionnez le dossier Build nouvellement créé à l'étape précédente.
Maintenant Cliquez sur le bouton Configurer et sélectionnez Xcode comme générateur avec l' option Utiliser les compilateurs natifs par défaut , puis cliquez sur Terminé .
Cochez l'option BUILD_SHARED_LIBS puis cliquez à nouveau sur le bouton Configurer et enfin cliquez sur le bouton Générer .
Après la génération CMake devrait ressembler à ceci
Maintenant, ouvrez Finder et goto / usr , créez un nom de dossier local s'il n'y en a pas déjà. Ouvrez le dossier local et créez deux dossiers include et lib si ce n'est déjà fait.
Maintenant, ouvrez le dossier GLFW et allez dans Build (où CMake avait construit les fichiers). Ouvrez le fichier GLFW.xcodeproj dans Xcode.
Sélectionnez Installer> Mon Mac , puis cliquez sur Exécuter (bouton Jouer en forme).
Il est maintenant installé avec succès (ignorez les avertissements).
Pour vous assurer que les dossiers Open Finder et goto / usr / local / lib et trois fichiers de bibliothèque GLFW sont déjà présents (sinon, ouvrez le dossier Build dans le dossier GLFW et accédez à src / Debug, copiez tous les fichiers dans / usr / local / lib). )
Open Finder et goto / usr / local / include et un dossier GLFW y sera déjà présent avec deux fichiers d'en-tête à l'intérieur, nommés glfw3.h et glfw3native.h
2. Installez GLEW
GLEW est une bibliothèque multi-plateforme qui permet d'interroger et de charger des extensions OpenGL. Il fournit des mécanismes d'exécution pour déterminer quelles extensions OpenGL sont prises en charge sur la plate-forme cible. Ce n'est que pour OpenGL moderne (OpenGL version 3.2 et supérieure qui nécessite des fonctions à déterminer à l'exécution). Pour installer d'abord, téléchargez ses fichiers depuis glew.sourceforge.net
Extrayez le dossier GLFW et son contenu ressemblera à ceci.
Maintenant, ouvrez Terminal, accédez au dossier GLEW et tapez les commandes suivantes
make
sudo make install
make clean
Maintenant, GLEW est installé avec succès. Pour vous assurer qu'il est installé, ouvrez Finder, accédez à / usr / local / include et un dossier GL y sera déjà présent avec trois fichiers d'en-tête à l'intérieur, nommés glew.h , glxew.h et wglew.h.
Ouvrez le Finder et accédez à / usr / local / lib et les fichiers de la bibliothèque GLEW y seront déjà présents.
3. Tester et exécuter
Nous avons maintenant installé avec succès GLFW et GLEW. Il est temps de coder. Ouvrez Xcode et créez un nouveau projet Xcode. Sélectionnez l' outil Ligne de commande, puis continuez et sélectionnez C ++ comme langage.
Xcode créera un nouveau projet de ligne de commande.
Cliquez sur le nom du projet et, sous l'onglet Paramètres de génération, passez de Basique à Tous , dans la section Chemins de recherche , ajoutez / usr / local / include dans les chemins de recherche d'en-tête et ajoutez / usr / local / lib dans les chemins de recherche de bibliothèque.
Cliquez sur le nom du projet et, sous l'onglet Construire des phases et sous Lien avec les bibliothèques binaires, ajoutez OpenGL.framework et ajoutez également les bibliothèques GLFW et GLEW récemment créées à partir de / usr / local / lib.
Nous sommes maintenant prêts à coder dans Modern Open GL 4.1 sur macOS en utilisant C ++ et Xcode. Le code suivant créera une fenêtre OpenGL utilisant GLFW avec une sortie d'écran vide.
#include <GL/glew.h>
#include <GLFW/glfw3.h>
// Define main function
int main()
{
// Initialize GLFW
glfwInit();
// Define version and compatibility settings
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
// Create OpenGL window and context
GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL", NULL, NULL);
glfwMakeContextCurrent(window);
// Check for window creation failure
if (!window)
{
// Terminate GLFW
glfwTerminate();
return 0;
}
// Initialize GLEW
glewExperimental = GL_TRUE; glewInit();
// Event loop
while(!glfwWindowShouldClose(window))
{
// Clear the screen to black
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT);
glfwSwapBuffers(window);
glfwPollEvents();
}
// Terminate GLFW
glfwTerminate(); return 0;
}
Créer un contexte Opengl avec Java et LWJGL 3.0
Dans cet exemple de code, nous allons créer une fenêtre Opengl vierge à l'aide de LWJGL 3.0+, qui ne contient pas d'étapes pour créer le projet dans votre IDE.
- Créez un nom de classe WindowManager qui contiendra tout le code de la plaque de la chaudière pour créer une fenêtre de contexte opengl à l'écran.
WindowManager.java
import org.lwjgl.glfw.*;
import static org.lwjgl.glfw.Callbacks.*;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.system.MemoryUtil.*;
/**
* Class Containing code related to inflating Opengl Window
*/
public class Displaymanager {
private static long window;
public static void createDisplay(){
// Setup an error callback. The default implementation
// will print the error message in System.err.
GLFWErrorCallback.createPrint(System.err).set();
// Initialize GLFW. Most GLFW functions will not work before doing this.
if ( !glfwInit() )
throw new IllegalStateException("Unable to initialize GLFW");
// Configure our window
glfwDefaultWindowHints(); // optional, the current window hints are already the default
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // the window will stay hidden after creation
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable
int WIDTH = 300;
int HEIGHT = 300;
// Create the window
window = glfwCreateWindow(WIDTH, HEIGHT, "Hello World!", NULL, NULL);
if ( window == NULL )
throw new RuntimeException("Failed to create the GLFW window");
// Setup a key callback. It will be called every time a key is pressed, repeated or released.
glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> {
if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE )
glfwSetWindowShouldClose(window, true); // We will detect this in our rendering loop
});
// Get the resolution of the primary monitor
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
// Center our window
glfwSetWindowPos(
window,
(vidmode.width() - WIDTH) / 2,
(vidmode.height() - HEIGHT) / 2
);
// Make the OpenGL context current
glfwMakeContextCurrent(window);
// Enable v-sync
glfwSwapInterval(1);
// Make the window visible
glfwShowWindow(window);
}
public static boolean isCloseRequested(){
return glfwWindowShouldClose(window);
}
public static void updateDisplay(){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer
glfwSwapBuffers(window); // swap the color buffers
// Poll for window events. The key callback above will only be
// invoked during this call.
glfwPollEvents();
}
public static void destroyDisplay(){
// Terminate GLFW and free the error callback
cleanUp();
glfwTerminate();
glfwSetErrorCallback(null).free();
}
private static void cleanUp() {
// Free the window callbacks and destroy the window
glfwFreeCallbacks(window);
glfwDestroyWindow(window);
}
}
- Ensuite, créez une classe contenant la boucle de rendu principale, qui appellera toutes les fonctions créées ci-dessus.
OpenGlMain.java
import org.lwjgl.opengl.GL;
import renderEngine.Displaymanager;
import static org.lwjgl.opengl.GL11.glClearColor;
/**
* Class to test the opengl Window
*/
public class OpenGlMain {
public static void main(String[] args) {
Displaymanager.createDisplay();
// This line is critical for LWJGL's interoperation with GLFW's
// OpenGL context, or any context that is managed externally.
// LWJGL detects the context that is current in the current thread,
// creates the GLCapabilities instance and makes the OpenGL
// bindings available for use.
GL.createCapabilities();
while (!Displaymanager.isCloseRequested()){
// Set the clear color
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
Displaymanager.updateDisplay();
}
Displaymanager.destroyDisplay();
}
}
Pour plus de détails, consultez le guide officiel LWJGL