カテゴリー:CSS Shader

リスト表示へ

CSS Shaderで遊ぶ(2)

2013年5月26日 08:15

前回記事、「CSS Shaderで遊ぶ」ではCSS Shaderの触りを書きました。
詳細にはほとんど触れていませんが、どんなことができるのか、CSS Shaderの可能性は感じてもらえたのではないでしょうか。

今回の記事では、前回触れたUIを作るという点にフォーカスを当てて、簡単なエフェクトを作ってみるとともに、シェーダでどんなことをやっているのかひとつずつ見ていこうと思います。とはいえ、自分もまだまだ勉強中の分野のため、間違いだったり勘違いがあるかもしれません。なので、読む方はそれを念頭に読んでいただければと思います。

CSS ShaderでUIを作る

ということで、さっそく今回のために作成したサンプルを見てみようと思います。サンプルはまたしてもjsdo.itに上げました。
UIということで、分かりやすく、Mac OSXのジニーエフェクトを模したものを作りました。
サンプルを見てもらうと分かりますが、実際書いているコードはそれほど多くありません。(JS部分はだいぶ荒削りになってますが)
(ちなみに、3つ並んでいるボタンの中央、「-」ボタンを押すことでエフェクトが実行されます。また、Shiftキーを押しながらクリックするとゆっくりエフェクトします)

CSS Shader適用の流れ

CSS Shaderでは以下の様な流れで状態を決定します。filterの指定の仕方は前回の記事を参照してください。

  • 適用した要素を、指定されたメッシュ数で分割する
  • 分割した部分のそれぞれの頂点位置情報を頂点シェーダに送る
  • 頂点シェーダ内で、CSSから渡された頂点位置情報を元に変形のための計算を行う
  • 頂点シェーダ内で計算された値を(必要であれば)フラグメントシェーダに送る
  • フラグメントシェーダで各ピクセルに対して計算を行い、最終的なピクセルの色を決定する

やや複雑ですが、大雑把に言うとこんな感じの流れになります。
前回の記事で掲載した画像が以下です。上記の処理を図にしたものです。

頂点シェーダに送られる位置情報は、上記画像で言うとcustom()と書かれた範囲の一番最初の、細切れにされたそれぞれの点の位置になります。

次に処理されるフラグメントシェーダはというと、上記の頂点情報から計算され変形された形をピクセル(フラグメント)に分割された各ピクセルに対して計算することになります。

頂点から次の頂点までの間のピクセルに関しては、頂点で計算された値は適切に補間されて値が渡されます。
つまり、頂点シェーダでの計算よりフラグメントシェーダでの計算のほうが回数が多く、単純に高負荷となります。

シェーダソース(Vertex shader)

さて、さっそくソースを見ていきます。今回は頂点シェーダ(Vertex shader)のみです。
フラグメントシェーダでは入力された値を、そのまま出力します(つまりなにもしない)。
まずはソース全文を載せ、続いて個別に解説していきます。

ソース解説

3Dのレンダリングは非常に細かい計算が必要です。そのため頂点シェーダでは基本、最高の精度で計算されます。
しかし当然ながらその分、計算の負荷が高くなります。今回のサンプルではそれを中精度で計算するよう宣言しています。(ただ、精度を指定しなくてもデフォルトが決まっているので問題はありません)
精度は3種類、lowp、mediump、highpが指定できます。

attribute変数

続いてattribute変数です。
attribute変数は頂点ごとに異なる値がCSSから渡される変数です。異なる値と言えば、頂点の位置情報がありますね。
位置以外にも色々情報があるので、頂点属性(attribute)と覚えておくといいでしょう。

前回記事で説明したように、指定されたDOM要素を複数のメッシュで分割し、それぞれの頂点位置を頂点シェーダに送ることで頂点位置を計算することができます。
頂点位置情報はa_position変数に自動で渡されます。つまり、頂点位置はすべて違った値ということになります。

また、頂点以外にも同じような値としてattribute変数に値が渡ってきます。今回の例で言えばa_texCoordです。
これは上記の頂点の位置情報と似たような値ですが、指定されたDOMをさもひとつのテクスチャ画像と見なし、現在計算中の頂点の位置にあるテクスチャ座標を渡してくれる変数です。

ただ注意点としては、a_positionで渡される値が[-0.5, 0.5]の間であるのに対し、a_texCoordは[0.0, 1.0]の間の値が渡されることです。今回のサンプルではこの値を角度に見立てて、下部に行くほど頂点位置が中央に寄っていく演出に利用しました。

a_position,a_texCoordはCSSから自動的に渡されるので、宣言をしておくだけで大丈夫です。(変数名は決まっているのでサンプルと同じように書いてください)

uniform変数

uniform変数はattribute変数と違い、全体で共通の値として設定される値です。
今回の例で言えばu_projectionMatrix,u_timeがそれに当たります。どちらも各頂点で同じ値が使用されます。

u_projectionMatrixは、透視投影(プロジェクション)変換行列です。
行列の意味は長くなるので割愛します。この値も同様にCSSから自動的に渡されるので、宣言をしておくだけで大丈夫です。

u_timeはサンプルで使用するために宣言している変数です。u_projectionMatrixなどと違い、制作者が任意に宣言、使用することができます。
今回の例では、「-」ボタンクリック後からの時間を計測し、それに伴って下部が徐々に縮小していく演出に用いています。

CSSが自動的に設定する変数と意味についてはQiitaの記事でまとめているのでそちらを参考にしてください。

const変数

constは他のプログラムと同様、定数を宣言する部分です。今回は円周率の値として定義しています。

main関数

main関数です。GLSLはC言語ライクな言語なので、main関数を必要とし、各頂点ごとにmain関数で記述した内容が実行されます。

細かい処理についてはコメントに詳細を記載しています。最初に書いたように、a_texCoord.yを角度に見立てて、頂点位置が下に移動すればするだけ中央に寄る計算を行なっています。

最後のgl_Position = u_projectionMatrix * pos;の部分は、計算した位置情報に、プロジェクション変換行列を掛けたものをgl_Positionという特殊変数に渡しています。
これはGLSLの仕様で、gl_Positionに位置情報を渡すことで頂点位置をプログラム側(WebGLやCSS Shaderなど)に伝えることができます。

まとめ

CSS Shaderは、CSSと名がついていながら、実際にはプログラムを記述する必要があります。しかし覚えれば表現できる幅が格段に広がるのでがんばってでも覚える価値はあるでしょう。

ですが、やはりプログラムはちょっと・・という人もいると思います。
幸いにしてシェーダ部分はふたつのファイルに分けて管理されるので、シェーダ部分はライブラリなどを利用し、ちょっとした変化や移動する度合いなどは上記で説明したuniform変数に渡すだけ、という使い方もできると思います。

個人的な意見としてですが、実装がもっと進めばそうしたライブラリがいずれ出てくると思うので、どう使ったらいいか、というのはぜひ覚えておくといいでしょう。

1