HTML5とCSSの進化(トランジション、アニメーション、変換、テキストシャドウ、ボックスシャドウ、グラデーション、SVGなど)により、グラフィックスとインタラクティブ性に関するHTMLの能力は大幅に強化されました。SVGフィルターエフェクトがフィルターエフェクト1.0に移行して、SVGだけでなくCSSおよびHTMLでも使用可能になり、グレースケール、セピアトーン、色相回転といったエフェクトをすべてのWebコンテンツで使用できるようになります。

注意:CSSシェーダーはCSSフィルターエフェクト仕様の一部になったため、CSSシェーダーは現在「CSSカスタムフィルター」と呼ばれています。

さらに高度なエフェクト、例えば図1に示す掘り出し効果なども実現できます。

アドビは2011年10月にW3C FXタスクフォースにCSSシェーダーの提案を提案しました。提案した機能は現在、CSSフィルターエフェクトドラフトの一部となっています。この記事では、最新のCSSフィルターエフェクトドラフトの構文を使用します。CSSシェーダーは、フィルターエフェクトの拡張機構を定義するもので、容易にアニメーション化できる高度なエフェクトをすべてのHTML5コンテンツで使用可能にするものです。特に、CSSアニメーションCSSトランジションとの組み合わせで効果を発揮します。

以下のビデオは、HTMLコンテンツでCSSシェーダーを使用した実例を示します。

この記事では、CSSシェーダーとその仕組みについて説明し、サンプルコードを示します。また、シェーダーを使ってカスタムエフェクトを作成する方法や、独自のカスタムシェーダーを作成する方法も紹介します。

ご注意:この作業はまだ進行中です。一般のコミュニティとの間の議論が進むにつれて、何らかの変更が行われる可能性があります。この記事で使用されている構文は、CSSシェーダーの提案に基づいています。しかし、W3C FXタスクフォース内の議論や一般のコミュニティとの間の議論が進むにつれて、この構文は変化する可能性があります。また、一般的な慣習に従って、提案された新しいプロパティには、WebKitプロトタイプで-webkit-というプレフィックスが付いています。読みやすさのために、このプレフィックスは、この記事の残りの部分では省かれています。

フィルターエフェクト1.0

CSSシェーダーを理解するには、定義済みのフィルターエフェクトの基本について理解しておく必要があります。以下のデモは、シンプルなフィルターエフェクトの例です。マウスを上に置くと、グレースケールからカラーへの単純なフェードエフェクトが起こります。

グレースケールエフェクトのコードを次に示します。

<html> <head> ... <style> #shaded { filter: grayscale(1); transition: filter ease-in 0.3s; } #shaded:hover { filter: grayscale(0); } </style> </head> <body> <div id="shaded"> <div id="multi-col"> <h2>The Creative Web</h2> <p>Lorem ipsum dolor ... </p> <img id="png-img" src="planes.jpg"/> <p>Mauris at ... </p> <img id="svg-img" src="picture.svg" /> <p>Morbi congue ....</p> <img id="css3-img" src="html5_css3_styling.svg" /> </div> </div> </body> </html>

フィルターエフェクトの使用は非常に簡単です。filterプロパティが、適用するフィルター(またはフィルターのチェーン)を示します。これはまた、CSSトランジションとも簡単に統合できます。filterプロパティはアニメーション可能です。

この例では、grayscale()フィルター関数がアニメーションによって徐々にフェードアウトするように設定されています。これは、ユーザーがshaded要素の上にマウスを置いたときに、フィルター関数のamountパラメーターが1(フルグレースケール)から0(グレースケールなし)までアニメーション化されることによって起こります。

W3Cのフィルターエフェクト1.0ドラフト仕様では、次の2つの内容が定義されています。

  • グラフ内でフィルタープリミティブを構築することによるフィルター定義の一般構文。
  • フィルター定義を参照するか、1つまたは複数のフィルター関数を使用できるCSSのfilterプロパティ。

