
【フリーのJSライブラリ、LiquidFunでサイエンス vol.3】異星の海を見てみよう~世界の重力と粒子の密度を変えてみる〔コード編〕~
あまり身近でなかった「液体」のシミュレート画像を見る機会が増えてきました。
液体のふるまいをシミュレートする手法のひとつが「粒子法」ですが、この分野でのフリーソフトはまだ少なく、アマチュアがちょっと試してみるのが難しいのが悩みです。
「LiquidFun(リキッド・ファン)」はGoogle社が提供する、粒子法によるシミュレートが可能な数少ないフリーソフトで、JavaScriptという言語で表記することで、アマチュアでも手軽に操る事ができます。
今回は、以下の記事でトライした現象のコードをご紹介します。
本記事では実装コードをご紹介していますが、様々な理由により、この説明通りにいかない場合がしばしばあります。申し訳ありませんが、自己責任・自己解決でお進めくださるよう、お願いいたします。
(再掲)LiquidFunの入手先とベースとなるプログラム
この記事では、LiquidFunのJavascript版をダウンロードして、このサンプルプログラムを修正する、というアプローチでコードを実装します。
同梱されているC言語のソースを使ったり、他のグラフィックスライブラリと連携させる事も可能な様ですが、上記が結果を得る最もシンプルな方法です。
以下の記事にもありますが、前準備を簡単に再掲します。
LiquidFunの入手先
LiquidFunは、以下のサイトで無料公開されています。
上記ページの下の方に、ダウンロード用のリンクがあります。
ここから、liquidfun-1.1.0.zip をクリックしてZIPファイルをダウンロードします。

ダウンロードしたZIPファイルを解凍すると、以下のフォルダが展開されます。

上記のフォルダの内、一番下にある、liquidfun というフォルダを開きます。

上記の一番上にある Box2D というサブフォルダを開きます。

その中に、lfjs というサブフォルダがあります。
この lfjs をどこか適当なところにコピーしてください。
これだけでインストールは終わりです。
他にある多数のファイルは、Cでコンパイルしてより高速化をめざす場合のソースコードなどです。Javascript のコードしか利用しない場合は他は不要です。
改変するファイル
lfjsフォルダに入っている、INDEX.html というHTMLファイルをダブルクリックしてみてください。

index.html がブラウザ上でWEBページとして開かれ、以下の画面が表示されます。ここから様々なサンプルプログラムを実行できます。
今回は、一番上にある、Dam Break(水中崩壊)を修正して使います。(修正結果は、この画面から確認します)
Dam Break はこんなデモです。




このWEBページはスタンドアロンで参照するページとなります。
このデモのファイルの在処は以下となります。
lfjsフォルダの中にある testbed フォルダを開きます。

その中にある、tests フォルダを開きます。

ここにプログラム本体である、testDamBreak.js があります。

