(株)回にてCMなどの制作に携わり、本誌199号(2015年3月号)のエフェクト特集にも寄稿してくれた小松 泰氏。今回はTDの経験をもつ小松氏に、自分でスクリプトを書くアーティスト向けのライブラリやエディタの紹介に加え、簡単なスクリプトも併せて解説してもらう。

※本記事は月刊「CGWORLD + digital video」vol. 205(2015年9月号)からの転載となります

TEXT_小松 泰(回) / Tai Komatsu(cai)
EDIT_小村仁美 / Hitomi Komura(CGWORLD)、山田桃子 / Momoko Yamada



自分でスクリプトを書くアーティスト向けのツール群

現在筆者の勤務する株式会社 回ではCMをメインに様々な案件の制作に携わっています。それらのほとんどは制作スパンの短いプロジェクトなので、できる限り小回りの利く環境での制作を心がけています。そのためほぼデフォルト環境に近い状態での制作が多いのですが、今回紹介するSOuPはフリーで配布されているということもあり、要所要所で活用しています。プロシージャルなデータの構築を念頭に置いて開発されているという点も、修正対応の速度といった観点から見逃せません。

  • 小松 泰(株式会社 回)
    都内プロダクションでTD、FXアーティストなどを経て2014年5月より株式会社回所属。フルCG映画のパイプライン制作やエフェクトショット制作など、幅広い分野での経験を活かし、最近ではゼネラリストとしてCM制作などに従事している。
    cai.lt
    blog.taikomatsu.com

また、既存のツールではどうしても解決が難しい、ネット上を探してはみたもののなかなか要求に合うものが見つからないということも多く、そういう場合は結局自作のスクリプトを書くのが一番早かったりもします。特にMayaはスクリプトの必要な場面が非常に多いと感じます。そういった場合に手軽にツールを書くことのできるPyMELは非常に重宝するライブラリです。しかしジオメトリのデータを扱うなど、PyMELでは速度的に不安があることもあります。そういった場合はMayaのAPIを叩くことのできるMaya標準のPythonライブラリOpenMayaを併用することで、比較的高速に処理を行うことができます。

加えて、Mayaでの作業のみに限らず、プログラムを書く際にはやはり使いやすいテキストエディタが欠かせません。ここでは、上記3つのツール、ライブラリに加えて、最近筆者が好んで使用しているテキストエディタAtomも併せて紹介したいと思います。

作業環境
●主な使用ソフトウェア
Autodesk Maya 2014
Houdini 14
Adobe After Effects CC
NUKE 9.0

次ページ:
TOOL 01 SOuP(Peter Shipkov)

[[SplitPage]]

TOOL 01
Tool:SOuP
Maker:Peter Shipkov

SOuPは、Peter Shipkov氏を中心に開発が進められているMaya用のノードライブラリプラグインです。その名前からもわかる通りHoudiniのSOPに大きく影響を受けており、プロシージャルなデータを構築するためのノード群が多数提供されています。具体的な何かを実現するためのものではなく、ノードの組み合わせにより様々なデータを構築することができ、これらを駆使することで多岐にわたる表現が可能です。また、Windows、OS X、Linux、全てのプラットフォームに対応して います。

SOuP
対応アプリケーション:Autodesk Maya 2014以降(2011~2013対応版もあり)
対応OS:対応アプリケーションに準ずる
価格:無料
問い合わせ:Peter Shipkov
E-mail:pshipkov@yahoo.com
www.soup-dev.com

入手方法

SOuPのWebサイト(www.soup-dev.com)のToolsからダウンロード可能です。この記事を執筆している時点で、最新版はMaya2014から2016までのバージョンで使用可能、それ以前のバージョンはOld buildsとして提供されています。中身はプラグイン本体とスクリプト、アイコンがメインです。これらを適切な場所にコピーすると使用可能になります。


SOuPWebサイトのダウンロードページ

基本機能

