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に転送して使えばたった1つの変数の転送だけですべてのパーティクルについて周期的な動きが再現できます。パーティクルの数が少ないとすぐにばれてしまうのですが、数を多くすることでごまかしが効いているかと思います。このように、初期値だけ与えておいて変動値だけをCPUで計算し、個別の計算はGPUにさせる方法を使えば、ハッタリの効いた表現も低コストで行えます。
最後に
WebGLコンテンツの作成にはThree.jsやAway3D等のライブラリが使用されるのが一般的ではありますが、各種ライブラリが中で何をやっているのか、WebGLで何が可能なのかを知ればライブラリを使用する際の理解が深まったり、ライブラリだけでは表現できないような処理を挟むこともできるため、ライブラリの使用に慣れてきた方は一度ひととおり勉強してみることをオススメします。とくにモバイル環境で大事な高速化(計算負荷の削減)を理解する上でとても役に立つこと請け合いなので是非チャレンジしてみてください。