Faciliter la modification du format de point WebGL en JavaScript

Ceux qui programment directement WebGL, version 1 ou 2, avec JavaScript connaissent l'enfer que représente l'élaboration d'un shader. Qu'un format de point soit modifié, et plus généralement qu'un attribut de point, une composante variable ou une variable uniforme soit supprimé, rajouté ou modifié dans le code GLSL d'un shader, et il faut :
  • mettre à jour leur liste avant la fonction main () du shader ;
  • dans le cas d'uniformes, mettre à jour la liste des appels à getUniformLocation () ;
  • dans le cas d'attributs, ce qui résulte inévitable de la modification du format de point :
    • mettre à jour la liste des appels à getAttribLocation () ;
    • mettre à jour la liste des appels à enableVertexAttribArray (), enableVertexAttribArray (), vertexAttribPointer () et disableVertexAttribArray ().
Exemple d'animation modifiée à la volée avec les shaders
Pour soulager la vie du développeur, WebGL 2 met désormais à disposition un nouvel objet : le Vertex Array Object, ou VAO pour les intimes. Il permet d'éviter de répéter dans le code des séquences d'appels pour configurer les attributs avant chaque rendu.
Toutefois, cela ne répond que très partiellement au problème évoqué à l'instant, car les mises à jour évoquées restent toujours aussi nécessaires et fastidieuses, quand bien même les occurences des appels à modifier sont réduites. N'y a-t-il pas moyen de se simplifier la vie ? Modeste proposition.
NB : Cet article renoue avec le format abrégé de premiers articles de ce blog. C'est qu'au terme d'une série de prises en main de technologies Web (WebG 2, promises, fonction fléchées, Web Components, etc.), un certain de nombre de difficultés rencontrées dans le cadre du développement d'une application à base de JavaScript et de WebGL en donnent l'opportunité.
Continuer la lecture de "Faciliter la modification du format de point WebGL en JavaScript"
Faciliter la modification du format de point WebGL en JavaScript

La promesse des promises en JavaScript

L'implémentation de la spécification ECMAScript 2015 sur laquelle JavaScript est fondé a conduit à introduire un nouvel objet standard en JavaScript : Promise. Il s'agit bien d'un objet standard, c'est-à-dire propre au langage comme String et non au navigateur, comme XMLHttpRequest.
Cet objet permet de faciliter et de sécuriser l'écriture d'un appel à une fonction asynchrone, c'est-à-dire à une fonction qui rend la main sans avoir encore retourné son résultat, s'engageant à signaler au programme qui l'a appelée quand ce résultat sera disponible, par exemple en appelant une fonction que le programme lui a fournie - une callback.
Le Serment des Horaces par David
Tout développeur JavaScript a très probablement déjà utilisé ce type de fonction, notamment pour charger des fichiers via l'objet XMLHttpRequest, ou pour déclencher une action après expiration d'un timer programmé via window.setTimeout ().
Avec l'objet Promise, l'asynchronisme a en quelque sorte été dégagé des objets qui viennent d'être cités pour accéder au rang d'aspect fonctionnel fondamental de JavaScript, au même titre que l'héritage basé sur les prototypes ou les closures. Partant tout développeur JavaScript doit maîtriser l'asynchronisme.
Pour autant, qui met le nez dans la spécification ne peut être que rapidement rebuté par l'opacité de cette dernière. Fort heureusement, il existe de nombreuses autres sources auxquelles se référer. Apportons ici une modeste contribution avec une présentation détaillée du système des promises destinée à qui découvre le sujet.
Mise à jour du 06/10/2021 : Correction d'une erreur mineure à la fin du propos sur le chaînage.
Continuer la lecture de "La promesse des promises en JavaScript"
La promesse des promises en JavaScript

Mandelbrot en WebAssembly

