Los filtros personalizados, o sombreadores de CSS, como solían llamarse, te permiten usar la potencia de los sombreadores de WebGL con tu contenido de DOM. Dado que, en la implementación actual, los sombreadores utilizados son prácticamente los mismos que los de WebGL, debes dar un paso atrás y comprender algunos términos de 3D y un poco de la canalización de gráficos.
Incluí una versión grabada de una presentación que di recientemente en LondonJS. En el video, explico la terminología 3D que debes comprender, los diferentes tipos de variables que encontrarás y cómo puedes comenzar a usar los filtros personalizados hoy mismo. También deberías descargar las diapositivas para que puedas jugar con las demostraciones.
Introducción a los sombreadores
Anteriormente, escribí una introducción a los sombreadores que te brindará un buen desglose de lo que son los sombreadores y cómo puedes usarlos desde el punto de vista de WebGL. Si nunca trabajaste con sombreadores, es una lectura obligatoria antes de avanzar, ya que muchos de los conceptos y el lenguaje de los filtros personalizados dependen de la terminología existente de los sombreadores de WebGL.
Dicho esto, habilitemos los filtros personalizados y sigamos adelante.
Habilitación de filtros personalizados
Los filtros personalizados están disponibles en Chrome, Canary y Chrome para Android. Solo ve a about:flags
, busca "CSS Shaders", habilítalos y reinicia el navegador. ¡Ya está todo listo!
La sintaxis
Los filtros personalizados expanden el conjunto de filtros que ya puedes aplicar, como blur
o sepia
, a tus elementos DOM. Eric Bidelman escribió una excelente herramienta de zona de pruebas para ellos, que deberías consultar.
Para aplicar un filtro personalizado a un elemento DOM, usa la siguiente sintaxis:
.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)
}
Verás que declaramos nuestros sombreadores de vértices y fragmentos, la cantidad de filas y columnas en las que queremos que se descomponga nuestro elemento DOM y, luego, cualquier uniforme que queramos pasar.
Por último, debemos señalar que usamos la función mix()
alrededor del sombreador de fragmentos con un modo de combinación (normal
) y un modo compuesto (source-atop
). Echemos un vistazo al sombreador de fragmentos para ver por qué necesitamos una función mix()
.
Envío de píxeles
Si conoces los sombreadores de WebGL, notarás que en los filtros personalizados las cosas son un poco diferentes. Por un lado, no creamos las texturas que usa nuestro sombreador de fragmentos para completar los píxeles. En cambio, el contenido del DOM al que se le aplicó el filtro se asigna a una textura automáticamente, lo que significa lo siguiente:
- Por motivos de seguridad, no podemos consultar los valores de color de píxeles individuales de la textura del DOM.
- Nosotros no configuramos (al menos en las implementaciones actuales) el color de píxel final, es decir,
gl_FragColor
está fuera de los límites. En cambio, se supone que querrás renderizar el contenido del DOM, y lo que puedes hacer es manipular sus píxeles de forma indirecta a través decss_ColorMatrix
ycss_MixColor
.
Eso significa que nuestro ejemplo de Hello World de sombreadores de fragmentos se ve más como esto:
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?
}
Cada píxel del contenido del DOM se multiplica por css_ColorMatrix
, que en el caso anterior no hace nada, ya que es la matriz de identidad y no cambia ninguno de los valores de RGBA. Si quisiéramos, por ejemplo, solo mantener los valores rojos, usaríamos un css_ColorMatrix
como este:
// 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);
Con suerte, puedes ver que, a medida que multiplicas los valores de píxeles 4D (RGBA) por la matriz, obtienes un valor de píxel manipulado del otro lado y, en este caso, uno que anula los componentes verde y azul.
El css_MixColor
se usa principalmente como un color base que quieres combinar con el contenido de tu DOM. La combinación se realiza a través de los modos de combinación que ya conoces de los paquetes de arte: superposición, pantalla, atenuación de color, luz dura, etcétera.
Existen muchas formas en que estas dos variables pueden manipular los píxeles. Consulta la especificación de efectos de filtro para comprender mejor cómo interactúan los modos de combinación y composición.
Creación de vértices
En WebGL, asumimos toda la responsabilidad de la creación de los puntos 3D de nuestra malla, pero en los filtros personalizados, todo lo que tienes que hacer es especificar la cantidad de filas y columnas que deseas, y el navegador desglosará automáticamente el contenido de tu DOM en un montón de triángulos:
Luego, cada uno de esos vértices se pasa a nuestro sombreador de vértices para su manipulación, lo que significa que podemos comenzar a moverlos en el espacio 3D según sea necesario. No pasará mucho tiempo antes de que puedas crear efectos fabulosos.
Cómo animar con sombreadores
Agregar animaciones a tus sombreadores es lo que los hace divertidos y atractivos. Para ello, simplemente usa una transición (o animación) en tu CSS para actualizar los valores 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);
}
Por lo tanto, lo que debes observar en el código anterior es que el tiempo pasará de 0
a 1
durante la transición. Dentro del sombreador, podemos declarar el time
uniforme y usar cualquier valor actual:
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;
}
¡Comienza a jugar!
Los filtros personalizados son muy divertidos y los efectos increíbles que puedes crear son difíciles (y, en algunos casos, imposibles) sin ellos. Aún estamos en los primeros días y las cosas cambian bastante, pero si los agregas, agregarás un poco de mundo del espectáculo a tus proyectos, así que ¿por qué no probarlos?