足(pod)が4本(tetra)ある、あの海岸などに積み上げられている、コンクリート製の大きな構造物、テトラポッドをモデリングしてみます。
「テトラポッド」と言うのは、商標登録されているそうで、日本で商品にこの名前を使えるのは一社だけだそうです。てっきり、一般名詞かと思っていました。一般的には、「消波ブロック」などと呼ぶそうです。
このHP的には、二次曲面を使ってモデリングしやすそうな形状なので、モチーフとして使います。
まずは、正四面体から入りましょうか。
正四面体は、4つの面からなる立体で、すべての面が正三角形で構成されています。上図のように、頂点が4つ(A〜D)あります。図で底面になっている三角形△BCDも、正三角形ですので、△BCDの高さに相当するhは、以下のように計算することができます。
正三角形(正四面体)の一片の長さを2×rとする。
頂点Bから辺CDへ垂直に下ろした直線hはCDと直角に交わり、CDを二等分する。
sin(∠BCD)=h÷(2r)
∴h=2r×sin(60°)
頂点Aから△BCDへ垂直に下ろした直線sは、△BCDと点Tで交わります。△ABTは直角三角形なので、三平方の定理を使って、sの長さが求められます。
sq(2r)=sq(s)+sq(BT)
Tは△BCDの高さh上の点で、線分hを2:1に分割することを使って、
BT=h×2÷3
∴s=√(sq(2r)−sq(h×2÷3))
重要なパラメータの算出が終わりましたので、ここでテトラポッドに戻ります。テトラポッドは、4つの足が等間隔に伸びていますので、正四面体の4つの頂点の方向に足を4本伸ばすと良いはずです。中心点は、4頂点の重心に置くのが良いでしょうね。まず、4つの頂点座標を求めて、そこから重心座標も求めてみます。
△BCDをx-y平面上に置く。
頂点Dがy軸上の正方向にあり、頂点Aがz軸上の正方向にあるものと考えると、
T点が原点であることがわかる。同時に線分BCがx軸の方向と一致することもわかる。
線分DTは、△BCDの高さhを2:1に分割するので、
D=(0, h×2÷3, 0)
同様に、
B=(-r, -h÷3, 0)
C=(r, -h÷3, 0)
頂点AはT(=原点)から垂直にz軸正方向へs離れた点なので、
A=(0, 0, s)
4つの頂点座標がわかりました。重心の座標Gは、G=(A+B+C+D)÷4で求めることができます。ちなみに、上で計算した頂点座標を代入してみると、G=(0, 0, s÷4)となり、線分ATの下から1/4の点にあることがわかります。
海岸のテトラポッドを思い出してみましょう。根元の方が少し太くて、先の方が少し細くなっている、先の方には面取り(?)がある。輪郭線は直線的なので、4本足の部分は円錐面(cone)を使った方がよさそうですね。
採寸したわけではないので、本物と全く同じではないですが、大体こんなイメージであっているかと思われます。気になる方は、ソースのパラメータをいじると調整できますが、根元の太さと先端の太さの比を極端に設定すると、円錐面の中心寄りの端が本体からはみ出してしまうことがあるのでご注意ください。
テトラポッドは、コンクリートでできた構造物ですが、海に漬かって変色したり、波の浸食を受けて凸凹になっていたり、物によって色味も質感も異なってきます。ここでは、比較的新品に近い物で、色は一様だということにして、凹凸マッピングを使ってコンクリート表面の質感を作ってみようと思います。
コンクリート表面は、基本的に平です。恐らく、成型するための型があって、その中にコンクリートを流し込み、固めて成型するものと思われます。よって、表面は型の平坦度を再現しようとします。ただし、コンクリートの場合、中に含まれる砂利などの細かさに依存したり、空胞の存在などで微小な凹凸ができます。
少なくとも、型によって抑えられているので、上左図のような、基準面に対して上下に凸凹ができるのではなく、上右図のように、基準面で頭打ちとなって、粒子の粗さや空胞などに起因する凹みだけが存在するモデルとなります。
まず、御影石を接写した、以下のデータを使います。
Olive+で読み込んで、「フィルタメニュー ⇒ 変形 ⇒ スクロール」を選択し、画像サイズ(240×240)の半分ほど、縦横にスクロールさせてみます。
うわっ、ちょっと見難いですけど、わかりますか?中央に十字に不連続な線が見えているのが。レイトレーシングでマッピングデータとして繰り返し張り付けると、この不連続線がタイルのように見えてしまうので、よくありません。Olive+の「編集メニュー ⇒ 簡易編集」機能を起動し、「設定」ウインドウで、以下のように設定して、「スタンプ●(相対)」コマンドを使って、不連続線を消しましょう。
図形描画(■、●):半透明(中心→周辺)を選択、不透明度=0.75程度に設定
この状態だと、先ほど示した断面図で言うところの、「基準面に対して上下に凸凹がある」状態ですので、真っ白なウインドウを生成(「編集メニュー ⇒ 消去」)して、御影石のデータと加算します。
「合成メニュー ⇒ 加算」を選び、御影石のデータに×1倍、白いウインドウのデータに×0.4倍程度(試してみて適切に調整します)の係数を設定して加算実行します。
もともと白っぽかったところは白いデータの加算によって、より白くなったはずですが、真っ白以上に白くはできないため、頭打ちになります。黒っぽかったところは白いデータの加算によって、少し明るくなりますが、真っ白まではならないため、黒っぽく残ります。つまり、この状態で、白で頭打ちとなり、凹み(黒い部分)だけが存在する、目的の質感に合ったデータになったわけです。
このデータを凹凸マッピングデータとして貼り付けます。円錐面の部分には、円柱マッピングを使って、ぐるっと一周包み込むように貼り付けます。足の先端部分は平面で切っており、そのまま円柱面マッピングを施してしまうと、不自然になってしまうので、この部分だけは平面マッピングを適用します。あとは、用意したマッピングデータの縦横比が狂わないように、注意しながらサイズを調整しています。
円柱マッピングでは、a)「円周方向にa枚貼り付け」と、b)「中心軸方向にサイズbで貼り付け」というパラメータが混在しています。a)は、張り付く場所の直径(半径)に依存して実サイズが変わりますが、b)の方は、直接実サイズで指定しますので、サイズが狂わないように調整する必要があります。具体的には、a)の実サイズを、足の先端の半径r1をつかって、2×π×r1(円周の長さ)とみなし、これに合わせてb)の実サイズを設定しています。
and
{
leverz $(p).x, $(p).y, $(p).z;
var fct1=5;
var fct2=100;
bump p_side=
{
nmap concrete, r1/fct2;
mcyl 0,0,0, 0,1,0, 0,0,1, fct1, 2*PI*r1/fct1;
};
con2p 0,0,0, 0,0,l, r1,r2;
bump p_face=
{
nmap concrete, r1/fct2;
mpla 0,0,0, 0,0,1, 0,1,0, 2*PI*r1/fct1, 2*PI*r1/fct1;
};
plane 0,0,0, 0,0,-1;
plane 0,0,l, 0,0,1;
// 先端形状
bump p_side;
para 0,0,l + r2 - RR3*r2;
cone 0,0,0, 0,0,1, 1;
}
足の方向を決定する一次変換、leverz命令の後で凹凸マッピングを適用しているので、凹凸マッピングにも一次変換がかかります(物体と一緒に回転する)。円錐面には凹凸マッピングp_side、平面部分にはp_faceを貼り付けていることに注目してください。
このHPで使用しているようなサイズのグラフィックデータサイズでは、よく伝わらないと思いますので、仕上がりはご自身で大きめの画像サイズでレンダリングしてみて、ご確認ください。
テトラポッドを作る手続きを1つ作れたら、後は同じ手続きを呼び出して、次々コピーを作り出せるのがXi-TRACEの便利なところ。しかも、スクリプトを使って配置する座標等を自動計算することもできます。
今回作ったテトラポッドは、正四面体の頂点に向かって足を延ばしているので、実は、元にした正四面体の重心座標Gを中心にした球体ですっぽり包み込むことができます。つまり、ぱっと見、複雑な形状をしていますが、ざっくりと球面に見なすことができるのです。
「球面を積み上げる」から思いつくのは、以前作成した「瓶に入れたビー玉」です。あの時は、瓶の中に、ビー玉を積み重ねて詰め込みました。
あの時の座標計算用スクリプト(bee_in_bin_pos.xitr)に少し手を入れて、横一線に積み上げるように変更したスクリプト(tetrapod_pos.xitr)をこのページの最後にリンクしてあります。テキストファイルの差分をとるツールをお持ちであれば、差分を見てみてください。差分が見やすくなるように、必要なところ以外は変えていないので、「ビー玉」とか「瓶の半径」とか、そのままにしてあります。
実際には4本足の消波ブロックであるにもかかわらず、無理やり球面とみなして、ビー玉と同じ積み上げ方をしているので、そのままだとスカスカで、宙に浮いてしまうテトラポッドも出てきます。このため、座標計算の時に使った球面の半径より、×1.5倍の大きさでテトラポッドを定義しています。
レイトレーシングしてみると、テトラポッド同士がめり込んでいるところもあるのはこのためです。無造作に積み上げるのは、なかなか、難しいですね……(1^^;)。特徴のある形状なので、規則的に積み上げる方がずっと簡単だと思われます。
最後に、打ち寄せる波を作ってみます。
簡単な方法は、水面に見立てた平面に、凹凸マッピングの機能を使って、波(のような法線ベクタ)を貼り付ける方法が手軽ですが、今回のケースではどうでしょう?凹凸マッピングでは、法線ベクタを貼り付けるだけなので、形状は、真っ平らな平面のままです。
これでは、消波ブロックに波が打ち寄せている様子を表現できません。
法線ベクタを貼り付けるだけ(凹凸マッピング)の波ではなく、やはり、実体のある波を作りたい。
波は、ポリゴンで作ることにします。polygon命令を使って作るのも一つの手ですが、波が打ち寄せる様子をアニメーションにするため、毎回形状を定義する必要があります。polygon命令だと実行速度が遅いので、ひとまず除外します。
今回は、沖の方から一定の間隔で滔々と打ち寄せる波をsin波で近似することにします。それほど複雑な形状ではないので、直接ポリゴンを1枚1枚定義しながら、実体を持った波を生成してみましょう。Xi-TRACEで、ポリゴンを定義するには、poly命令と、そのブロック内だけで有効なpt命令を使います。
上図のような細長いポリゴンを定義するには、以下のように記述します。
poly
{
pt A.x, A.y, A.z;
pt B.x, B.y, B.z;
pt C.x, C.y, C.z;
pt D.x, D.y, D.z;
}
頂点Aの座標を(A.x, A.y, A.z)としました。polyブロックの中で、頂点座標をpt命令を使って指定します。この例では、A→B→C→Dの順に指定していますが、ポリゴンには表裏が無いので、A→D→C→Bの様に逆順で記述してもOKです。
このように、1つのpolyブロック内で指定された頂点を持つポリゴンが1枚定義されます。頂点数は、3点以上であれば、何点でもOKですが、すべて同一平面上にある必要があります。もし、同一平面上にない点が指定されたときは、適切な平面上に写像してポリゴンが生成されます。
この細長いポリゴンを、下図のように波の形に繋げていきます。
ポリゴンの分割数が粗いと、カクカクした波になってしまいますが、ポリゴンのスムーシング機能で解決できます。以下は、波のポリゴンを生成している記述の抜粋です。
idx=-wavacr;
smooth : while (idx<wavacr)
{
:
: 座標計算
:
poly
{
pt wx,wy,wz;
pt wx2,wy,wz;
pt wx2,wy2,wz2;
pt wx,wy2,wz2;
}
// 継続
idx=idx + 1;
}
poly命令で1枚のポリゴンを定義し、それを外側のwhileループで複数回繰り返しています。while命令がsmooth命令で修飾されているところがスムーシング機能の指定部分です。whileブロック内で定義されたすべてのポリゴンを一塊とみなして、共通する頂点をリストアップ、頂点毎に色や法線ベクタが滑らかにつながるよう、スムーシングしなさい、という指定になります。試しに「smooth:」を削除して実行していただくと、カクカクした波になることがわかります。
完成したデータをリンクしておきます。
1) | tetrapod.xitr |
tetrapod_concrete.jpg | |
tetrapod_pos.txt | |
2) | tetrapod_pos.xitr |
1)で波が打ち寄せるテトラポッドのアニメーションデータを生成します。2)はテトラポッドの座標ファイル(tetrapod_pos.txt)を生成するためのスクリプトです。当方で生成したものが1)に含まれていますので、ご自身で生成する場合以外は必要ありません。
波のモデリングでは、簡単に計算するためsin波を使ってしまったので、海岸で見かける波と比べると少し違和感があります。実際の波はもっと複雑な形をしているみたいです。ただ、ちゃんと実体のある波にしたのは正解でした。