シャッターエフェクト用のモデルを作るためのばらばらにするスクリプトというと、なんだかすごく複雑そうなイメージを持ってしまいますが、実はすごく簡単な理論で書けます。
今回はシャッターエフェクトを作るのによく使われるボロノイ分割という境界分割手法を用いたシャッターモデル作成のMELスクリプトを紹介したいと思います。
まずは下のスクリプトを見てください。これはボロノイ分割を用いてモデルをばらばらにするMELスクリプトです。基本的には同じ処理をすべての点に対して繰り返していくだけなので、たったこれだけの量で書けてしまいます。
このスクリプトの簡単な説明をしたいと思います。まず、ボロノイ分割とは何かというところから。
ボロノイ分割とはある点群から隣り合う点間を結ぶ直線に垂直二等分線を引き、各点の領域を分割する手法です。
下の図でいうと点と点を点線で結んだときに、点と点の中間点でその直線に垂直な線を引っ張ってきて、それを二つの点の境界線にしようという考え方です。
白い線が境界線となります。
MELではpolyCutというコマンドを使うと対象ポリゴンモデルを平面で分断できるので、この平面の中心座標と向きさえ求めることができれば、分断できてしまいます。
点Aと点Bがあったとして、それらのそれぞれの座標をPa、Pbとすると、中心座標Pcを求める式は次のように書けます。
次に平面の向きを求めるための式ですが、要はPa-Pbからなるベクトルの傾きが求められれば良いので、ベクトルの3つの要素から逆三角関数でベクトルの角度を求めれば良いということになります。
ベクトルをベクトルの大きさ(3次元座標空間で考えると距離)で正規化してやると、ベクトルの大きさが1になるので、下の図のように直角三角形の斜辺が1という風に考えられ、簡単に考えられるようになります。
このときに三角関数は
となります。
逆三角関数はMELではasin, acos, atanと書けるので、その表記を用いると次のように書けます。
スクリプトでの中ではsindなどのようにdが付く関数を使っていますが、これはdegreeという意味でラジアンではなくて角度で結果を返すという意味になります。
この逆三角関数を求める関数を使えば、角度θは簡単に求めることができます。スクリプト内で180を足しているのはpolyCutコマンドのカットした面を消すオプションを利用するためにカットする平面を反転させて、破片となるポリゴンを残すためです。
これらの処理を隣接する点同士で計算してカットを繰り返していけば、シャッターエフェクト用の破片の完成となります。上記のMELスクリプトでは隣接する点同士ではなくて全ての点同士を見ているので、かなり遅いです。その辺は手抜きで申し訳ないですが、ご勘弁を。
ということで理論の説明は以上となります。
実際にこのスクリプトを走らせた結果を載せておきます。
targetという名前の球体とボロノイ分割で使う点群を作り、点群だけ選択した状態で次のようにプロシージャを実行します。
例えば、次のような点群を作ったとします。
点群を選択してプロシージャを実行すると結果は次のようになります。
断面はこんな感じになっています。点の数が50個くらいなので、かなり荒い断面になっていますが、ちゃんと境界で分割されているのがわかると思います。
このスクリプトでは点を自分で定義しなければならない仕様になっていますが、rand関数やgauss関数などを使ってランダムで点を発生させるスクリプトを書けば、点の定義も自動化できるので便利になります。
ただし、MELスクリプトではやはり動作がかなり遅いので、細かい分割が必要な場合はプラグインで作るべきでしょう。
今回はシャッターエフェクトを作るのによく使われるボロノイ分割という境界分割手法を用いたシャッターモデル作成のMELスクリプトを紹介したいと思います。
まずは下のスクリプトを見てください。これはボロノイ分割を用いてモデルをばらばらにするMELスクリプトです。基本的には同じ処理をすべての点に対して繰り返していくだけなので、たったこれだけの量で書けてしまいます。
global proc CutByVoronoi(string $target, float $in_pos1[], float $in_pos2[]) { float $v[], $p[], $r[], $norm; int $i; //** Calculate the center point between two points for($i=0; $i<3; $i++){ $v[$i] = $in_pos1[$i] - $in_pos2[$i]; $p[$i] = $v[$i]/2 + $in_pos2[$i]; } //** Compute cut plane degree $norm=sqrt($v[0]*$v[0]+$v[1]*$v[1]+$v[2]*$v[2]); $r[0] = asind($v[1]/$norm) + 180; $r[1] = atan2d($v[0],$v[2]) + 180; $r[2] = 0.0; //** Cut target polyCut -ch false -df true -pc $p[0] $p[1] $p[2] -ro $r[0] $r[1] $r[2] $target; polyCloseBorder -ch false $target; } global proc ShatterByVoronoi(string $target){ string $tmp[],$dupObj, $sels[] = `ls -sl`; int $i=0; for($t1 in $sels){ $tmp=`duplicate -rr -name ("piece"+$i) $target`; $dupObj = $tmp[0]; for($t2 in $sels){ if($t1==$t2){ continue; } vector $pos1 = `xform -ws -q -t $t1`; vector $pos2 = `xform -ws -q -t $t2`; CutByVoronoi($dupObj, $pos1, $pos2); } $i++; } }
このスクリプトの簡単な説明をしたいと思います。まず、ボロノイ分割とは何かというところから。
ボロノイ分割とはある点群から隣り合う点間を結ぶ直線に垂直二等分線を引き、各点の領域を分割する手法です。
下の図でいうと点と点を点線で結んだときに、点と点の中間点でその直線に垂直な線を引っ張ってきて、それを二つの点の境界線にしようという考え方です。
白い線が境界線となります。
MELではpolyCutというコマンドを使うと対象ポリゴンモデルを平面で分断できるので、この平面の中心座標と向きさえ求めることができれば、分断できてしまいます。
点Aと点Bがあったとして、それらのそれぞれの座標をPa、Pbとすると、中心座標Pcを求める式は次のように書けます。
Pc = (Pa - Pb) / 2 + Pb
次に平面の向きを求めるための式ですが、要はPa-Pbからなるベクトルの傾きが求められれば良いので、ベクトルの3つの要素から逆三角関数でベクトルの角度を求めれば良いということになります。
ベクトルをベクトルの大きさ(3次元座標空間で考えると距離)で正規化してやると、ベクトルの大きさが1になるので、下の図のように直角三角形の斜辺が1という風に考えられ、簡単に考えられるようになります。
このときに三角関数は
sin(θ) = b
cos(θ) = a
tan(θ) = b/a
cos(θ) = a
tan(θ) = b/a
となります。
逆三角関数はMELではasin, acos, atanと書けるので、その表記を用いると次のように書けます。
asin(b) = θ
acos(a) = θ
atan(b/a) = θ
acos(a) = θ
atan(b/a) = θ
スクリプトでの中ではsindなどのようにdが付く関数を使っていますが、これはdegreeという意味でラジアンではなくて角度で結果を返すという意味になります。
この逆三角関数を求める関数を使えば、角度θは簡単に求めることができます。スクリプト内で180を足しているのはpolyCutコマンドのカットした面を消すオプションを利用するためにカットする平面を反転させて、破片となるポリゴンを残すためです。
これらの処理を隣接する点同士で計算してカットを繰り返していけば、シャッターエフェクト用の破片の完成となります。上記のMELスクリプトでは隣接する点同士ではなくて全ての点同士を見ているので、かなり遅いです。その辺は手抜きで申し訳ないですが、ご勘弁を。
ということで理論の説明は以上となります。
実際にこのスクリプトを走らせた結果を載せておきます。
targetという名前の球体とボロノイ分割で使う点群を作り、点群だけ選択した状態で次のようにプロシージャを実行します。
ShatterByVoronoi("target");
例えば、次のような点群を作ったとします。
点群を選択してプロシージャを実行すると結果は次のようになります。
断面はこんな感じになっています。点の数が50個くらいなので、かなり荒い断面になっていますが、ちゃんと境界で分割されているのがわかると思います。
このスクリプトでは点を自分で定義しなければならない仕様になっていますが、rand関数やgauss関数などを使ってランダムで点を発生させるスクリプトを書けば、点の定義も自動化できるので便利になります。
ただし、MELスクリプトではやはり動作がかなり遅いので、細かい分割が必要な場合はプラグインで作るべきでしょう。