SOuPは特定の何かを実現するためのツールというよりは、よりプロシージャルなデータをつくるためのツールセットです。そのため、応用によっては開発者さえ想像していなかった表現をすることも可能です。現在サンプルとして公式サイトで紹介されているだけでも、FluidのUpResやDelta Mush、VoronoiFractureなどのポピュラーなCG技術を実装したものから、Paint Effectsのデータをいじったりポリゴンを部分的に削除するといったちょっと地味なものまで多数紹介されています。

SOuPは非常に強力な反面、主に扱うデータが配列だったりジオメトリだったりと、少しとっつきづらい部分があり、そのせいか思ったほどユーザーも多くない印象です。なので今回はプロシージャルなポリゴンの部分削除という初歩的なケースの紹介をしつつ、SOuPを使用したフローに軽く触れてみたいと思います。この例で使用するSOuPノードは、Group、BoundingObjectの2つです。Groupはその名の通りグループを作成するノードですが、ここで言うグループはMayaのグループではなくHoudiniのそれに近いものです。Groupで条件を指定することで、その条件に合致したコンポーネントがグループとしてまとめられます。今回はBounding Objectで囲った部分をGroupとして登録したいと思います。

まず何らかのオブジェクトを作成します。これを複製し、2つの平面が重なっている状態にします。今回はStanford Bunnyを使用することにします。それらを選択した状態でNode Editorを開きます。Node Editor上でTabキーからdeleteComponentとgroupをそれぞれ作成します。それを画像Aのように接続します。一番左のmeshノードが大元の形状となります。それをgroupに接続しboundingObjectを使って範囲指定を行うことで、グループが作成されます。これをdeleteComponentsに渡し、指定した箇所のフェイスを削除します。deleteComponentsはMayaのデフォルトノードでその名の通りコンポーネントを消すためのもので、通常のモデリングなどの際にも裏で勝手につくられている非常にシンプルなノードです。groupノードを介することで、手で選んだもの以外にもプロシージャルに消すことができるようになります。Bounding Objectは、他のノードに比べると接続するものが少しややこしいのですが、groupのAttribute Editorから作成することが可能です。これを使うと直感的に作業が行えるので非常に便利です。


ノードの接続例。赤で囲ってある箇所が手動で接続した箇所


  • Bounding Objectで囲った箇所を削除。group1でPolygon Faceを選択することで、ポリゴン単位での削除を行なっています


  • 赤で囲われたボタンを押すことで新たにBounding Objectが作成され、自動で接続されます。Bounding Objectは複数接続することが可能。AttributeTransferでも同様に作成できます

活用例

SOuPの良さのひとつはやはり大量の内部情報(ジオメトリの頂点やノーマルなど)を切り離してシンプルに扱えることだと思います。例えばこれまでパーティクルやスクリプトを通してしか実現できなかったことも、比較的手軽に扱うことができます。Mayaのパーティクルはその柔軟性こそ特筆すべきもので はありますが、キャッシュなどの扱いが面倒なケースも多々あります。

SOuPでは任意のジオメトリの表面、または内部にポイントクラウドを発生させ、これをソースにしてインスタンサにデータを渡すことができます。

また、SOuPといえばこれというほど有名な機能として、FluidのUpResがあります。これは対象のFluidコンテナを選択し、シェルフ上のSOuPアイコンを押すと出てくるメニューからupresFluidを選択するだけで実行可能です。


SOuPで木々の配置を行なった例。ポイントクラウドはmesh2arraysで作成。メッシュの表面にポイントクラウドを発生させたい場合は、scatterよりもmesh2arraysの方がポイントのリラックスなどがある分、扱いやすいです。ジオメトリのノーマルはpointsOnMeshInfoを使用して取得。最後に作成したarrayデータをそれぞれarrayToDynArraysに繋いでインスタンサに渡しています


