Navigation▲
Tutoriel précédent : indexation de VBO |
Tutoriel suivant : texte 2D |
I. La canal alpha▲
Le concept du canal alpha est très simple. À la place d'écrire un résultat en RGB, vous écrivez un résultat en RGBA :
// Données de sorties : c'est maintenant un vec4
out
vec4
color;
les trois premières composantes sont toujours accessibles avec .xyz, tandis que la dernière est accessible avec .a :
color.a =
0
.3
;
Contre toute logique, alpha = opacité, donc alpha = 1 signifie complètement opaque alors que alpha = 0 signifie complètement transparent.
Ici, on a simplement codé en dur le canal alpha à 0.3, mais vous souhaitez sûrement utiliser une variable uniforme ou la lire à partir d'une texture RGBA (le format TGA supporte le canal alpha et GLFW supporte le format TGA).
Voici le résultat. Assurez-vous de désactiver le « backface culling » (suppression des faces arrières) (glDisable(GL_CULL_FACE)) car comme on peut voir à travers le modèle, on pourrait penser qu'il n'a pas de face « arrière ».
II. L'ordre est important▲
La capture d'écran précédente paraît correcte, mais c'est uniquement car on est chanceux.
II-A. Le problème▲
Ici, j'ai dessiné deux carrés ayant 50 % d'alpha, un vert et un rouge. Vous pouvez voir que l'ordre est important, la couleur finale donne une importante piste visuelle sur la perception de la profondeur.
Ce phénomène se produit aussi sur la scène. Changez le point de vue :
En fait, c'est un problème très complexe. Vous ne voyez jamais énormément de transparence dans les jeux vidéo, n'est-ce pas ?
II-B. Solution classique▲
La solution classique est de trier tous les triangles transparents. Oui, TOUS les triangles transparents.
- Dessinez la partie du monde opaque afin que le tampon de profondeur puisse déjà rejeter les triangles transparents cachés.
- Triez les triangles transparents, du plus loin aux plus proches.
- Dessinez les triangles transparents.
Vous pouvez trier ce que vous voulez avec qsort (en C) ou std::sort (en C++). Je n'entrerai pas dans les détails, car…
II-C. Mise en garde▲
Faire comme cela fonctionne (plus de détails dans la section suivante), mais :
- vous allez être limité par la bande passante. En effet, chaque fragment sera écrit 10, 20 fois ou même plus. Cela est beaucoup trop pour le pauvre bus mémoire. Habituellement, le tampon de profondeur permet de rejeter assez de fragments « lointains », mais là, vous les triez explicitement, faisant que le tampon de profondeur soit totalement inutile ;
- vous allez faire cela quatre fois par pixel (on utilise le 4xMSAA), sauf si vous utilisez une optimisation plus intelligente ;
- le tri des triangles prend du temps ;
- si vous devez changer de texture, ou pire, de shader, de triangle en triangle, vous allez avoir de sérieux problèmes de performance. Ne le faites pas.
Une solution assez bonne est souvent de :
- limitez à un maximum le nombre de polygones transparents ;
- utilisez le même shader et la même texture pour tous les polygones transparents ;
- s'ils sont sensés être très différents, utilisez votre texture ;
- si vous pouvez éviter le tri et que cela n'est pas trop moche, vous pouvez vous dire chanceux.
II-D. Transparence indépendante de l'ordre▲
De nombreuses autres techniques sont intéressantes si votre moteur a vraiment, vraiment besoin de l'état de l'art de la transparence :
- le papier original 2001 Depth Peeling : résultats parfait au niveau du pixel, pas très rapide ;
- Dual Depth Peeling : une petite amélioration ;
- de nombreux papiers sur le tri comptage (bucket sort) utilisent un tableau de fragments ; les tris par la profondeur dans le shader ;
- démonstration d'ATI Mecha : bon et rapide, mais compliqué à implémenter et nécessite du matériel récent. Utilise une liste chaînées de fragments ;
- variation de la technique d'ATI par Cyril Crassin : implémentation encore plus compliquée.
Même un jeu récent comme Little Big Planet, s'exécutant sur une console puissante, utilise une unique couche de transparence.
III. La fonction de mélange▲
Afin que le code précédent fonctionne, vous devez initialiser la fonction de mélange :
// Active le mélange
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
Ce qui signifie :
nouvelle couleur dans le tampon d'image =
alpha actuel dans le tampon d'image * couleur actuelle dans le tampon d'image +
(1 - alpha actuel dans le tampon d'image) * la couleur de sortie du shader
Exemple de l'image ci-dessus, avec le rouge au-dessus :
nouvelle couleur = 0.5 * (0,1,0) 1+ (1-0.5) * (1,0.5,0.5) ; // (le rouge était déjà mélangé avec le fond blanc) ;
nouvelle couleur = (1, 0.75, 0.25) = le même orange
IV. Remerciements▲
Cet article est une traduction autorisée dont le texte original peut être trouvé sur opengl-tutorial.org.
Navigation▲
Tutoriel précédent : indexation de VBO |
Tutoriel suivant : texte 2D |