Tutoriel 9 - Interpolation

Ogldev

Traducteur

Sylvain DOREMUS

Ce tutoriel montre une partie très importante du pipeline 3D : l'interpolation effectuée par le rasterizer sur les variables qui sortent du vertex shader.

Commentez

- Titre : Tutoriel 9 - Interpolation

- Auteur : Ogldev

- Traducteur : Sylvain DOREMUS

- Parution : 3 juillet 2014

- Public visé :

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2014 Etay Meiri. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- Navigation

- II. Contexte

- III. Explication du code

- IV. Sources

- V. Remerciements

- Navigation

Navigation

Tutoriel précédent : les mises à l'échelle

Sommaire

Tutoriel suivant : dessins indexés

II - Contexte

Comme vous l'avez déjà vu, afin d'obtenir quelque chose de significatif à l'écran, vous devez utiliser une des variables de sortie du vertex shader : gl_Position. Il s'agit d'un vecteur à quatre dimensions qui contient les coordonnées homogènes du sommet.

Les composantes XYZ de ce vecteur sont divisées par la composante W (procédé connu sous le nom (anglais) de « perspective divide » et que nous expliquons dans le tutoriel dédié à ce sujet) et toutes les composantes qui sortent de la boîte normalisée ([-1, 1] pour XY ; [0, 1] pour Z) sont découpées.

Le résultat est transformé dans le système de coordonnées écran et ensuite le triangle (ou toute autre type de primitive supporté) est rendu à l'écran par le rasterizer.

Le rasterizer effectue une interpolation entre les trois sommets du triangle (soit de ligne en ligne ou par une autre technique) et « visite » chaque pixel dans le triangle en exécutant le fragment shader.

Le fragment shader doit renvoyer une couleur de pixel que le rasterizer place dans le tampon de couleur pour l'affichage (après plusieurs étapes additionnelles telles que le test de profondeur, etc.).

N'importe quelle autre variable sortant du vertex shader ne passe pas par les étapes décrites ci-dessus.

Si le fragment shader ne requiert pas explicitement cette variable (et vous pouvez mixer et associer plusieurs fragment shaders avec le même vertex shader), alors une optimisation commune sera de supprimer toutes les instructions, dans le vertex shader, qui n'affectent que cette variable (pour ce programme spécifique qui combine cette paire de vertex shader et fragment shader).

Cependant si le fragment shader utilise cette variable, le rasterizer l'interpole durant la rasterization et fournit à chaque invocation du fragment shader la valeur interpolée qui correspond à cet emplacement spécifique.

Cela signifie généralement que les valeurs pour les pixels qui sont proches les uns des autres seront un peu différentes (même si, le triangle s'éloignant de plus en plus de la caméra, cela devient moins probable).

Deux variables très communes se basent sur l'interpolation : la normale du triangle et les coordonnées de texture.

La normale d'un sommet est habituellement calculée comme la moyenne des normales de tous les triangles qui contiennent ce sommet. Si cet objet n'est pas complètement plat, cela signifie habituellement que les normales des trois sommets de chaque triangle seront différentes les unes des autres

Dans ce cas, nous nous fions à l'interpolation pour calculer la normale de chaque pixel. Cette normale est utilisée dans les calculs d'éclairage afin de générer une représentation plus crédible des effets de lumières.

Le cas des coordonnées de texture est similaire. Ces coordonnées font partie du modèle et sont spécifiées par sommet. Afin de couvrir le triangle avec une texture, vous devez effectuer l'opération d'échantillonnage pour chaque pixel et spécifier les coordonnées de texture correctes pour ce pixel. Ces coordonnées sont le résultat de l'interpolation.

Dans ce tutoriel nous allons voir les effets de l'interpolation en interpolant différentes couleurs au travers de la face triangulaire.

Comme je suis fainéant, je vais générer la couleur dans le vertex shader.

Une approche plus fastidieuse serait de la fournir dans le tampon de sommets. Généralement nous ne fournissons pas de couleurs à partir du tampon de sommets, nous fournissons plutôt des coordonnées de texture et échantillonnons la couleur à partir de la texture.

Cette couleur sera ensuite utilisée par les calculs d'éclairage.

III - Explication du code

Les paramètres passés entre les étapes du pipeline doivent être déclarés en utilisant le mot clé out dans le contexte global du shader.

La couleur est un vecteur à quatre dimensions car les composantes XYZ contiennent les composantes RGB (respectivement) et la composante W correspond à la valeur d'alpha (transparence du pixel).

Color = vec4(clamp(Position, 0.0, 1.0), 1.0);

La couleur, dans les pipelines graphiques, est généralement représentée en utilisant des valeurs en nombres flottants dans l'ensemble [0.0, 1.0].

Cette valeur est ensuite mise en correspondance avec un entier entre 0 et 255 pour chaque canal de la couleur (pour un total de 16 M de couleurs).

Nous définissons la couleur du sommet comme fonction de sa position. Tout d'abord nous utilisons la fonction intégrée clamp() afin d'être sûr que les valeurs ne sortent pas des bornes [0.0, 1.0].

La raison est que le sommet en bas à gauche du triangle est positionné en (-1, -1). Si nous prenons cette valeur telle quelle, elle sera interpolée par le rasterizer et, jusqu'à ce que X et Y dépassent zéro, nous ne verrons rien car chaque valeur inférieure ou égale à zéro sera rendue en noir.

Cela signifie que la moitié du bord dans chaque direction sera noire avant que la couleur dépasse zéro et devienne significative. En fixant la valeur, nous faisons en sorte que seule la partie en bas à gauche soit noire, mais comme nous nous en éloignons rapidement, la couleur devient rapidement plus claire.

Essayez de jouer avec la fonction clamp(), en l'enlevant ou en modifiant ses paramètres, pour en voir l'effet.

Le résultat de la fonction clamp() ne va pas directement dans la variable de sortie, car c'est un vecteur à quatre dimensions, alors que la position est un vecteur à trois dimensions (clamp() ne change pas le nombre de composantes, seulement leur valeur.).

Du point de vue de GLSL il n'y a pas de conversion par défaut pour cela et nous devons donc le faire explicitement.

Nous le faisons en utilisant la notation vec4(vec3, W) qui crée un vecteur 4D en concaténant un vecteur 3D avec la valeur W fournie. Dans notre cas nous utilisons 1.0, car elle s'inscrit dans la composante alpha de la couleur et nous voulons que le pixel soit complètement opaque.

in vec4 Color;

La partie opposée de la couleur sortie du vertex shader est la couleur en entrée du fragment shader. Cette variable subit l'interpolation par le rasterizer afin que chaque fragment shader soit (probablement) exécuté avec une couleur différente.

FragColor = Color;

Nous utilisons la couleur interpolée comme couleur du fragment sans autre changement et cela termine ce tutoriel.

Résultat

IV - Sources

Vous pouvez télécharger les sources de ce projet en suivant ce lien :

Récupérer les sources

V - Remerciements

Merci à Etay Meiri de nous permettre de traduire son tutoriel.

Merci à LittleWhite pour ses corrections et à jacques_jean pour sa relecture.

Navigation

Tutoriel précédent : les mises à l'échelle

Sommaire

Tutoriel suivant : dessins indexés