CANNON.jsを使って3Dに物理演算を持ち込む

カテゴリ:WebGL 2013年12月 8日 09:09

さて、今回はGraphical Web Advent Calendar 2013の第7日目です。

Graphical Webということで、前回、スマホでも動く物理演算で転がる3Dサイコロを作ったのでその解説で紹介したサイコロのデモで使用したCANNON.jsについて書いてみたいと思います。

CANNON.jsとは

CANNON.jsは、物理演算を提供してくれるいわゆる「物理エンジン」ライブラリです。
3D向けの物理エンジンライブラリはいくつかあり、有名なのは「ammo.js」というものがあります。
これは、物理エンジンの(おそらく)デファクトスタンダードとも呼べる「Bullet(弾丸)」というエンジンがあり、それをJavaScriptに変換したものです。
余談ですが、Bulletに対してammoは「弾薬」という意味です。そしてCANNONは弾丸より強くしたいということでカノン(大砲)という名が付けられたそうです。

CANNON.jsのデモもあるので、どんなものかすぐに見ることが出来ます。

物理エンジンを使うための知識

物理エンジンとは、物体が衝突したら跳ねたり回転したり、といった物理的な挙動を計算によって導き出してくれるエンジンです。
ゲームなどで使われるリアルタイムシミュレーションでは、それぞれの物体を「剛体(RigidBody)」と呼び、変形することのない物体として扱います。(瞬間的に見れば変形していないと見なせるためです)

そして剛体には位置、回転などの「姿勢情報」と、反発係数(restitution)や摩擦係数(friction)など剛体の「性質」、そしてどんな「」か、という情報が必要です。
物理エンジンを使う上で、このあたりは知っておく必要があるでしょう。

それから、大事な決め事として大体の物理エンジンは「KMS」という単位で計算が行われます。つまり、「キログラム、メートル、秒」です。
もし仮に、画面に表示するのと同じサイズ(例えば200)を剛体に設定すると、それは200m級の超巨大な物体になります。400とかを指定したら、それこそ東京タワーが落ちてくるようなサイズ感になってしまいます。

これを意識しておかないと、思ったんと違う!という結果になったりします。特に3Dは単位が存在しないため、1という数字がどれくらいのものか、というのは自分で決める必要があります。

CANNON.jsのセットアップ

さて、前提知識を書いたところで、実際にCANNON.jsを使ったデモを作ってみます。実際に作ったデモはjsdo.itに上げてあります。

なにはともあれ物理演算ワールドを作る

Three.jsでも同様ですが、まずは物理演算の対象となる剛体を管理するための「物理演算ワールド」を作成します。

いくつか設定項目がありますが、それぞれ上記の設定でほぼ問題ないでしょう。
意味を説明すると、gravityはそのまま重力ですね。重力加速度は9.8m/s^2というのは知られていると思います。それを設定しています。

続いてbroadphase(ブロードフェーズ)とは、ざっくり言うとぶつかっている「可能性のある」剛体同士を見つける作業です。
物理演算はとても処理の重い計算です。すべての物体を総当りで計算していては速度が間に合いません。
そのために、まずは衝突している可能性のある剛体同士を見つけ、その後のフェーズ(ナローフェーズと呼ばれます)で本当に衝突しているのか、衝突していたらどれくらいめり込んでいるのか、という詳細情報を計算します。(ちなみにざっくりとした物理エンジンの動作の仕組みはQiitaに「剛体シミュレーションをまとめてみる」という記事を書いたので、興味がある人は見てみてください)

次のiterationsは反復計算回数です。リアルタイムシミュレーションは実はだいぶ適当な、間引いた処理をして高速化を実現しています。
そのため不正確さがどうしても出てきてしまいます。それを、この反復計算で繰り返し計算を行うことで誤差を縮める、ということをしています。
大体5〜10回程度で不正確さは収束するようです。この間の数値を指定しておけばいいでしょう。(当然ですが、回数を増やすだけ誤差は減りますが、計算回数が増えるため処理が重くなります)

