vendredi 29 janvier 2021

Afficher un quad plein écran avec OpenGL

Je suis en train de m'intéresser aux "skyboxes", c'est à dire l'affichage d'un environement tout autour de la caméra via une texture. En gros, l'idée est de créer un cube texturé qui suive la position de la caméra (voyez les explications techniques ici) pour afficher le "fond" du paysage, comme un ciel, une galaxie, un horizon urbain, qui remplisse le monde à peu de frais avant d'afficher les vrais objets à proximité. Manifestement, juste afficher un gros cube est maintenant dépassé, et bien plus recommandé est l'idée d'afficher juste un quad formé de deux triangles qui couvrent complètement l´écran, et de projeter la texture correctement. Je passe sur les détails de la projection avec lesquels je suis encore en train de me battre, pour vous parler plutôt de l'affichage du quad.

Comment afficher ce polygone ? La première idée qui vient, évidemment, c'est de créer les vertex buffers, les element buffers, de faire tous les appels à "bind" qui vont bien, et tout le toutim. Mais il semblerait qu'il y ait des solutions bien plus intelligentes...

En effet, pourquoi ne pas mettre toute la géométrie directement dans le vertex shader, puisqu'elle est fixe ? On sait que l'on veut des triangles étirés entres les coordonnées (-1, -1 ) et (1, 1), et l'on sait également quelles seront les coordonnées de textures voulues.

Le truc, c'est qu'un vertex shader a accès à l'index du vertex qui est en train d'être calculé. Donc, en incluant dans le shader un tableau qui indique pour chaque index quelles sont ses coordonnés de position et de texture, il n'y a plus qu'à retrouver les données à la volée, et c'est bon. Regardez moi ça, je vous le fais en simplifié :

Dans le code C++, on a un simple appel à draw, à poil, sans rien autour (à part la mise en oeuvre du shader)

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

Et dans le vertex shader, on utilise l'index gl_VertexID pour accéder aux coordonnées voulues dans les tableaux positions et coords, et on fixe notre z à 0.99 pour l'avoir au fond :

out vec2 textureCoords;

void main() {
    const vec2 positions[4] = vec2[](
        vec2(-1, -1),
        vec2(+1, -1),
        vec2(-1, +1),
        vec2(+1, +1)
    );
    const vec2 coords[4] = vec2[](
        vec2(0, 0),
        vec2(1, 0),
        vec2(0, 1),
        vec2(1, 1)
    );

    textureCoords = coords[gl_VertexID];
    gl_Position = vec4(positions[gl_VertexID], 0.99, 1.0);
}

Voilà, c'est incroyablement simple. Avec un seul appel OpenGL (1 ! UN !) on arrive à afficher le Quad qui va bien. Magique, non ?

(Plus d'infos, et plus d'approches supplémentaires, VBO, geometry shaders et tout le toutim, sur la sur la question StackOverflow qui va bien)

Aucun commentaire: