Création d'une fenêtre SDL2 avec contexte OpenGL en C. [SDL2 + Glad]

Introduction

Pour comprendre ce tutoriel il est nécessaire d'avoir quelques bases de connaissances en langage C.

Ce guide se base sur le tutoriel disponible à cette adresse:
Wikibooks - OpenGL Programming/Modern OpenGL Introduction

J'ai converti le code source de ce tutoriel afin qu'il soit pleinement compatible en langage C.

Aussi je vais tenter d'expliquer en quoi consiste chacune des fonctions qui seront déclarées dans ce tutoriel.
Et à la différence du guide disponible sur le wikibooks, nous allons utiliser la bibliothèque Glad plutôt que Glew.

OpenGL en bref.

OpenGL est une API graphique pour le traitement d'image 2D et 3D lancé par Silicon Graphics en 1992. Cette interface de programmation est multiplateforme et écrite en C.

OpenGL permet à un programme de déclarer la géométrie d'objets sous forme de points, de vecteurs, de polygones et de textures. OpenGL effectue ensuite des calculs de projection en vue de déterminer l'image à l'écran, en tenant compte de la distance, de l'orientation, des ombres, de la transparence et du cadrage.

Source: Wikipedia - OpenGL

SDL

Simple DirectMedia Layer est une bibliothèque de développement multiplateforme conçue pour fournir un accès de bas niveau au matériel audio, clavier, souris, joystick et graphique via OpenGL/Direct3D/Metal/Vulkan.

Source: Wiki - SDL

En somme c'est une bibliothèque multimédia permettant entre autre de concevoir des jeux-vidéos ou applications graphique comme nous allons le faire dans ce tutoriel. Cette bibliothèque nous permettra de créer un contexte OpenGL dans une fenêtre gérée par la bibliothèque SDL.

Qu'est-ce que Glad?

GLAD simplifie le processus de gestion des pointeurs de fonctions d'OpenGL. Il génère du code spécifique à la plate-forme pour les fonctions OpenGL, ceci simplifie l'utilisation d'OpenGL et de manière multiplateforme.

Les prérequis

Donc, il vous faudra pour ce tutoriel:

Mise en place

Créez un répertoire pour ce projet dans lequel vous créez aussi le nouveau fichier main.c.
Téléchargez glad.zip pour OpenGL 2.1 et extrayez-la dans le dossier que vous venez de créer.
Deux dossiers apparaissent à côté de main.c:

Déplacer le fichier glad.c se trouvant dans le dossier src vers le premier répertoire que vous avez créé. Donc vous devriez avoir à présent main.c et glad.c dans le même dossier.

Commençons le code

Je vais faire un code minimaliste pour commencer ce tutoriel.

Éditez avec votre bloc note ou votre IDE le fichier main.c et inscrivez-y ce qui suit.

main.c


//Directives préprocesseur: chargement des bibliothèques SDL2 et Glad
#include <SDL2/SDL.h>
#include "glad/glad.h"

//Fonction principale, point d'entrée du programme
int main(int argc, char* argv[]){
	
    //Fonction d'initialisation de la SDL (seulement la vidéo dans ce tutoriel)
    SDL_Init(SDL_INIT_VIDEO);

	//Fin réussie du programme, retourne 0
	return EXIT_SUCCESS;
}


Je vous encourage à le tester si la compilation fonctionne chez vous.
Pour la compilation il faut au préalable avoir installé la librairie SDL2, ainsi que la bibliothèque Glad via la procédure expliquée ci-dessus concernant Glad.

Un dernier détail, il vous faudra "linker" ces bibliothèques vis-a-vis du compilateur GCC. Les paramètres -lSDL2 et -ldl.

Voici la ligne de commande à utiliser pour la compilation du projet dans son dossier:

gcc -I ./include glad.c main.c -o main -lSDL2 -ldl

Après cette première compilation, revenons-en au code. Nous allons créer la fenêtre SDL2 ainsi que le contexte OpenGL dans cette partie.

Tapez le code qui suit sous la ligne:

