揺れもの YURAYURA.pyのおすすめ設定方法


A-s-C-e-n-S-i-o-N本家さん

http://a-s-c-e-n-s-i-o-n.blogspot.com/2017/12/maya-yurayurapy.html

maya : 骨にヘアを揺れ物シミュレーションするツール YURAYURA.pyの説明
髪の毛などの数珠繋ぎ階層にヘアシミュレーションをかける揺れ物ツールです。

YURAYURAのおすすめ設定というか、揺れすぎていろんな破滅を起こすので
一番ゆれない状態から設定していくのがいいかもしれないと思い、
一番揺れない設定にしてみた。(ゆれるけど)
といってもnHAIRの設定を見たらわかる。

https://knowledge.autodesk.com/ja/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2016/JPN/Maya/files/GUID-89B58075-7927-4117-8D5E-5FECE5669108-htm.html

■■■GENERATE■■■:
髪の毛のルートを選択して、GENERATEボタンを押すと
YURAYURAシミュレーションセットが作られます。
複数選択でき、一度作成した後でも追加で使用できます。

■■■SETTING■■■:

■Gravity:
重力
■Start Curve Attract
開始カーブに引き付け(Start Curve Attract)
現在のヘアの位置を(変形する)開始位置へ引き付ける量を決定します。このアトリビュートは、たとえば、硬めのヘアやキャラクタとともに動くヘアの場合など、多くのヘア ワークフローで役立ちます。さらに、キーフレームを設定されたアニメーションを開始カーブに配置する場合、開始カーブに引き付け(Start Curve Attract)アトリビュートを使用して、シミュレーションと開始カーブ アニメーションをブレンドすることができます。

流れるような長いヘアの場合には、通常、開始カーブに引き付け(Start Curve Attract)の値はゼロのままにします。ただし、短いヘアの場合は、ヘアの十分な剛性を保つことが困難になる場合があります。ヘアの剛性を非常に大きくして、同時に、いくつかのダイナミックなプロパティを持たせる必要がある場合、ゼロより大きい値を使用します。1.0 の値のとき、ヘア位置はカーブ開始位置です(トランスフォームされる毛根からの相対位置になります)。衝突およびフォースだけは、ヘアを偏向させます。Start Curve Attract がない場合、Substep と Damp の値を増やす必要があります(特に、ヘアごとの CV の数が多いとき)。

■Attraction Damp:
引き付けのダンプ(Attraction Damp)
開始カーブに引き付け(Start Curve Attract)のエフェクトをダンプし、ヘアをカーブの開始位置の方に移動させて、ヘアの速度を遅くします。これは、ヘアの弾力性(開始カーブに引き付け(Start Curve Attract)の値が高い場合に発生)を小さくしたい場合や、アニメートされたヘアの頂点にダイナミック フィールドを適用したい場合に便利です。引き付けのダンプ(Attraction Damp)を 1 にすると、開始カーブの方に移動するヘアの動きは完全にダンプされ、開始位置(Start Positions)とフィールド フォースだけをそのままにして、後から任意でその動きにインフルエンスを及ぼせるようにします。

■Drag:
ドラッグ(Drag)
これは、シミュレーションを安定させるのに役立つだけではなく、空気摩擦をシミュレートします。ドラッグ(Drag)の値が 1.0 のとき、ヘアは慣性のモーションやフォロースルーがないように動作するため、粘度が高い液体の中にあるかのように動きます。
■Tangential Drag:
接線のドラッグ(Tangential Drag)
ヘアの方向に沿ってドラッグする量を指定します。0 に設定すると、ヘアは接線方向またはシェイプの方向に沿った移動において抵抗がなくなります。接線のドラッグ(Tangential Drag)を 1 に設定すると、ドラッグ(Drag)はすべての方向に均一になります。ドラッグ(Drag)のレベルが高く、接線のドラッグ(Tangential Drag)が低い場合、ヘアは水中を移動する蛇のような揺らぎモーションの動きをします。
■Motion Drag:
モーション ドラッグ(Motion Drag)
毛根の動きに相対的なヘア カーブの動きをダンプします。モーション ドラッグ(Motion Drag)の値は、ヘア カーブが毛根に合わせて動く量、またヘアのシェイプが他のフォースに影響される程度を決定します。これにより、Substeps の数を増やさなくても、ボビングやウィグルのようなヘアの余分な動きをダンプすることができます。

