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

Paul Lewis

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

我添加了最近向 LondonJS 提交的演示文稿的录制版本。在本视频中,我将简要介绍您需要了解的 3D 术语、您将遇到的不同变量类型,以及如何立即开始使用自定义过滤器。您还应观看幻灯片,以便自己动手演示。

着色器简介

我之前撰写了着色器简介,其中详细介绍了什么是着色器,以及如何从 WebGL 的角度使用着色器。如果您从未接触过着色器,那么在您继续深入学习之前,有必要读一读,因为自定义滤镜的许多概念和语言都依赖于现有的 WebGL 着色器术语。

话虽如此,让我们启用“自定义过滤条件”并继续操作!

启用自定义过滤器

Chrome 和 Canary 版以及 Android 版 Chrome 均支持自定义过滤器。只需前往 about:flags 并搜索“CSS 着色器”,将其启用并重启浏览器。现在,您可以开始操作了!

语法

自定义过滤器扩展了您已应用到 DOM 元素的一系列过滤器,例如 blursepia。Eric Bidelman 为此类请求编写了一个很棒的游乐场工具,值得一看。

要将自定义过滤器应用到 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。

这里最后要指出的一点是,我们针对 fragment 着色器使用了 mix() 函数,其中包含混合模式 (normal) 和复合模式 (source-atop)。我们来看一下 fragment 着色器本身,看看为什么还需要 mix() 函数。

像素推送

如果您熟悉 WebGL 的着色器,就会发现“自定义滤镜”中的内容略有不同。例如,我们不会创建片段着色器用来填充像素的纹理。相反,应用了滤镜的 DOM 内容会自动映射到纹理,这意味着两点:

  1. 出于安全考虑,我们无法查询 DOM 纹理的各个像素颜色值
  2. 我们没有(至少在当前实现中)自行设置最终像素颜色,即禁止使用 gl_FragColor。而是假定您想要渲染 DOM 内容,并且您执行的操作是通过 css_ColorMatrixcss_MixColor 间接操控其像素。

这意味着 Fragment 着色器的 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 相乘,在上述情况下,该像素不会作为其身份矩阵,也不会更改任何 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 内容混合在一起。混合是通过您在艺术包中熟悉的混合模式完成的:叠加、屏幕、色彩减弱、硬光等。

这两个变量可以通过很多方法操控像素。您应查看“滤镜效果”规范,以更好地了解混合模式与合成模式的交互方式。

顶点创建

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

开始玩吧!

自定义滤镜非常有趣,但如果没有自定义滤镜,制作出的神奇效果将非常困难(在某些情况下,甚至无法实现)。现在仍处于早期阶段,一切都发生了很大变化,但添加项目可为您的项目增添一点风潮,何不大胆一试?

其他资源