SDL_Init(SDL_INIT_VIDEO);

    //Crée la fenêtre SDL (titre, position, taille, utilisable avec un contexte OpenGL)
    SDL_Window* win =SDL_CreateWindow("Triangle SDL2 + OpenGL",
        SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,
        640,480,
        SDL_WINDOW_OPENGL|SDL_WINDOW_OPENGL);

    //Crée le contexte OpenGL => gContext
    SDL_GLContext gContext=SDL_GL_CreateContext(win);

À ce stade si vous compilez ce code et que vous exécutez l'application vous pourrez apercevoir une fenêtre s'ouvrir et se fermer aussitôt, car il n'y a aucune pause dans ce code donc le programme quitte instantanément.

Continuons! Nous allons implémenter une fonction pour libérer les ressources utilisées par le programme, juste avant de quitter l'application.
Cette fonction sera appelée freeRessource.

Juste au-dessus de la fonction principale main ajoutez ceci.

//Fonction qui libère les ressources
void freeRessources(SDL_Window*win,SDL_GLContext gContext){

    //Supprime le contexte OpenGL
    SDL_GL_DeleteContext(gContext);
    //Détruit la fenêtre
    SDL_DestroyWindow(win);
    //Quitte la SDL2
    SDL_Quit();

}

Cette fonction prend en paramètre la fenêtre SDL_Window ainsi que le contexte OpenGL pour le moment, car c'est tous ce qu'on a créé comme ressources.

Placez la ligne suivant dans la fonction principale main du programme juste avant les lignes:

	//Fin réussie du programme, retourne 0
	return EXIT_SUCCESS;

	//Libère les ressources du programme.
    freeRessources(win,gContext);

Cette ligne fait appel à la fonction qui libèrera les ressource juste avant de terminer le programme.

Configuration du contexte OpenGL via la SDL2

Pour définir la configuration souhaitée d'OpenGL dans notre application, il est nécessaire de passer quelques lignes de codes.

Vous pouvez les placer juste à la suite de l'initialisation de la SDL (vidéo) sous la ligne:

SDL_Init(SDL_INIT_VIDEO);

    //Définition des attributs du contexte OpenGL
    //Active le double buffering
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    //Version d'OpenGL que nous souhaitons utiliser => 2.1
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
    //Cette fonction force l'utilisation des fonctions dites CORE d'OpenGL 2.1
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);

Chargement de Glad

Il nous faudra charger Glad afin d'utiliser OpenGL. Pour ce faire nous devons faire appel à la fonction gladLoadGLLoader(). Inscrivez les lignes qui suivent dans votre code juste après la création de contexte OpenGL, donc à la suite de la ligne ci-dessous:

    SDL_GLContext gContext=SDL_GL_CreateContext(win);

    //Chargement de Glad
    int version = gladLoadGLLoader((GLADloadproc) SDL_GL_GetProcAddress);
    if(!version){
        printf("Glad was not initialized.\n");
        exit(1);
    }
    else{
        printf("OpenGL Renderer: %s\n",glGetString(GL_VENDOR));
        printf("OpenGL Renderer: %s\n",glGetString(GL_VERSION));
    }

Normalement si vous compilez et lancé le programme à ce stade votre invite de commande devrait afficher des informations concernant votre carte graphique (vendeur) ainsi que la version d'OpenGL utilisée.

Boucle principale

Afin que l'application puisse continuer presque indéfiniment de tourner, il nous faut créer une boucle dans notre programme.

Pour ce faire nous allons créer une fonction qui contiendra la boucle principale de l'application et qui sera appelée dans la fonction main() de notre programme.

Placez cette ligne de code dans le fichier main.c juste avant l'appel de fonction qui libère les ressource; c.a.d:

    //Libère les ressources du programme.
    freeRessources(win,gContext,program);
	
    //Boucle principale du programme.
    mainLoop();

A l'extérieur, au dessus de la fonction main(), créez la fonction mainLoop() en inscrivant ces lignes:

	
void mainLoop(){
    //Boolean qui permet de quitter la boucle
    SDL_bool quit=SDL_FALSE;
    //Boucle principale (Boucle si la variable quit = faux)
    while(!quit){
    }
}

