Accessibilité Web
Découvrez les bases de l'accessibilité web avec une touche de convivialité, inspirée par la formation de la talentueuse Sara Soueidan.
25/07/2024
Ici, nous allons explorer les principes fondamentaux de WebGL, une technologie puissante qui permet de créer des graphiques 3D interactifs directement dans les navigateurs web. Que vous soyez un développeur débutant ou simplement curieux de comprendre comment fonctionnent les animations 3D sur le web, nous vous guiderons à travers les concepts de base de WebGL de manière claire et accessible.
L’objectif est de démystifier cette technologie afin que chacun puisse apprécier son potentiel et, pourquoi pas, l’implémenter dans ses propres projets créatifs.
L'infographie tridimensionnelle (3D) est une technique utilisée pour créer des images numériques qui ont l'apparence de la profondeur et du volume. Contrairement aux images 2D, qui ne montrent que la hauteur et la largeur, les images 3D ajoutent une troisième dimension, la profondeur, ce qui permet de représenter des objets de manière plus réaliste. L’infographie tridimensionnelle est une technique essentielle dans de nombreux domaines créatifs et techniques, offrant des possibilités infinies pour la visualisation et l'interaction.
Une API 3D est une interface de programmation qui permet aux développeurs de créer, manipuler et rendre des graphiques tridimensionnels. Ces API fournissent un ensemble de fonctions et de commandes standardisées que les développeurs peuvent utiliser pour dessiner des objets 3D, gérer l'éclairage, les textures, les animations et les effets visuels dans leurs applications. Les API rendent le développement plus facile en cachant la complexité technique et en fournissant des fonctions simples que les développeurs peuvent utiliser sans avoir à comprendre les détails techniques.
Les principales API 3D incluent OpenGL, une API multiplateforme utilisée pour le rendu 2D et 3D dans les jeux vidéo et les simulations scientifiques, et Direct3D, une composante de DirectX optimisée pour les systèmes Windows. Vulkan, développée par le Khronos Group, offre un contrôle de bas niveau sur le matériel graphique pour des performances accrues. Metal, d'Apple, fournit des performances élevées sur iOS et macOS. WebGL permet le rendu 3D dans les navigateurs web sans plugins supplémentaires, facilitant le développement d'applications interactives.
WebGL est une API web basée sur l’API OpenGL (Open Graphics Library). Introduit en 2011, WebGL est basé sur OpenGL ES 2.0, une version allégée d'OpenGL sortie en 2007 et initialement conçue pour les appareils mobiles. En exploitant la puissance du GPU (processeur graphique), cette API permet de créer des graphiques 3D interactifs et des animations complexes directement dans le navigateur, enrichissant l'expérience utilisateur.
Des librairies JavaScript ont été développées pour simplifier le développement d'applications 3D. Parmi les plus connues figurent Three.js, la plus connue d’entre-elles, Babylon.js , et OGL, cette dernière se distinguant par sa légèreté et sa simplicité. Ces librairies permettent de tirer pleinement parti de WebGL pour créer des expériences web immersives et performantes.
Nous avons récemment mis en ligne le portfolio du collectif créatif Rodeo. En utilisant l'API WebGL pour concevoir la grille de projets, nous avons pu intégrer des animations fluides et des effets visuels poussés. Cet exemple illustre parfaitement les capacités d'animation offertes par WebGL.
La question que l’on peut se poser est la suivante : quel intérêt d’apprendre le vanilla WebGL si ces librairies existent ? Il existe plusieurs points qui peuvent justifier un apprentissage plus poussé de l’API sans passer par ces librairies :
Utiliser les librairies sans compréhension des concepts fondamentaux de WebGL peut très rapidement amener à des complexifications lors de la phase de développement. Une connaissance approfondie du vanilla WebGL facilite le débogage et l'optimisation des performances, car vous comprenez mieux ce qui se passe sous le capot. Apprendre les bases de WebGL vous donne une base solide pour comprendre et utiliser efficacement les librairies 3D comme Three.js ou Babylon.js, mais permet aussi d’avoir des bases solides pour apprendre d'autres API graphiques comme OpenGL ou Vulkan.
Bien qu’elles soient très utiles et complètes, les librairies javascript sont lourdes. Il est parfois plus simple et efficace de réaliser certains projets directement en utilisant le vanilla WebGL, sans avoir recours à ces librairies. Se passer de librairies peut alléger énormément de poids sur votre fichier javascript final. Par exemple, Three.js pèse 671 kB (173 kB gzip), Babylon peut aller jusqu’à 5.03 MB (1.09 MB gzip) tandis qu’OLG ne pèse "que" 126 kB (37.4 kB gzip).
L'objectif de cette section est de vous guider, étape par étape, pour afficher votre premier triangle en utilisant l'API WebGL sur une page web. Créer ce triangle est l'équivalent du traditionnel "Hello world" dans un langage de programmation. Bien que cela puisse sembler trivial, vous découvrirez rapidement que la création d'un simple triangle en WebGL comporte quelques défis intéressants à relever !
Plusieurs étapes sont nécessaires à la réalisation d’un rendu 3D en WebGL. Ces étapes peuvent être regroupées en six parties :
Génération et traitement des données
Nous générons et traitons nos données en JavaScript, puis nous les transmettons au GPU (processeur graphique).
Création des points dans l’espace
Le GPU traite les données pour créer des points dans l’espace (les sommets). Ces points peuvent ensuite être reliés pour former des formes ou objets vectoriels.
Transformation des formes vectorielles
Les formes vectorielles générées sont transformées et positionnées dans l’espace 3D à l'aide des shaders de vertex.
Rasterisation
Les formes vectorielles sont ensuite converties en pixels via le processus de rasterisation, qui détermine quels pixels de l'écran représentent les formes.
Coloration des pixels
Avant d'afficher les pixels, chaque pixel est coloré en fonction de la couleur, de la texture, des ombres et des lumières appliquées dans la scène. Cela se fait à l'aide des shaders de fragment.
Affichage
Les pixels colorés sont finalement renvoyés à l’écran, permettant ainsi d’afficher l’image rendue.
Ces étapes forment la pipeline de rendu WebGL, un processus complexe mais essentiel pour afficher des graphiques 3D interactifs dans le navigateur.
En tant que développeur, nous n'avons pas besoin de nous occuper de toutes ces étapes. Notre travail consiste principalement à fournir les instructions au GPU pour les étapes de "Vertex Shader" et "Fragment Shader". Les autres étapes sont gérées automatiquement par le GPU.
Le Vertex Shader intervient au début de la pipeline de rendu graphique, après que les données des sommets (vertex data) ont été envoyées au GPU depuis le programme JavaScript. Il intervient entre l'étape 1 et l'étape 2 du processus de rendu et transforme les coordonnées des sommets, applique des transformations et des calculs d'attributs, et prépare les sommets pour les étapes suivantes du rendu 3D.
Le Fragment Shader intervient quant à lui plutôt vers la fin de la pipeline, après la rasterisation pour déterminer la couleur finale de chaque fragment avant leur affichage sur l'écran.
En WebGL, les coordonnées sont normalisées pour aller de -1 à +1 quelle que soit la taille du canvas. Cela signifie que le point (-1, -1) correspond au coin inférieur gauche du canvas et le point (+1, +1) au coin supérieur droit. Cette normalisation permet de travailler indépendamment de la résolution ou des dimensions spécifiques du canvas, facilitant ainsi le rendu et les transformations des objets 3D. Les coordonnées du centre du canvas sont (0, 0).
Pour commencer à utiliser WebGL, un simple élément HTML <canvas> suffit dans votre fichier index.html. Cet élément sert de surface de rendu sur laquelle WebGL dessine les graphiques 3D. En ajoutant un <canvas> à votre page HTML, vous créez l'espace nécessaire pour que WebGL puisse fonctionner.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebGL Introduction</title>
<link rel="stylesheet" href="./assets/style.css">
</head>
<body>
<canvas id="canvas"></canvas>
</body>
</html>
Du côté JavaScript, on récupère notre element canvas et on lui applique une hauteur et une largeur. Pour avoir la même taille sur différents écrans, on multiplie la hauteur et la largeur de l’écran par le “devicePixelRatio” (1 par défaut, +2 pour les écrans retina apple). Pour terminer avec le canvas, on lui définit un contexte. Il existe plusieurs contextes selon la technologie que l’on souhaite utiliser (2d, webgl, webgpu, bitmaprenderer, etc). Nous allons dans notre cas choisir le contexte “webgl”.
const canvas = document.getElementById('canvas')
const canvasSizes = {
width: 512,
height: 512
}
canvas.width = canvasSizes.width * window.devicePixelRatio
canvas.height = canvasSizes.height * window.devicePixelRatio
canvas.style.width = `${canvasSizes.width}px`
canvas.style.height = `${canvasSizes.height}px`
const gl = canvas.getContext('webgl')
Pour générer notre triangle, nous devons définir des points. Dans un espace 3D, un point a trois coordonnées géographiques (x, y, z). Les points fournis par le JavaScript au GPU sont plus communément appelés "vertices". Un triangle est composé de trois points. Ces vertices seront transmises au programme pour la création de notre rendu 3D.
Pour cet exemple, nous allons utiliser un triangle basé uniquement sur les axes X et Y (2 coordonnées par vertex). Nos coordonnées ne sont pas stockées dans un array standard mais dans un Float32Array, qui n'accepte que des valeurs de type float et int. WebGL n'accepte pas les arrays classiques.
Nous allons également associer une couleur à chaque point de notre triangle. Notez qu'en WebGL, les valeurs de couleur vont de 0 à 1, plutôt que de 0 à 255 comme en RGB. Ainsi, une couleur RGB de 255 correspond à 1 en WebGL.
// Coordonnées des points du triangle
const vertices = new Float32Array([
-1, -1, // Point 1
1, 1, // Point 2
0, 1 // Point 3
])
// Couleurs des points du triangle
const colors = new Float32Array([
1, 0, 0, // Red (Équivalent Hexa: 255, 0, 0)
0, 1, 0, // Green (Équivalent Hexa: 0, 255, 0)
0, 0, 1 // Blue (Équivalent Hexa: 0, 0, 255)
])
Une fois les données créés, nous devons indiquer au programme comment les utiliser pour générer un rendu 3D. Pour cela, nous allons écrire deux shaders en GLSL, le langage de programmation de WebGL : un vertex shader pour l'étape de transformation des vertices et un fragment shader pour l'étape de coloration des fragments. Ces shaders auront accès aux données fournies par le JavaScript.
Chaque point va exécuter le programme (vertex et fragment) en parallèle.
Vertex: Positionnement des points dans l’espace
Durant cette étape, on récupère les données de positions et de couleurs pour ce point et on affecte la valeur position à la variable gl_Position (x, y, z, w).
Fragment: Définition de la couleur final du pixel
Pendant l'étape fragment, on récupère la valeur vColor fournie par le vertex shader et on affecte cette valeur à la variable de type vec4 gl_FragColor (r,g,b, a).
Nous allons clarifier la fonction renderFrame
en créant deux fonctions : createProgram
et createShader
.
La fonction createProgram
crée et lie un programme WebGL en combinant le vertex et le fragment shader. Elle initialise un nouveau programme, attache les deux shaders, les lie ensemble, et vérifie si l'opération a réussi. Elle retourne le programme prêt à être utilisé.
La fonction createShader
crée et compile un shader WebGL à partir du type spécifié (vertex ou fragment) et de son code source. Elle initialise un shader, lui associe le code source, puis le compile. Elle retourne le shader prêt à être utilisé.
Les fonctions createShader
et createProgram
sont essentielles pour préparer et lier les shaders dans WebGL. Ensemble, elles permettent de définir et d'exécuter les opérations de rendu graphique en WebGL.
function createShader(type, source) { // return WebGLShader Object
const shader = gl.createShader(type) // vertex or fragment
gl.shaderSource(shader, source)
gl.compileShader(shader)
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
return shader
}
console.error(`
Error while compiling shader:
${gl.getShaderInfoLog(shader)}
`)
gl.deleteShader(shader)
}
function createProgram(vertexShader, fragmentShader) { // return WebGLProgram Object
const program = gl.createProgram()
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
gl.linkProgram(program)
if (gl.getProgramParameter(program, gl.LINK_STATUS)) {
return program
}
console.error(`
Error while linking program on the GPU:
${gl.getShaderInfoLog(program)}
`)
gl.deleteProgram(program)
}
Une fois les shaders créés, nous devons indiquer au programme comment les utiliser pour générer un rendu 3D. Les données que nous souhaitons fournir à notre programme sont prêtes côté JavaScript. Il faut désormais les transmettre au programme. Pour cela, nous allons créer un buffer.
Un buffer est une zone de mémoire utilisée pour stocker temporairement des données pendant leur transfert. En WebGL, un buffer sert à faire transiter des données du CPU (processeur central) vers le GPU (processeur graphique).
// Création des shaders et du programme
const vertexShader = createShader(gl.VERTEX_SHADER, vertexShaderSource)
const fragmentShader = createShader(gl.FRAGMENT_SHADER, fragmentShaderSource)
const drawingProgram = createProgram(vertexShader, fragmentShader)
// Création des buffers pour les positions, couleurs
const vertexBuffer = gl.createBuffer()
const colorBuffer = gl.createBuffer()
// Attribution des positions et couleurs aux attributs du shader
const positionAttributeLocation = gl.getAttribLocation(drawingProgram, 'position')
const colorAttributeLocation = gl.getAttribLocation(drawingProgram, 'color')
Une fois que le GPU est prêt à recevoir les données et que nos données sont bien formatées, nous pouvons exécuter la fonction de rendu renderFrame
qui affichera notre image sur le canvas.
Cette étape consiste à lier les buffers au GPU et à lui donner les instructions sur ce qu'il doit lire et comment le lire pour effectuer le rendu. Dans cet exemple, nous expliquons au GPU comment gérer nos données pour positionner et colorer les points dans l’espace, avant de réaliser le rendu et de l’afficher dans le canvas.
Dans la première partie du code, nous allons effacer le contenu de l'element canvas avant de lui appliquer une couleur de fond avant la création de notre rendu 3D (ici un gris foncé).
Nous allons ensuite préparer le GPU afin qu'il se prépare à recevoir et à traiter les données des positions des vertices pour le rendu 3D. La même chose est faite pour le traitement des couleurs.
Et enfin les dernières lignes finalisent le processus de dessin d'un rendu 3D. Il configure les buffers nécessaires, active le programme de dessin avec les shaders appropriés, et envoie une commande au GPU pour effectuer le rendu final à l'écran.
function renderFrame() {
requestAnimationFrame(renderFrame)
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight)
gl.clearColor(0.1, 0.1, 0.1, 1)
gl.clear(gl.COLOR_BUFFER_BIT)
{
// Bind webgl buffer
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
// Link positions to vertexBuffer, supply array to webgl buffer
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW)
// Enable the program on GPU
gl.enableVertexAttribArray(positionAttributeLocation)
// Tell to the GPU how to "unpack" the vertexBuffer WebGLBuffer into the position attribute
gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0)
}
// Same for colors
{
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer)
gl.bufferData(gl.ARRAY_BUFFER, colors, gl.DYNAMIC_DRAW)
gl.enableVertexAttribArray(colorAttributeLocation)
gl.vertexAttribPointer(colorAttributeLocation, 3, gl.FLOAT, false, 0, 0)
}
// Explicitly use my webgl program
gl.useProgram(drawingProgram)
// Issue a draw command to the GPU
gl.drawArrays(gl.TRIANGLES, 0, positions.length / 2)
}
Voila, notre triangle est visible !
Nos troa points ont été positionnés puis connectés dans l’espace pour former un triangle.
Pour chaque point, une couleur a bien été attribué et rendu. Automatiquement, les couleurs ont été interpolé par le GPU et a généré ce dégradé de couleur.
Après avoir créé votre premier triangle en WebGL, vous pouvez approfondir vos compétences en explorant des formes plus complexes. Voici quelques idées pour enrichir vos projets WebGL :
Créer des formes plus complexes en combinant plusieurs triangles. Par exemple, un carré peut être formé de deux triangles. En maîtrisant cette technique, vous pourrez modéliser des objets 3D plus élaborés et explorer des concepts avancés comme les textures et les transformations.
Ajouter de la dimension à vos vertices et passez d'un espace 2D à 3D en incluant une coordonnée supplémentaire (z) pour chaque vertex. Cela permet de positionner vos objets dans un espace tridimensionnel.
Animer les données, telles que les positions et les couleurs, en utilisant des bibliothèques d'animation comme GSAP ou anime.js. Cela permet de créer des expériences interactives et dynamiques en ajustant en temps réel l'apparence et le mouvement des objets 3D.
Rodeo Film
Sublimer l'authenticité de l'analogique par la puissance du numérique
Encore une histoire, s'il te plaît !
Découvrez les bases de l'accessibilité web avec une touche de convivialité, inspirée par la formation de la talentueuse Sara Soueidan.
Nous sommes très fiers d’être nominés sur Awwwards pour le studio de l’année & le e-commerce de l’année avec VJ TYPE.
Comprendre le référencement naturel et transformer votre site en une source incontournable pour vos prospects.