2014年9月にリリースされたiOS 8においてWebGLが動作するようになったことで、スマートフォン環境での3D表現に可能性を感じ、興味を持った方も多いと思います。そこで今回、写真を読み込んで3D空間上に配置し、パーティクルに分解して次の画像に切り替わるスライドーショーのデモをJSライブラリ(Three.jsなど)を使わずにイチからHTML5とWebGLで作ってみました。WebGLが動作する端末ならモバイルでもなめらかに動作するので、是非試してみてください。マウスやタッチの操作でビューを回転させることができます。

制作環境について

今回のデモを作成するにあたり、WebGLのAPIを直接呼び出す形でコードを記述し、AltJSとしてTypeScriptを採用しました。WebGLで描画を行うためにはcanvasタグから取得したWebGLコンテキスト(WebGLRenderingContextオブジェクト)に命令を実行させる必要があります。このWebGLコンテキストとはcanvasが描画処理を行うための様々なメソッド、プロパティが実装されているオブジェクトなのですが、3D描画のためのフローの各所で必要な全ての定数がこのオブジェクトのプロパティとして定義されているため、スペルミスの危険性もありとても煩雑に感じました。こういったケースでは各種エディターでコード補完の効くTypeScriptが非常に有用と感じます。

画像のピクセルカラーの取得

今回、スライドショーとして読み込んだ画像の色をパーティクルに反映させるため、画像のピクセルごとの色情報を取得しました。canvasタグの2Dコンテキスト(CanvasRenderingContext2Dオブジェクト)を使用し、下記のようなコードで実現できます。

// canvasから2Dコンテキストを取得します。
context2d = canvas.getContext('2d');

// canvasのサイズを画像サイズに合わせます。
canvas2d.width = image.width;
canvas2d.height = image.height;

// 2Dコンテキストを通してcanvasに画像を描画します。
context2d.drawImage(image, 0, 0);

// ピクセルカラー値の配列を2Dコンテキストから取得します。
var imageDataArray = context2d.getImageData(0, 0, image.width, image.height).data;

こうすることで、画像のピクセルごとのRGBA値が格納された配列が取得できます。この情報をもとに各パーティクルを配置した座標に対応した色に反映させました。注意する点として、1つのcanvasエレメントに対してコンテキストは2D、WebGL合わせて1つしか取得することはできないため、3D描画用のcanvasとは別に画像取得用のcanvasエレメントをHTMLに追加しました。

パーティクル描画について

さて、今回のデモでは6万個以上のパーティクルを使用しています。これだけの個数のオブジェクトの座標計算を個別に行うとなると、CPUでは限界があります。また、一般的にGPUに値を転送するコストはGPUの計算処理能力に比べとても高いため、なるべくならパーティクル全ての座標更新を毎フレーム行うのは避けたいところです。そこでパーティクルそれぞれの座標計算はGPUにやってもらうことにしましょう。デモをよく見ると、パーティクルの動きは一見ランダムに見えるのですが実はパーティクル1つだけ抜き出してみると周期的に同じ動きしかしていません。

2つの状態の座標を予めCPUで計算しておきます

上記の図のように、パーティクルそれぞれについて、「状態1:整列」「状態2:ランダム」の座標をCPUで予め計算してGPUに転送しておき、GPU上で下記の式を計算させることで0~1のttimeScaleに応じて状態1と状態2の間を行き来させます。

// パーティクルの座標ベクトルをtimeScaleに応じて線形で変化するよう計算します。
vec3 position = position1 + (position2 - position1) * timeScale;

そうしてフレームごとにCPUでtimeScaleの値をMath.cos()関数で変動させ、GPUに転送して使えばたった一つの変数の転送だけで全てのパーティクルについて周期的な動きが再現できます。パーティクルの数が少ないとすぐにばれてしまうのですが、数を多くすることでごまかしが効いているかと思います。このように、初期値だけ与えておいて変動値だけをCPUで計算し、個別の計算はGPUにさせる方法を使えば、ハッタリの効いた表現も低コストで行えます。

最後に

WebGLコンテンツの作成にはThree.jsやAway3D等のライブラリが使用されるのが一般的ではありますが、各種ライブラリが中で何をやっているのか、WebGLで何が可能なのかを知ればライブラリを使用する際の理解が深まったり、ライブラリだけでは表現できないような処理を挟むこともできるため、ライブラリの使用に慣れてきた方は一度ひと通り勉強してみることをおすすめします。特にモバイル環境で大事な高速化(計算負荷の削減)を理解する上でとても役に立つこと請け合いなので是非チャレンジしてみてください。