Ainsi donc, grâce à WebAssembly, il serait possible de faire tourner des programmes fulgurants dans une page Web.
Mazette ! Pourquoi ne pas tester la proposition en réalisant un petit programme qui requiert traditionnellement une certaine puissance de calcul, comme une représentation de l'ensemble de Mandelbrot ? Ce serait plus sympa que le jeu de la vie de Conway comme proposé ici, qui est certes fascinant, mais pas des plus sexy à regarder.
Une représentation de l'ensemble de Mandelbrot
Ce serait aussi l'opportunité de discuter un peu des performances des opérations graphiques nécessitant de travailler au pixel pour produire une image dans une page Web. Oh! nous ne sortirons pas l'artillerie lourde, à savoir WebGL, qui a été longuement présenté dans plusieurs articles sur ce blog. Nous nous concentrerons simplement sur le contexte 2D d'un objet <canvas>, en dessinant au pixel.
Tout cela, et plus encore, dans ce qui suit.
Continuer la lecture de "Mandelbrot en WebAssembly"
Mandelbrot en WebAssembly

WebAssembly : programmer en assembleur dans une page Web

Qui programme en assembleur de nos jours ? Plus qu'il n'y paraît en fait, si l'on veut bien considérer qu'avec WebAssembly - Wasm pour les intimes -, il est devenu possible d'écrire des programmes dans un langage qui s'en approche, et de les exécuter dans le contexte d'une page Web.
Wasm, un assembleur pour le Web
L'intérêt ? Des performances annoncées de très loin supérieures à celle de tout code JavaScript, aussi optimisé qu'il soit, sans perdre le bénéfice d'une intégration parfaite dans l'environnement d'exécution d'une page Web, c'est-à-dire en pouvant facilement accéder à des données et à des sous-programmes Wasm depuis du code JavaScript, mais aussi faire l'inverse.
Comment écrire en Wasm ? Comment exécuter du Wasm dans une page Web ? Et comment dialoguer entre Wasm et JavaScript ? Tout cela et plus encore dans ce qui suit...
Mise à jour du 21/12/2018 : Rajout d'un encadré pour remercier Dan Gohman, l'auteur du WebAssembly Reference Manual.
Mise à jour du 18/12/2018 : Mention à une représentation intelligible de la syntaxe d'un module, finalement trouvée (en creusant profond) ici.
Mise à jour du 17/12/2018 : Correction de petits bogues dans importExportMemory.zip.
Mise à jour du 16/12/2018 : Ajout d'une section liminaire "WebAssembly, c'est quoi ?", et d'une conclusion "WebAssembly en vaut-il la peine ?".
Continuer la lecture de "WebAssembly : programmer en assembleur dans une page Web"
WebAssembly : programmer en assembleur dans une page Web

Copier des données dans le clipboard en JavaScript

Comment copier des données quelconques dans le clipboard en JavaScript dans le contexte d'une page Web ? Ce problème est assez simple à régler. Il suffit d'ajouter un gestionnaire de l'événement copy à l'objet document.
Le problème se complique s'il est exigé que ce gestionnaire d'événement soit anonyme et par ailleurs qu'il soit strictement éphémère, c'est-à-dire qu'il se retire de l'objet document une fois que l'événement copy est survenu, comme ce serait le cas si l'utilisateur cliquait sur un bouton "Copier" à usage ponctuel. En effet, en mode strict, il n'est plus possible d'utiliser arguments.callee pour faire référence à une fonction anonyme dans le corps de cette dernière. Par conséquent, impossible de faire référence à la fonction dans un appel à removeEventListener ().
Mise à jour du 26/12/2018 : Il est désormais possible d'utiliser l'option "once" lors de l'appel à addEventListener () (ex: addEventListener ("click", function (e) {}, { once: true })) pour ajouter un gestionnaire d'événement éphémère. Plus simple !
Continuer la lecture de "Copier des données dans le clipboard en JavaScript"
Copier des données dans le clipboard en JavaScript

Itérateur et générateur en JavaScript