FluidにUpResを施したところ。左側がオリジナルで、右側がUpResされたもの。Base Resolutionが120、Auto Resizeなしで計算したものにResolution Multiplierで5を与えたのでUpRes後のBase Resolutionは600。Deltaの値などを少しいじって調整しました。いちいちシミュレーションし直さなくても結果が見られるのは素晴らしいです


SOuPのシェルフのボタンを押すと出てくるメニュー。ここでノードを選ぶと簡単なものであれば適切にセットアップが行われます。upresFluidなどはここから一発で設定可能。しかし先ほどの森の例のように、複雑なネットワークになる場合は最初からNodeEditorで組んでしまった方が楽かもしれません

次ページ:
TOOL 02 PyMEL(Luma Pictures)

[[SplitPage]]

TOOL 02
Tool:PyMEL
Maker:Luma Pictures

PyMELは数々のハリウッド映画のVFXを手がけるVFXスタジオのLumaPicturesにより開発されているMaya用Pythonライブラリです。BSDライセンスのオープンソースソフトウェアで、GitHubにてソースコードが公開されています(github.com/lumapictures/pymel)。Maya 2011からはMayaに標準搭載となり、インストール作業なしにすぐ使えるようになりました。

PyMEL
対応アプリケーション:Autodesk Maya(2011からは標準搭載)
対応OS:対応アプリケーションに準ずる
価格:無料
問い合わせ:Luma Pictures
E-mail:www.lumapictures.com
GitHub.com/LumaPictures/pymel

入手方法

Mayaに標準搭載されているため、2011以降のバージョンをお使いの方はMayaのインストールのみでそのままお使いいただけます。2010以前の方は、前述のGitHubからソースをダウンロードし、Pythonパスを設定する必要があります。

[cmds]
from Maya import cods
for o in cmds.ls(sl=True, type='transform'):
cmds.setAttr('%s.tx' % o, 10)

[PyMEL]
import pymel.core as pm
for o in pm.selected(type='transform'):
o.tx.set(10)

基本機能

PyMELはMaya標準のcmdsと、一部OpenMaya(MayaAPIライブラリ)をラッピングしたライブラリで、よりPythonらしく、かつ簡潔にツールを書くことができます。cmdsとの大きなちがいはオブジェクト単位での処理を行いやすいということです。ここで言うオブジェクトとはMayaのオブジェクトではなく、Pythonのオブジェクトのことです。cmdsではMELと同様に文字列を扱ってオブジェクトを判別したりしますが、PyMELでは各ノードやアトリビュートにクラスが用意されており、オブジェクト指向での記述が可能です。

例えば選択したオブジェクト全てのtranslateXを10にしたい場合、cmdsとPyMELではそれぞれ左のようになります。cmdsでは文字列を使いアトリビュートを判定しているのに対し、PyMELではオブジェクトのプロパティに対して処理を行なっています。cmdsではsetAttrという関数を実行する際に、どのオブジェクトに対して実行するのかという情報をいちいち与えないといけません。これに対しPyMELでは、ループで与えられたオブジェクトに対してtxをセットするだけです。PyMELはcmdsのラッパーなので、実際には内部でsetAttrが動いているのですが、ユーザーが細かいことを気にする必要はありません。

この程度の例では大きなちがいは感じられないかもしれませんが、もう少し大きなツールを書く際にはこれがじわじわと効いてきます。ただし、filterExpandなど一部のコマンドでは文字列が返ってきたりなどする場合もあるので、慣れるまではどういう情報が返ってきているか適宜確認しながら進めるのが良いでしょう。

活用例

試しに何か簡単なツールを書いてみたいと思います。筆者が書くスクリプトはほとんどがUIなしの簡単なスクリプトで、さらにその大半がそのショットを作成する際に使用するのみの捨てスクリプトです。そういう場合によく登場するのが、大量オブジェクトに対してのループ処理です。例えば任意のオブジェクトの階層以下にある特定のネーミングルールを満たしたオブジェクトに対して何らかの値をセットする、または削除する、setsにまとめるなど、手作業で行うには面倒な上にエラーも介在しそうなケースや、ただひたすら数が多い場合など、ループ処理はとにかく頻繁に使用します。

