Présentation des filtres personnalisés (ou nuanceurs CSS)

Les filtres personnalisés, ou "shaders CSS" comme on les appelait auparavant, vous permettent d'utiliser la puissance des nuanceurs WebGL avec votre contenu DOM. Dans l'implémentation actuelle, les nuanceurs utilisés sont pratiquement les mêmes que ceux de WebGL. Vous devez donc prendre du recul et comprendre certains termes de 3D et un peu du pipeline graphique.

J'ai inclus une version enregistrée d'une présentation que j'ai récemment donnée à LondonJS. Dans cette vidéo, je vous présente la terminologie 3D que vous devez connaître, les différents types de variables que vous rencontrerez et comment commencer à utiliser les filtres personnalisés dès aujourd'hui. Vous pouvez également télécharger les diapositives pour tester les démonstrations vous-même.

Présentation des nuanceurs

J'ai déjà rédigé une présentation des nuanceurs qui vous donnera une bonne description de ce que sont les nuanceurs et comment les utiliser du point de vue de WebGL. Si vous n'avez jamais travaillé avec des nuanceurs, vous devez lire cet article avant de continuer, car de nombreux concepts et termes des filtres personnalisés reposent sur la terminologie des nuanceurs WebGL existants.

Alors, allons-y !

Activer les filtres personnalisés

Les filtres personnalisés sont disponibles dans Chrome et Canary, ainsi que dans Chrome pour Android. Accédez simplement à about:flags, recherchez "Shaders CSS", activez-les, puis redémarrez le navigateur. Vous pouvez maintenant commencer !

Syntaxe

Les filtres personnalisés étendent l'ensemble de filtres que vous pouvez déjà appliquer, comme blur ou sepia, à vos éléments DOM. Eric Bidelman a écrit un excellent outil de test pour ces cas, que vous devriez consulter.

Pour appliquer un filtre personnalisé à un élément DOM, utilisez la syntaxe suivante:

.customShader {
    -webkit-filter:

    custom(
        url(vertexshader.vert)
        mix(url(fragment.frag) normal source-atop),

    /* Row, columns - the vertices are made automatically */
    4 5,

    /* We set uniforms; we can't set attributes */
    time 0)
}

Vous pouvez voir que nous déclarons nos nuanceurs de sommet et de fragment, le nombre de lignes et de colonnes que nous souhaitons que notre élément DOM soit décomposé, puis tous les uniformes que nous souhaitons transmettre.

Enfin, notez que nous utilisons la fonction mix() autour du nuanceur de fragment avec un mode de mélange (normal) et un mode composite (source-atop). Examinons le nuanceur de fragment lui-même pour voir pourquoi nous avons besoin d'une fonction mix().

Pixel Pushing

Si vous connaissez les nuanceurs WebGL, vous remarquerez que les choses sont un peu différentes dans les filtres personnalisés. Tout d'abord, nous ne créons pas la ou les textures que notre nuanceur de fragment utilise pour remplir les pixels. Le contenu DOM auquel le filtre est appliqué est mappé automatiquement sur une texture. Cela signifie deux choses:

  1. Pour des raisons de sécurité, nous ne pouvons pas interroger les valeurs de couleur des pixels individuels de la texture du DOM.
  2. Nous ne définissons pas nous-mêmes la couleur finale du pixel (du moins dans les implémentations actuelles), c'est-à-dire que gl_FragColor est interdit. Il est plutôt supposé que vous souhaitez afficher le contenu DOM, et vous pouvez manipuler ses pixels indirectement via css_ColorMatrix et css_MixColor.

Cela signifie que notre "Hello World" de nuanceurs de fragments se présente plutôt comme suit:

void main() {
    css_ColorMatrix = mat4(1.0, 0.0, 0.0, 0.0,
                            0.0, 1.0, 0.0, 0.0,
                            0.0, 0.0, 1.0, 0.0,
                            0.0, 0.0, 0.0, 1.0);

    css_MixColor = vec4(0.0, 0.0, 0.0, 0.0);

    // umm, where did gl_FragColor go?
}

Chaque pixel du contenu DOM est multiplié par css_ColorMatrix, qui, dans le cas ci-dessus, ne fait rien, car il s'agit de la matrice d'identité et ne modifie aucune des valeurs RGBA. Si nous souhaitions, par exemple, conserver uniquement les valeurs rouges, nous utiliserions un css_ColorMatrix comme suit:

// keep only red and alpha
css_ColorMatrix = mat4(1.0, 0.0, 0.0, 0.0,
                        0.0, 0.0, 0.0, 0.0,
                        0.0, 0.0, 0.0, 0.0,
                        0.0, 0.0, 0.0, 1.0);

Vous devriez pouvoir constater que lorsque vous multipliez les valeurs de pixel 4D (RGBA) par la matrice, vous obtenez une valeur de pixel manipulée de l'autre côté, et dans ce cas, une valeur qui met à zéro les composants vert et bleu.

css_MixColor est principalement utilisé comme couleur de base que vous souhaitez mélanger à votre contenu DOM. Le mélange est effectué à l'aide des modes de fusion que vous connaissez déjà des packages d'illustration: superposition, écran, estompage des couleurs, lumière dure, etc.

Ces deux variables peuvent manipuler les pixels de nombreuses façons. Consultez la spécification des effets de filtre pour mieux comprendre l'interaction entre les modes de fusion et de composition.

Création de sommets

Dans WebGL, nous prenons l'entière responsabilité de la création des points 3D de notre maillage, mais dans les filtres personnalisés, il vous suffit de spécifier le nombre de lignes et de colonnes souhaitées. Le navigateur décomposera automatiquement votre contenu DOM en plusieurs triangles:

Création de sommets
Image décomposée en lignes et colonnes

Chacun de ces sommets est ensuite transmis à notre nuanceur de sommets pour être manipulé. Cela signifie que nous pouvons commencer à les déplacer dans l'espace 3D selon nos besoins. Vous pourrez bientôt créer des effets vraiment fabuleux !

Effet accordéon
Image déformée par un effet accordéon

Animer avec des nuanceurs

Les animations dans vos nuanceurs les rendent amusants et captivants. Pour ce faire, il vous suffit d'utiliser une transition (ou une animation) dans votre CSS pour mettre à jour les valeurs uniformes:

.shader {
    /* transition on the filter property */
    -webkit-transition: -webkit-filter 2500ms ease-out;

    -webkit-filter: custom(
    url(vshader.vert)
    mix(url(fshader.frag) normal source-atop),
    1 1,
    time 0);
}

    .shader:hover {
    -webkit-filter: custom(
    url(vshader.vert)
    mix(url(fshader.frag) normal source-atop),
    1 1,
    time 1);
}

La chose à noter dans le code ci-dessus est que le temps va passer de 0 à 1 pendant la transition. Dans le nuanceur, nous pouvons déclarer l'élément uniforme time et utiliser sa valeur actuelle:

    uniform float time;

uniform mat4 u_projectionMatrix;
attribute vec4 a_position;

void main() {
    // copy a_position to position - attributes are read only!
    vec4 position = a_position;

    // use our time uniform from the CSS declaration
    position.x += time;

    gl_Position = u_projectionMatrix * position;
}

Jouez !

Les filtres personnalisés sont très amusants à utiliser, et les effets incroyables que vous pouvez créer sont difficiles (et dans certains cas impossibles) à obtenir sans eux. Nous en sommes encore aux premiers balbutiements, et les choses changent beaucoup, mais les ajouter ajoutera un peu de piquant à vos projets. Alors pourquoi ne pas essayer ?

Autres ressources