自定义过滤器(也称为 CSS 着色器)简介

Paul Lewis

借助自定义滤镜(以前称为 CSS 着色器),您可以将 WebGL 着色器的强大功能用于 DOM 内容。由于当前实现中使用的着色器与 WebGL 中的着色器几乎相同,因此您需要先了解一些 3D 术语和图形管道。

我附上了最近在 LondonJS 上演讲的录制版本。在该视频中,我将简要介绍您需要了解的 3D 术语、您会遇到的不同变量类型,以及如何立即开始使用自定义过滤条件。您还应下载幻灯片,以便自行试用演示。

着色器简介

我之前曾撰写过一篇着色器简介,其中详细介绍了着色器的含义以及如何从 WebGL 的角度使用着色器。如果您从未处理过着色器,则必须先阅读相关内容,然后才能继续深入学习,因为许多自定义滤镜概念和语言都依赖于现有的 WebGL 着色器术语。

话不多说,我们来启用自定义过滤器,继续前进!

启用自定义过滤器

自定义过滤器适用于 Chrome、Canary 版和 Chrome(Android 版)。只需前往 about:flags 并搜索“CSS 着色器”,然后启用它们并重启浏览器即可。现在,您可以开始操作了!

语法

自定义过滤器是对您已经可以对 DOM 元素应用的一组过滤器(例如 blursepia)的扩展。Eric Bidelman 为此编写了一款出色的Playground 工具,您应该试用一下。

如需将自定义过滤器应用于 DOM 元素,请使用以下语法:

.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)
}

从中可以看出,我们声明了顶点着色器和片段着色器、我们希望 DOM 元素分解为的行数和列数,以及我们要传递的任何 uniform。

最后需要指出的是,我们在片段着色器周围使用了 mix() 函数,并采用了混合模式 (normal) 和复合模式 (source-atop)。我们来看看片段着色器本身,了解为什么我们需要 mix() 函数。

像素推送

如果您熟悉 WebGL 的着色器,就会发现自定义滤镜中的用法略有不同。首先,我们没有创建 fragment 着色器用于填充像素的纹理。而是会自动将应用了过滤器的 DOM 内容映射到纹理,这意味着两点:

  1. 出于安全考虑,我们无法查询 DOM 纹理的各个像素颜色值
  2. 我们不会(至少在当前实现中)自行设置最终像素颜色,即 gl_FragColor 不受限制。而是假定您希望渲染 DOM 内容,而您需要做的是通过 css_ColorMatrixcss_MixColor 间接操控其像素。

也就是说,我们的片段着色器 Hello World 更像这样:

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?
}

DOM 内容的每个像素都会乘以 css_ColorMatrix,在上述示例中,由于 css_ColorMatrix标识矩阵,因此不会执行任何操作,也不会更改任何 RGBA 值。如果我们想只保留红色值,则可以使用如下 css_ColorMatrix

// 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);

您应该会发现,将 4D (RGBA) 像素值乘以矩阵后,您会在另一侧获得经过处理的像素值,在本例中,该值会将绿色和蓝色分量设为零。

css_MixColor 主要用作您要与 DOM 内容混合的底色。混合是通过您熟悉的图形软件包中的混合模式完成的:叠加、屏幕、色彩 dodge、硬光等。

这两个变量可以通过多种方式操控像素。您应参阅滤镜效果规范,以更好地了解混合模式和复合模式之间的互动方式。

顶点创建

在 WebGL 中,我们全权负责创建网格的 3D 点,但在自定义过滤器中,您只需指定所需的行数和列数,浏览器就会自动将 DOM 内容细分为一系列三角形:

顶点创建
图片被分解为行和列

然后,这些顶点中的每一个都会传递给顶点着色器进行处理,这意味着我们可以根据需要在 3D 空间中移动它们。很快,您就可以制作出一些非常棒的效果了!

折叠效果
一张图片正在被“折叠效果”扭曲

使用着色器添加动画效果

为着色器引入动画可让其变得有趣且富有吸引力。为此,您只需在 CSS 中使用转换(或动画)来更新统一值:

.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);
}

因此,在上述代码中,请注意,时间将在过渡期间从 0 缓慢过渡到 1。在着色器中,我们可以声明均匀的 time 并使用其当前值:

    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;
}

开始玩吧!

自定义滤镜非常有趣,如果没有它们,您就很难(在某些情况下甚至无法)制作出令人惊叹的效果。这项功能仍处于起步阶段,并且情况会发生很大变化,但添加它们会为您的项目增添一点娱乐性,不妨试试吧!

其他资源