Attention:
A ce stade, après compilation du code, vous devriez avoir une application qui tourne en boucle sans moyen d'autre de sortie que de forcer l'arrêt de l'application via par exemple votre gestionnaire de tâche (Ctrl+Alt+Delete sous Windows).

Aussi vous noterez, que l'application n'a pas une couleur définie en fond. C'est parce que nous n'avons pas encore fait appel à une fonction de rendu, donc rien ne s'affiche correctement si ce n'est que le contour de la fenêtre pour le moment. Nous verrons la fonction de rendu dans prochainement sur cette page, en attendant nous allons tenter de quitter ce programme correctement via un événement (interactivité entre l'utilisateur et la machine).

Evênements SDL

Pour quitter le programme il faudra gérer un événement dans notre boucle principale. Celui-ci se chargera de capter un événement de fermeture d'application via la librairie SDL.

Pour ce faire nous allons transformer la fonction mainLoop() en y inscrivant le code qui suit à la place:

		
void mainLoop(){
    //Boolean qui permet de quitter la boucle
    SDL_bool quit=SDL_FALSE;
    //Evênement SDL (e)
    SDL_Event e;
    //Boucle principale (Boucle si la variable quit = faux)
    while(!quit){
        //Test si un événement à lieu
        if(SDL_PollEvent(&e)!=0){
            /*Si c'est un événement de type SDL_QUIT Fermeture de la fenêtre
            la variable "quit" aura la valeur true.=> 1 et on sortira de la boucle.*/
            if(e.type==SDL_QUIT)
                quit=SDL_TRUE;
            //Teste si une touche est pressée.
            else if(e.type==SDL_KEYDOWN){
				//Si on appuie sur la touche ESCAPE => On quitte la boucle (quit=true).
                if(e.key.keysym.sym==SDLK_ESCAPE)
                    quit=SDL_TRUE;
            }
        }
    }
}

Voilà, maintenant nous pouvons compiler le programme, le tester et le quitter proprement. Il nous reste plus que de nous occuper du rendu dans le contexte OpenGL.

Fonction de Rendu

Nous allons faire un rendu minimaliste, c'est à dire un unique fond de couleur noir dans le contexte OpenGL.

La procédure que nous allons utiliser est de créer une fonction de rendu qui sera appelée dans la boucle principale précédemment créée.

Pourquoi placer la fonction de rendu dans la boucle principale?
Pour rafraichir l'affichage du contexte OpenGL et éviter ainsi que notre fenêtre donne des signe de "bugs" d'affichage en restant bien d'une couleur unie.

À présent, créez la nouvelle fonction juste après les deux lignes #include ... tout en haut du fichier main.c et nommez cette nouvelle fonction render() en tapant ceci:

	
//Fonction de rendu
void render(SDL_Window* win){
	
	// Choisi une couleur de fond (au format rouge, vert, bleu et alpha)
	glClearColor(0.0, 0.0, 0.0, 1.0);
	// Applique la couleur de fond
	glClear(GL_COLOR_BUFFER_BIT);
	
	/* Met à jour le résultat*/
	SDL_GL_SwapWindow(win);
}

Aussi, faite appel à cette nouvelle fonction dans la boucle principale du programme après la section de test d'évènements:

if(SDL_PollEvent(&e)!=0){...}
	
	//Appel la fonction de rendu
    render(win);

Constatez que l'on passe la fenêtre en paramètre de fonction 'win'.
Donc, nous devons récupérer également la fenêtre SDL_Window dans la fonction mainLoop

Pour ce faire changez la première ligne du prototype de fonction de mainLoop avec ceci:

		
void mainLoop(SDL_Window *win){

Et dans la fonction main on change l'appel de fonction mainLoop(); en ceci pour passer la fenêtre SDL_Window créée au tout début de ce tutoriel:

	
	mainLoop(win);

On est bon pour la compilation et observer le résultat! Une magnifique fenêtre OpenGL avec fond noir.

le code source complet de ce tutoriel est disponible ici

Retour vers la page d'accueil