簡単なニュートン物理を使って物体の移動軌跡を計算し、それに従って物体を動かすアニメーションを作成してみます。
スクリプト言語仕様のmodelaを使うと計算はお手の物ですが、ちょっと難しいところがあります。
物体の自由落下を計算してみたいと思います。ある物体があって、それが重力に引かれて地面へ向かって落下する、と想定します。
地上では、全ての物体は重力加速度:g=9.8[m/s2]で地面へ向かう加速度がついています。初速度v0=0[m/s]だった場合、時間:t[s]後には、落下速度:v(t)=v0+g×tとなります。
物体の初期座標:pz0、地面から上空へ向かう方向がz軸の正方向と仮定すると、pz=pz0−∫v(t) dtです。
この∫v(t) dtの計算が面倒ですね。
実は、今回の自由落下ではv(t)が比較的簡単な式であるため、数学的に計算して∫v(t) dt=1/2×g×t2とする方法もあるのですが、応用が利きにくいので、微小時間dt毎にvとpzを更新していく方法で座標を計算していきます。
今回は、30FPS(Frames Per Second)のアニメーションを作ろうと思いますので、微小時間dtを1/30[s]に設定します。現在時刻の落下速度:v(t)、z座標:pz(t)とし、この位置からdt時間後の座標をpz(t+dt)=pz(t)−v(t)×dt と近似します。同じくdt時間後の落下速度:v(t+dt)=v(t)+g×dt と、近似します。この近似はdt→0で実際の値に漸近します。
さて、v(t+dt)の計算も、pz(t+dt)の計算も、1つ前の時間におけるv(t)、pz(t)を使用することがわかります。
Xi-TRACEのアニメーションについては、このデータで解説しました。1フレームの計算の度にmodelaはプログラム全体を実行し、一旦終了してからレイトレーシングを実行します。modelaは毎回リセットされ、最初から実行しなおすため、nフレームの処理を実行する時には(n−1)フレームの計算結果はリセットされて消えてしまっています。つまり、v(t)とpz(t)を計算に使おうにも、無いわけです。
やむを得ず、毎回最初から計算をやり直す方法にしました。
毎回最初から計算しなおすため、後ろの方のフレームになればなるほど座標計算に時間を要します。対象記述は以下です。
var idx=0;
while (idx<fn)
{
c=500 - pz_#(idx2);
if (c<0)
{
// 天井の内部に居る間は、v0で等速直線運動
b=-g;
}
elif (c<mp)
{
// 天井から顔を出した辺りから、重力に引かれ始める
b=-g*(mp - c)/mp;
}
else
{
// 落下を始めると、空気抵抗を受ける
b=k*v_#(idx2);
}
a=g + b;
v_#(idx2)=v_#(idx2) + a/frate;
pz_#(idx2)=pz_#(idx2) + v_#(idx2)/frate;
idx=idx + 1;
}
落下物がn_drop(=64)ケあるので、pz_#(idx2)の様に配列変数もどきを使っています。
毎回最初から計算しなおすのはスマートではありませんので、もう少しスマートに取り扱えるよう、modelaの機能を拡張予定です。
シーンとしては、天井があって、そこから液体状の落下物がポトポト滴り落ちているイメージです。view命令で天地を反転させているので、逆に空へ登っていくような映像になっています。
// 天地を反転(上方向ベクタが(0,0,-1))しているので、落下物が
// 空へ登っていくように見える。
view 20, 0,0,-1, 0,0,500 - mp, 0,-500,400, 5;
完成したデータと動画をご覧に入れます。
Xi-TRACEデータ:drop.xitr
ちなみに、データ中、重力加速度:g=9.8を使いました。地球上と想定すると、単位は[m/s2]ですので、データ中の1という座標値は1[m]を表していることになります。落下物が天井を離れると、小さな球体になって落下しているのが見えると思いますが、この球の直径が1[m]です。思いのほか巨大な球体が落下している映像だったわけです。