たとえば、モーション ドラッグ(Motion Drag)を 1.0 に設定すると、ヘアは毛根に合わせて動くので、周囲の大気をそれに合わせてドラッグしているように見えます。
■Damp:
ダンプ(Damp)
ヘアのベンドおよび伸長の方法に影響する個々のヘアの相対的シェイプの変化をダンピングします。
■Stretch Damp:
伸長のダンプ(Stretch Damp)
ヘア カーブ頂点間の伸長による速度がどのくらいダンピングされるかを指定します。伸長のダンプ(Stretch Damp)を大きくすると、ヘアは弾まずに伸長できます。
■■■MODIFY■■■:
SETTING欄の数値を変更して、下にあるMODIFY SETTING ( REFRESH )
ボタンで適用できます。

■■■COLLIDE■■■:
コリジョンをセットしたい場合は、コリジョンオブジェクトを選択して
ADD COLLIDEボタンを押します。

■■■BAKE■■■:
シミュレーション結果が良さそうであれば、BAKEボタンを押すと
元に選択していた階層すべてのRotateにBake Simulationをかけます。
YURAYURAシミュレーションセットは消去され、UIも閉じます。

nHairを使用した揺れものリグセットアップの自動化をしたよ。JointChain Head Select To Make SplineIK nHair Rig

リグブログさんの
https://backbone-studio.com/blog-nhairrig/

この記事を参考にやった。

nHairを使った感じの揺れものシミュレーション

①ジョイントを作成します。
ジョイントはX軸が子供を向くように設定しましょう。
X軸を子供に向けておくと、SplineIK機能のAdvanced Twist Controls(根元と先端のツイストを制御できる)の設定がしやすくなります。
※Advanced Twist Controlsの説明に関しては割愛します。

②ジョイントにSplineIKを設定します。

③作られたcurve1をルートに移動しておく。
curve1を選択して、FX > nHair > Assign Hair System > New Hair Systemを実行します。
※curve1は、自動的にfollicle1の階層に移動します。

④再生すると、シミュレーションによりcurve2は動いていますが、curve1は動いていないことが確認できます。
ノードエディターでSplineIKとnHairの接続関係を確認してみましょう。
上側がSplineIK、下側がnHairの入出力です。
nHairからの情報はcurveShape2へ繋がっているので、nHairのシミュレーションによりcurveShape2が動きます。
一方で、ikHandle1にはnHairからの情報が繋がっていません。

⑤curveShape2のworldSpace[0]出力をikHandle1のInCurveに入力することで、nHairの情報がSplineIKに渡り、ジョイントが動くようになります。
curveShape2のWorldSpace[0]をikHandle1のinCurveに繋ぎます。
各機能の関係性は、下図のようになります。

⑥再生して、ジョイントが動くことを確認しましょう。
あんまり動かないことも。。。

動かないのでいきなり⑨にとんでみた。

⑨ジョイント全体を制御したい場合は、curve1を制御してください。
今回はジョイントの根元に別のジョイントchain_handle_01を作成し、curve1をペアレントコンストレインしてみました。
再生するとエンドフレームでループしていい感じに動き始めた。

なのでリグの方のジョイントチェーンをスケルトンのジョイントチェーンに方向コンストレインしていく

最後にスケルトンのジョイントチェーンにchain_handle_01を位置コンストレインしてあげれば動くはず。
ルートを選択して再生しながら動かしたらスケルトン通りに動きました。