これをメモ帳などで編集します。
バックアップは取っておきましょう。
修正したプログラム
以下が、修正したプログラムです。コメントアウトした部分が、改変部です。
function TestDamBreak() {
//-----★カメラ★------
camera.position.x = 4;//def2
camera.position.y = 2;//def2
camera.position.z = 4;//def3
var bodyDef = new b2BodyDef();
//-----★重力★------
world.SetGravity(new b2Vec2(0, -1.4));//def(0,-10)|地上-10、タイタン-1.4
var ground = world.CreateBody(bodyDef);
var chainShape = new b2ChainShape();
//-----★底の形★------
chainShape.vertices.push(new b2Vec2(0, 0));
chainShape.vertices.push(new b2Vec2(4, 0));
//傾斜
chainShape.vertices.push(new b2Vec2(6, 1.3));
chainShape.vertices.push(new b2Vec2(6.5, 1.3));
chainShape.vertices.push(new b2Vec2(6.5, 0));
chainShape.vertices.push(new b2Vec2(8, 0));
chainShape.vertices.push(new b2Vec2(8, 4));
chainShape.vertices.push(new b2Vec2(0, 4));
chainShape.CreateLoop();
ground.CreateFixtureFromShape(chainShape, 0);
var psd = new b2ParticleSystemDef();
psd.radius = 0.022;//半径 def:0.025
psd.dampingStrength = 0.2;//減衰率 def:0.2
psd.density = 0.45;//def1か|水1、メタン0.42
psd.viscousStrength=0.3;//粘性 def:無し
var particleSystem = world.CreateParticleSystem(psd);
var shape = new b2PolygonShape;
//-----★粒子団の形★------
shape.SetAsBoxXYCenterAngle(1, 0.8, new b2Vec2(1.5, 1), 0);
var pd = new b2ParticleGroupDef();
pd.shape = shape;
//-----★粒子団の色を追加★------
pd.color.Set(0, 0, 255, 255);//◆粒子の色:R・G・B・濃さ
var particleSystem = world.CreateParticleSystem(psd);
var group = particleSystem.CreateParticleGroup(pd);
}
順に解説します。
なお、デモプログラムは、Box2Dという2次元物理エンジン(シミュレートアプリ)を組み合わせた形を取っており、プログラムの大半は、Box2Dに関する内容です。
ご注意
プログラム中の数値が、現実世界での、時間や距離とどう関係するか、一切確認できていません。(プログラムの1が現実世界の1mとか、実行時の経過時間1秒が現実世界の1分とか)
現段階ではすべて、相対的な評価(より高くなるとか、より時間がかかるなど)しか得られない点をご了承ください。
カメラの設定
後述する器の形が入る様、カメラの位置を変えます。
//-----★カメラ★------
camera.position.x = 4;//def2
camera.position.y = 2;//def2
camera.position.z = 4;//def3
重力の設定
重力は、物理系 world の子関数 SetGravity(2次元ベクトル) で設定します。2次元ベクトルは、b2Vec2(右方向、上方向)という関数で指定しますので、コードは、以下の様に記述します。
SetGravity( b2Vec2(右方向、上方向))
デフォルトは、おそらくですが(0,-10)のようですので、タイタンでは、この1/7の(0,-10)で設定します。(2つ目がマイナスなので下向きに重力が働いている事を示します)
//-----★重力★------
world.SetGravity(new b2Vec2(0, -1.4));//def(0,-10)|地上-10、タイタン-1.4
重力の設定は以上です。
器の形
器は、デモでは chainShape という折れ線のオブジェクトで定義されていて、このオブジェクトに頂点を vertices.push(追加するベクトル) という子関数で追加してきます。
具体的には以下コードで記述します。
chainShape.vertices.push(new b2Vec2(x座標, y座標));
この中身を修正します。
浜辺を模した傾斜部のある形にしました。また、横に長くしました。
var chainShape = new b2ChainShape();
//-----★底の形★------
chainShape.vertices.push(new b2Vec2(0, 0));
chainShape.vertices.push(new b2Vec2(4, 0));
//傾斜
chainShape.vertices.push(new b2Vec2(6, 1.3));
chainShape.vertices.push(new b2Vec2(6.5, 1.3));
chainShape.vertices.push(new b2Vec2(6.5, 0));
chainShape.vertices.push(new b2Vec2(8, 0));
chainShape.vertices.push(new b2Vec2(8, 4));
chainShape.vertices.push(new b2Vec2(0, 4));
chainShape.CreateLoop();
粒子の密度など
タイタンの海はメタンで、密度が水の0.45程度でした。
粒子系のオブジェクトは、psd ですが、このアブジェクトの属性で、密度は指定できます。(ただしLiquidFunで指定できるのは1つの値のみです)
密度は、density という属性で指定できます。
デフォルトでは1の様なので、仮にこれを水とみて、0.45を代入しました。
また、他の属性として、粒子半径 radius、減衰率(くずれ易さ?)dampingStrength、粘性 viscousStrength なども指定できます。
これらについての知識を持ち合わせていないのですが、”波っぽく”見える値を試行錯誤で選びました。(この3つは、あまり物理的な妥当性はありません)
psd.radius = 0.022;//半径 def:0.025
psd.dampingStrength = 0.2;//減衰率 def:0.2
psd.density = 0.45;//def1か|水1、メタン0.42
psd.viscousStrength=0.3;//粘性 def:無し
粒子団(水塊)の色と形
デモでは、粒子団(水塊)は、 pd というオブジェクトで与えられています。
粒子団の形を少し変え、大きな塊をやや高い所から落とす様にしました。
粒子団の形は、SetAsBoxXYCenterAngle(幅、高さ、中心ベクトル) という子関数で与えます。
また、若干見た目に気を遣って、粒子団の色を設定しています。粒子団の色は、color.Set(赤、緑、青、濃さ) という子関数で与えます。
//-----★粒子団の形★------
shape.SetAsBoxXYCenterAngle(1, 0.8, new b2Vec2(1.5, 1), 0);
・・・
pd.shape = shape;
・・・
//-----★粒子団の色を追加★------
pd.color.Set(0, 0, 255, 255);//◆粒子の色:R・G・B・濃さ
以上で、プログラムの改変ができました。
比較となる地上でのシミュレートは、重力の値を(0,-10)に、粒子密度を1にすると確認できます。
プログラムの実行
デモ起動画面 index.html から実行してみましょう。

初期状態は、この様な形になります。

地上でのパラメータ
重力と粒子密度を地上での値とした場合は、以下の様になるはずです。




タイタンでのパラメータ
タイタンでは非常にゆっくりと水塊が崩れます。

しぶきも高く舞い・・・

浜辺を波がより遠くまで駆け上がります。


右端の潮だまりは、地上での場合より深くなっています。

すべてがスローモーションの様にすすみました。
現実世界でも、低重力の異星ではこういった波の立ち方をするのでしょうか。
