HTML5による物理シミュレーション環境の構築 ~WebGLライブラリThree.js 入門(3/3)~
本稿では新しいWEBページ用の言語であるHTML5+WebGLを利用して、 ウェブブラウザ上で、物理シミュレーションの計算と描画をリアルタイムに実行する「物理シミュレータ」の作成までの手順を記述します。 本稿では、物理シミュレータの例として、任意の初期条件に対する2重振り子の時間発展を4次のルンゲクッタ法を計算する「2重振子シミュレータ」を構築します。また、本稿では WebGL を簡単に利用することができるWebGLライブラリとして著名な Three.js を用いています。 物理や工学とは無関係に、初めて Three.js を利用する人がチュートリアルとしても利用できることも意図しております。 パート1では、主に Three.js の使い方について触れました。 パート2では、拘束力のある運動の代表例である単振子を取り上げ、物理演算結果のリアルタイム描画+物理量の時間発展の様子をグラフ化します。最後の本パートでは、単振子シミュレータにさらに機能を付け加え、最後に2重振り子シミュレータへと発展させます。 (※)グラフ描画はflotr2 というJavascriptライブラリを利用します。
目次
パート1(すべて表示)
- HTML5とWebGL
- 3次元物理シミュレーション:2重振子シミュレータの作成
- Three.jsの動作確認
- Three.js によるアニメーション
- Three.js によるマウスイベント
- 本稿でで参考にしたページ
パート2 (すべて表示)
- 仮想物理実験室の構築
- 支柱・おもり・ひもの描画
- ニュートンの運動方程式
- 微分方程式の数値解計算アルゴリズム(4次のルンゲクッタ法)の実装
- インターフェースの実装(物理量の出力とボタンと設置)
- グラフの描画(Javascript ライブラリ flotr2 の使い方)
- 物理量のリアルタイムグラフ描画
- おまけ
パート3
単振子シミュレータへの機能の追加
本稿の最終目的である2重振子シミュレータを作成することが目的ですが、
その前に単振子シミュレータに次の機能を追加します。
物理シミュレータというからには
(1)任意の初期パラメータを設定して、シミュレーションを再スタートできる機能
(2)任意の時刻までの計算結果(数値データ)の出力機能
が必要です。本章では上記2つの機能を HTML と Javascript を利用して実装します。
また、シミュレータの性能とは関係ないですが、「背景テクスチャとフォグ」Three.js の機能を利用して追加します。
再スタート時のパラメータの設定
物理シミュレータというためには、任意の初期パラメータに対する運動をシミュレーションできる必要があります。
HTMLには 「input 要素」とよばれるテキストデータを入力するためのインターフェースが用意されてるので、
入力した数値を初期パラメータとしてシミュレーションを実行します。
その実装のために必要な手順は次のとおりです。
(1)HTML文書に初期パラメータ入力用の「input 要素」とリセットボタンの追加
(2)初期パラメータ再設定用関数の定義
(1)HTML文書に初期パラメータ入力用の「input 要素」とリセットボタンの追加
body 要素に、初期パラメータ入力用の「input 要素」とリセットボタンの追加を追加します。 具体的には、球の位置(x,y,z)、速度(vx,vy,vz)並びに質量(m)を入力する input 要素によるフォームと、「button 要素」を用いてリセットボタンを準備します。button 要素には、クリック時実行する javascript 関数「resetButton()」を指定します。
<h2>再スタート:<button onclick="resetButton();">リセット</button></h2> <table> <tr> <td>球の座標[m]</td><td>(<input type="text" id="ix" value="0" />,<input type="text" id="iy" value="200" />,<input type="text" id="iz" value="300" /> )</td> </tr> <tr> <td>球の速度[m/s]</td><td>(<input type="text" id="ivx" value="0" />,<input type="text" id="ivy" value="0" />,<input type="text" id="ivz" value="0" /> )</td> </tr> <tr> <td>球の質量[kg]</td><td><input type="text" id="im" value="1" /></td> </tr> </table>
(2)初期パラメータ再設定用関数の定義
(1)で指定したリセットボタンクリック時に実行する関数「resetButton()」を定義します。 フォーム入力された値を、Ballクラスのオブジェクト「ball」のパラメータに設定します。 その際に時刻のリセット(t=0)とグラフデータの初期化(d1=[], d2=[])も行います。
function resetButton(){ t=0; ball.m = parseFloat(document.getElementById("im").value); ball.x = parseFloat(document.getElementById("ix").value); ball.y = parseFloat(document.getElementById("iy").value); ball.z = parseFloat(document.getElementById("iz").value); ball.vx = parseFloat(document.getElementById("ivx").value); ball.vy = parseFloat(document.getElementById("ivy").value); ball.vz = parseFloat(document.getElementById("ivz").value); d1 = []; d2 = []; }
実行結果
下記のフォームに任意の初期値を入力して、「リセット」ボタンを押してみてください。 その際に注意が必要なのは、初速度を拘束と矛盾しないように設定する必要があります。 例えば、球の位置を支柱(z=300)と水平(例 (0,200,300))に設定した場合、 初速度に水平成分(例えば v=(0,1,0))を与えると、時間と共にひもの長さが長くなり発散してしまいます。 これは、アルゴリズム導出時の条件を破ってしまうために起こります。
以下はデモページのキャプチャ画象です → デモページへ
計算結果(数値データ)の出力
物理シミュレータというためには、せっかく計算した結果を出力することが必要となる場面もあることも考えられます。 Javascript の「File API」を利用します。 「File API」は、ファイルの入出力を行うことができるAPIで、 HTML5 と同様 W3C によって規格化されており、多くのブラウザで実行することができます。 本節では、グラフに描画している単振子の「ひもの張力」とおもりの「z座標」の時系列の計算結果をスペース区切りで出力します。 計算結果を記録する配列「file_data」を宣言し、グラフ描画用データと同じようにデータをセットします。
function loop() { (省略) file_data.push([t, ball.tension(), ball.z]); (省略) }
セットした各時刻のデータを取り出し、スペース区切りに加工し、さらに連結します。 出力用データをBlob型オブジェクトとして扱い、Javascript で別ページに出力することを行います(Chromeでは直接ファイル出力が行われます)。
function f_write(){ if(!pause) startPauseButton(); //シミュレータをストップ var str = ""; //出力用文字列の宣言 for (var i = 0; i < file_data.length; i++) { var b = file_data[i]; //時刻ごとのデータを取り出す str += b[0] + " " + b[1] + " " + b[2] + "\n" //スペース区切りに加工 } var blobBuilder; //Blob型データ用のオブジェクトを宣言 if ("MozBlobBuilder" in window) { blobBuilder = new MozBlobBuilder(); } else if ("WebKitBlobBuilder" in window) { blobBuilder = new WebKitBlobBuilder(); } blobBuilder.append(str); //Blob型データ用のオブジェクトにデータを付加する if (window.URL) { window.open(window.URL.createObjectURL(blobBuilder.getBlob()) , "New Window", ""); } else if (window.webkitURL) { window.open(window.webkitURL.createObjectURL(blobBuilder.getBlob()), "New Window", ""); } document.getElementById("download").innerHTML = 'ファイルが出力されました (t='+ t +')'; }
以下はデモページのキャプチャ画象です → デモページへ
【参考】JavaScriptでファイル操作!? File APIを使いこなそう(連載:人気順に説明する初めてのHTML5開発)
背景テクスチャとフォグの追加
単振子シミュレータの機能とは直接関係ないですが、背景が白だと寂しいので背景テクスチャを用意します。 また、OpenGL(WebGL)にも用意されているフォグ機能(遠くに行くほど物体が白く霞んでいく)を追加します。 Three.jsでも1行で実装することができます。
function initObject(){ var sky = new THREE.Mesh( //背景描画用オブジェクトを宣言 new THREE.SphereGeometry( 1000, 20, 20 ), //球型の形状(半径:1000, 分割数20,20) new THREE.MeshBasicMaterial({ color: 0xffffff, //基本色の設定 map: THREE.ImageUtils.loadTexture( "t1.png" ) }) //マッピングする画像データの設定 ); sky.flipSided = true; //球の内側(裏面)を利用する scene.add( sky ); //シーンに追加 scene.fog = new THREE.FogExp2( 0xffffff, 0.0002 ); //フォグをシーンに追加する(引数:フォグの色, フォグの濃さ) (省略) }
以下はデモページのキャプチャ画象です → デモページへ
単振子のオブジェクト指向的構造化
ここまでで、単振子シミュレータの機能追加は終わりです。 本節では次節に2重振り子を仕上げるために、単振子をオブジェクト指向言語的な構造化を行います。 単振子を表す「Pendulumクラス」のメンバとして、支柱を表す「Pollクラス」とおもりを表す「Bollクラス」のオブジェクトを持ちます。 各クラスのプロパティとメソッドは下の図のとおりです。
Pollクラス | プロパティ | x,y,z | 位置座標 |
object | Three.jsの Meshクラスのオブジェクト | ||
Ballクラス | プロパティ | x,y,z | 位置座標 |
vx,vy,vz | 速度の各成分 | ||
object | Three.jsの Meshクラスのオブジェクト | ||
Pendulumクラス | メンバ | BallクラスとPollクラスのオブジェクト | |
メソッド | RungeKutta4() | ルンゲ・クッタ法にて計算 | |
fx(t, x, y, z, vx, vy, vz) fvx(t, x, y, z, vx, vy, vz) fy(t, x, y, z, vx, vy, vz) fvy(t, x, y, z, vx, vy, vz) fz(t, x, y, z, vx, vy, vz) fvz(t, x, y, z, vx, vy, vz) | ルンゲ・クッタ法の計算時に利用する関数 | ||
tension() | 張力を計算 | ||
write() | HTML文書へプロパティを書きだす |
以下はデモページのキャプチャ画象です → デモページへ
2重振子のオブジェクト指向的構造化
いよいよ、本稿の最終目的である2重振り子シミュレータの作成です。 計算アルゴリズムは「ラグランジュ未定乗数法を用いた2重振子のシミュレーション」をご覧下さい。 単振子と同様にオブジェクト指向言語的な構造化を行います。 2重振り子を表す「Pendulum2クラス」のメンバとして、支柱を表す「Pollクラス」とおもりを表す「Bollクラス」のオブジェクトを持ちます。 ただし、2重振子はおもりを2つ持つため「Bollクラス」のオブジェクトは2つとなります。 各クラスのプロパティとメソッドは下の図のとおりです。
Pollクラス | プロパティ | x,y,z | 位置座標 |
object | Three.jsの Meshクラスのオブジェクト | ||
Ballクラス | プロパティ | x,y,z | 位置座標 |
vx,vy,vz | 速度の各成分 | ||
object | Three.jsの Meshクラスのオブジェクト | ||
Pendulum2クラス | メンバ | BallクラスとPollクラスのオブジェクト | |
プロパティ | L01, L12 | 支柱からおもり1とおもり1からおもり2のひもの各長さ | |
cos12 | ひも1とひも2とのなす角のcos | ||
lambda1, lambda2 | ラグランジュの未定定数 | ||
S01, S12 | ひも1、ひも2の張力 | ||
T, V, E | 運動エネルギー、ポテンシャルエネルギー、力学的エネルギー | ||
tension01_max tension12_max | ひも1、ひも2の最大張力 | ||
メソッド | RungeKutta4() | ルンゲ・クッタ法にて計算 | |
x1(t, m1, x1, y1, z1, vx1, vy1, vz1, m2, x2, y2, z2, vx2, vy2, vz2) fvx1(t, m1, x1, y1, z1, vx1, vy1, vz1, m2, x2, y2, z2, vx2, vy2, vz2) fy1(t, m1, x1, y1, z1, vx1, vy1, vz1, m2, x2, y2, z2, vx2, vy2, vz2) fvy1(t, m1, x1, y1, z1, vx1, vy1, vz1, m2, x2, y2, z2, vx2, vy2, vz2) fz1(t, m1, x1, y1, z1, vx1, vy1, vz1, m2, x2, y2, z2, vx2, vy2, vz2) fvz1(t, m1, x1, y1, z1, vx1, vy1, vz1, m2, x2, y2, z2, vx2, vy2, vz2) fx2(t, m1, x1, y1, z1, vx1, vy1, vz1, m2, x2, y2, z2, vx2, vy2, vz2) fvx2(t, m1, x1, y1, z1, vx1, vy1, vz1, m2, x2, y2, z2, vx2, vy2, vz2) fy2(t, m1, x1, y1, z1, vx1, vy1, vz1, m2, x2, y2, z2, vx2, vy2, vz2) fvy2(t, m1, x1, y1, z1, vx1, vy1, vz1, m2, x2, y2, z2, vx2, vy2, vz2) fz2(t, m1, x1, y1, z1, vx1, vy1, vz1, m2, x2, y2, z2, vx2, vy2, vz2) fvz2(t, m1, x1, y1, z1, vx1, vy1, vz1, m2, x2, y2, z2, vx2, vy2, vz2) | ルンゲ・クッタ法の計算時に利用する関数 | ||
cul(t, m1, x1, y1, z1, vx1, vy1, vz1, m2, x2, y2, z2, vx2, vy2, vz2) | 各プロパティを計算 | ||
tension() | 張力を計算 | ||
write() | HTML文書へプロパティを書きだす |
以下はデモページのキャプチャ画象です → デモページへ
まとめ
お疲れ様でした。 これで、Three.jsを利用したWebGL による物理シミュレーション環境の構築するまでの手順を理解できたと思います。 HTML を利用することで、ウェブブラウザがこれまで培ったインタラクティブ性を活用した力学系のシミュレーションの計算と、Three.js による描画が容易であることもわかりました。 また、Javascriptによる物理演算の実行速度が想定したよりも自分が想定してよりも相当早く、60回/秒の描画のタイミングに対して、500~1000単位時間ステップを計算しても、処理落ちを実感しなかったほどです。 これにあわせて、ウェブブラウザのクロスプラットフォーム的優位性と、WebGL による精密な描画が加われば、これまでに想定していなかった用途が生まれる可能性を感じました。 今回はシミュレータとして利用しましたが、他にも例えば、大学教育などにおけるインタラクティブな教材、計算結果の3次元ビューア(プロッター)などへの利用が考えられます。File API を利用するとブラウザの特定の箇所に計算結果のファイルをドラック&ドロップするだけで、3次元描画してくれるツールなんかも簡単に作れそうです。 物理や工学の分野において、計算結果を描画ツールは「gnuplot」が有名ですが、重いデータの3次元プロットを行うと、マウスにて視点を移動しようとした時に、描画時間が異常に掛かったり、場合によっては、描画にすら失敗することもあります(自分の場合)。 WebGLはマシンの GPU に描画すべきデータを予め渡すことができるので、一度データを GPU 側に送ることが出来れば、快適な操作性を実現できるのではないかと考えられます。さらには、Javascript なので自分で簡単にカスタマイズできるのも魅力となるかもしれません。 とは言え、本サイトではいろんな物理現象をWebGL(Three.js)を利用して、描画してみたいと思います。また「Web Workers」を利用することで、Javascript による計算を並列化できるみたいなので、次回はそれに挑戦してみたいと思います。
本稿でで参考にしたページ
Three.js について
- ■ FHTR.ORG(Ilmari Heikkinen氏)のBasics of Three.js
- ■ 本家のサンプル:three.js examples
- ■ THREE.js で WebGL(小山田晃浩氏)
- ■ JavaScript 3DレンダリングエンジンのTHREE.jsを試す(takuru氏)
- ■ WebGLが熱い(santarh 氏)
- ■ THREE.js Doc(Yasushi ANDO 氏)
WebGLについて
- ■ Learning WebGL(Giles氏)(日本語翻訳のページ(Hack The WebGL (WebGL勉強会)))
- ■ WebGL(Wikkipedia)
- ■ Demo Repository
Javascriptについて
- ■ 第4回 JavaScriptでオブジェクト指向プログラミング(山田祥寛氏)
- ■ JavaScriptでファイル操作!? File APIを使いこなそう(連載:人気順に説明する初めてのHTML5開発)
その他
- ■ 天体テクスチャ:Planet Earth Texture Maps
- ■ HTML5:次世代 HTML 標準 HTML5 情報サイト(有限会社futomi社)