filterプロパティは、いくつかの定義済みフィルター関数を使用できます。blur(ブラー)、drop-shadow(ドロップシャドウ)、gamma(ガンマ)、grayscale(グレースケール)、hue-rotate(色相回転)、invert(反転)、opacity(不透明)、saturate(彩度)、sepia(セピア)、sharpen(シャープ)が使用できます。図2に、それぞれの効果を示します。

  1. blur(5, 5)
  2. drop-shadow(10, 5, 5)
  3. hue-rotate(328deg)
  4. saturate(5)
  5. invert(1)
  6. grayscale(1)
  7. opacity(0.5)
  8. gamma(1.1, 3.6, 0)
  9. sepia(0.5)

フィルターエフェクトの利点は、構文の単純さと、CSSアニメーションやCSSトランジションとの統合です。

ただし、これほど簡単には行かないこともあります。例えば、要素の一部だけをグレースケールに変えたい場合はどうすればいいでしょうか。あるいは、トランジションを別の方法で、例えば要素の上のスワイプエフェクトで実現したい場合はどうすればいいでしょうか。あるいは、定義済みのフィルター関数のセットや、デフォルトのフィルタープリミティブのセットに含まれていないフィルターエフェクトを使用したい場合はどうすればいいでしょうか。そんなときは、CSSシェーダーの出番です。

CSSシェーダーの提案では、フィルターエフェクトにcustom()フィルター関数を追加することを提案しています。この関数は、定義済みのフィルターやCSSアニメーションおよびトランジションと統合されます。CSSシェーダーは、単純なものから複雑なものまで、あらゆるエフェクトの作成に必要な柔軟性と表現力を備えています。

CSSシェーダーによるカスタムフィルターエフェクト

まず例を1つ示します。先ほど示したエフェクト(グレースケールからカラーへのフェード)はよくできていますが、まだ改良することもできます。次のビデオはさらに高度なエフェクトを示します。グレースケールからカラーに移る際に、コンテンツが一瞬揺らぎ、同時にカラースワイプが下から上にコンテンツを通り抜けます。

このデモでは、揺らぎのエフェクトに頂点シェーダーを、カラースワイプエフェクトにピクセルシェーダーを使用しています。どちらも単純にcustom()フィルター関数から参照されており、関数にはシェーダーのパラメーターといくつかの設定オプションが含まれています。コードは次のとおりです。

<html> <head> ... <style> #shaded { filter: custom(url('wobble.vs') /* wobble effect */ mix(url('color-swipe.fs') normal source-atop), /* swipe effect */ 40 40, /* mesh lines/cols */ amplitude 60, /* wobble strength */ amount 0.0); /* effect amount */ transition: filter ease-in-out 2s; ...; } #shaded:hover { filter: custom(url('wobble.vs') mix(url('color-swipe.fs') normal source-atop), 40 40, amplitude 60, amount 1.0); } </style> </head> <body> <div id="shaded"> <div id="multi-col"> <h2>The Creative Web</h2> <!-- Same as previous example --> </div> </div> </body> </html>

コードが前のグレースケールのサンプルとよく似ていることに注目してください。他のフィルターエフェクトの場合と同様、filterプロパティを使用してエフェクトを定義します。違いは、定義済みのフィルター関数を使用する代わりに、custom()関数を使用して2つのシェーダーを参照することです。各シェーダーについて詳しくは、後の方で説明します。ここで知っておいていただきたいのは、各シェーダーが特定のエフェクトを実現し、そのパラメーターをCSSから設定してアニメーション化できるということです。

この例のカスタムフィルターは、wobble.vs頂点シェーダー(変形用)と、color-swipe.fsフラグメントシェーダー(グレースケールからカラーへのスワイプエフェクト用)を使用しています。

シェーダーとは何か

シェーダーは、3Dグラフィックスで一般的に使用されます。シェーダーとは、3Dジオメトリ(頂点シェーダー)およびピクセルのカラー(フラグメントシェーダー)を処理する(通常は)短いプログラムです。

例えば、頂点シェーダーを使って、平面上に旗がなびくようなエフェクトを作成したり、前の例のような揺らぎのエフェクトを作成したりできます。フラグメントシェーダー(別名ピクセルシェーダー)を使えば、任意の計算に基づいてピクセルのカラーを決定できます。CSSシェーダーは、ハードウェアアクセラレーションによるシェーダープログラムの能力を利用できます。