とは言えただのループ処理を書くだけでは面白くないので、エクスプレッションを自動で設定するスクリプトを書いてみます。これはsetAttrをくり返すのではなく、エクスプレッションとなる文字列を作ってexpression関数で一気に設定するものです。

このシーンは赤青のボックスとロケータがあるだけのシンプルなシーンです。これをロケータの動きに合わせてボックスが回転するようにエクスプレッションを組んでいきます。ただし、完全にロケータの動きに沿ってしまうと単調な動きにも見えてしまいそうなので、少しだけ動きに幅をもたせるために、各種オフセットのパラメータを各ボックスに設定したいと思います。下がそのコードです。

expressionコマンドでエクスプレッションを作成するための文字列を作成するのと同時に、各ボックスでオフセットなどのパラメータをいじることができるようにするためのアトリビュートを追加しています。また、そのアトリビュートに対してランダムな値を入れることで、動きに多少のバラつきをもたせています。これを全てのボックスに対して手動で行おうと思うと大変面倒です。サンプルでは30個程度のボックスに対して適用したので最悪手動でも設定できそうですが、これが1,000、2,000という数字だったらもはや現実的ではありません。

PyMELを使うことで、cmdsで文字列を駆使するのに比べるとどのオブジェクトに対しての処理なのかがわかりやすくなるため、今後の使い回しや軽い調整などの際にも有効だと考えられます。これはあくまでも個人的な感想ではあるのですが、PyMELに慣れてしまった今となっては、たったこれだけの長さのスクリプトでもcmdsのように文字列を切り貼りするスタイルでスクリプトを書くのはなかなか辛いものがあります。まして言語の限界というわけではないと思うとなおさらです。ぜひ一度実際に試してみてその快適さを体感していただきたいです。もちろんcmdsの方が楽だった場合にはcmdsに戻るのもありだと思います。

import pymel.core as pm
from random import uniform

expr_init_tmpl = '''
float $p, $_loc, $w, $pp, $np;
float $loc = {ctl}.tx;
float $max_angle = {angle};
'''

expr_tmpl = '''
$p = {obj}.tx;
$_loc = $loc + {obj}.offset;
$w = {ctl}.sx * {obj}.influenceWidthRatio * 0.5;
$pp = $p + $w;
$np = $p - $w;
{obj}.ry = linstep($np, $pp, $_loc) * $max_angle;
'''

ctl = pm.PyNode('ctrl_box1')
expr_str = expr_init_tmpl.format(ctl=ctl.name(), angle=90.0)
for o in pm.listRelatives('box1_grp', c=True, pa=True, type='transform'):
 try:
  o.addAttr('influenceWidthRatio', sn='ir', at='double', dv=1.0)
  o.addAttr('offset', sn='off', at='double', dv=0)
 except Exception, e:
  print e
 o.ir.set(uniform(0.5, 1.2), k=True)
 o.off.set(uniform(-2.0, 2.0), k=True)
 expr_str += expr_tmpl.format(obj=o.name(), ctl=ctl.name())
pm.expression(s=expr_str, alwaysEvaluate=True, unitConversion='all')
print('# Done')


セットアップ前のシンプルなシーン。赤青にマテリアルが設定された縦長のキューブのグループとロケータがあるだけ


ロケータを左から右に動かした例。上段は回転幅を狭くしてランダムを強くしたもの、下段が回転幅を広くしてランダムを弱くしたもの。エクスプレッション側で回転の幅とロケータのスケールが接続されており、直感的にグラデーションの幅を決めることができます


