Fondamentaux de l'initialisation avec OpenGL

Concepts fondamentaux lors de l'initialisation d'OpenGL

Modes de rendu

OpenGL propose deux modes principaux : le mode immédiat et le mode cœur (core profile). Le mode immédiat, utilisé dans les anciennes versions, cachait la plupart des fonctionnalités dans des bibliothèques, offrant une interface simple mais limitant le contrôle du développeur. À partir de la version 3.2, ce mode a été abandonné au profit du mode cœur qui offre plus de flexibilité.

Gestion des extensions

OpenGL supporte les extensions, permettant aux fabricants de cartes graphiques d'implémenter de nouvelles fonctionnalités avancées dans leurs pilotes. Ces extensions offrent des capacités graphiques plus performantes sur les systèmes compatibles.

Architecture en machine d'états

OpenGL fonctionne comme une machine d'états immense où l'ensemble des variables détermine le comportement actuel. Ce contexte est modifié par des options et des opérations sur les tampons avant d'être utilisé pour le rendu.

Bibliothèques essentielles

GLFW fournit une interface minimale pour créer des contextes OpenGL, définir les paramètres des fenêtres et gérer les entrées utilisateur :

GLFWwindow* fenetre = glfwCreateWindow(800, 600, "ApprentissageOpenGL", NULL, NULL);
if (fenetre == NULL)
{
    std::cout << "Échec de création de la fenêtre GLFW" << std::endl;
    glfwTerminate();
    return -1;
}
glfwMakeContextCurrent(fenetre);

GLAD s'occupe des pointeurs de fonctions OpenGL :

if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
    std::cout << "Échec d'initialisation de GLAD" << std::endl;
    return -1;
}

Configuration du viewport

Avant le rendu, il faut spécifier les dimensions de la zone d'affichage :

void callback_taille_fenetre(GLFWwindow* fenetre, int largeur, int hauteur);

void callback_taille_fenetre(GLFWwindow* fenetre, int largeur, int hauteur)
{
    glViewport(0, 0, largeur, hauteur);
}

glfwSetFramebufferSizeCallback(fenetre, callback_taille_fenetre);

Boucle de rendu

Toutes les opérations de rendu doivent être placées dans une boucle continue :

// Boucle de rendu
while(!glfwWindowShouldClose(fenetre))
{
    // Traitement des entrées
    traiterEntree(fenetre);

    // Instructions de rendu
    ...

    // Gestion des événements et échange des tampons
    glfwPollEvents();
    glfwSwapBuffers(fenetre);
}

Pipeline de rendu graphique

Le pipeline transforme les coordonnées 3D en pixels 2D à l'écran. Il se divise en plusieurs étapes : conversion 3D vers 2D, puis transformation en pixels colorés.

Étapes principales

  • Données de sommets : ensemble de coordonnées 3D regroupées en attributs
  • Ombre de sommet : convertit les coordonnées individuelles et traite les attributs
  • Assemblage de primitives : regroupe les sommets en formes géométriques
  • Étape de rastériastion : convertit les primitives en fragments
  • Ombre de fragment : calcule la couleur finale des pixels
  • Fusion alpha : détermine la transparence et les couches visibles

Objets de tampon

Les objets de tampon de sommets (VBO) stockent les données dans la mémoire GPU :

glGenBuffers(1, &tamponSommet);    // Création d'un objet VBO
glBindBuffer(GL_ARRAY_BUFFER, tamponSommet);    // Liaison au tampon cible
// Copie des données dans le tampon
glBufferData(GL_ARRAY_BUFFER, sizeof(sommets), sommets, GL_STATIC_DRAW);

Types de gestion des données :

  • GL_STATIC_DRAW : données fixes ou rarement modifiées
  • GL_DYNAMIC_DRAW : données fréquemment changées
  • GL_STREAM_DRAW : données modifiées à chaque frame

Objets de tableau de sommets

Les objets de tableau de sommets (VAO) encapsulent les configurations de tampon :

glGenVertexArrays(1, &tableauSommet);
glGenBuffers(1, &tamponSommet);
glGenBuffers(1, &tamponElement);

// Liaison du VAO
glBindVertexArray(tableauSommet);
// Chargement des données
glBindBuffer(GL_ARRAY_BUFFER, tamponSommet);
glBufferData(GL_ARRAY_BUFFER, sizeof(sommets), sommets, GL_STATIC_DRAW);

// Liaison du tampon d'éléments
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tamponElement);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

// Configuration des attributs de sommet
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *) 0);
glEnableVertexAttribArray(0);

Shaders et GLSL

Un shader de sommet minimal :

/**
 * Shader de sommet
 */
static const char *sourceShaderSommet =
        "#version 320 es\n"        // Numéro de version
        "layout (location = 0) in vec3 position;\n"    
        "void main()\n"
        "{\n"
        "   gl_Position = vec4(position.x, position.y, position.z, 1.0);\n"
        "}\0";

Un shader de fragment simple :

/**
 * Shader de fragment
 */
