2次元物理演算ライブラリ、LiquidFun.jsを使ってみた

今回は、Googleが提供する2次元物理演算ライブラリ、LiquidFun.jsを使ってみました。

20160609_img

LiquidFunは、Googleが提供する、2次元物理演算ライブラリの定番Box2Dをベースとした拡張ライブラリで、以下の機能を有しています。

  • 重力 (Box2D)
  • 摩擦/剛体/跳ね返り (Box2D)
  • 衝突判定 (Box2D)
  • ジョイント (Box2D)
  • 流体 (LiquidFunで拡張)

C++のライブラリですが、JavaやJavaScriptに移植されているので、JavaScript版のLiquidFun.jsを使うことで、ブラウザで2次元物理演算を行う事が出来ます。

特徴

LiquidFunは、他のJavaScript制の物理演算エンジンに比べると以下の特徴が挙げられます。

  • 流体表現向けの機能が充実している。
  • asm.jsで書かれていて、パフォーマンスが良い。

asm.jsについてほとんど知識がないのですが、ざっくりと特徴を要約すると、高速実行のために設計された、JavaScript言語仕様のサブセットという事だそうです。使用用途によるそうなのですが、実行速度をネイティブの半分程度を目標としているようですので、つまりLiquidFun.jsは、ams.jsで記述されているので、他のJavaScript制の物理演算エンジンに比べるとパフォーマンスがいい事が予測されます。

Box2DJSとLiquidFun.jsのパフォーマンス比較

実際にどれくらいのパフォーマンスが出せるのか、試してみないとピンと来ないと思いますので、同じ様な物理演算をBox2DJSとLiquidFun.jsにさせて、実行速度の違いが体感できる様にサンプルを作ってみました。

※サンプル下部の「Dynamic Circle Value」の値を変更すると、物理演算させる円の数を変更する事が出来ます。

▼ Box2DJS版
20160609_img2

▼ LiquidFun.js版
20160609_img3

環境によって体感は違うと思いますが、参考までに僕の環境では、Box2DJS版では処理落ちしないのはせいぜい400個程度でしたが、LiquidFun.js版では700個くらいまでは60FPS出せている感じですので、あくまで参考ですが、1.75倍くらいのパフォーマンスは出せると言ってもいいかもしれません。

Box2DJSとLiquidFun.jsのソースコード比較

次に実装してみて感じたソースコードの違いを比較してみました。

LiquidFunはBox2DJSをベースとした拡張ライブラリという事ですが、違う点がいくつかありますので、ピックアップしました。

LiquidFun.jsは、ネームスペースがない

以下の例を見ていただくとわかる通り、Box2DJSでは「Box2D.Dynamics.」といった様に、グローバルスコープを汚染することを防ぐためのネームスペースを用意していますが、LiquidFun.jsでは、そういたネームスペースはなく、クラスやメソッドの接頭に「b2」とついているだけです。だからと言ってそれほど不都合があるとも思いませんが。

▼ Box2DJS

var world = new Box2D.Dynamics.b2World(new Box2D.Common.Math.b2Vec2(0, 30), true); 

▼ LiquidFun.js

var world = new b2World(new b2Vec2(0, 30));

Box2DJSとLiquidFun.jsは基準点の位置とサイズの指定方法が違う

Box2DJSの演算対象要素の座標の基準点はその形状の中心となっていて、サイズも中心からの距離を指定します。変わってLiquidFun.jsの場合は、座標の基準点は左上となっていて、サイズもその形状の幅と高さを指定します。CSSと一緒でLiquidFun.jsの方が馴染みやすいですね。

20160609_img5

LiquidFun.jsは、デバッグ用の表示機能を有していない

Box2DJSは、デバッグ用の表示機能を有しています。ちょっと物理演算を試したいときなど、この機能を使えば、物理演算の結果を簡単に確認する事が出来ます。

▼ Box2DJS

var debugDraw = new Box2D.Dynamics.b2DebugDraw();
debugDraw.SetSprite(document.getElementById("canvas").getContext("2d"));
debugDraw.SetDrawScale(10);
debugDraw.SetFillAlpha(0.5);
debugDraw.SetLineThickness(1);
debugDraw.SetFlags(Box2D.Dynamics.b2DebugDraw.e_shapeBit);
world_.SetDebugDraw(debugDraw);

この機能をLiquidFun.jsは有していません。公式サンプルではthree.jsでWebGLを使って表示しているようですが、LiquidFun.jsをちょっと試してみたいときに、わざわざ物理演算結果を表示するための実装も行わなければならないので、めんどくさいというか、敷居が高いといいますか。物理演算エンジンとしてパフォーマンス重視で割り切りがいいとも言えるのでしょうか。

ちなみにパフォーマンス比較のLiquidFun.jsのサンプルでは、Box2DJSのデバッグ用の表示機能に合わせて、Canvasに描画しています。

その他

あとは、個人的にはBox2DはActionScript3.0に移植されていますので、ActionScript形式のDocumentがあり、とても見やすかったです。あくまで個人的な事ですが。LiquidFunはC++のドキュメントを見る事になります。

流体シミュレーションを実装サンプル

ではLiquidFunの本領発揮で、流体シミュレーションの実装を行いました。

20160609_img4

※サンプル下部の「Size」で流体のサイズ、「Density」で流体の密度、「Flags」で流体の性質を変更する事ができます。流体のサイズが大きく(Sizeの値が大きい)、密度が濃い(Densityの値が小さい)と、それだけ負荷がかかります。

b2ParticleGroupDef.flagsを変更する事で、流体にいろいろな性質を与える事が出来ます。

  • b2_waterParticle : 液体(デフォルト)
  • b2_elasticParticle : 軟体(ゼリー状の物体)
  • b2_powderParticle : 粒子(サラサラした砂のような感じ)
  • b2_viscousParticle : 粘稠(粘性のある液体)
  • b2_tensileParticle : 引張(それぞれが反発し合う感じ)
  • b2_colorMixingParticle : 色が混ざりあう液体

用意されている性質一覧

また、以下のように、性質を|演算子で結合することで、複数の組み合わせも可能になります。

var particleGroupDef = new b2ParticleGroupDef();
particleGroupDef.flags = b2_waterParticle | b2_elasticParticle

まとめ

軽量で高機能な2次元物理演算ライブラリ「LiquidFun.js」を触ってみました。流体を使わないとしても、パフォーマンスを考えると2次元物理演算ライブラリとして、LiquidFun.jsを使わない理由はないかもしれません。できればいろいろ試したいところですが、デバッグ用の表示がないので、表示を一から実装しなくてならず、すこし億劫な感じは否めませんが。

このライブラリで行った流体シミュレーションを実際に、液体などに見せるためには、表現上の工夫が課題になるかと思います。WebGLで描画しながら、GLSLを使ってごにょごにょといった感じなのでしょうか。さて、困りました。

なにはともあれ、流体シミュレーションは見ているだけで面白いですね。