金平糖をモデリングしてみたいと思います。
金平糖と言えば、カラカラと回転する釜に、核となるザラメを投入、糖蜜をかけながら、2日〜14日(大きさによって異なる)もかかるそうです。
この製法で、あの独特な角(つの)はどういうメカニズムでできるのでしょう?
冒頭にも書きましたが、あの角は、どうやって、出来て行くのでしょうか?レイトレーシングで金平糖をモデリングする際にも、カギとなるのは、角の作り方でしょう。
例えば、経度・緯度方向に規則的に角を生やしたとしても、金平糖に見えないであろうことは火を見るより明らかです。
まず、角を生やす場所(座標)を表現する方法を決めておきます。
上図の様に、経度方向の角度s(-180≦s≦180)、緯度方向の角度t(-90≦t≦90)で球面上の座標を特定します。(s, t)と、球面の半径rがあると、通常の三次元座標(x, y, z)に変換することができます。
procedure calc_st2xyz $$xx,$$yy,$$zz, ss,tt,rr
{
// (s, t, r)座標から(x, y, z)座標に変換して返す。
var r2;
$(yy)=rr*sind (tt);
r2=rr*cosd (tt);
$(xx)=r2*cosd (ss);
$(zz)=r2*sind (ss);
}
極方向にy軸、経度の原点がx軸になっています。図示していませんが、z軸はx軸、y軸に垂直な方向にあります。
乱数を使って座標値(s, t)を決定すると、ある程度ばらけた座標を得ることができます。ただ、その性格上、上下の極付近の密度が高くなり、赤道付近の密度が低くなる傾向があります。
そこで、もう一手間かけて、密度が一定になるような座標を求めてみます。
1つの球面上にN個の角を生成しようとして、N個の座標(si, ti)、i=0〜(N−1)をランダムに生成したとします。注目するj番目の座標(sj, tj)について、他のすべての座標との距離Liを計算し、距離の総和Lを求めます。
各座標間の距離は、正確には球面上の距離(球面を地球儀に見立てた時に飛行機が最短で飛ぶ距離)が理想ですが、単純化して2点間の距離で十分だと思われます。
(si, ti)→(xi, yi, zi)は、手続きcalc_st2xyzで求める
Li=√(sq(xi−xj)+sq(yi−yj)+sq(zi−zj))
L=ΣLi (i=0〜(N−1)、i=jを除く)
要は、求めた距離の総和Lがなるべく大きくなるように、(s, t)を決定すれば良いわけです。「隣接する座標値から離れたい」という要求を強調するため、距離の大小を反転した上で、二乗して加算する方法を取ります。
(si, ti)→(xi, yi, zi)は、手続きcalc_st2xyzで求める
Li=√(sq(xi−xj)+sq(yi−yj)+sq(zi−zj))
L’=Σsq(2−Li) (i=0〜(N−1)、i=jを除く)
変わったのは最後のL’を計算しているところです。球面の絶対的な大きさ、半径rは計算に影響を与えないので、r=1と考えて進みます。すべての座標が球面上の点である以上、一番遠く離れた場合(直径に相当)で、Li=2×r=2となります。よって、2−Liは、一番離れた場合が0(最小値)、全く同じ座標に重なった時が2(最大値)となります。これを二乗(sq(x)=x×x)しているので、隣り合う座標値との距離が近ければ近いほど、L’の値は大きくなります。先ほど(L)とは大小関係が逆転して、L’をなるべく小さくすることが目的となっていることに注意してください。
さて、着目する座標値(sj, tj)に戻りましょう。この座標値に対して、微小な変位量(ds, dt)を設定し、新たに座標(sj+ds, tj+dt)を考えます。
L1 ← (sj, tj)に対して求めたL’
L2 ← (sj+ds, tj+dt)に対して求めたL’
L’の値をなるべく小さくすることが目的ですので、もし、L1>L2であれば、(sj+ds, tj+dt)をj番目の座標として更新します。
(sj, tj)=(sj+ds, tj+dt)
(ds, dt)をランダムに設定しながら、全てのj=0〜(N−1)に対して何回か繰り返し実行すると、互いになるべく遠くに離れようとする力が働いた結果、密度が均一となる場所に落ち着くはずです。
前節で説明した方法で求めた座標に対して、金平糖の角を生やすと、モデリングが完成するはずです。円錐面(cone)や、楕円放物面(paraboloid)などを使って角を形成するのも、デフォルメされた感じで面白いかもしれませんね。
今回は、もう少しリアルな金平糖に近い形状になるよう、メタボールを使ってみます。メタボールの制御は、中々難しくて、「こうすればOK」という断定的なことを示すことは困難です。作者自身、パラメータを少し修正してはレイトレーシングしてみて、金平糖らしく見えるパラメータを模索しました。
なかなかかわいらしいモデルになったのではないか?と思っているのですが、いかが?
基本的には、砂糖の塊?向こう側が透けて見えるような、クリアな透明ではないですよね。屈折率もどのくらいの物なのかわかりませんが、強い光を当てると、向こう側に色が透けるくらいな、半透明かな?と考えました。よって、半透明な属性を与えてみましたので、後ほど完成動画等で金平糖の影にも注目して見てください。
ここで、メタボールによる造形の確認を兼ねて、前節の座標計算で、各座標点が互いに遠ざかりながら収束していく様子を動画にしてみましたので、ご覧ください。
冒頭、画面右側に固まっていた小球が、時間の経過とともに互いに遠ざかりながら、大球の表面に満遍なく散らばっていく様子が見て取れると思います。
虫みたいな動きといい、ビビットな色合いとも相まって、ちょっとグロテスクではありますが……。
上動画を生成するために使ったデータ(confetti_horn.xitr)では、座標計算が収束していく様子が見え易い様に、わざと、最初の座標を1箇所に固めていたり、収束が遅くなるように変位量(ds, dt)の最大値を小さくしていたりしています。
最終データ(confetti.xitr)では、もっと収束の速いパラメータを使って実現しています。
完成したデータをリンクしておきます。
1) | Xi-TRACEデータ | confetti.xitr |
2) | マッピングデータ | confetti_bm.jpg |
confetti_cm.xpic | ||
3) | 金平糖座標ファイル | confetti_pos.txt |
4) | 座標計算スクリプト | bee_in_bin_pos.xitr |
5) | Xi-TRACEデータ#2 | confetti_horn.xitr |
1)〜3)を使って下の動画を生成しました。4)は、3)を生成するためのスクリプトで、「瓶に入れたビー玉」で使ったものと同じです。このスクリプト先頭付近のパラメータを以下の様に変更して生成します。
var BR=25 - 5; // 瓶の半径
var BH=BR*3; // 瓶の高さ
var TR=3; // ビー玉の半径
var TN=20; // ビー玉の数
var SF=4; // 描画時のスケールファクタ
var enbd2d=false; // 2Dで試行する
ファイル名「bee_in_bin_pos.txt」で作成されますので、ファイルを上書きしてしまわないようにご注意ください。「bee_in_bin_pos.txt」→「confetti_pos.txt」にリネームして使います。上のリンクから「confetti_pos.txt」をダウンロードして使う場合、4)は不要です。
5)は角の座標が収束していく様子の動画を生成するデータです。
ケースをパカッと開くと、中にはカラフルな金平糖が!つい1つ、つまみたくなりますね。
蓋のデザインに使った凹凸マッピングデータの生成は、「ジンジャクッキィ」で作成したスクリプト(ginger_cookie.olv)を使っています。