暫く、部屋の気温・湿度を測定してログを取る、電子工作に嵌まっていたので、温度計をモチーフにモデリングしてみました。
電子工作については、「Arduinoを使った電子工作の部屋」に詳しく記録してあります。
温度計をモデリングしてはみたものの、そこからどうしようか?と手が止まりました。温度計って、ダイナミックに動くものでもなし、作ったはいいけれど、何も進展がありません。
そこで、お湯を沸かしてみることにしました。今まさに沸騰しようとしている、お湯の温度を測定しているところ、なら、だいぶ速く温度が変化してもつじつまは合いそうです。
小さめの片手鍋に水を入れて沸かしているところに温度計を差し込んでいる、という、理科の実験さながらの、日常生活ではあまりお目にかかれない情景となりました。
作者宅はIHコンロなので、モデリングしやすくて助かりました。ガスコンロだったら、炎のモデリングとか、まず、無理だったところです(1^^;)。
あと、磨き上げられた金属っぽい質感にするため、鍋には縦方向に細かい筋をマッピングしてみました。マッピングデータは、ランダムに濃淡の違う点を打っておいて、縦方向だけにぼかしをかけて生成しました。
// ランダムな点を打つ
var ix,iy;
iy=0;
while (iy<fhei)
{
ix=0;
while (ix<fwid)
{
pc=rnd (0.5) + 0.5;
col2pix pc,pc,pc, pc;
pset ix,iy, pc;
ix=ix + 1;
}
iy=iy + 1;
}
// 縦方向にぼかす(movdif)、他、調整
filter trimming, 0,0,2*fwid,3*fhei, 0;
window close, 1;
filter movdif, 0,fwid/4,false,false;
window close, 1;
filter whitebal, 0,true;
window close, 1;
bmap steel, 0,0,fwid - 1,fhei*2/3 - 1, fwid,fhei*2/3;
水が沸騰していく様子のモデリングですが、最初はただの水が張られただけの鍋からスタートです。鍋の中で一番温度が高くなっている、底の部分から沸騰し始め、気化した水が気泡になって上がってきます。最初は小さな泡がぽつぽつと、次第に大きな泡が沢山湧いてきます。
まず、泡の大きさ。ランダムに決めた泡の大きさ(半径)wrに対して、フレーム番号wfから決めた係数kkを掛け算して、時間の経過と泡の大きさを連動させました。
var kk;
kk=wf/(fn_pt1 - fps);
clip kk=0,1;
fprint fout,"# # # # #"$,,wf,wx,wy,wz,wr*kk;
時間の経過に伴って、少しづつ泡が大きくなって、沸騰している感じが表現できるはず。
さらに、沸騰が進むと、泡の数も増えるはず。ということで、泡が発生するフレーム番号をランダムに決めるとき、より後ろのフレームでの発生確率が上がるよう、生成した乱数を二乗(sq()関数)しています。
wf=floor ((1 - sq (rnd (1)))*fn_pt1);
鍋の底面がx-y平面上にあり、底面の中心座標が原点(0,0,0)に設定してあります。鉛直方向がz軸ですので、x,y座標は適当に乱数で決め、z座標は常に0(底面)から始めて、少しづつ上に登っていき、水面まで到達する座標を計算しています。
// 泡の初期座標
var wf, wx,wy,wz, wr;
var th;
wf=floor ((1 - sq (rnd (1)))*fn_pt1);
wr=rnd (PR - 10);
th=rnd (360);
wx=wr*cosd (th);
wy=wr*sind (th);
wr=rnd (5) + 5;
// 泡の軌跡計算
wz=0;
while (wz - wr<WH & wf<fn_pt1)
{
// wf, wx, wy, wz, wr
var kk;
kk=wf/(fn_pt1 - fps);
clip kk=0,1;
fprint fout,"# # # # #"$,,wf,wx,wy,wz,wr*kk;
wf=wf + 1;
// 継続
wz=wz + WH*2/fps;
}
水面まで到達した泡は、はじけて消えます。一応、以下のような感じで、水面に到達した泡がはじけて消える様子を表現しているのですが、残念ながら、30フレーム/s(FPS)のアニメーションでは、ほとんどわかりません……。
procedure bubble l,m,n, r
{
// 泡
// 中心座標(l,m,n)、半径r
// z座標nがWHを超えると泡がはじけ始め、n−rがWHを超えると消える。
var nr;
if (n<=WH)
{
// 水中。完全な球体
nr=r;
}
else
{
// 水面上。上の方から泡が消えていく
nr=r - 2*(n - WH);
}
para l,m,n;
and
{
sphere 0,0,0, r;
sphere 0,0,0, -(r - 0.1);
plane 0,0,nr, 0,0,1;
}
}
わざわざスローモーションにして見てみるほどのエフェクトでもないので、「自己満足」ということで……。
さて、最初にモデリングした「温度計」は隅に追いやられてしまいました。一応、温度が上がっていく様子を表現しているのですが、隅の方に小さく映っているだけなため、赤い液体が上がっていく様子がほとんど見えません。
どうしたものでしょう?仕方ないので、温度計のメモリ部分のアップ画像をレイトレーシングしてみました。
鍋を俯瞰した動画と、温度計に注目した動画を1つにまとめたいのですが、さすがにレイトレーシングだけで実現するのは無理。と、いう訳で、スクリプト(thermometer_comb.xitr)を使って、2つの動画を1つにまとめてみます。
本当は、「ワイプ」の様に、メインとなる動画の一部にサブ動画の窓をつけるように合成したかったのですが、さすがにそれをスクリプトでやるのはつらすぎるので、2つの動画から抜き出した2枚の画像を、各々適切な大きさにトリミングして、左・右に結合することで、1つの動画にまとめることにしました。
後にリンクするアニメーションデータをレイトレーシングすると、前半に鍋の俯瞰、後半に温度計のアップの動画が1本の動画になって完成します。フレーム(コマ)数は、20秒×30FPS×2(前・後半)=1200フレームの動画になっています。
前半部分と後半部分の切れ目は、ちょうど真ん中の600フレーム目なので、(1)1フレーム目と、600フレーム目を各々読み込み、(2)トリミングして、(3)結合し、(4)新たな動画としてセーブする、という手順を600回繰り返して合成します。
var idx;
idx=0;
while (idx<fn_all2)
{
// 前半のトリミング
window front_id, win1;
load $(avi_in), idx;
filter trimming, r11.x,r11.y,r12.x,r12.y, 2;
win12=win_id;
// 後半のトリミング
window front_id, win2;
load $(avi_in), fn_all2 + idx;
filter trimming, r21.x,r21.y,r22.x,r22.y, 2;
win22=win_id;
// 結合
window front_id, win22;
window front_id, win12;
filter joint, 0,1,1;
// AVIセーブ
save $(avi_out);
// 不要なウインドウを閉じる
window close, 0;
window close_id, win12;
window close_id, win22;
// 継続
idx=idx + 1;
}
大したフィルタをかけているわけでもないのに、さすがにフルHD、600フレームを処理すると、そこそこ時間がかかります。
完成したデータをリンクしておきます。
1) | Xi-TRACEデータ | thermometer.xitr |
2) | Xi-TRACEスクリプト | thermometer_comb.xitr |
1)でレイトレーシングして、動画ファイルを生成します。この動画は、前半に鍋の俯瞰動画、後半に温度計のアップ動画が入っていますので、2)を使って2つを1つの動画に合成します。出来上がった動画が下↓です。
そもそも、最初にモデリングした「温度計」は、すっかり脇役になってしまいました……。まあ、モチーフが静止画向き、だったのでしょうね……。