エクスプレッションをアサインすると同時にアトリビュートを追加し、乱数で値を設定。influenceWidthRatioはロケータの動きに対する回転幅です。これを下げると小さな幅で一気に回転します。offsetはロケータの位置との差に対するオフセット。本来回転が始まる場所に到達する前に回転する、またはその逆ができるため、回転開始のバラつきを作成することができます。こういったパラメータを用いてエクスプレッションの動きにワンクッション置くことでバラつきが個々で制御可能になり、ほしい結果がつくりやすくなると考えて追加しました

次ページ:
COLUMN PyMELの速度問題とOpenMaya

[[SplitPage]]

COLUMN PyMELの速度問題とOpenMaya

前述の通りPyMELはcmdsやOpenMayaのラッパーであり、使いやすさを実現するために裏であれこれ様々な処理を行なっています。そのため、速度的には不満なケースも多々あります。筆者が行なった、簡単なシュリンクラップのテストでは、PyMELとOpenMayaで50倍以上の速度差が出ました。頂点の移動など、大量のデータを扱う際にはOpenMayaの使用も検討してみると良いかもしれません。

OpenMayaはMayaのAPIライブラリで、もともとC++でのみ使用可能なライブラリでしたが、MayaがPythonに対応した際にPythonからも実行可能になり、C++でプラグインを作成せずとも通常のスクリプトからAPIを叩くことが可能になりました。OpenMayaの詳細に関しては今回は割愛しますが、速度比較の際に使用したスクリプトを例に簡単に解説したいと思います。右がその構文です。

PyMELのみ使用する関数と、OpenMayaを併用する関数の2つを用意しました。今回はOpenMaya版に関してのみ解説を行いたいと思います。この関数の肝はMFnMeshというクラスにあります。これは任意のメッシュの情報を取得したり設定したりするもので、様々なメソッドをもっています。今回は貼り付ける側(球)と貼り付けられる側(キューブ)の2種類があるので2つのMFnMeshのインスタンス(dmfn、tmfn)が必要になります。この2つのインスタンスを作成する際に、MDagPathを引数として渡しています。これはMayaの内部データを表すもののひとつで、特に階層やトランスフォームの情報が必要な場合に使用します。これをMFnMeshに渡すことによって、ワールド座標でのポイントの取得が可能になります。

今度は貼り付ける側の球のポイント全てを取得します。ここで取得できる情報はMPointArrayというポイントの配列用のデータです。ここで取得したポイント全てに対し、forループを使い、getClosestPointで貼り付けられる側のキューブの最近点を取得します。取得したポイントでMPointArrayの情報を更新し、全ての頂点に対して処理が終わったらsetPointsを行い、完了となります。PyMELと比較すると多少煩わしいと感じられるかもしれませんが、プラグインを書くまでもなく、メッシュなどMayaの様々な情報に高速でアクセスできるという点はやはり大きな魅力です。

Arrayや返り値の扱いにおいて、現状のOpenMayaモジュールではPythonらしくない記述が散見されますが、これはMaya 2016で拡充されたPython API 2.0で大幅に改善されているとのことです。今後のさらなる発展に期待が寄せられます。

from pymel.core import *
from time import time
from maya import OpenMaya

def calc_time(f):
 def fn(*args, **kwargs):
  s = time()
  r = f(*args, **kwargs)
  e = time()
  print('[TIME] %f sec' % (e - s))
  return r
 return fn

# [TIME] 17.135000 sec
@calc_time
def stick_pts_pymel(dst, target):
 d = PyNode(dst).getShape()
 t = PyNode(target).getShape()
 s = 'world'
 pts = d.getPoints(space=s)
 for i, p in enumerate(pts):
  pts[i] = t.getClosestPoint(p, space=s)[0]
 d.setPoints(pts, space=s)
stick_pts_pymel('pSphere1', 'pCube1')

