#はじめに
こんにちは。フジヤマタカシです。
今回は割とありがちですが、X-menのミスティークの変身エフェクトをやってみたいと思います。
所でチュートリアルとは関係ありませんが、現映画でミスティークを演じるジェニファー・ローレンスはなんか違うって思いませんか?彼女は名女優だとは思いますがミスティークかと言われるとどうも。ハンガーゲームの印象が強いからですかね?でも調べてみるとX-men First Classのほうがハンガーゲームより先なんですね。 それから個人的にはX-men3でミスティークがキュアによってミュータント能力を失って普通の人間に戻るシーンが印象に残ってるからそう思うのかもしれません。あれは映画史に残るエロ美しいシーンだと思ってます。アレを見たとき・・・フフ・・・なんていうか・・・・その・・・下品何ですが・・・「モナリザを見た吉良吉影状態」になってしまいましてね。
#準備
ミスティークエフェクトは検索すればたくさんテストやチュートリアルが出てきますが、止まってるものが多いのでここでは動いてるものに対してやりましょう。
そのために歩く人2人のabcファイルを用意しました。これはMixamoからダウンロードして無理やり体格を合わせました。
2つのアニメーションはTimeShiftとTransformのエクスプレッションで永久に歩き続けるようにしてあります。
#羽のためのポイントの作成
男性モデルから女性モデルへトランスフォームを行うとして男性モデルから「閉じたシェイプ」服と皮膚などの重なりを取り払ったモデルを作ります。
VDBにしてポリゴンにコンバートして元のオブジェクトにRayをかければだいたい何とかなると思います。(襟は今回気にしない)困難なモデルもあったりしますが。
そして、そのモデルに対しScatterをあてます。これをSolver内で変身エリアが広がる計算をします。
今回欲しいのはactive(変換後)とactiveTime(変換タイム)とdir(広がりの方向)なのinitializeのwrangle内でこの様にアトリビュートを設定しました。
int ptnum = pcfind(1,"P",@P,100,1)[0];
vector otherP = point(1,"P",ptnum);
if( distance(@P,otherP) < 0.005 ){
i@active = 1;
@activateTime = 1;
v@dir = normalize(@P - otherP);
}else{
i@active = 0;
@activateTime = 9999;
v@dir = 0;
}
@Cd = @active;
#広がる計算
activeのポイントだけを抽出してそれをpcfindで拾ってアクティブにするよくあるやり方です。
int ptnum = pcfind(1,"P",@P,100,1)[0];
vector otherP = point(1,"P",ptnum);
if( distance(@P,otherP) < 0.01 ){
i@active = 1;
@activateTime = @Frame;
int handle = pcopen(1,"P",@P,0.05,10);
v@dir = normalize(@P - pcfilter(handle,"P"));
}
@Cd = @active;
dirの取得だけpcopenを使っているのは方向をなめらかにするためです。
このシミュレーションは全体に広がったあたりのフレームでTimeShiftを使って止めます。
Solverのactiveの広がりをそのまま使う方法もありますが、1年前の記事で書いたようにアニメーション(Time Dependency)してるポイントにCopyを使うのを避けるためです。 代わりにactiveTimeアトリビュートを用意してるので、後にこれを元にアニメーションを行います。この方法だとactiveTimeを編集することでタイミングの調整が容易になります。
#羽コピー植付け
羽をCopyで植えますが、その前に必要アトリビュートを整理しておきます。
i@id = @ptnum;
v@up = v@dir;
v@center = @P;
v@normal = @N;
そして、2ndインプットと3rdインプット、それぞれにキャラ1と2に最初のフレームオブジェクトを入れてxyzdistによってprimとprimuvを取得します。
これで後に動いてるモデルの位置を参照することができます。
xyzdist(1,@P,i@hitprim1,v@hitprimuv1);
xyzdist(2,@P,i@hitprim2,v@hitprimuv2);
#Wrangleでアニメーションさせる。
この羽それぞれにはポイントが持っていたアトリビュートを引き継いでいるので、それを利用してアニメーションさせます。
まずは変形させやすくするためにピボットをずらします。予め作っていたcenterアトリビュートを使います。
@P -= v@center;
@P += v@center;
この様にしてこの間で変形を行います。
まずはタイミングの設定。
float transition = fit( @Frame - @activateTime * ch("speed") + pow(rand(@id),12) * 0 ,0,10,0,1);
transitionの0から1への変化がAからBへ変化で、 スピードで変形が広がるスピードを調整できるようにしてます。
そのtransition変数を利用して回転とスケールをアニメーションさせてやります。
回転は-90度から90度に回転させるだけ。スケールはランプで0から元のサイズに拡大してまた0になるようにしてるだけです。
///Rot///
vector axis = normalize(cross(v@normal,v@dir));
matrix3 rot = ident();
rotate(rot,fit01(transition,PI * -0.5, PI * 0.5),axis);
@P *= rot;
///Scale////
float size = chramp("size",transition);
@P *= size * fit01(pow(rand(@id + 333),20),0.5,1.5);
さらにベンドを加えます。ベンドに関しては以前書いたので、詳しくはそちらの記事を参考にしてください。
TwistとBendデフォーメーションの重ね掛け
@P -= v@center;
float transition = fit( @Frame - @activateTime * ch("speed") + pow(rand(@id),12) * 0 ,0,10,0,1);
///Bend///
`chs("../Bend_fn/snippet")`
@P *= fn_bend(@P, 0, v@normal, cross(v@normal, v@dir), ch("../grid1/sizey"), sin(transition * PI * 2) * PI * 0.5);
///Rot///
vector axis = normalize(cross(v@normal,v@dir));
matrix3 rot = ident();
rotate(rot,fit01(transition,PI * -0.5, PI * 0.5),axis);
@P *= rot;
///Scale////
float size = chramp("size",transition);
@P *= size * fit01(pow(rand(@id + 333),20),0.5,1.5);
///rePosition///
@P += v@center;
あとはこれを動いてるキャラに当て込むだけですが、位置だけならprimuvを利用してprimuv(2,"P",i@hitprim1,v@hitprimuv1)
みたいに出来ますが、動いたときにサーフェイスの向きも変わっているので回転も考慮する必要があります。ここでFujitaさんのマトリックスの記事にあった法線とタンジェントを利用した回転制御を使います。
インプット2に動いてないキャラ1、インプット3に動いているキャラ1、インプット4に動いているキャラ2を入れます。
動いてるキャラ2はキャラ1とほぼ同じなのでとりあえず動いてるキャラ1へオリエントさせる事にします。
matrix3 surfMat(int input, prim;vector uv){
vector z = normalize(primuv(input,"N",prim,uv));
vector x = normalize( cross(primuv(input,"tangentu",prim,uv),z ) );
vector y = normalize( cross( z,x ) );
matrix3 trans = set(x,y,z);
return trans;
}
サーフェイスのタンジェントから回転マトリックスを作るための関数です。
これを使い止まってるキャラ1と動いてるキャラ2の回転マトリックスを取得します。
matrix3 rot_ch0 = surfMat(1, i@hitprim1,v@hitprimuv1);
matrix3 rot_ch1 = surfMat(2, i@hitprim1,v@hitprimuv1);
そうしたら、後はインバート動いてないと動いてるマトリックスを順に掛けるだけです。
@P *= invert(rot_ch0) * rot_ch1;
ポジションはキャラ1とキャラ2の位置を取得してtransition変数でlerpさせましょう。
vector pos_ch1 = primuv(2,"P",i@hitprim1,v@hitprimuv1);
vector pos_ch2 = primuv(3,"P",i@hitprim2,v@hitprimuv2);
@P += lerp(pos_ch1,pos_ch2,transition);
カラーも似たような事をします。
vector Cd_ch1 = primuv(2,"Cd",i@hitprim1,v@hitprimuv1);
vector Cd_ch2 = primuv(3,"Cd",i@hitprim2,v@hitprimuv2);
@Cd = lerp(Cd_ch1,Cd_ch2,transition);
元のキャラアニメーションモデルにAlphaで適当にフェードさせてこうなります。
@P -= v@center;
float transition = fit( @Frame - @activateTime * ch("speed") + pow(rand(@id),12) * 0 ,0,10,0,1);
///Bend///
`chs("../Bend_fn/snippet")`
@P *= fn_bend(@P, 0, v@normal, cross(v@normal, v@dir), ch("../grid1/sizey"), sin(transition * PI * 2) * PI * 0.5);
///Rot///
vector axis = normalize(cross(v@normal,v@dir));
matrix3 rot = ident();
rotate(rot,fit01(transition,PI * -0.5, PI * 0.5),axis);
@P *= rot;
///Scale////
float size = chramp("size",transition);
@P *= size * fit01(pow(rand(@id + 333),20),0.5,1.5);
///reOrient///
matrix3 surfMat(int input, prim;vector uv){
vector z = normalize(primuv(input,"N",prim,uv));
vector x = normalize( cross(primuv(input,"tangentu",prim,uv),z ) );
vector y = normalize( cross( z,x ) );
matrix3 trans = set(x,y,z);
return trans;
}
matrix3 rot_ch0 = surfMat(1, i@hitprim1,v@hitprimuv1);
matrix3 rot_ch1 = surfMat(2, i@hitprim1,v@hitprimuv1);
@P *= invert(rot_ch0) * rot_ch1;
///rePosition///
vector pos_ch1 = primuv(2,"P",i@hitprim1,v@hitprimuv1);
vector pos_ch2 = primuv(3,"P",i@hitprim2,v@hitprimuv2);
@P += lerp(pos_ch1,pos_ch2,transition);
///Color///
vector Cd_ch1 = primuv(2,"Cd",i@hitprim1,v@hitprimuv1);
vector Cd_ch2 = primuv(3,"Cd",i@hitprim2,v@hitprimuv2);
@Cd = lerp(Cd_ch1,Cd_ch2,transition);
作成したファイルはこちらになります。
https://drive.google.com/open?id=1HTN5-dU6mkiewF2u1AnPsSBU3Z9iuMjB
では今回は以上になります。ここまで読んでいただきありがとうございます!