#はじめに
こんにちは。フジヤマタカシです。Houdini Apprentice Advent Calendar 2018のほうに空きがあるようなので連続で新たに記事を書かさせて頂こうと思います。昨日の記事もまだの方はよろしければ目を通してみてください。
今回はWrangleのfor Loopを使ってTwistとBendする方法を紹介したいと思います。
ベンドに関してはまたしてもfujitaさんの昔の記事を参考にさせて頂いてます。
ベンドは単純な変形ですが自力で出来るようになると、かなり回転や三角関数などの知識がつくと思います。
それではよろしくお願いします。
#関数の準備
先ずはじめに、いくつかデフォームに必要なファンクションを用意したいと思います。
まずはRotation(回転)とTranslate(移動)のマトリックスです。
標準ですとそれぞれ
matrix3 rot = ident();
rotate(rot, float angle, vector axis);
matrix4 tras = ident();
translate(trans, vector position);
と言うふうに一度定義した変数を関数の中に入れる事で再計算されます。
たった2行ですが、何度も使う必要があるので、それぞれ1行で定義できる関数にします。
////function fn_rot////
matrix3 fn_rot(float r_val; vector axis){
matrix3 rotM = ident();
rotate(rotM, r_val ,normalize(set(axis)));
return rotM;
}
////function fn_trans////
matrix fn_trans(vector position){
matrix transM = ident();
translate(transM, position);
return transM;
}
これで回転はmatrix3 rot = fn_rot(float angle, vector axis);
移動はmatrix trans = fn_trans(vector position);
と定義出来るようになりました。
次にTwistの関数です。
ツイスト変形は簡単で基点から距離に応じて回転させるだけです。
ツイストの軸がXYZに平行になっていなくてもdot productを使えば軸方向への距離を出す事が出来ます。
/////function fn_twist//////
matrix fn_twist(vector P, pivot, axis; float dist, angle){
matrix twistM = ident();
twistM *= invert( fn_trans( pivot ));
float dot = dot(normalize(axis),P*twistM);
matrix horizonDir = twistM * invert( fn_trans( normalize(axis) * dot));
float bendArea = fit(dot,0,dist,0,1);
if(bendArea < 1){
twistM *= fn_rot(angle * bendArea, axis);
}else{
twistM *= fn_rot(angle, axis);
}
twistM *= fn_trans( pivot );
return twistM;
}
次にベンドです。
こちらも細かいところは省きますがdotを使うことで曲げる軸の距離を出しています。
/////function fn_bend//////
matrix fn_bend(vector P, pivot, dir, axis; float dist, angle){
matrix bendM = ident();
bendM *= invert( fn_trans( pivot ));
float dot = dot(normalize(dir),P*bendM);
matrix horizonDir = bendM * invert( fn_trans( normalize(dir) * dot));
float bendArea = fit(dot,0,dist,0,1);
if(bendArea < 1){
bendM = horizonDir;
}else{
bendM *= invert( fn_trans(normalize(dir) * dist));
}
float angAmount = angle * min(dot,dist)/dist;
float axisOffset = dist/angle;
vector axisPos = normalize(cross(axis,dir)) * axisOffset;
matrix3 rot = fn_rot(angAmount,normalize(axis) );
if(bendArea > 0 && angle != 0){
bendM = bendM * invert(fn_trans(axisPos)) * matrix(rot) * fn_trans(axisPos) * fn_trans(pivot);
}else{
bendM = ident();
}
return bendM;
}
#関数を試してみる。
先ほどの関数は適当に別にWrangleノードを準備してそこに置いておきます。
そして、実際に変形用に使うWrangleでそれらを参照します。
こうすることで複数のノードにまたがって関数の共有をする事が出来ますし、作業スペースの行数を抑える事が出来ます。
そして、先ほどの関数を使っての変形が確認できました。
#For Loopによる重ねがけ
そしてこのツイストとベンドを複数のユニークパラメータを掛けて行きたいので、For LoopとフォルダメニューのMultiParmと組み合わせます。
MultiParmはfujitaさんの昔の漫画でも触れられていますが、フォルダ内のパラメータがparm1,parm2,parm3.....と定義されます。
なので、Wrangle内コードでそれらを参照するためにFor Loop毎に参照先を変えます。
stringの最後に数字を加えるにはconcatとitoaを使えば良いので
ch(concat("parm",itoa(i)))
となります。
下の図では準備として簡単にMultiParm内から高さと色を参照して塗っていく物を作りました。
ツイストベンドデフォームもこれと同じようにしたから段階的に掛けていきますが、デフォームさせた場合、変形先から上の位置も方向も変わってしまうので、それらの基点もFor Loop内で更新していく事になるので、事前にpivot(基点), aim(方向), cross(軸)と言う変数を準備しておきます。
`chs("../fn/snippet")`
@rest = @P;
int iteration = chi("iteration");
vector pivot = set(0,0,0);
vector aim = set(0,1,0);
vector cross = set(0,0,1);
float heightLimit = 0;
for(int i=1; i<=iteration; i++){
float range = ch( concat("range", itoa(i)) );
float twistAngle = ch( concat("twist", itoa(i)) );
float bendAngle = ch( concat("bend", itoa(i)) );
if(@rest.y > heightLimit){
@P *= fn_twist(@P, pivot, aim, range, twistAngle);
@P *= fn_bend(@P, pivot, aim, cross, range, bendAngle);
}
vector tempPivot = pivot;
tempPivot += aim * range;
tempPivot *= fn_bend(tempPivot, pivot, aim, cross, range, bendAngle);
pivot = tempPivot;
matrix3 twistbend = fn_rot(twistAngle ,aim) * fn_rot(bendAngle ,cross);
aim *= twistbend;
cross *= twistbend;
heightLimit += range;
}
以下の値をベンドとツイストに掛けてアニメーションさせてみました。
float valueAnim = clamp(@Frame/10 - i,0,0.6)/0.6;
パーティクルに対して掛ければデフォームの流れに沿ってパーティクルを流す事も可能です。
作成したファイルはこちらになります。
https://drive.google.com/file/d/18_gJj0ly6gZcgiu436O8sVhAAcOFtjZc/view?usp=sharing
では今回は以上になります。ここまで読んでいただきありがとうございます!