static const char *sourceShaderFragment =
        "#version 320 es\n"
        "precision mediump float;\n"        
        "out vec4 couleurSortie;\n"             
        "void main()\n"
        "{\n"
        "   couleurSortie = vec4(1.0f, 0.7f, 0.3f, 1.0f);\n"
        "}\n\0";

Compilation et liaison

Fonction de compilation des shaders :

/**
 * Compilation du shader
 * @param typeShader
 * @param source
 * @return
 */
GLuint compilateShader(GLenum typeShader, const char *source) {
    GLuint shader = glCreateShader(typeShader);
    if (shader) {
        glShaderSource(shader, 1, &source, NULL);
        glCompileShader(shader);
        GLint resultat = 0;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &resultat);
        if (!resultat) {
            GLint longueurInfo = 0;
            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &longueurInfo);
            if (longueurInfo) {
                char *buffer = (char *) malloc(longueurInfo);
                if (buffer) {
                    glGetShaderInfoLog(shader, longueurInfo, NULL, buffer);
                    LOGE("Impossible de compiler le shader %d:\n%s\n",
                         typeShader, buffer);
                    free(buffer);
                }
                glDeleteShader(shader);
                shader = 0;
            }
        }
    }
    return shader;
}

Création du programme

/**
 * Création du programme shader
 * @param sourceSommet  Code shader sommet
 * @param sourceFragment Code shader fragment
 * @return
 */
GLuint creerProgramme(const char *sourceSommet, const char *sourceFragment) {
    shaderSommet = compilateShader(GL_VERTEX_SHADER, sourceSommet);
    if (!shaderSommet) {
        return 0;
    }

    shaderFragment = compilateShader(GL_FRAGMENT_SHADER, sourceFragment);
    if (!shaderFragment) {
        return 0;
    }

    programmeShader = glCreateProgram();
    if (programmeShader) {
        glAttachShader(programmeShader, shaderSommet);
        checkGlError("glAttachShader");
        glAttachShader(programmeShader, shaderFragment);
        checkGlError("glAttachShader");
        glLinkProgram(programmeShader);
        GLint statutLien = GL_FALSE;
        glGetProgramiv(programmeShader, GL_LINK_STATUS, &statutLien);
        if (statutLien != GL_TRUE) {
            GLint tailleBuffer = 0;
            glGetProgramiv(programmeShader, GL_INFO_LOG_LENGTH, &tailleBuffer);
            if (tailleBuffer) {
                char *buffer = (char *) malloc(tailleBuffer);
                if (buffer) {
                    glGetProgramInfoLog(programmeShader, tailleBuffer, NULL, buffer);
                    LOGE("Impossible de lier programmeShader:\n%s\n", buffer);
                    free(buffer);
                }
            }
            glDeleteProgram(programmeShader);
            programmeShader = 0;
        }
    }
    return programmeShader;
}

programmeFinal = creerProgramme(sourceShaderSommet, sourceShaderFragment);
if (!programmeFinal) {
    LOGE("Impossible de créer le programme shader.");
    return false;
}

attributPosition = glGetAttribLocation(programmeFinal, "position");
LOGI("glGetAttribLocation(\"position\") = %d\n",
     attributPosition);

Textures

Coordonnées de texture dans l'intervalle [0,1] pour échantillonner les pixels de l'image.

Modes de répétition

  • GL_REPEAT : répétition normale
  • GL_MIRRORED_REPEAT : répétition en miroir
  • GL_CLAMP_TO_EDGE : extension des bords
  • GL_CLAMP_TO_BORDER : bord personnalisé
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);

float couleurBord[] = { 1.0f, 1.0f, 0.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, couleurBord);

Filtrage des textures

  • GL_NEAREST : filtre voisin le plus proche
  • GL_LINEAR : interpolation linéaire

Création d'une texture

unsigned int textureId;
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
// Configuration du filtrage
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);   
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Chargement de l'image
int largeur, hauteur, canaux;
unsigned char *donnees = stbi_load("image.jpg", &largeur, &hauteur, &canaux, 0);
if (donnees)
{
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, largeur, hauteur
                    , 0, GL_RGB, GL_UNSIGNED_BYTE, donnees);
    glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
    std::cout << "Échec du chargement de la texture" << std::endl;
}
stbi_image_free(donnees);

Transformations

Utilisation de matrices pour manipuler les objets dans l'espace 3D.

Matrices de transformation

  • Matrice modèle : positionnement dans l'espace monde
  • Matrice de vue : positionnement relatif à la caméra
  • Matrice de projection : conversion vers l'espace écran

Exemple avec GLM

// Matrice orthographique
glm::ortho(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f);

// Matrice perspective
glm::mat4 projection = glm::perspective(glm::radians(45.0f)
    , (float)largeur/(float)hauteur, 0.1f, 100.0f);

Système de coordonnées

Transformation progressive des coordonnées :

  1. Espace local : origine relative à l'objet
  2. Espace monde : position dans le monde global
  3. Espace de vue : perspective de la caméra
  4. Espace de clipping : coordonnées normalisées [-1,1]
  5. Espace écran : pixels finaux

La transformation finale combine les trois matrices : M = Projection × Vue × Modèle

Étiquettes: OpenGL graphics shaders rendering computer-graphics

Publié le 8 août à 18h59