ーーーー以下連続したいときのショート版ーーーーー
1,ジョイントチェーンの複製してグループ化しておく
2,リギング>スケルトン>SplineIKで親と最短クリック
3,できたcurve3に(curveの名前を変えちゃダメ)
FX > nHair > Assign Hair System > New Hair System
していく※curve3は、自動的にfollicle1の階層に移動
hairSystem2OutputCurvesの下にいる
curve4
ノードエディタの+で新規タブを開き入力と出力を接続ボタンを押し
curveShape4のWorldSpace[0]をikHandle1のinCurveに繋ぎます。
ジョイントから複製そしてchain_handle_01を作成し、curve1をペアレントコンストレインでリグ1本完成
リグの方のジョイントチェーンをスケルトンのジョイントチェーンに方向コンストレインしていく
最後にスケルトンのジョイントチェーン頭にchain_handle_01を位置コンストレインしてあげれば動くはず。

この作業を自動化した。
ジョイントチェーンの頭のジョイントを選択して実行するとnHairリグが作られる。
ーーMELーーーーーーーーーーーーーーーーーー

//JointChain Head Select To Make nHairRig 
string $selectedArr[] = `ls -shortNames -sl`;
string $selectedArrFUllPath[] = `ls -long -sl`;
print($selectedArr);
string $selectedJoint = $selectedArr[0];
string $buffer[];
$numTokens = `tokenize $selectedArr[0] "|" $buffer`;
$bufferlong=size($buffer);
string $selectedJointShort = $buffer[$bufferlong-1]+"";

print("$selectedJoint= "+$selectedJoint+"\n");
print("$selectedJointShort= "+$selectedJointShort+"\n");
string $selectedJointFullPath = $selectedArrFUllPath[0];
print("$selectedJointFullPath= "+$selectedJointFullPath+"\n");
string $jointNodes[] = `ls -dag $selectedJoint`;
string $jointNodesFullPath[] = `ls -long -dag $selectedJointFullPath`;
//duplicate
string $RigJoints[] =`duplicate $selectedJoint`;

$RigJointslong=size($RigJoints);
//name check
for($i = 0; $i <$RigJointslong;$i++){
    string $rig_j_name=$RigJoints[$i];
    print("$rig_j_name= "+$rig_j_name+"\n");
}
string $RigJointTop = $RigJoints[0];
print("RigJointTop= "+$RigJointTop+"\n");
parent -world $RigJointTop;
string $groupName = $selectedJointShort+"_group";
group -n $groupName $RigJointTop;
print("groupName= "+$groupName+"\n");
string $rigJointTop =`rename $RigJointTop $selectedJoint`;
//SplineIK
print("$rigJointTop= "+$rigJointTop+"\n");
string $rigjointNodes[] = `ls -dag $rigJointTop`;
print($rigjointNodes);
$rigjointNodeslong=size($rigjointNodes);
print("$rigjointNodeslong= "+$rigjointNodeslong+"\n");
string $rigJointEnd = $rigjointNodes[$rigjointNodeslong-1];
print("$rigJointEnd= "+$rigJointEnd+"\n");

string $ikHandleReturn[] =`ikHandle -solver ikSplineSolver -startJoint $rigJointTop -endEffector $rigJointEnd`;
print($ikHandleReturn);
string $ikHandleRef = $ikHandleReturn[0];
print("$ikHandleRef= "+$ikHandleRef+"\n");
$ikHandleReturnlong=size($ikHandleReturn);
//name check
string $curveRef;
for($i = 0; $i <$ikHandleReturnlong;$i++){
    string $ikHandleRet_name=$ikHandleReturn[$i];
    print("$ikHandleRet_name= "+$ikHandleRet_name+"\n");
    string $type = `objectType $ikHandleRet_name`;
    string $nodetype= `nodeType $ikHandleRet_name`;
    print("$type= "+$type+"\n");
    //if($type == "transform"){
    print("$ikHandleRet_name= "+$ikHandleRet_name+" type= "+$type+" nodetype= "+$nodetype+"\n");
    //}
    $index=`gmatch $ikHandleRet_name "curve*"`;
    if($index > 0){
        $curveRef = $ikHandleRet_name;
        print("$ikHandleRet_name= "+$ikHandleRet_name+" isHit!! "+"\n");
    }
}