Comment créer facilement une liste d'éléments qu'il sera possible de parcourir à l'aide de l'instruction for... of... (à ne pas confondre avec for... in...) ?
Cette liste doit pouvoir être totalement spécifique, et non seulement une liste prédéfinie dans JavaScript, comme notamment un tableau. En particulier, ses éléments doivent pouvoir n'être générés qu'au fil de l'itération.
Continuer la lecture de "Itérateur et générateur en JavaScript"
Itérateur et générateur en JavaScript

Tracer un arc avec le canvas 2D et SVG

Comment tracer un arc d'un angle donné, centré sur un point O donné et à partir d'un point A donné, avec le contexte 2d du canvas et avec SVG ?
Tracé d'un arc
La tâche n'a rien d'évident, comme il est possible d'en juger d'après le code permettant de la remplir dans l'un et l'autre cas :
  • Avec le contexte 2d du canvas, il faut demander le tracé d'un chemin en utilisant les fonctions moveTo (x0, y0) et arcTo (x1, y1, x2, y2, r), l'arc étant défini comme la plus petite partie du cercle de rayon r tangent aux droites joignant les points de tangence de ce cercle avec la droite de (x1, y1) à (x0, y0), d'une part, et avec la droite de (x1, y1) à (x2, y2), d'autre part.
  • En SVG, il faut demander le tracé d'un chemin en utilisant un objet path dont l'attribut p prend la valeur M x0 y0 A r r 0 largeArc sweep x1 y1, où (x0, y0) est le point de départ de l'arc et (x1, y1) son point d'arrivée, le cercle dont l'arc est une partie étant de rayon r et de centre déterminé par une combinaison de largeArc et sweep.
Continuer la lecture de "Tracer un arc avec le canvas 2D et SVG"
Tracer un arc avec le canvas 2D et SVG

Closures pour gérer les événements en JavaScript

La fonction addEventListener () permet d'associer une fonction (le gestionnaire d'événement) à un événement. Lorsque l'événement survient, le navigateur appelle le gestionnaire d'événement en lui transmettant un unique argument sous la forme d'un objet décrivant l'événement.
Comment transmettre autre chose à ce gestionnaire, comme par exemple une référence sur un objet spécifique ?

Continuer la lecture de "Closures pour gérer les événements en JavaScript"

Closures pour gérer les événements en JavaScript

Désactivation de l’anti-aliasing du canvas 2D

L'anti-aliasing est activé par défaut dans le canvas. Il permet de limiter la perception de l'effet d'escalier que la juxtaposition de pixels aux couleurs contrastées peut générer du fait de leur forme rectangulaire.
Ce traitement peut se révéler gênant dans certains cas (dessin au pixel, jeu old school, etc.), car il n'y a plus de certitude sur les pixels qu'une opération graphique vient modifier.
Par exemple, sur un canvas où on aura d'abord dessiné un rectangle jaune :
context2d.strokeStyle = "rgb (255, 255, 0)";
context2d.fillStyle = "rgb (0,255, 0)";
context2d.fillRect (2, 2, 1, 1);
context2d.strokeRect (1, 1, 2, 2);
Anti-aliasing avec fillRect () et strokeRect ()
Dès lors, comment s'y prendre pour désactiver l'anti-aliasing dans le canvas ?
Mise à jour du 07/09/2018 : la propriété imageSmoothingEnabled, qui n'est pas évoquée ici, ne sert qu'à désactiver l'anti-aliasing pour l'affichage d'images.
Continuer la lecture de "Désactivation de l’anti-aliasing du canvas 2D"
Désactivation de l’anti-aliasing du canvas 2D

Rendu vectoriel avec WebGL

Comment utiliser la puissance de WebGL pour effectuer le rendu de figures vectorielles, c'est-à-dire d'objets bidimensionnels, dans une page Web, sachant que WebGL est une API pour le rendu d'objets tridimensionnels ? Par exemple, le rendu d'un triangle dont les couleurs assignées aux sommets sont interpolées sur toute la surface :
Rendu d'un triangle 2D avec interpolation de couleurs
Continuer la lecture de "Rendu vectoriel avec WebGL"
Rendu vectoriel avec WebGL