自訂篩選器簡介 (又稱 CSS 著色器)

Paul Lewis

您可以使用自訂濾鏡 (或稱 CSS 著色器),將 WebGL 的著色器與 DOM 內容結合。由於目前實作中使用的著色器幾乎與 WebGL 中的著色器相同,因此您需要先瞭解一些 3D 術語和圖形管線。

我附上最近在 LondonJS 發表的錄製版簡報。在影片中,我會逐步介紹需要瞭解的 3D 術語、您會遇到的不同變數類型,以及如何立即開始使用自訂篩選器。您也可以下載投影片面,自行操作示範。

著色器簡介

我先前曾撰寫著色器簡介,詳細說明著色器的定義,以及如何從 WebGL 的角度使用它們。如果您從未處理過著色器,就必須先閱讀相關資訊,才能進一步瞭解這項技術,因為許多自訂濾鏡概念和語言都與現有的 WebGL 著色器術語有關。

那麼,讓我們啟用自訂篩選器,繼續進行吧!

啟用自訂篩選器

自訂篩選器適用於 Chrome、Canary 和 Android 版 Chrome。只要前往 about:flags 並搜尋「CSS 著色器」,啟用後重新啟動瀏覽器即可。一切就緒!

語法

自訂篩選器可擴充您已套用的篩選器組合,例如 blursepia,套用至 DOM 元素。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 元素拆分的列數和欄數,以及要傳遞的任何統一變數。

最後要指出的是,我們會在具有混合模式 (normal) 和合成模式 (source-atop) 的片段著色器周圍使用 mix() 函式。讓我們看看片段著色器本身,瞭解為何我們需要 mix() 函式。

推送像素

如果您熟悉 WebGL 的著色器,就會發現自訂濾鏡的運作方式有些不同。首先,我們並未建立片段著色器用來填入像素的紋理。相反地,已套用篩選器的 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 內容混合的基礎顏色。混合作業會透過您在圖片包中熟悉的混合模式完成:疊加、螢幕、顏色減淡、硬光等。

這兩個變數可以透過多種方式操控像素。您應該查看濾鏡效果規格,進一步瞭解混合和合成模式的互動方式。

建立頂點

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

開始玩遊戲吧!

自訂濾鏡非常好玩,而且您可以利用自訂濾鏡創造出精彩的效果,而無需濾鏡,您很難 (在某些情況下甚至無法) 創造出這類效果。這項功能仍處於早期階段,且仍有許多變動,但加入這項功能可為專案增添一點娛樂效果,不妨試試看吧!

其他資源