//string $curveRef = $ikHandleReturn[2];
print("$curveRef= "+$curveRef+"\n");

select -r $curveRef;
assignNewHairSystem;
//root hairsystem all arr
string $rootHairSystemAll[]=`ls -assemblies "hairSystem*"`;
print($rootHairSystemAll);
print("$groupName= "+$groupName+"\n");
//parent $rootHairSystemAll[0] $groupName;
//parent $rootHairSystemAll[1] $groupName;
//parent $rootHairSystemAll[2] $groupName;
$rootHairSystemAlllong=size($rootHairSystemAll);
//name check
string $hairSystemShape;
for($i = 0; $i <$rootHairSystemAlllong;$i++){
    string $rig_hs_name=$rootHairSystemAll[$i];
    print("$rig_hs_name= "+$rig_hs_name+"\n");
    parent $rig_hs_name $groupName;
    string $rig_hs_Arr[]=`listRelatives -children $rig_hs_name`;
    print($rig_hs_Arr);
    $rig_hs_Arrlong=size($rig_hs_Arr);
    if($rig_hs_Arrlong>0){
        $index=`gmatch $rig_hs_Arr[0] "hairSystemShape*"`;
        if($index>0){
            $hairSystemShape = $rig_hs_Arr[0];
            print("$hairSystemShape= "+$hairSystemShape+"\n");
        }
    }

}
string $hairSystemShapeAtt1=$hairSystemShape+".solverDisplay";
string $hairSystemShapeAtt2=$hairSystemShape+".collideWidthOffset";
setAttr $hairSystemShapeAtt1 1;
setAttr $hairSystemShapeAtt2 1;

parent $ikHandleRef $groupName;
string $rootNucleus[]=`ls -assemblies "nucleus*"`;
print($rootNucleus);
$rootNucleuslong=size($rootNucleus);
if($rootNucleuslong>0){
    print("$rootNucleuslong= "+$rootNucleuslong+"\n");
    if( `objExists $rootNucleus[0]` ) {
        parent $rootNucleus[0] $groupName;
    } else {
        warning("No nucleus exists");
    }
}

string $hairSystem2OutputCurves=$rootHairSystemAll[2];
string $hairSystem2OutputCurveArr[]=`listRelatives -children $hairSystem2OutputCurves`;
print($hairSystem2OutputCurveArr);
string $curve2 = $hairSystem2OutputCurveArr[0];
string $curve2Arr[]=`listRelatives -children $curve2`;
print($curve2Arr);
string $curveShape2 = $curve2Arr[0];
print("$curveShape2= "+$curveShape2+"\n");
string $output1 = $curveShape2 + ".worldSpace[0]";
print("$output1= "+$output1+"\n");
string $input1 = $ikHandleRef + ".inCurve";
print("$input1= "+$input1+"\n");
connectAttr -f $output1 $input1;

