您可以使用自訂濾鏡 (或稱 CSS 著色器),將 WebGL 的著色器與 DOM 內容結合。由於目前實作中使用的著色器幾乎與 WebGL 中的著色器相同,因此您需要先瞭解一些 3D 術語和圖形管線。
我附上最近在 LondonJS 發表的錄製版簡報。在影片中,我會逐步介紹需要瞭解的 3D 術語、您會遇到的不同變數類型,以及如何立即開始使用自訂篩選器。您也可以下載投影片面,自行操作示範。
著色器簡介
我先前曾撰寫著色器簡介,詳細說明著色器的定義,以及如何從 WebGL 的角度使用它們。如果您從未處理過著色器,就必須先閱讀相關資訊,才能進一步瞭解這項技術,因為許多自訂濾鏡概念和語言都與現有的 WebGL 著色器術語有關。
那麼,讓我們啟用自訂篩選器,繼續進行吧!
啟用自訂篩選器
自訂篩選器適用於 Chrome、Canary 和 Android 版 Chrome。只要前往 about:flags
並搜尋「CSS 著色器」,啟用後重新啟動瀏覽器即可。一切就緒!
語法
自訂篩選器可擴充您已套用的篩選器組合,例如 blur
或 sepia
,套用至 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 內容會自動對應至紋理,這代表兩件事:
- 基於安全考量,我們無法查詢 DOM 紋理的個別像素顏色值
- 我們不會 (至少在目前的實作中) 自行設定最終像素顏色,也就是
gl_FragColor
是禁止的。相反地,系統會假設您想要轉譯 DOM 內容,而您要做的事就是透過css_ColorMatrix
和css_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;
}
開始玩遊戲吧!
自訂濾鏡非常好玩,而且您可以利用自訂濾鏡創造出精彩的效果,而無需濾鏡,您很難 (在某些情況下甚至無法) 創造出這類效果。這項功能仍處於早期階段,且仍有許多變動,但加入這項功能可為專案增添一點娛樂效果,不妨試試看吧!