CSSシェーダーの仕組み

CSSシェーダーを使えば、HTMLまたはSVG要素を頂点メッシュに変換できます。これは図3のステップ1に示されています。このメッシュは頂点シェーダーで処理され(ステップ2)、3次元空間で可能なあらゆる種類の変形を実現できます。ステップ3では、メッシュがピクセルにレンダリング(ラスタライズ)され、フラグメントシェーダーでカラーが付けられます。

オーサリング時には、メッシュの精細度と、シェーダーを制御するパラメーターを指定できます。

この例では、shadedというIDを持つ要素のfilterプロパティが次のように設定されています。

custom(url('wobble.vs') /* wobble effect */ mix(url('color-swipe.fs') normal source-atop), /* swipe effect */ 40 40, /* mesh lines/cols */ amplitude 60, /* wobble strength */ amount 0.0); /* effect amount */

wobble.vscolor-swipe.fsの2つのシェーダーは、amountパラメーターが0に設定されたときに、揺らぎがなく、ピクセルシェーダーが一様なグレースケールを要素に適用するようにプログラムされています。

ユーザーがshaded要素の上にマウスを置くと、そのfilterプロパティは次のように設定されます。

custom(url('wobble.vs') mix(url('color-swipe.fs') normal source-atop), 40 40, amplitude 60, amount 1)

この場合も、パラメーターの意味はシェーダーごとに異なります。この例のシェーダーは、amountが0.5のときに揺らぎのエフェクトが最大になり、1.0で静止状態に戻るように設計されています。カラースワイプシェーダーでは、amountが0.0から1.0に変化する間に、下から上へのカラースワイプが作成され、同時に輝きのエフェクトが追加されます。このためには、スワイプの水平分割と同じ位置のピクセル値に白が付加されています。視覚的エフェクトを理解するには、上のビデオを見ていただくのが一番の早道です。

前のグレースケールフィルターエフェクトの場合と同様、CSSトランジションとの統合により、エフェクトを動作させるのは非常に簡単であり、同じ方法で、すなわちtransitionプロパティを使用して実現できます。

シェーダーの作成

実際には、多くの人々が、前の例のように容易に設定できるパラメーターを持つシェーダーを利用して、素晴らしいカスタムエフェクトを実現することになるでしょう。

シェーダーの作成は、多少練習が必要ですが、それほど難しくはありません。また、あまり知られていないことですが、実はとても楽しい作業です。

シェーダーの作成には、OpenGL ESシェーディング言語を使用します。これはWebGLシェーダーに使用されているのと同じ言語です。前の例の揺らぎのエフェクトを作成する頂点シェーダーの例を次に示します。

シェーダーになじみがない方のために、サンプルの理解に役立つ用語の定義を以下に記します。

  • 頂点:シェーダーの処理対象となるジオメトリ上の座標。
  • テクスチャ:ラスターイメージ(オフスクリーンイメージ)。CSSシェーダーは、要素のレンダリングを、頂点シェーダーやフラグメントシェーダーが使用して処理するテクスチャに変換します。
  • 属性:各頂点ごとのパラメーターで、頂点シェーダーに渡されます。
  • ユニフォーム:すべての頂点やピクセルに関して同じ値を持つグローバルパラメーター。これらは、custom()関数からシェーダーに渡されます。
  • 投影マトリックス:正規化された頂点座標範囲(各軸上の[-0.5, +0.5])内の座標を実際のビューポート座標系に変換するマトリクス。通常は頂点シェーダーで用いられます。
