チュートリアル / 読んで触ってよくわかる!Mayaを使いこなす為のAtoZ
第20回:ノードに関するあれこれ
- Maya
- ゲーム
- コラム
- チュートリアル
- 中級者
- 学生・初心者
- 教育
- 映画・TV
ヒストリの使い方 → ヒストリはノードである → 基本オブジェクトのノード構成 → ノードの編集。ヒストリの回から数えれば実に6回にわたりノードに関して見てきました。普段はそれほど意識してみることのない内部構造を通して、Mayaってこういう風に動いているんだ、と感じてもらえましたでしょうか。
まあ、さすがにこれだけ長くかしこまった内容が続くと眠たくなってくると思いますので、これまで見てきた中でフォローできてない、ノードに関する断片的な情報を紹介して締めたいと思います。
よくわからない場合は「そういうものか~」という感じで眺めておいてもらえると幸いです。
基本的にどちらで実行しても同じノードが出来ます。polyExtrudeFaceノードがヒストリとして作られます。
Extrudeを実行した時の選択情報はpolyExtrudeFaceノードのinputComponentsというアトリビュートに記録されます。フェースが選択されていればフェースが、オブジェクトが選択してあればオブジェクト全体を選択情報としてセットします。
このinputComponentsはcomponentListというタイプのアトリビュートです。通常隠してあるので普通には見ることも後から編集することもできません。しかしスクリプトでアクセスして編集することができます。
選択情報はコンポーネントの番号で指定されます。なので後からコンポーネント数が変わる変更を加えると、ヒストリがうまく機能しなくなる可能性があります。
まあ逆に言えば、ヒストリを操作すると形状が壊れることがあると思いますが、これが原因です。
さてさて、それでは実際にコンポーネントリストをいじって見ましょう。
1)Create > Polygon Primitives > Cubeでキューブを作ります。
2)キューブの上面フェースを選択し、Edit Mesh > Extrudeで押し出します。
3)次のMELスクリプトをスクリプトエディタで実行して、登録されている選択情報を確認してみましょう。
“polyExtrudeFace1”はpolyExtrudeFaceノード名です。もし名前が違うようなら書き換えてください。
実行すると“f[1]”という結果が返ってくると思います。
つまりフェースの1番を押し出してます、ということです。俗にID1のフェースと呼んだりします。
(コンポーネントの番号は0始まりなので0, 1, 2, 3...となります。なのでID1なら2番目のフェースです。)
MELでコンポーネントリストを変更してみましょう。
以下を実行してみてください。
するとこうなります。
押し出される場所が変わりました。
それではMELの意味を確認していきましょう。
setAttr はアトリビュートをセットする時に使うMELです。この後に続く文字はすべてこのコマンドのオプションになります。
polyExtrudeFace1.inputComponents はセットするアトリビュートを「ノード名.アトリビュート名」で指定しています。
-type componentList はアトリビュートのタイプを指定しています。ここではcomponentListタイプを指定します。
1 “f[0:2]” の1はセットするコンポーネントリストの数で、数字の分この後にコンポーネントリストを指定できます。
“f[0:2]”はフェースの0~2番という意味になります。コンポーネントIDは書き方にルールがあり、” ~”の代わりにコロン”:”を使います。例えばエッジの10番から15番ならe[10:15]となります。
では続けてほかの設定でも試してみましょう。以下を実行してみてください。
するとこうなります。
フェースの0番と2番が押し出されるようになりました。
最後に以下を実行してみましょう。
結果はこうなります。
*は全てという意味になります。すべてのフェースが押し出されます。
こうしてあればpolyExtrudeFaceより前のヒストリでポリゴン数が増減しても、すべてのフェースが押し出されるようになります。そう、これが重要なのです。
例えば次の例を見てください。
キューブを作ってMesh > Smoothを実行する際、すべてのフェースを選択して実行するとコンポーネントリストはf[0:5]になります。
後からpolyCubeのヒストリを変更すると、フェース数が増えてしまいます。しかしコンポーネントリストは5番目のフェースまでしか処理しないように設定されているので、スムースがオブジェクト全体に効かずに、下図の様に結果が変になります。
ここではSubdivisions Heightを1から2へ増やしたため、縦方向にポリゴンが増え、結果として側面の一部がスムースされなくなります。
代わりにオブジェクトを選択してスムースすればコンポーネントリストはf[*]となります。
なので、あとからキューブの分割数を変更してもオブジェクト全体がスムースされます。
場合によってはオブジェクトを選択して実行するのと、フェースをすべて選択して実行するのとでは結果が異なることに注意してください。なるべくオブジェクトを選択して実行した方がヒストリが壊れにくくてよいと思います。
TranslateやRotateといった、ユーザーが数字を入力できるアトリビュートのタイプは数字です。プログラム的にさらに細かいタイプの種類があるのですが、まあ、数字です。ノードからノードへ、単純に数字を渡しています。
一方メッシュデータはmeshというデータタイプで、頂点・エッジ・フェース・UV・法線などのデータをひとまとめにしています。ユーザーが簡単に作ることは出来ません。
データはヒストリの始めで作られ、順々にノード間で受け渡して各々がmeshデータを直接加工しています。
なのでヒストリの数分meshデータがあるのではなく、”一つ”のmeshデータをノード間で流用して加工しています。ノードでmeshデータを受け取り、加工して次に渡す。渡してしまえばそのノード内のmeshデータは消えます。(基本的にはそうなのですが、例外もあります)
図にしてみると、こんな感じで、一つのmeshデータを加工しながら渡していきます。
最後には画面に見えているmeshデータのみになります。なのでメモリ内のデータ量は多くならずに済みます。
問題はシェーダーと関係ないヒストリのノードの削除方法です。これらは見つけにくいのです。
例えばハイパーシェードに出てくるノードはすべてアイコンとして見られます。
ノードが一つ一つ表示されるので、普通に選択して捨てればいいだけです。なので特に問題にはなりません。
一方polySmoothFaceなどのヒストリが、Delete Historyで削除されずにシーン内に残ってしまうと厄介です。例えばヒストリが単体のノードとして残っている場合、一度ハイパーグラフから消えると下図の様に、見た目上消えてしまいます。
こうなるとGUIから簡単にアクセスできなくなります。なので、ゴミノードとしてシーン内を漂い続けることになります。もっとも、表示されないので何も処理はされないので、パフォーマンスが落ちることは無いと思います。ただし、ファイル容量とメモリ容量を浪費してしまいます。
大抵のものはFile > Optimize Scene Sizeを実行することで削除することができます。
が、削除されなく残るものも結構あることがあります。実は、最も確実なゴミ捨ては、必要なものだけ選んでExport Selectionすることです。選択したものに関連するものだけを書き出す方が、関係ないものを捨てるより簡単なんです。
シーン内にあるオブジェクト数に対してやたらとファイルサイズが大きい場合、大抵こういうゴミノードが大量に残っています。サイズが極端に大きい場合は中間オブジェクトが残っている可能性が高いです。
ゴミ中間オブジェクトは割とよく発生します。スキニングしたオブジェクトを複製すると、スキニングに使用される中間オブジェクトも複製されて残ってしまいます。
以下のMELをスクリプトエディタで実行してもらえばゴミ中間オブジェクト(meshノード)を削除できます。
下図の様にすっきり消えます。
というわけでようやく念願だった、なかなか読むことのできないマニアックなMayaネタを取り上げることができました。ここまでMayaを把握できればさらに複雑で便利なテクニックを知ることができます。次回はセットとパーティションについてみていきたいと思います。
まあ、さすがにこれだけ長くかしこまった内容が続くと眠たくなってくると思いますので、これまで見てきた中でフォローできてない、ノードに関する断片的な情報を紹介して締めたいと思います。
よくわからない場合は「そういうものか~」という感じで眺めておいてもらえると幸いです。
コンポーネントリストとは?
例えばフェースを選択してEdit Mesh > Extrudeするのと、オブジェクトを丸ごと選択してExtrudeするのとでは、結果の見た目は変わりません。基本的にどちらで実行しても同じノードが出来ます。polyExtrudeFaceノードがヒストリとして作られます。
Extrudeを実行した時の選択情報はpolyExtrudeFaceノードのinputComponentsというアトリビュートに記録されます。フェースが選択されていればフェースが、オブジェクトが選択してあればオブジェクト全体を選択情報としてセットします。
このinputComponentsはcomponentListというタイプのアトリビュートです。通常隠してあるので普通には見ることも後から編集することもできません。しかしスクリプトでアクセスして編集することができます。
選択情報はコンポーネントの番号で指定されます。なので後からコンポーネント数が変わる変更を加えると、ヒストリがうまく機能しなくなる可能性があります。
まあ逆に言えば、ヒストリを操作すると形状が壊れることがあると思いますが、これが原因です。
さてさて、それでは実際にコンポーネントリストをいじって見ましょう。
1)Create > Polygon Primitives > Cubeでキューブを作ります。
2)キューブの上面フェースを選択し、Edit Mesh > Extrudeで押し出します。
3)次のMELスクリプトをスクリプトエディタで実行して、登録されている選択情報を確認してみましょう。
getAttr polyExtrudeFace1.inputComponents;
“polyExtrudeFace1”はpolyExtrudeFaceノード名です。もし名前が違うようなら書き換えてください。
実行すると“f[1]”という結果が返ってくると思います。
つまりフェースの1番を押し出してます、ということです。俗にID1のフェースと呼んだりします。
(コンポーネントの番号は0始まりなので0, 1, 2, 3...となります。なのでID1なら2番目のフェースです。)
MELでコンポーネントリストを変更してみましょう。
以下を実行してみてください。
setAttr polyExtrudeFace1.inputComponents -type componentList 1 "f[0:2]";
するとこうなります。
押し出される場所が変わりました。
それではMELの意味を確認していきましょう。
setAttr はアトリビュートをセットする時に使うMELです。この後に続く文字はすべてこのコマンドのオプションになります。
polyExtrudeFace1.inputComponents はセットするアトリビュートを「ノード名.アトリビュート名」で指定しています。
-type componentList はアトリビュートのタイプを指定しています。ここではcomponentListタイプを指定します。
1 “f[0:2]” の1はセットするコンポーネントリストの数で、数字の分この後にコンポーネントリストを指定できます。
“f[0:2]”はフェースの0~2番という意味になります。コンポーネントIDは書き方にルールがあり、” ~”の代わりにコロン”:”を使います。例えばエッジの10番から15番ならe[10:15]となります。
では続けてほかの設定でも試してみましょう。以下を実行してみてください。
setAttr polyExtrudeFace1.inputComponents -type componentList 2 "f[0]" "f[2]";
するとこうなります。
フェースの0番と2番が押し出されるようになりました。
最後に以下を実行してみましょう。
setAttr polyExtrudeFace1.inputComponents -type componentList 1 "f[*]";
結果はこうなります。
*は全てという意味になります。すべてのフェースが押し出されます。
こうしてあればpolyExtrudeFaceより前のヒストリでポリゴン数が増減しても、すべてのフェースが押し出されるようになります。そう、これが重要なのです。
例えば次の例を見てください。
キューブを作ってMesh > Smoothを実行する際、すべてのフェースを選択して実行するとコンポーネントリストはf[0:5]になります。
後からpolyCubeのヒストリを変更すると、フェース数が増えてしまいます。しかしコンポーネントリストは5番目のフェースまでしか処理しないように設定されているので、スムースがオブジェクト全体に効かずに、下図の様に結果が変になります。
ここではSubdivisions Heightを1から2へ増やしたため、縦方向にポリゴンが増え、結果として側面の一部がスムースされなくなります。
代わりにオブジェクトを選択してスムースすればコンポーネントリストはf[*]となります。
なので、あとからキューブの分割数を変更してもオブジェクト全体がスムースされます。
場合によってはオブジェクトを選択して実行するのと、フェースをすべて選択して実行するのとでは結果が異なることに注意してください。なるべくオブジェクトを選択して実行した方がヒストリが壊れにくくてよいと思います。
コンポーネントIDを確認する
コンポーネントの番号は、オブジェクトを選択してDisplay > Polygons > Component IDsで表示して確認できます。ノード間のデータのやり取りはどう行われているのか
ノードにはアトリビュートがあり、アトリビュートにはタイプがあります。TranslateやRotateといった、ユーザーが数字を入力できるアトリビュートのタイプは数字です。プログラム的にさらに細かいタイプの種類があるのですが、まあ、数字です。ノードからノードへ、単純に数字を渡しています。
一方メッシュデータはmeshというデータタイプで、頂点・エッジ・フェース・UV・法線などのデータをひとまとめにしています。ユーザーが簡単に作ることは出来ません。
データはヒストリの始めで作られ、順々にノード間で受け渡して各々がmeshデータを直接加工しています。
なのでヒストリの数分meshデータがあるのではなく、”一つ”のmeshデータをノード間で流用して加工しています。ノードでmeshデータを受け取り、加工して次に渡す。渡してしまえばそのノード内のmeshデータは消えます。(基本的にはそうなのですが、例外もあります)
図にしてみると、こんな感じで、一つのmeshデータを加工しながら渡していきます。
最後には画面に見えているmeshデータのみになります。なのでメモリ内のデータ量は多くならずに済みます。
使用していないノードの削除方法
シェーダーの場合はハイパーシェードのメニューでEdit > Delete Unused Nodesを実行すれば使用していないノードが削除されます。問題はシェーダーと関係ないヒストリのノードの削除方法です。これらは見つけにくいのです。
例えばハイパーシェードに出てくるノードはすべてアイコンとして見られます。
ノードが一つ一つ表示されるので、普通に選択して捨てればいいだけです。なので特に問題にはなりません。
一方polySmoothFaceなどのヒストリが、Delete Historyで削除されずにシーン内に残ってしまうと厄介です。例えばヒストリが単体のノードとして残っている場合、一度ハイパーグラフから消えると下図の様に、見た目上消えてしまいます。
こうなるとGUIから簡単にアクセスできなくなります。なので、ゴミノードとしてシーン内を漂い続けることになります。もっとも、表示されないので何も処理はされないので、パフォーマンスが落ちることは無いと思います。ただし、ファイル容量とメモリ容量を浪費してしまいます。
大抵のものはFile > Optimize Scene Sizeを実行することで削除することができます。
が、削除されなく残るものも結構あることがあります。実は、最も確実なゴミ捨ては、必要なものだけ選んでExport Selectionすることです。選択したものに関連するものだけを書き出す方が、関係ないものを捨てるより簡単なんです。
シーン内にあるオブジェクト数に対してやたらとファイルサイズが大きい場合、大抵こういうゴミノードが大量に残っています。サイズが極端に大きい場合は中間オブジェクトが残っている可能性が高いです。
ゴミ中間オブジェクトは割とよく発生します。スキニングしたオブジェクトを複製すると、スキニングに使用される中間オブジェクトも複製されて残ってしまいます。
以下のMELをスクリプトエディタで実行してもらえばゴミ中間オブジェクト(meshノード)を削除できます。
proc optimizeUnusedIntermediateMesh()
{
string $nodes[] = `ls -type "mesh" -intermediateObjects`;
string $unused[];
for ( $node in $nodes ){
if ( !size(`listConnections -s false -d true $node`) ){
$unused[ size($unused) ] = $node;
}
}
int $size = `size($unused)`;
if ( $size ){
delete $unused;
}
// Show summary.
//
print ("Removing unused intermediate mesh nodes\n");
print "-------------------------------------\n";
string $longSummary = "Removed " + $size + " unused intermediate meshes";
print $longSummary;
}
optimizeUnusedIntermediateMesh();
{
string $nodes[] = `ls -type "mesh" -intermediateObjects`;
string $unused[];
for ( $node in $nodes ){
if ( !size(`listConnections -s false -d true $node`) ){
$unused[ size($unused) ] = $node;
}
}
int $size = `size($unused)`;
if ( $size ){
delete $unused;
}
// Show summary.
//
print ("Removing unused intermediate mesh nodes\n");
print "-------------------------------------\n";
string $longSummary = "Removed " + $size + " unused intermediate meshes";
print $longSummary;
}
optimizeUnusedIntermediateMesh();
下図の様にすっきり消えます。
まとめ
ノードはMayaの根幹部分なので、これがわかってくると俄然面白くなってきます。できることにも幅が出てきますし、問題が起きても対処がしやすくなります。これが扱えれば十分Maya上級者の仲間入りです。というわけでようやく念願だった、なかなか読むことのできないマニアックなMayaネタを取り上げることができました。ここまでMayaを把握できればさらに複雑で便利なテクニックを知ることができます。次回はセットとパーティションについてみていきたいと思います。