# [TIME] 0.319000 sec
# 53.7 times faster than pymel version!!
@calc_time
def stick_pts_openmaya(dst, target):
 d = api.toMDagPath(dst)
 t = api.toMDagPath(target)
 d.extendToShape()
 t.extendToShape()
 dmfn = OpenMaya.MFnMesh(d)
 tmfn = OpenMaya.MFnMesh(t)
 s = OpenMaya.MSpace.kWorld
 pts = OpenMaya.MPointArray()
 dmfn.getPoints(pts, s)
 p = OpenMaya.MPoint()
 for i in xrange(pts.length()):
  tmfn.getClosestPoint(pts[i], p, s)
  pts.set(p, i)
 dmfn.setPoints(pts)
stick_pts_openmaya('pSphere1', 'pCube1')

次ページ:
TOOL03 Atom(GitHub)

[[SplitPage]]

TOOL 03
Tool:Atom
Maker:GitHub

Atomはgitのオンラインレポジトリサービスで有名なGitHubが提供するオープンソースのテキストエディタです。同社のElectronフレームワークを用いて開発されており、HTML、JavaScript、CSSといったWeb関連の技術でカスタマイズ可能であるという点が非常にユニークです。拡張機能を追加するパッケージや見た目を変化させるテーマのインストールもエディタ上から行えるため、好みのカスタマイズも簡単です。

PyMEL
対応OS:Windows 8、7、OS X 10.8以降、Red Hat Linux、Ubuntu
価格:無料
問い合わせ:GitHub
atom.io

入手方法

Atomの公式サイト(atom.io)からダウンロードが可能です。Windows、OS X、Linuxのいずれのプラットフォームでも動作します。


基本機能

Atomは比較的新しいテキストエディタで、パッケージによるカスタマイズ、ウインドウの分割、タブによるファイルの切り替え、ショートカットのカスタマイズなどなど、最近のテキストエディタの主な機能はほとんど備えていると言って問題ないでしょう。また、GitHubが開発していることもあり、gitとの連携も取りやすくなっているようです。

リリース当時はテキストの置換が遅かったり、各種パッケージの出来があまり良くないなどの問題もあったようですが、GitHubが主導して開発を行なっているためかなりの速度で開発が進んでおり、徐々に大きな問題が解決されてきているようです。


Atomの画面。拡張子でファイルを判別して色分けを行います。Pythonなどのメジャーな言語はデフォルトで色分けが行われるように設定されています。テーマもGitHub上で多数公開されており、当然カスタマイズも可能


パッケージのインストール画面。画面上部の検索窓に任意のキーワードを入れると関連したパッケージがずらりと表示されます。内容を詳しく知りたい場合はパッケージ名をクリックするとWebページが開きます。インストールしたい場合はInstallボタンをクリックするだけでOK

活用例

Atomでは非常に様々なパッケージが公開されており、ソースコードの記述補助などは当然のこと、AtomからMaya Pythonを実行するためのパッケージも公開されています。Mayaパッケージを使用する際、Maya側では送られたコマンドを受け取るためuserSetup.pyに下のコードのような記述が必要です。あとはAtomにMayaパッケージをインストールして、[Ctrl+Alt+r]を押すことでコマンドの実行が可能となります。

from maya import cmds
cmds.commandPort(name=":7005", sourceType="python")


  • "maya"と入力して検索を行なったところ。なぜかMayaだけでなくMODOやNUKEのパッケージも見つかりました


  • Atom上からPythonを実行したところ。2,500個のキューブをPyMELで配置しマテリアルのアサインを行いました。特にもたつくこともなく快適に使用できます。どこも選択していない場合は全体が実行されます。ただし現状ではMayaの結果を取得する仕様になっているらしく、無限ループなどを書いてしまった場合はAtomごと(そのタブのみ)止まってしまうので注意が必要です



  • 月刊CGWORLD + digital video vol.205(2015年9月号)
    第1特集「お役立ちツールカタログ 」
    第2特集 映画『進撃の巨人 ATTACK ON TITAN』

    定価:1,512円(税込)
    判型:A4ワイド
    総ページ数:152
    発売日:2015年8月10日
    ASIN:B00YA7RMAA