precision mediump float; // Required. // ===== Built-in Per-vertex Attributes ===== attribute vec3 a_position; // The vertex's coordinates. attribute vec2 a_texCoord; // The vertex's texture coordinate. // ===== Built-in Parameters ===== // Uniform parameters are available to shaders and have the // same value for each vertex and fragment. uniform mat4 u_projectionMatrix; // The projection matrix. // ===== CSS Parameters ===== uniform float amplitude; uniform float amount; // ===== Varyings ===== // Varying are set in the vertex shader and available in the // fragment shader. // A fragment's value for a varying is a weighted average based // on its distance from the three vertices surrounding it. varying vec2 v_texCoord; // ===== Constants ====== const float rotate = 20.0; const float PI = 3.1415926; // ===== Helper Functions ====== mat4 rotateX(float a) {...} mat4 rotateY(float a) {...} mat4 rotateZ(float a) {...} // ===== Shader Entry Point ===== // void main() { v_texCoord = a_texCoord.xy; vec4 pos = vec4(a_position, 1.0); float r = 1.0 - abs((amount - 0.5) / 0.5); float a = r * rotate * PI / 180.0; mat4 rotX = rotateX(a); mat4 rotY = rotateY(a / 4.0); mat4 rotZ = rotateZ(a / 8.0); float dx = 0.01 * cos(3.0 * PI * (pos.x + amount)) * r; float dy = 0.01 * cos(3.0 * PI * (pos.y + amount)) * r; float dz = 0.1 * cos(3.0 * PI * (pos.x + pos.y + amount)) * r; pos.x += dx; pos.y += dy; pos.z += dz; gl_Position = u_projectionMatrix * rotZ * rotY * rotX * pos; }

次に、例に使用したカラースワイプフラグメントシェーダーを示します。

precision mediump float; // Required. // ===== CSS Parameters ===== uniform float amplitude; // Unused in the fragment shader. uniform float amount; // ===== Varyings ====== varying vec2 v_texCoord; // ===== Constants ====== const vec3 scanlineColor = vec3(1.0, 1.0, 1.0); const float gradientHeight = 0.1; const mat4 grayscaleMatrix = mat4( … ); // ==== Shader Entry Point ===== void main() { // The scanline goes from the bottom of the element (1.0) to // just above the top of the element (-gradientHeight). // This makes sure the gradient is out of view at amount = 1.0. float scanlineTravelDistance = 1.0 + gradientHeight; // Scale amount from [0,1] to [0,scanlineTravelDistance]. float scanlineAmount = amount * scanlineTravelDistance; // Make the scanline start at the bottom and progress upward. // Its position goes from [1.0, -gradientHeight]. float scanlinePosition = 1.0 - scanlineAmount; if (v_texCoord.y < scanlinePosition) { // Make the element grayscale above the scanline. css_ColorMatrix = grayscaleMatrix; } else { // Apply a gradient below the scanline. float distanceFromScanline = v_texCoord.y - scanlinePosition; float gradientStrength = (gradientHeight - min(distanceFromScanline, gradientHeight)) / gradientHeight; css_MixColor = vec4(scanlineColor, gradientStrength); } }

注意:フラグメントシェーダーがCSSシェーダーでピクセルカラー値を算出する方法は、セキュリティの制約により、若干の違いがあります。

Web上には、シェーダーの作成方法に関する有用なリソースや、優れたシェーダーを収録した様々なライブラリが公開されています(末尾のリファレンスを参照)。

OpenGL ESシェーディング言語を初めとするシェーディング言語は、視覚的エフェクトを容易にプログラムできるように設計されています。CSSシェーダーを使えば、シェーダー言語の表現力をCSSの構文に結び付けることができ、エフェクトの使用とアニメーション化が容易になります。

WebGLとの比較

WebGLは、HTML5のcanvas要素の実装方法です。WebGLは、キャンバスの3Dコンテキストを提供するもので、そのコンテキストの中でピクセルシェーダーが使用できます(また、WebGLに用意されている頂点シェーダーやその他のすべての3D機能も利用できます)。WebGLはキャンバスのコンテキストを提供し、キャンバスの境界内で動作します。

これに対してCSSシェーダーは、任意のシェーダーを任意のWebコンテンツに適用する方法を提供するものです。

次のステップ

アドビはCSSシェーダーをFXタスクフォースの一部としてW3Cに提出しており、W3Cによる承認を待って、このコードをWebKitに提供開始する予定です。これにより、Webデベロッパーはこの新しい強力なフィルターエフェクトのセットを利用できるようになります。

それまでの間、CSSアニメーションとエフェクトについて知るには、以下のリソースを参照してください。