Advent Calendar 21日目の記事です。
すでに散々書いていて今さら感もありますが、今回はいくつかの記事で紹介してきたFiltersについてさらに詳しく紹介したいと思います。
この記事の対象はWebGLに興味があってGLSLをこれから学習したい人
すでにがっつりGLSLを書いている人はFiltersで遊んでみてくださいw
ここでは主に、どういう点が学習に向いているか、どう使うのかについて書いています。
Filtersとは
Filtersは、PCブラウザからGLSLを記述し、それをリアルタイムに同期を取ってiPhone側のフィルターとしてプレビューできるまったく新しいタイプのフィルターアプリです。
なにがいいのか
ずばり、GLSLを楽しく学べる点です。
GLSLは(個人的には)学習するのが大変な言語のひとつだと思っています。
理由としては3Dの基本的な知識はもちろん、OpenGL(WebGL)を理解していないとなりません。
OpenGL(WebGL)に関しては色々な言語で記述することができるので、言語的な学習コストはあまりないかもしれませんが、素で書こうと思うと手順がとても冗長で、単純なポリゴンを描くまでもだいぶ時間がかかってしまいます。
そしてOpenGL(WebGL)のプログラムが書けたとして、GLSLはそこからやっとスタート、となります。
さらにはGLSLの場合、頂点シェーダとフラグメントシェーダと言われるふたつのシェーダを書かないとなりません。
つまり、セットアップだけでお腹いっぱいになること必至です。
こうしたことからも、GLSLは「Hallo world」がとてもしづらいものだと思います。
FiltersではすぐにGLSLを書き始められる
以上のように、GLSLを書こうと思ってもそれをセットアップするまでの手順が多いだけでなく、OpenGL(WebGL)も同時に学習しないとなりません。
しかし、Filtersはカメラのフィルターアプリなのでなにもしなくてもカメラの映像が、リアルタイムに変化するテクスチャとして表示されるよう準備されています。つまりOpenGL(WebGL)の知識は必要ないわけです。
(もちろん、GLSLを学ぼうという人がOpenGLをやらないわけにはいかないでしょうが)
加えて、他にたくさん投稿されているフィルターをForkしたり(あるいはGLSL Sandboxから引用したり!)して、徐々に変化をつけながらGLSLを学んでいくことが出来ます。
これが、自分が思うFiltersでのGLSL学習のメリットです。
GLSLが書けるサービスは他にないの?
もちろんFiltersでなくても、前述したGLSL SandboxのようにGLSLを気軽に書けるサービスはあります。
ですが、どんな模様を描くかはプログラム次第です。(要はがんばらないと画面になにも出てこない)
でもFiltersは前述した通り、カメラのフィルターアプリなのでデフォルトでカメラの映像が表示されます。
さらに静止画ではなく今撮っている映像がリアルタイムに表示されるので、画面の変化も感じやすいのがいいところです。
[本題] 使ってみる
だいぶ前置きが長くなりましたが、Filtersの機能を見て行きましょう。
Filtersアプリをインストールして起動すると以下のような画面になります。
[[画像入れる]]
- PICK UP は投稿されたものが一覧で並んでいます。
- RANKING には人気のフィルターが並びます。
- FAVORITE は自分がお気に入りに入れたフィルターリストです。
Forkしてみる
ざっとフィルターを眺めているとたまに気になるフィルターが見つかると思います。
どうやって作っているのか気になるフィルターや、普通に使ってみたいフィルター、あるいは見ていたフィルター同士を組み合わせて面白いのが作れそうだ、と思わせてくれるフィルターなどなど。
気になったフィルターがあったら Fork してみましょう。
Forkするには、気になるフィルターを開き、タブの中の「Fork」ボタンを押下してForkします。
Forkすると以下のようにQRコードを撮影する画面になるので、PCブラウザで編集ページ (http://filters.kayac.com/edit) を開きます。
[[QRコード撮影画面キャプチャ]]
編集ページを開くとQRコードが表示されるので、先ほどアプリ側で表示されたQRコード撮影カメラでQRコードを撮影します。
すると以下のような編集画面が開かれ、アプリ側の画面も編集中を示す画面に切り替わります。
これでアプリとPCブラウザが同期されました。
左側のエディタでGLSLを編集してみましょう。
リアルタイムに同期が取られているので、エディタ側でコードを編集するとまもなくアプリ側の画面も変わるはずです。
さらに画面右側もプレビューになっていて、エディタで編集した内容が反映されるようになっています。
ここはWebGLが使われていて、記述したGLSLがそのまま適用されています。
ピンチイン・ピンチアウト、タッチ位置などに対応
Filtersの面白いもうひとつの点として、 ピンチイン・ピンチアウト、タッチ位置の捕捉 があります。
使いたいフィルターを起動して画面をピンチインさせたりすると、それがデータとしてGLSLに送られてきます。
それをなにかしらの係数として扱ってやれば、画面を触ると変化が起こるフィルターが作れる、というわけです。
ちなみにシステムから送られてくるパラメータはエディタの下に記載があります。
具体的には以下です。
それぞれ以下の意味になります。
型 | 変数名 | 意味 |
---|---|---|
vec2 | iScreen | いわゆるUV座標 |
vec2 | iResolution | 画面サイズ。iPhoneのカメラの画角がそのままピクセルとして渡される |
float | iTime | フィルターを開始してからの経過時間。sinなどで使うと変化を出せる |
vec2 | iPosition | タッチ位置。(0, 0)〜(1, 1)で値が渡ってくる |
float | iSize | ピンチイン・ピンチアウトの現在のスケール値。デフォルトは1。 |
sampler2D | iCamera | カメラ映像のテクスチャ |
実際に作ってみる
今回の記事のために、簡単なフィルター(と呼べるか怪しいw)を作ってみました。
画面をタッチしてドラッグするとカメラの映像が繰り返しになって移動する、というものです。
↓こんな感じ(動画)
まずは新規作成
フィルターを新規作成すると以下のコードが記述されています。
void main() {
vec2 uv = iScreen;
vec4 color = texture2D(iCamera, uv);
gl_FragColor = vec4(color.rgb, 1.0);
}
とてもシンプルですね。これだけで、カメラからの映像をそのまま表示するものになっています。
いちおうGLSLにあまり馴染みがない人に簡単に説明しておくと、GLSLはC言語由来の言語になっており、基本的な構文などはC言語と非常に似ています。
また、今回いじっているGLSLのコードは フラグメントシェーダ と呼ばれ、主に画面にピクセルをレンダリングする計算をするシェーダになっています。
画面を真っ青にしてみる
試しに以下のようにコードを書き換えてみると画面が真っ青になります。
vec4 blue = vec4(0.0, 0.0, 1.0, 1.0);
gl_FragColor = blue;
gl_FragColor
というのはフラグメントシェーダで計算された色を最終的な画面の色として決定する、特殊な組み込み変数です。(なのでGLSLはreturn
文ではなくgl_FragColor
変数に色を設定します)
そしてその上の行で定義しているのが「青色」の意味になります。
GLSLでは基本的に、値を0.0〜±1.0
の範囲で扱います。
vec4
は4要素のベクトルで、つまりはRGBA
を意味しています。
上記コードは0, 0, 1, 1
となり、つまり不透明な青になる、というわけですね。
だから画面が真っ青になったわけです。
カメラからの映像を取得する
さて、それでは最初のコードに戻って見てみましょう。
void main() {
vec2 uv = iScreen;
vec4 color = texture2D(iCamera, uv);
gl_FragColor = vec4(color.rgb, 1.0);
}
なにかをcolor
という変数に取り出してから、gl_FragColor
に設定しています。
texture2D
はビルトイン関数でGLSLで定義されている関数です。
なんとなくなにをしているか分かると思いますが、要はカメラテクスチャ(画像)から、該当箇所(uv
)のピクセルの色(通称テクセル)を抜き出してくれる関数です。
そして最後に、その抜き出した色のrgb
成分、つまり4要素のうち最初の3つを取り出し、アルファを1.0
に設定した色をgl_FragColor
に設定しているわけです。
まぁたんにカメラの映像を出力しているだけですねw
余談
余談ですが、GLSLのフラグメントシェーダの役割は、画面内の 全ピクセル に対してどんな色が画面に出力されるべきか、を計算することです。
例えば、1920 x 1080のディスプレイであれば 2,073,600ピクセル もの計算を行う必要があるわけです。
だからグラフィック処理は重いんですね。
そしてこの1ピクセル1ピクセルにどんな色を出力すべきかを決定しているのがgl_FragColor
で指定された色、というわけです。(実際にはアルファテストやブレンドなどさらに複雑な処理がありますが)
閑話休題
さて、ではどうしたらフィルター効果をつけれるでしょうか。
例えばtexture2D
の取得位置を変えてやれば上下左右反転、なんかはできそうですよね。
あるいは適当に生成した色とテクセルの色を合成してgl_FragColor
に設定してやれば色味を変えるフィルターの出来上がりです。
実際のところ、よく目にするフィルターの中身はとてもシンプルなことが結構あったりします。
さっきの真っ青にしたコードと、デフォルトのを組み合わせたらどうなるでしょうか。
void main() {
vec2 uv = iScreen;
vec4 blue = vec4(0.0, 0.0, 1.0, 1.0);
vec4 color = texture2D(iCamera, uv);
vec4 dstColor = vec4(color.rgb, 1.0) + blue;
gl_FragColor = dstColor;
}
最初のコードにblue
を足しただけのものです。
これを実行すると以下のような、微妙に青みがかった映像になりました。
作ったフィルター
今回作ったフィルターも全然大したことはやっていません。
コードを見てもらうと、デフォルトのものと行数は一緒です。
というか十数文字くらいしか違いがありませんw
void main()
{
vec2 uv = mod(iScreen - iPosition, 1.0);
vec4 texColor = texture2D(iCamera, uv);
gl_FragColor = texColor;
}
変更点は以下の部分のみ。
vec2 uv = mod(iScreen - iPosition, 1.0)
iPosition
はシステムから送られてくるタッチ位置ですね。
それをiScreen
から引いています。つまりテクセルを取り出す位置をその分ずらしている、というわけです。
そしてそれをmod
関数で計算した結果をuv
変数に入れています。
mod
関数は「モジュロ」と呼ばれ、要は余りを計算する関数です。
(GLSLは0.0〜±1.0
で計算されるため、1.0
の余りを求め、0.0〜±1.0
の間になるようにしています)
このテクニックはタイル上にするフィルターなどにも使われ、比較的よく使われているものだと思います。
あとは、その求めたuv
を使って、カメラテクスチャからテクセルを取り出してやれば今回のように移動するフィルターの完成となります。
Filters紹介ムービー
最後に。
Filtersの紹介ムービーを見ると実際に動かしているところが見られるので、興味がある人は見てみてください。