Xi-TRACEの新機能、polygon命令を使って、数式で表現される曲面をポリゴンでモデリングしてみます。
まずは、polygon命令の説明から。
任意の物体を想定した時、任意の座標(x, y, z)が、その物体の内側にあるのか/外側にあるのか、を数値として返す手続きを定義できるなら、その物体をポリゴンに変換することができます。
polygon <procedure_name>, <x1,y1,z1>, <x2,y2,z2>, <rr> [, <save_name>] ;
polygon命令では、物体の内側なのか/外側なのか?を判定するための手続き名、<procedure_name>、物体が存在する定義域(直方体領域)の対角線<x1,y1,z1>−<x2,y2,z2>、ポリゴンメッシュの細かさ<rr>を指定します。
手続き<procedure_name>は、予め以下のように定義されている必要があります。
procedure <procedure_name> x, y, z
{
<procedure_name>=sq (x) + sq (y) + sq (z) - sq (10);
}
引数の数は3つで、引数の意味は座標値(x, y, z)でなければなりません。手続きの中でどのような処理を記述してもOKですが、最終的には<procedure_name>と同名の変数に以下の様な値をセットして終了する必要があります。
便宜上、数値0(物体の表面)を定義していますが、実際の処理では、<procedure_name>≦0の場合を物体の内部として扱っていますので、必ずしも0を返す必要はありません(0は負の数と同じ扱いです)。
上で例として示した手続きは、言わずと知れた球の方程式(原点を中心として半径が10)ですので、この手続きで生成されるポリゴンは球体となります。
定義域の指定では、<x1><<x2>となるように、内部で自動的に<x1>と<x2>を入れ替えて使用します。y座標/z座標に関しても同様です。
メッシュの細かさ<rr>は、直方体領域をどのくらいの細かさで区切って、ポリゴンを生成するか?を表す値です。x軸方向で考えると、(<x2>−<x1>)/(2×<rr>)ケに分割して、各点が物体の内側にあるのか/外側にあるのか?を、各点ごとに手続き<procedure_name> を実行して、物体の外形を求め、ポリゴンを生成します。
定義域とメッシュサイズ、<save_name>を指定して書き出ししたOBJファイルには、一次変換はかかりません。OBJファイルへの書き出しをしなかった場合には、変換後のポリゴンに対して一次変換がかかります。
<save_name>はオプションで、指定されると、生成したポリゴンをWavefront社OBJ形式で出力することができます。<save_name>の拡張子".obj"で、ファイル形式を認識してポリゴンデータを書き出します。現時点では".obj"以外の拡張子が指定されると、エラーとなります。
polygon命令は、全メッシュに対して手続きを実行するため、非常に実行時間がかかります(遅い)。毎回ポリゴン生成を実行するのは非効率なので、最初の一回目にポリゴンを生成してOBJファイルを書き出しておき、2回目以降はOBJファイルを取り込んで使用する、と言った方法が現実的です。
procedure 球体 x, y, z
{
球体=sq (x) + sq (y) + sq (z) - sq (10);
}
ifnacc "球体.obj"
{
// OBJファイルが見つからなかったので、生成する
polygon 球体, -10,-10,-10, 10,10,10, 2, "球体.obj";
}
// OBJデータを取り込む
wobj "球体.obj";
<save_name>を指定した場合と、しなかった場合の注意事項があります。<save_name>を指定して、ファイルへの書き出しを選択した場合、ポリゴンデータはXi-TRACEのデータベースには登録されず、指定したファイルへ書き出されるので、レンダリングされません。一方、<save_name>を指定しなかった場合は、生成したポリゴンがXi-TRACEのデータベースに登録されて、レンダリングされます。即ち上例の様に、データファイルへの書き出しを指定した場合は、wobj命令でデータを取り込まない限り、レンダリングされません。
ここからは、polygon命令を使って、色々な数式をポリゴンに変換してレンダリングしてみます。
ドーナッツみたいな形です。
上図の様に大半径R1、小半径R2で形状が決まります(R3=R1+R2です)。トーラスは、以下の様な数式で表すことができます。
残念ながら、Xi-TRACEで表現可能な二次曲面の数式とは、だいぶかけ離れています。実際、二次曲面でモデリングするXi-TRACEでは、トーラスの様な形は非常にモデリングし難い形状なのです。正三角形→正五角形→正10角形→…→正360角形と画数を増やしていくと、最後はほぼ円に見えてくるように、円柱面をたくさん使ってトーラスモドキをモデリングするのがやっと、でしょう。
しかし、polygon命令が使えるようになったことで、世界が変わってきます。数式でトーラスの内部/外部を判定できるのですから、内/外判定手続きを定義してやれば、polygon命令でポリゴンデータに変換してレンダリングすることが可能となります。
まず、内/外判定用手続きを定義しましょう。
procedure トーラス x, y, z
{
トーラス=sq (sqrt (sq (x) + sq (y)) - R1) + sq (z) - sq (R2);
}
先ほど示した数式を左辺に移項して、そのまま記述しただけです。この式だと、トーラスの中心は原点にあり、大半径R1が描く円がx-y平面上にありますので、polygon命令で指定すべき立方体領域(定義域)は、(−(R1+R2), −(R1+R2), −R2)−(R1+R2, R1+R2, R2)となります。
後はメッシュサイズですね。polygon命令は、定義域をメッシュサイズ(の2倍)で区切って、各点が物体の内部か/外部かを手続きを実行した結果で判定し、ポリゴンを生成しています。よって、メッシュサイズが大きいと大きなポリゴンで粗くモデリングされ、メッシュサイズが小さければ小さなポリゴンで細かくモデリングできます。
特に、肉厚の薄い形状を変換しようとするときは注意してください。肉厚の薄い部分(厚さ方向)に、最低でも3つのメッシュ点が含まれるようにメッシュサイズを調整しないと、うまくポリゴン変換できません。
細かくモデリングすると、ポリゴン数が増加し、レンダリング速度が遅くなるのは当然ですが、それに加えて、polygon命令によるポリゴン生成処理にかかる時間も増大します。インタープリタ方式のスクリプト言語でメッシュ点毎に手続きを実行するので、だいぶ粗く設定しても、そこそこ時間がかかります。覚悟して使ってみてください。
polygon トーラス, -R3,-R3,-R2, R3,R3,R2, R3/10;
レイトレーシングしてみた結果がこちら。
ポリゴンの形が分かるようにレンダリングしました。通常は、ポリゴンに対して法線ベクタのスムーシングを掛けると、滑らかな曲面に見えます。
smooth : model
{
polygon トーラス, -R3,-R3,-R2, R3,R3,R2, R3/10;
}
輪郭のカクカクは、polygon命令のメッシュサイズを小さくすれば、目立たなくなります。
Xi-TRACEデータ:pgon_torus.xitr
トーラスの例でわかるように、定義したい形状に穴があろうが、凹んでいようが、問題なくポリゴン変換可能です。二つに分離した形状ではどうでしょうか?
procedure two_parts x, y, z
{
var tp1;
var tp2;
tp1=sq (x) + sq (y) + sq (z) - sq (R);
tp2=sq (x - PX) + sq (y) + sq (z) - sq (R/2);
if (tp1<=0 | tp2<=0)
{
// 内部
two_parts=-1;
}
else
{
// 外部
two_parts=1;
}
}
tp1で中心(0, 0, 0)半径Rの球、tp2で中心(PX, 0, 0)半径R/2の球を表現しています。if文でORを取っていますので、PX>R+R/2の間は大小2つの球がポリゴン変換されます。
もちろん、こんな形状も問題なしです。
面白いのはPX>R+R/2の状態から、PXが少しづつ小さくなってPX<R+R/2となった場合で、領域が重なった瞬間、2つの物体→1つの物体につながります。……実際には、2つの物体が近づいてメッシュ点の細かさでは「離れている」と認識できない距離まで近づくと、ポリゴンがくっついてしまいますが……。同様に、細い溝や尖った角、小さな凹みなどがメッシュサイズに対して小さいと、うまく表現できないのが弱点ではあります。
データと動画は、このページの最後にまとめておきます。
「ハートマーク色々」で使用した、ハート型のポリゴンデータを作成します。数式は、以下でしたね。
procedure ハート x, y, z
{
// 大きさを調整する
x=x/R;
y=y/R;
z=z/R;
// y軸方向に立てるため、z座標とy座標を入れ替える
var tmp;
tmp=y;
y=z;
z=tmp;
ハート=(x^2 + (9/4)*y^2 + z^2 - 1)^3 - x^2*z^3 - (9/80)*y^2*z^3;
}
(x, y, z)には一次変換がかかりませんので、拡大縮小・回転・平行移動などの変換を掛けたい場合には、自分で処理する必要があります。この例では、ハートの方向を変えるためにy座標とz座標を入れ替えています。
定義域がちょっと正確にはわかりませんが、試行錯誤で決めました。
Xi-TRACEデータ:pgon_heart.xitr
sinc関数をポリゴン変換してみます。
波はx-y平面方向に広がり、z軸方向に振幅していることにしました。内/外判定手続きには、(x, y, z)座標がパラメータとして渡されますので、x、y座標値を局座標(s, t)に変換して波高uを計算、z≦uの時に内部と判定して返します。
procedure sinc x,y,z
{
// 局座標へ変換(ただし、角度tは未使用、未計算)
var s=sqrt (sq (x) + sq (y));
var t;
// サイズ調整
s=s/H*3*PI;
// 内/外判定
var u;
if (s=0)
{
u=H;
}
else
{
u=H*sin (s)/s;
}
if (z<=u)
{
// 内部
sinc=-1;
}
else
{
// 外部
sinc=1;
}
}
レンダリング結果は以下。
Xi-TRACEデータ:pgon_sinc.xitr
完成したデータと動画です。
まずは、2つに分離した物体が近づいて行って、1つになってしまうのをポリゴンで生成した動画。毎回ポリゴンデータを生成しなおす必要があり、コンディションによっては、レイトレーシングその物の時間より、ポリゴンデータ生成の方が時間がかかるくらいです。
Xi-TRACEデータ:two_parts.xitr
トーラスを使って作った鎖をウネらせてみました。最初の一回だけ、トーラスのポリゴンデータを生成するので、時間がかかります。その後は、何回レイトレーシングしなおしてもポリゴンデータを作り直しません。メッシュサイズを変更したりして、ポリゴンデータの再生成が必要になった場合は、"〜_out.obj"ファイルを削除して再実行してください。
Xi-TRACEデータ:torus_chain.xitr