Séance 2 · Travaux pratiques
TP 2 · Couleurs et formes
Trois objets dans la même scène : un triangle multicolore, un rectangle décrit avec un buffer entrelacé puis des indices, et un disque dont on règle le nombre de côtés. Une teinte globale, choisie dans la page, s'applique à tout.
Télécharger le code de départtp2-depart.zip · 5 Ko · à décompresser, puis ouvrir le dossier tp2/depart/ dans VS Code
Objectifs
- Ajouter un attribut et un varying ; observer l'interpolation.
- Décrire un buffer entrelacé avec
strideetoffset. - Piloter un uniform depuis l'interface.
- Utiliser des indices et générer une géométrie par programme.
Lancez la page avec Live Server : un triangle blanc s'affiche. Si une erreur survient, elle s'affiche maintenant en bas de la page en plus de la console, avec le code du shader numéroté.
Le triangle multicolore
On donne une couleur à chaque sommet, dans un second buffer.
- 1.1 Vertex shader : déclarez
in vec3 a_couleur;etout vec3 v_couleur;, et recopiez l'un dans l'autre dansmain. - 1.2 Fragment shader : déclarez
in vec3 v_couleur;et utilisez-le :couleur = vec4(v_couleur, 1.0); - 1.3 Récupérez
locCouleuravecgetAttribLocation. - 1.4 et 1.5 Créez le tableau des couleurs (rouge, vert, bleu) et son buffer, sur le modèle du buffer des positions. Attention : 3 composantes par sommet.
Le triangle, en haut à gauche, présente un dégradé du rouge au vert et au bleu.
Combien de fois le vertex shader est-il exécuté pour ce triangle ? Et le fragment shader, approximativement ? (Le triangle couvre environ 13 % du canvas de 600 × 600.) D'où viennent les couleurs intermédiaires ?
Le rectangle entrelacé
Cette fois, un seul buffer contient tout : pour chaque sommet, x, y, r, g, b.
- 2.1 Écrivez
rectSommets: 6 sommets (2 triangles), de (0.15, 0.15) à (0.9, 0.85), avec des couleurs de votre choix. Écrivez un sommet par ligne, c'est beaucoup plus lisible. - 2.2 Créez
vaoRectet un buffer. Décrivez les deux attributs sur le même buffer :vertexAttribPointer(loc, taille, gl.FLOAT, false, stride, offset). Le stride et l'offset sont en octets ; utilisez la constanteF(4 octets). - 2.3 Dans
dessiner(), liezvaoRectet dessinez ses 6 sommets.
Un rectangle dégradé en haut à droite. Si vous obtenez des formes aberrantes ou des couleurs qui « glissent », vérifiez le stride (taille d'un sommet complet) et l'offset de la couleur.
Que valent stride et offset pour chacun des deux attributs ? Si on ajoutait des coordonnées de texture (u, v) après la couleur, que deviendraient-ils ?
La teinte globale (uniform)
- 3.1 Fragment shader : déclarez
uniform vec3 u_teinte;et multipliez :couleur = vec4(v_couleur * u_teinte, 1.0); - 3.2 Récupérez
locTeinteavecgl.getUniformLocation. - 3.3 Ajoutez un écouteur
"input"surinputTeintequi appelledessiner. - 3.4 Dans
dessiner(), aprèsuseProgram:gl.uniform3fv(locTeinte, couleurHex(inputTeinte.value));
Choisir du rouge dans le sélecteur rend le triangle rouge sombre (seule la composante rouge subsiste) et le rectangle orangé. Le blanc restitue les couleurs d'origine.
Pourquoi multiplier les couleurs plutôt que les additionner ? Que se passe-t-il si on appelle gl.uniform3fv avant gl.useProgram lors du tout premier dessin ?
Partager les sommets : les indices
Votre rectangle répète 2 sommets sur 6. Supprimons les doublons.
- Réduisez
rectSommetsà 4 sommets uniques, dans l'ordre bas-gauche, bas-droite, haut-droite, haut-gauche. - Créez
new Uint16Array([0, 1, 2, 0, 2, 3])et un buffer lié àgl.ELEMENT_ARRAY_BUFFER, pendant quevaoRectest lié (le VAO s'en souvient). - Remplacez le dessin du rectangle par
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0).
Le rectangle est identique à l'étape 2. Un triangle manque ? Vérifiez l'ordre des sommets et des indices.
Le disque
On approche un disque par un polygone à n côtés en éventail (TRIANGLE_FAN) : le centre, puis les points du bord. Le point i est à l'angle a = 2π · i / n, donc en (cx + r·cos a, cy + r·sin a).
- 5.1 Complétez la fonction
disque: une boucle dei = 0ài = ninclus, qui ajoute pour chaque point ses coordonnées et la couleurteinte(i / n). Astuce :d.push(x, y, ...teinte(i / n)). - 5.2 Créez
vaoDisqueetbufDisque, avec les mêmes réglages d'attributs que le rectangle (sans données pour l'instant). - 5.3 Écrivez
majDisque(n): elle calculedisque(0, -0.45, 0.42, n), liebufDisque, y copie les sommets avecbufferData, et retient le nombre de sommets (n + 2) dans une variable. - 5.4 Appelez
majDisqueau démarrage avec la valeur du curseur, puis à chaque changement du curseur (suivi dedessiner()). - 5.5 Dessinez le disque avec
gl.drawArrays(gl.TRIANGLE_FAN, 0, nbSommets).
Un hexagone en bas, blanc au centre, en arc-en-ciel sur le bord. Avec le curseur à 64, on ne distingue plus le polygone d'un cercle. À 3, c'est un triangle.
Pourquoi faut-il n + 2 sommets pour n triangles ? Combien en faudrait-il avec gl.TRIANGLES ? Avec drawElements ?
Bonus
- A. Étoile. Écrivez
etoile(cx, cy, rExt, rInt, branches): comme le disque, mais les points du bord alternent entre deux rayons. (2 × branches points sur le bord.) - B. Contour. Dessinez le contour du disque avec
gl.LINE_LOOPen réutilisant le même buffer : à partir du sommet 1, n sommets. - C. Motif procédural. Dans le fragment shader, utilisez
gl_FragCoord.xypour assombrir une ligne de pixels sur deux (effet écran cathodique) :mod(gl_FragCoord.y, 2.0).