最後のtoleranceは「許容値」という意味の英単語です。これも不正確さに関する設定ですが、ある程度の誤差を許容することで不安定さを解消する役割があります。この許容値をどれくらいにするか、という設定です。

以上で、物理演算ワールドの設定は終わりです。あとは、計算対象となる剛体を作り、それをどんどん追加していきます。

剛体を作る

物理演算ワールドが作れたので、次は物理演算される対象となる剛体を作っていきます。
今回はシンプルにするため、地面と箱のみを作ります。

物理エンジンを使う上で必要な知識、ということで説明した通り、剛体には位置の他に形や性質を指定する必要があります。
そのため、まず「どんな形(shape)か」という情報を定義し、それと質量(mass)を指定して剛体を作ります。(new CANNON.RigidBody(mass, shape)

CANNON.jsでは、質量0の剛体をSTATICとして扱います。つまり衝突しても動かない剛体ですね。今回は地面をそれにしています。
そして落下してくる箱を定義します。こちらは質量1なので衝突したら跳ね返ったり回転したりするようになります。

生成した箱は、初期位置をY軸の10として設定しています。(body.position.y = 10;
また、初期の回転速度をZ軸に対して10だけつけています。(body.angularVelocity.set(0, 0, 10);
最後のbody.angularDamping = 0.1;は、ダンパ(減衰率)を0.1に設定しています。これを設定することで、時間が経つにつれて徐々に回転が遅くなっていきます。小さめに設定しておけば空気抵抗のような感じになりますね。

さぁ、これで準備が整いました。これを実行してみると・・画面はなにも変化しませんw
当然ですが、物理演算の結果はただの数値でしかありません。人間が見れるものになっていないんですね。
なので、結果を画面に出力してやる必要があります。今回の例では、Three.jsを使って3Dのレンダリングを行っています。

Three.jsと相性のいいCANNON.js

CANNON.jsのREADMEを読むと書いてありますが、Three.jsをインスパイアしていて、Three.jsととても相性がよく出来ています。
上記で書いた画面への出力ですが、Three.jsの剛体を生成しておけば、以下のようにするだけで簡単に位置と回転を反映することが出来てしまいます。

見たままですねw
ちなみにquaternion(クォータニオン)は四元数と呼ばれる、回転を表すのに都合のいい概念です。興味がある人は調べてみるといいでしょう。

物理演算ワールドの時間を進める

さて、これで本当に準備が整いました。
あとは作った物理演算ワールドの時間を進めるだけです。時間を進めるにはworld.step(timeStep);というように、stepメソッドに進める時間を秒で指定します。基本的には60FPS相当である0.016(16ミリ秒)を指定すればいいでしょう。

world.stepを一回実行するとその時間だけ時が進みます。setTimeoutやrequestAnimationFrameなどのループを使って時間を常に進めていけば、デモのようにリアルタイムの動きを実現することができるようになります。

いかがでしょう。基本的な部分だけであれば、それほどむずかしくなく物理エンジンを組み込むことが分かってもらえたのではないかと思います。
今回は箱がひとつだけでしたが、何個も箱を作ればそれぞれが衝突しあって、リアルな動きを再現してくれます。
やはり3Dを使うからには物理エンジンを導入してリアルな動きを見せたいですね。

ちなみにjsdo.itに上げたデモはちょっとだけ追加して、クリックすると上方向に箱が持ち上がるようになっています。
うまくクリックを調整すると箱がずっと浮いた状態になります。物理エンジンを使えば簡単なゲームも発想次第で作れそうです。

この記事のカテゴリー一覧を見る⇒WebGL

  • このエントリーをはてなブックマークに追加

トラックバックURL

http://css-eblog.com/cgi-bin/mt/mt-tb.cgi/220