Filtersを使ってGLSLを楽しく学んじゃおう！


[Advent Calendar](http://qiita.com/advent-calendar/2014/webgl) 21日目の記事です。

すでに散々書いていて今さら感もありますが、今回はいくつかの記事で紹介してきた[Filters](http://filters.kayac.com/)についてさらに詳しく紹介したいと思います。

[![Filters](https://qiita-image-store.s3.amazonaws.com/0/5616/8b929291-2e54-5b2c-f2bf-4b043eddb73c.png "Filters")](https://itunes.apple.com/jp/app/filters-wu-xianni-zengerufiruta/id926621140)


## この記事の対象はWebGLに興味があってGLSLをこれから学習したい人

すでにがっつりGLSLを書いている人は[Filters](http://filters.kayac.com/)で遊んでみてくださいｗ
ここでは主に、どういう点が学習に向いているか、どう使うのかについて書いています。


## 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](http://glslsandbox.com/)から引用したり！）して、徐々に変化をつけながらGLSLを学んでいくことが出来ます。

これが、自分が思うFiltersでのGLSL学習のメリットです。

### GLSLが書けるサービスは他にないの？

もちろんFiltersでなくても、前述した[GLSL Sandbox](http://glslsandbox.com/)のように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ブラウザが同期されました。

![Editor](https://qiita-image-store.s3.amazonaws.com/0/5616/bf1f2221-aac6-3838-467f-b8efc7dbb00e.jpeg "Editor")



左側のエディタでGLSLを編集してみましょう。
リアルタイムに同期が取られているので、エディタ側でコードを編集するとまもなくアプリ側の画面も変わるはずです。
さらに画面右側もプレビューになっていて、エディタで編集した内容が反映されるようになっています。
ここはWebGLが使われていて、記述したGLSLがそのまま適用されています。


## ピンチイン・ピンチアウト、タッチ位置などに対応

Filtersの面白いもうひとつの点として、 **ピンチイン・ピンチアウト、タッチ位置の捕捉** があります。
使いたいフィルターを起動して画面をピンチインさせたりすると、それがデータとしてGLSLに送られてきます。
それをなにかしらの係数として扱ってやれば、画面を触ると変化が起こるフィルターが作れる、というわけです。

ちなみにシステムから送られてくるパラメータはエディタの下に記載があります。
具体的には以下です。

![param](https://qiita-image-store.s3.amazonaws.com/0/5616/57f5d593-0c6a-570a-04af-96bca53cc3ef.jpeg "param")

それぞれ以下の意味になります。

| 型 | 変数名 | 意味 |
|:----|:--------|:-----------|
| vec2 | iScreen | いわゆるUV座標 |
| vec2 | iResolution | 画面サイズ。iPhoneのカメラの画角がそのままピクセルとして渡される |
| float | iTime | フィルターを開始してからの経過時間。sinなどで使うと変化を出せる |
| vec2 | iPosition | タッチ位置。(0, 0)〜(1, 1)で値が渡ってくる |
| float | iSize | ピンチイン・ピンチアウトの現在のスケール値。デフォルトは1。 |
| sampler2D | iCamera | カメラ映像のテクスチャ |


## 実際に作ってみる

今回の記事のために、簡単なフィルター（と呼べるか怪しいｗ）を作ってみました。
画面をタッチしてドラッグするとカメラの映像が繰り返しになって移動する、というものです。

↓こんな感じ（[動画](https://www.youtube.com/watch?v=k_TzdHYoHEM&feature=youtu.be)）
[![moving-filter](https://qiita-image-store.s3.amazonaws.com/0/5616/e73c2a09-4d19-5c1c-de9f-4bffaa298c53.jpeg "moving-filter")](https://www.youtube.com/watch?v=k_TzdHYoHEM&feature=youtu.be)

### まずは新規作成

フィルターを新規作成すると以下のコードが記述されています。

```glsl
void main() {
	vec2 uv = iScreen;
	
	vec4 color = texture2D(iCamera, uv);
	
	gl_FragColor = vec4(color.rgb, 1.0);
}
```

とてもシンプルですね。これだけで、カメラからの映像をそのまま表示するものになっています。

いちおうGLSLにあまり馴染みがない人に簡単に説明しておくと、GLSLはC言語由来の言語になっており、基本的な構文などはC言語と非常に似ています。
また、今回いじっているGLSLのコードは **フラグメントシェーダ** と呼ばれ、主に画面にピクセルをレンダリングする計算をするシェーダになっています。

#### 画面を真っ青にしてみる

試しに以下のようにコードを書き換えてみると画面が真っ青になります。

```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`となり、つまり不透明な青になる、というわけですね。
だから画面が真っ青になったわけです。


#### カメラからの映像を取得する

さて、それでは最初のコードに戻って見てみましょう。

```glsl
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`に設定しているわけです。
まぁたんにカメラの映像を出力しているだけですねｗ

##### 余談

余談ですが、GLSLのフラグメントシェーダの役割は、画面内の **全ピクセル** に対してどんな色が画面に出力されるべきか、を計算することです。
例えば、1920 x 1080のディスプレイであれば **2,073,600ピクセル** もの計算を行う必要があるわけです。
だからグラフィック処理は重いんですね。

そしてこの1ピクセル1ピクセルにどんな色を出力すべきかを決定しているのが`gl_FragColor`で指定された色、というわけです。（実際にはアルファテストやブレンドなどさらに複雑な処理がありますが）

##### 閑話休題

さて、ではどうしたらフィルター効果をつけれるでしょうか。
例えば`texture2D`の取得位置を変えてやれば上下左右反転、なんかはできそうですよね。
あるいは適当に生成した色とテクセルの色を合成して`gl_FragColor`に設定してやれば色味を変えるフィルターの出来上がりです。

実際のところ、よく目にするフィルターの中身はとてもシンプルなことが結構あったりします。

さっきの真っ青にしたコードと、デフォルトのを組み合わせたらどうなるでしょうか。

```glsl
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`を足しただけのものです。
これを実行すると以下のような、微妙に青みがかった映像になりました。

![blue-filter](https://qiita-image-store.s3.amazonaws.com/0/5616/3d772a95-a878-65b8-2f3a-8c00747df45e.jpeg "blue-filter")


### 作ったフィルター

今回作ったフィルターも全然大したことはやっていません。
コードを見てもらうと、デフォルトのものと行数は一緒です。
というか十数文字くらいしか違いがありませんｗ


```glsl
void main()
{
	vec2 uv = mod(iScreen - iPosition, 1.0);
  
	vec4 texColor = texture2D(iCamera, uv);
	
	gl_FragColor = texColor;
}
```

変更点は以下の部分のみ。

```glsl:diff
vec2 uv = mod(iScreen - iPosition, 1.0)
```

`iPosition`はシステムから送られてくるタッチ位置ですね。
それを`iScreen`から引いています。つまりテクセルを取り出す位置をその分ずらしている、というわけです。

そしてそれを`mod`関数で計算した結果を`uv`変数に入れています。
`mod`関数は「[モジュロ](http://yougo.ascii.jp/caltar/%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%AD)」と呼ばれ、要は余りを計算する関数です。
（GLSLは`0.0〜±1.0`で計算されるため、`1.0`の余りを求め、`0.0〜±1.0`の間になるようにしています）

このテクニックはタイル上にするフィルターなどにも使われ、比較的よく使われているものだと思います。

あとは、その求めた`uv`を使って、カメラテクスチャからテクセルを取り出してやれば今回のように移動するフィルターの完成となります。

## Filters紹介ムービー

最後に。
Filtersの紹介ムービーを見ると実際に動かしているところが見られるので、興味がある人は見てみてください。

[![Filters PV](https://qiita-image-store.s3.amazonaws.com/0/5616/53d143d5-3c75-01ba-248b-c2f67f7ef9b9.png "Filters PV")](http://vimeo.com/113485095)