//select -r $rigJointTop;
string $RigHandleJoints[] =`duplicate $rigJointTop`;
$RigHandJointslong=size($RigHandleJoints);
//name check
for($i = 0; $i <$RigHandJointslong;$i++){
    string $rig_hj_name=$RigHandleJoints[$i];
    print("$rig_hj_name= "+$rig_hj_name+"\n");
}
string $RigHandleJointsChild=$groupName+"|"+$RigHandleJoints[0]+"|"+$RigHandleJoints[1];
print("$RigHandleJointsChild= "+$RigHandleJointsChild+"\n");
delete $RigHandleJointsChild;
string $RigHandleJointsTop=$groupName+"|"+$RigHandleJoints[0];
print("$RigHandleJointsTop= "+$RigHandleJointsTop+"\n");
string $RigHandleJointsTopName=$selectedJointShort+"_handle";
print("$RigHandleJointsTopName= "+$RigHandleJointsTopName+"\n");
string $rigJointTop =`rename $RigHandleJointsTop $RigHandleJointsTopName`;
string $RigHandleJointsTopNameRadiusAtt  = $RigHandleJointsTopName + ".radius";
setAttr $RigHandleJointsTopNameRadiusAtt 15;
parentConstraint -maintainOffset $RigHandleJointsTopName $curveRef;
/////////////////////////////////////////RIG OK
$rigJointlong=size($rigjointNodes);
for($i = 0; $i <$rigJointlong;$i++){
    string $rig_j_name=$rigjointNodes[$i];
    print("$rig_j_name= "+$rig_j_name+"\n");
    string $joint_j_name = $jointNodesFullPath[$i];
    print("$joint_j_name= "+$joint_j_name+"\n");
    orientConstraint -maintainOffset $rig_j_name $joint_j_name;
}
////////////////////////////Joint Move OK
pointConstraint -maintainOffset $selectedJointFullPath $RigHandleJointsTopName;
string $blendShapeReturn[] =`blendShape $curveRef $curve2`;
$blendShapeReturnlong=size($blendShapeReturn);
string $blendShapeName;
for($i = 0; $i <$blendShapeReturnlong;$i++){
    string $blendshapeRet_name=$blendShapeReturn[$i];
    print("$blendshapeRet_name= "+$blendshapeRet_name+"\n");
    $index=`gmatch $blendshapeRet_name "blendShape*"`;
    if($index > 0){
        $blendShapeName = $blendshapeRet_name;
        print("$blendshapeRet_name= "+$blendshapeRet_name+" isHit!! "+"\n");
    }
}

string $blendShapeAtt=$blendShapeName+"."+$curveRef;
//0 is Move 1 is Stop
setAttr $blendShapeAtt 0.90;

こんなかんじ 画像をクリックしてGIF再生

——–以下しらべもの———–
whatIs assignNewHairSystem;
// 結果: Mel procedure found in: C:/Program Files/Autodesk/Maya2016/scripts/startup/DynCreateHairMenu.mel //

string $hsystems[] = ls -type hairSystem;
int $i;
for( $i = 0; $i < size( $hsystems ); $i++ ){
string $hsys = $hsystems[$i];
string $annot = format -s $hsys $fmt;
menuItem -label $hsys
-image “hairAssignHairSystem.png”
-command (“assignHairSystem “+$hsys)
-annotation $annot;
}

whatIs assignHairSystem;

// 結果: Mel procedure found in: C:/Program Files/Autodesk/Maya2016/scripts/others/assignHairSystem.mel //
ーーーーーーーーーーーーーーーーーーー
⑦次に衝突判定(コリジョン)の設定を行います。
体はバラバラなので体を結合したメッシュを用意して
Genesis8FemaleSkin_for_MantCollider
衝突判定用のジオメトリに使います。
Genesis8FemaleSkin_for_MantColliderを選択して FX > nCloth > Create Passive Collider を実行します。
nRigid1が作成されます。

ーーーブレンドシェイプをつかった調整ーー
⑩揺れ具合などの調整は、hairSystem1の各アトリビュートで行いますが、タイミング毎に調整するのは大変です。
揺れを抑えるのみであれば、BlendShapeのWeightアトリビュートを使用すると簡単に制御できます。
curve1とcurve2を選択してBlendShapeを設定し、
curve2のアトリビュートについたBlendShape1のcurve1というWeightアトリビュート(0~1)を調整して制御します。

ーーーオフセット用のコントローラーを付ける場合ーー
⑪次に、オフセット用のジョイント(コントローラー)の設定を行います。
再生を止めて0フレーム目に戻し、初期ポーズにします。
joint1~7のジョイントを複製し、joint1Offset~7に名前を変更します。
joint1Offset~7のジョイントを複製し、joint1Offset_space~7に名前を変更します。
joint1Offset_spaceジョイントの中にそれぞれjoint1Offsetを入れていきます。
※Spaceノードについてはこちら。https://backbone-studio.com/post-237-3/

⑫一番親のjoint1Offset_spaceを選択し、FreezeTransformationsを実行してRotateの値をjointOrientに移植します。

joint1Offset_space~7からjoint1~7へTranslateとRotateをコンストレインします。