The Future’s So Bright
We Gotta Wear Shade(r)s
Un seul PNG, un seul fragment shader, dix-huit uniforms.
Déplacez n’importe quel curseur et regardez le JavaScript vanille se réécrire.
Démo — WebGL2 + @paper-design/shaders
Heatmap : une image fixe devenue chaleur
Le shader heatmap de Paper prend une image fixe et la lit comme une source de chaleur. Des vagues de couleur montent à travers la silhouette, débordent de ses contours et se logent dans son intérieur. L’image source utilisée ici est un simple mockup produit — rien n’y est animé. Chaque image du mouvement est de l’arithmétique dans le fragment shader, pilotée par les uniforms ci-dessous.
Heatmap n’est qu’un shader parmi les vingt-neuf de la bibliothèque, et c’est justement celui qui accepte une image. Tous les autres génèrent leur contenu à partir de rien : dégradés maillés et radiaux, metaballs, bruits de Voronoï, simplex et Perlin, anneaux de fumée, rayons divins, tourbillons, spirales, vagues, distorsions, tramage et similigravure, métal liquide, verre cannelé, eau et texture papier. Vous pouvez les parcourir en direct sur shaders.paper.design , où chacun dispose du même panneau de contrôles et vous remet son code. Les sources, le GLSL et les liaisons React sont sur github.com/paper-design/shaders sous licence Apache-2.0. Tous se montent exactement comme le fait cette page — changez le fragment shader, changez les uniforms.
WebGL2 n’est pas disponible dans ce navigateur, le shader ne peut donc pas fonctionner. Les contrôles et le code généré ci-dessous restent actifs.
Compilation…
Sous le capot
Trois flous rangés dans une seule texture
Un GPU ne peut pas se permettre un flou large par pixel et par image, alors le travail se fait une seule fois, sur le CPU, avant tout rendu. toProcessedHeatmap() charge le PNG dans un canvas 2D, l’entoure d’une marge, le convertit en niveaux de gris, puis applique trois flous de rayons différents et range chaque résultat dans un canal de couleur distinct :
- R — un flou serré de 5px : le contour, le tracé net de la forme.
- V — un flou de 150px : le large halo sur lequel s’appuie la lueur externe.
- B — un flou de 18px : le remplissage doux de l’intérieur.
Le shader échantillonne ensuite cette texture unique et lit chaque canal comme un champ de distance séparé. u_contour, u_outerGlow et u_innerGlow ne font que doser la part de chaque canal mélangée dans la valeur de chaleur — laquelle sert finalement de position le long de votre dégradé de couleurs. Les pixels sombres de l’image d’origine sont lus comme les plus chauds : voilà pourquoi le corps du téléphone s’illumine tandis que l’écran blanc reste froid.
// une texture, trois champs
float shape = img[0];
float outerBlur = 1. - mix(1., img[1], shape);
float innerBlur = mix(img[1], 0., shape);
float contour = mix(img[2], 0., shape);
// vos curseurs pondèrent chacun d'eux
inner *= mix(0., 2., u_innerGlow);
inner += (u_contour * 2.) * contour;
outer *= mix(0., 5., pow(u_outerGlow, 2.));
// la chaleur devient une position
float heat = clamp(inner + outer, 0., 1.);
float mixer = heat * u_colorsCount;