WebGLで遊ぶ！　GPGPU入門


　OpenGLはいろいろなプラットフォームで使えますが、その中でも**WebGLはブラウザ上のJavaScriptでササっと動作確認**できるので非常に便利です。
今回はこれを使って、自分の勉強がてらGPGPUの入門＆体験ができるような記事をまとめてみました。
　なおWebGLについては [wgld.org](https://wgld.org/)  が大変強力です。これだけ豊富な内容が無料かつ日本語で読めるのは神のようなサイトです！　ここにはお世話になりました。

#GPGPUとは#
　GPUはグラフィックを扱うハードウェア... というくらいの理解は皆さん持っていると思います。これを使ってグラフィック以外の汎用計算に活用しようというのが**GPGPU (General Purpose GPU)** ですが、GPUの特徴をひとことで言うと **SIMD (Single Instruction Multiple Data)**という並列処理機構になります。
　並列処理というと代表的なものは**マルチスレッド**ですが、この場合それぞれのスレッドが全く別の処理をすることができます。スレッドＡがマウスやキーボードの入力を処理し、スレッドＢがネットワークの非同期処理を担当し、スレッドＣが動画の再生をする、みたいなことはみなさんのPCでも日常的に行われていますね。物理的な処理ユニットはCPUの「コア」ですが、最近のPCでは４とか８のコア数が多い状況で、ハイエンドなXeonでは20程度のコアが利用できます。（実際には、OSがコアを使いまわして数十～数百のスレッドを疑似的に同時実行します）
　これに対しGPUでは、**「数千」**の処理ユニットを搭載しており、**CPUとは２桁違います**。これだけ見るとスゲー！と思いますが、SIMDなので、「Single Instruction」なのです。数学でいえば何かの**関数 f(x) があって、f(1)～f(1000)の1000個を並行に計算する**、というイメージです。**"f"自体は全体で共通**で、そうホイホイ変更することはできません。**処理は共通だが入力が違う**、というタイプの並列計算になります。
　昔だとやはりグラフィックくらいしか主要な適用分野がなかったのですが、最近ディープラーニングというGPGPUに超うってつけのネタが登場したので注目度は高まりました。

#シェーダー言語#
この共通の**"関数f"を記述するのがシェーダー言語**です。最初に出力結果を確認したほうが理解しやすいと思うので見ておきましょう。

![qiita_webgl_1.png](https://qiita-image-store.s3.amazonaws.com/0/59073/66ac3fa5-8c18-fdaa-51e7-c69650430f3b.png)

10段階のグレースケールの四角（ここでは**ユニット**と呼ぶことにします）が並んでますが、この画像を出力しているシェーダーはこうなります。

```html
<script id="fs1" type="x-shader/x-fragment">
precision mediump float;
varying vec2 index; // 0～9の10段階の値を受け取る
uniform float count; //全体で共通で、値 10 が格納されている
void main(void){
	float c = index.x / count; // 0～9の値が0.0～0.9の10段階になる
	gl_FragColor = vec4(c,c,c,1.0); // 色を決定
}
</script>
```

WebGLの場合、**コイツをいきなりscriptタグでHTMLの中に埋め込んで**しまいます。type="x-shader/x-fragment" となっていると、ブラウザはこのスクリプトはJavaScriptではない、と認識するだけで終わってしまいますが、別の場所のJavaScriptがこれを読んでシェーダーとしてコンパイルし、GPUに転送して実行します。
シェーダー言語は**１つの立派なプログラム言語**で、どのプラットフォームでOpenGLを使うにしても避けて通れません。

 - Cに似た構文。forループやifの分岐もあるし、独自の関数定義もできる。
 - プリミティブ型として、intやfloatはもちろん、4次元までのベクトルや行列も持っている。

といった特徴がありますが、**なんといってもSIMDなので、10個のユニットを並行して描画している様子を想像してほしい**と思います。（実際には並列数は10ではないですし、この前段の頂点シェーダのステップもあるのですが、話を単純にするため最初は10個の並列、という理解でOKと思います）

このシェーダをもう少し詳しく解説します。

```glsl
varying vec2 index;
```

　これはユニットの一つ一つについて受け取ってくるパラメータで、**vec2というのは２次元ベクトルの型**です。今は１方向のグラデーションなので、x要素のみ使用しています。varyingというキーワードが見慣れませんが、最初は**SIMDの"MD"部分**、と理解してください。ユニットごとに異なる値です。
　具体的にどうやってユニットごとの値を渡してやるのか、は長くなるので大胆に省略しますが、末尾に実際に動作するHTMLページへのリンクをつけますので、興味のある方はその中のJavaScriptをじっくり読んでください。
　次の行にいきます。

```glsl
uniform float count;
```

　これもシェーダーの入力引数で、プログラマが指定できる値ですが、**全ユニットで共通**です。ユニット間では共通ですが、描画が１回完全に終われば次回の描画時にセットしなおせるので、時間とともにuniform変数をゆるやかに変化させてやればイカしたアニメーションも簡単に作れます。ここでは、ユニットの個数"10"を受け取る役割にしています。

```glsl
float c = index.x / count;
gl_FragColor = vec4(c,c,c,1.0);
```

　最後のここのmain関数本体です。**gl_FragColorは色を出力するための特殊な変数**です。これはvec4型(4次元ベクトル)で、RGBAの順で色を指定し、各要素は0.0～1.0の値です。vec4(c,c,c,1.0) なのでRGBの値は等しくグレースケールになります。vec4(c,0,0,1.0)なら赤くなりますし、vec4(c,c,c,0.5)なら半透明になるので別の画像と重ねることもできます。

　入力値がどう渡ってくるのかの説明は省略していますが、そのあとの部分はSIMDの処理がイメージできるのではないかと思いますがどうでしょうか？

#if分岐#

```html
<script id="fs2" type="x-shader/x-fragment">
precision mediump float;
varying vec2 index;
uniform float count;
void main(void){
	float c = index.x / count;
	if(mod(index.x, 2.0) == 0.0)
		gl_FragColor = vec4(c,0,0,1.0);
	else
		gl_FragColor = vec4(0,0,c,1.0);
}
</script>
```

　次はコイツです。if文で mod(index.x, 2.0) の条件で分岐して、vec4(c,0,0,1.0)かvec4(0,0,c,1.0)を出力しています。これで予想できると思いますが、**index.xの偶数・奇数によって赤か青かを出す**わけです。剰余計算は**mod関数を使い、C言語の % ではない**ことに注意してください。
　これを実行するとこのような画像になります。

![qiita_webgl_2.png](https://qiita-image-store.s3.amazonaws.com/0/59073/f391e3ee-71df-1d3d-9adc-327c375135c4.png)


WebGLのいいところは、**こうやって修正結果をすぐグラフィックで確認できる**ところですね。PCでもスマホでもいけますし、本当にすばらしい。

#２次元ベクトルを扱う#

```html
<script id="fs3" type="x-shader/x-fragment">
precision mediump float;
varying vec2 index;
uniform float count;
void main(void){
	gl_FragColor = vec4(index.x / count, 0, index.y / count, 1.0);
}
</script>
```

　せっかく２次元ベクトルを受け取っているのでXYの両方の要素を使ってみましょう。**x座標で赤の要素が変化、y座標で青の要素が変化**、としています。Gは0, Aは1で固定です。なので出力結果はこうなります。

![qiita_webgl_3.png](https://qiita-image-store.s3.amazonaws.com/0/59073/bfba7af4-3cd3-b80c-ef68-702d439eca64.png)


だんだん雰囲気がわかってきたでしょうか？

#ユニット数が多くても大丈夫#

```html
<script id="fs4" type="x-shader/x-fragment">
precision mediump float;
varying vec2 index;
uniform float count;
uniform float timer;
void main(void){
	float cx = mod(index.x / count + timer*0.01, 2.0);
	float cy = mod(index.y / count + timer*0.01, 2.0);
	cx = cx>1.0? 2.0-cx : cx;
	cy = cy>1.0? 2.0-cy : cy;
	gl_FragColor = vec4(cx,0, cy,1.0);
}
</script>
```
　今度は**uniform変数timerを使ってアニメーションをやります**。最初に2.0で割った余りを求めて0～2.0の値を得て、そこから0->1->0->1と連続的変化するような値を得てから色を決定します。
　さらに調子にのって、**描画のユニット数をなんと50*50の2500個**にしてしまいます。CPUで一つ一つ描いていたらさすがに重くなりそうですが、SIMDの威力をもってすればこの程度は全く問題になりません。

![qiita_webgl_4.png](https://qiita-image-store.s3.amazonaws.com/0/59073/c06c6156-1c47-6aad-77f2-1f7360c76fcd.png)

Qiitaの記事だと静止画しか載せられませんが、[こちらにはちゃんとアニメーションするデモ](https://s3-ap-northeast-1.amazonaws.com/poderosa/2D_practice.html)をつけています。

#GPGPUといいつつ画像を出しているだけじゃんか#
　その質問はごもっともです。ただ、結果を目に見えるように出力するには画像にするのが手っ取り早かっただけのことです。実際には、**画像のふりをしたメモリ上の一定の領域へシェーダーに書き出してもらい**、後でそこに何が描いてあるかを調べるという手順になります。画像のレンダリング機構を借用して目的の計算をするわけですが、ユニットごとの出力が32ビットで収まらないようなケースでどうするのか、とかまだ調べ切れていないところはいくつかあります。

#デモ#
[上記４種の実際に動くHTML+JavaScript](https://s3-ap-northeast-1.amazonaws.com/poderosa/2D_practice.html)を 用意しました。
四角形の列を定義するところも含め、特に外部のライブラリ等を使うことなく素直に書いたので、丁寧に読めば全容を把握できると思います。少ない行数でかなり高度なことができますね！

#おわりに#
WebGLというと、ブラウザ上で3Dをやるための技術と思われがちですが、WebGLでの2Dグラフィックを先にやった方がGPUの動作イメージやSIMDの練習にはぴったりだと思います。面白いですよ！