Séance 1 · Travaux pratiques
TP 1 · Premier triangle
Vous allez écrire, sans aucune bibliothèque, le programme WebGL complet qui affiche un triangle, puis une maison. C'est le TP le plus « bas niveau » du module : tout ce qui suivra réutilise ce que vous écrivez aujourd'hui.
Télécharger le code de départtp1-depart.zip · 3 Ko · à décompresser, puis ouvrir le dossier tp1/depart/ dans VS Code
Objectifs
- Obtenir un contexte WebGL 2 et effacer le canvas.
- Écrire, compiler et déboguer un vertex shader et un fragment shader.
- Envoyer des sommets au GPU (buffer, VAO) et les dessiner.
Mise en route
- Ouvrez le dossier
tp1/departdans VS Code. - Clic droit sur
index.html→ Open with Live Server. La page s'ouvre à une adresse du typehttp://127.0.0.1:5500/index.html. - Ouvrez la console du navigateur (F12, onglet Console).
La console affiche « main.js chargé ». Gardez-la ouverte pendant tout le TP.
Le contexte et le fond
Le contexte gl est l'objet par lequel passent tous les appels à WebGL.
- TODO 1.1 : récupérez le contexte avec
canvas.getContext("webgl2"). S'il vautnull, levez une erreur (throw new Error("…")). - TODO 1.2 : dans
dessiner(), appelezgl.viewport(0, 0, canvas.width, canvas.height), choisissez une couleur de fond avecgl.clearColor(r, g, b, 1.0)puis effacez avecgl.clear(gl.COLOR_BUFFER_BIT).
Le canvas prend la couleur choisie (un bleu nuit, par exemple 0.08, 0.1, 0.2). Si le canvas reste noir sans erreur, vérifiez que dessiner() est bien appelée.
Les composantes de clearColor vont de 0 à 1. Quelle couleur obtient-on avec (1, 1, 0, 1) ? Et avec (0.5, 0.5, 0.5, 1) ?
Les shaders
Un shader est un petit programme GLSL, écrit dans une chaîne JavaScript, que le GPU compile.
- TODO 2.1 : dans
vertexSource, déclarezin vec2 a_position;avantmain, et dansmainécrivezgl_Position = vec4(a_position, 0.0, 1.0); - TODO 2.2 : dans
fragmentSource, donnez une valeur àcouleur, par exemplevec4(1.0, 0.55, 0.2, 1.0). - TODO 2.3 : complétez
createShaderen suivant les commentaires. La vérification d'erreur n'est pas optionnelle : sans elle, une faute de frappe dans un shader donne un écran noir sans explication. - TODO 2.4 : créez le programme :
const program = createProgram(gl, vertexSource, fragmentSource);
Rien ne change à l'écran, c'est normal. Pour vérifier que votre gestion d'erreur fonctionne, supprimez volontairement le point-virgule après gl_Position = … et rechargez. La console doit afficher un message du type :
Uncaught Error: Erreur de compilation du shader :
ERROR: 0:6: '}' : syntax error
Le 0:6 signifie : ligne 6 du shader. Corrigez, puis essayez aussi d'écrire vec3 à la place de vec4 dans le fragment shader. Lisez le message, puis corrigez.
Je suis bloqué sur createShader
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
const message = gl.getShaderInfoLog(shader);
// ... supprimer le shader, puis lever l'erreur
}
Le triangle
- TODO 3.1 : créez un
Float32Arraycontenant 3 sommets, soit 6 nombres. Rappel : l'espace de découpage va de −1 à 1 et l'axe y monte. - TODO 3.2 :
gl.createBuffer(),gl.bindBuffer(gl.ARRAY_BUFFER, …),gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW). - TODO 3.3 : créez et liez un VAO (
gl.createVertexArray,gl.bindVertexArray), récupérez l'emplacement de l'attribut avecgl.getAttribLocation(program, "a_position"), activez-le avecgl.enableVertexAttribArray(loc), puis décrivez-le avecgl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0). - TODO 3.4 : dans
dessiner(), après l'effacement :gl.useProgram(program),gl.bindVertexArray(vao),gl.drawArrays(gl.TRIANGLES, 0, 3).
Un triangle de votre couleur apparaît sur le fond. Si ce n'est pas le cas : l'emplacement renvoyé par getAttribLocation vaut-il -1 (faute de frappe dans le nom) ? Les coordonnées sont-elles entre −1 et 1 ?
Que signifient le deuxième et le troisième paramètre ? Que se passerait-il avec 3 au lieu de 2 ?
Expériences
Modifiez le code pour répondre aux questions suivantes. Notez vos réponses : elles serviront pour la suite.
- Placez un sommet en (1.5, 0). Que voit-on ? Pourquoi ?
- Dans
index.html, passez le canvas àwidth="900" height="450". Que devient votre triangle ? Expliquez. - Supprimez l'appel à
gl.clear. Le résultat change-t-il ? (Remettez-le ensuite.) - Déplacez
gl.bindVertexArray(vao)avantgl.useProgram. Est-ce que ça change quelque chose ? Et si on l'enlève complètement dedessiner()? Pourquoi le triangle s'affiche-t-il encore ?
Une maison
Remplacez le triangle par une maison composée d'un mur carré et d'un toit triangulaire. Un carré se dessine avec deux triangles.
- Sur papier, faites le croquis de la maison dans le repère [−1, 1] et numérotez les sommets.
- Écrivez les 9 sommets (3 triangles × 3 sommets) dans le
Float32Array. - Adaptez le nombre de sommets dans
drawArrays.
Une maison d'une seule couleur, sans trou ni triangle manquant. Si un morceau manque, vérifiez le dernier paramètre de drawArrays.
Bonus
- A. Remplacez
gl.TRIANGLESpargl.LINE_LOOP, puisgl.POINTS. Pour voir les points, ajoutezgl_PointSize = 10.0;dans le vertex shader. - B. Dessinez l'initiale de votre prénom avec des triangles.
- C. Ajoutez une porte d'une autre couleur. Ajoutez ses 6 sommets à la fin du même buffer, créez un second programme avec un autre fragment shader, et dessinez la porte avec
gl.drawArrays(gl.TRIANGLES, 9, 6)(à partir du sommet 9, 6 sommets) après avoir appeléuseProgramsur ce second programme. Pour réutiliser le même VAO, l'attribut doit avoir le même emplacement dans les deux programmes : écrivezlayout(location = 0) in vec2 a_position;dans les vertex shaders.
La semaine prochaine, nous verrons comment faire tout cela beaucoup plus simplement, avec une couleur par sommet.