バグフィックス Hardware render animations v0.2.1
前回更新したツールで、背景色が解像度を覆い尽くさないというバグがありました。 masayuki-osaka.hatenablog.com
ので、修正版をリリースしました。 マイナーバージョンアップなのでリンク先は同じです。 ↓ からダウンロードしてください。 http://maya2blender.xsrv.jp/tools/hardware_render_animations/v020/document/
Blender, Maya両対応のハードウェアレンダリングツールを更新いたしました。(v0.2.0)
以前にリリースした、自動でハードウェアレンダー(プレイブラスト、opengl)を行うツールの更新版をリリースいたしました。
ダウンロードページ
Hardware Render Animations (version0.2.0 0.2.1)
http://maya2blender.xsrv.jp/tools/hardware_render_animations/v020/document/index.html
概要
自動的にカメラの画角をメッシュにフィットさせる機能を持ち、沢山のシーンを立て続けにハードウェアレンダリングします。
これにより、カメラアングルの確定していないモーション(特にゲーム用モーション)のプレビューをサポートします。
また、アセット向けに、ターンテーブル動画を作成する機能も持ちます。
※Blenderでは2.79への対応のみです。2.8へは対応していません。
※また、Blenderへの対応は完全ではなく、一部シーンへの opengl 命令が失敗することがあります。
スクリプティングでのコンセプト
自身の練習用に作成したもので、スクリプティングが未熟ですが、試していただけると幸いです。 作成にあたってのコンセプトを以下のようにしました。
基幹部分をDCCツールを問わない共通のモジュールで作る
DCCに依存する部分だけ分岐する
コードをご覧いただければ、MayaとBlenderでの、APIの違いが観察できるかと思われます。
version0.1.0からの変更点
ライセンス
本ツールは GPLv2 or Later の下でリリースされています。
一つの.blendファイルでEeveeとCycles両方をレンダリングする(リンクを重視する方法)
前回の記事で紹介した、一つの.blendファイルに複数のシーンを持たせる方法を使用して
を行ってみます。
Eeveeには影のみを受け取る設定が標準では存在しないため、それをCyclesで補おうというアプローチです。
と、記事を書こうとしている最中に、既に同じことをしている動画を見つけてしまいました・・・
youtu.be
なんとも間が悪い上に、自身もビューレイヤーの有効な使いかたをこれで知り、前回の記事に手を加えた次第です。
影の調整やレンダリング設定の最適化にも触れているので、この手法をお勧めします。
さて、書くことが無くなってしまうので、ここでは敢えてシーンとオブジェクト、コレクションのリンクに拘った手法を紹介します。
この手法はリスクを伴いますが、リンクについて考える一助になれば幸いです。
シーン共通の設定を行う
スザンヌが地面に影を落とすシチュエーションを想定します。
このシーンを基に、Eevee用のシーンとCycles用のシーンへ拡張していきますが、レンダー設定は各シーンで別々に管理されます。
そのため、解像度などの共通させたいシーン設定は、最初の段階で行うことをお勧めします。今回の例では、レンダリングの背景を透明にするための設定をこの段階で行います。
シーンを増やす
シーンを増やす際、Linked Copyを選択してください。
この段階で、Sceneと新しく出来たScene.001、オブジェクト等のリンク関係は図のようになります。
★赤丸のView Layer, Scene Collectionは同じ名前で同じアイコンですが、この二種は別々の物として扱われます。
レンダラーの設定
SceneとScene.001のレンダラーをそれぞれ、EeveeとCyclesにします。
また、それぞれのシーン名をScene.eevee, Scene.cyclesへと変えました。
この結果、各シーンでの関係は次のようになります。
階層とリンク関係を変更
PlaneはCyclesでの落ち影ちとしてのみ必要なので、Eeveeのシーンから取り除きます。
ただし、そのまま削除してしまうと両シーンから失われてしまいます。そこで、Scene.cyclesのアウトライナで、PlaneをScene Collection直下に移動させます。
PlaneがScene.eeveeのScene Collectionから辿れない場所へ移ったことで、Scene.cyclesのみで取り扱えるようになりました。
マテリアルとリンク関係
この段階に限らず、スザンヌにマテリアルを割り当てると両方のシーンへ反映されます。
その理由は、マテリアルはスザンヌの下の階層に配置され、どちらのシーンからでも辿れるようになるからです。
Eevee側でのレンダリング
Scene.eeveeに切り替えてレンダリングすると、プレーンはリンクが無いため現れません。
今回の例ではEevee側でプレーン以外をレンダリングするのが目的なので、これでスザンヌのみのレンダリング結果が得られました。
Cycles側でのレンダリング
Scene.cyclesに切り替えて、落ち影のみを取得するために以下の設定を行います。
この2つは共にCyclesのみの機能で、Eeveeには搭載されていません。
これらはシーンに対してではなくオブジェクトに対しての設定です。そのため、Scene.eevee側へも影響します。
ただし、Eeveeはこの設定を活用することができないため、Scene.eeveeではプロパティにもレンダリング結果にも反映されません。
設定後、レンダリングを行うと落ち影のみが取得できます。
コンポジット
前回の記事で紹介した動画では、Eeveeだけを使用して落ち影用オブジェクトを作成するため、コンポジットが不要でした。
youtu.be
ですが本記事の手法ではコンポジットが必要になります。
コンポジットエディタでRender Layerノードを複製し、それぞれScene.cycles, Scene.eeveeのレンダリング結果を表示するように設定します。
あとは落ち影にスザンヌを重ねると最終結果が得られます。
コンポジットとシーンの関係
コンポジットは、シーン毎に別々で保持されています。
そのため例えばScene.cyclesでコンポジットを組んだ場合、Scene.eeveeに切り替えてレンダリングを行うと臨んだ最終結果が得られません。
組んだコンポジットを見失ったときは、シーンを切り替えてみてください。
逆にこれを利用すれば複数のコンポジットを用意することができ、シーンを切り替えてF12を押すだけで最終イメージを選べます。
コンポジットのノウハウを貯めたファイルをひとつ持っておくのも良いかもしれません。
最後に
Blender2.8ではコレクションとビューレイヤーの登場により、シーンの切り替えはそれほど頻繁には使用しなくなるかも知れません。
ですが取得したいレンダリング結果の種類が増えるほど、選択肢として有効になっていきます。
また、シーンに限らずリンクの概念は混乱しやすいものですが、慣れると様々な取り回しが出来るようになります。
是非、リンクの図を思い浮かべながら色々操作してみてください。
一つの.blendファイルに複数のシーンを持たせる
- <2019/07/30 修正> シーン内でのUnlinkは危険なため、ビューレイヤーの使用を推奨する記述に変更いたしました。
Cyclesとは違い、Eeveeには影のみを受け取るオブジェクト設定がありません。
そのためEevee単体でそういったオブジェクトが必要になる場合、どのようにすると良いかという質問がTwitterの#blender質問室に挙がっていました。
自分なりに調べていたのですが、他の方から回答に挙がったのがこちら。
t.co
Eevee内のノードで自作するアプローチです。すごく良いです。
ただBlenderに限らずアプローチは色々あるもので、試したことをお蔵入りさせるのも勿体無いと思い、書き残すことにしました。
自分のトライしたやり方は、1つのファイル内に2つの「シーン」を持たせる方法です。
それぞれの設定を変えることでEeveeとCyclesのレンダリングを一度に行い、コンポジットで合成するというものでした。
これはこれで出来ることが増えるので、以下、シーンを複数持たせる方法のお話です。
リンクについて
まず、シーンを増やす前に、リンクについて説明します。
オブジェクトを選択してショートカットalt + dを押すと、特殊な複製になります。この方法で増えたものは、片方の内容が変わるともう片方も影響を受けます。
アウトライナを見てみると、メッシュ「Suzznne」は二か所にありますが、同じ名前で同じデータの種類です。つまり同じものが二か所にリストアップされています。
この関係を、「リンク」と呼びます。
シーンの増やし方
シーンを増やすボタンは、ウィンドウ右上にあります。
押すと挙動を四種類から選択することになりますが、それぞれ大まかに以下のような違いがあります。New
なにもない、まっさらなシーンを作ります。レンダー設定など、オブジェクト以外のものも引き継がれずに初期状態と同じになります。Copy Settings
レンダー設定等はコピーされますが、オブジェクト等は引き継がれません。そのため、ビューポートはまっさらになります。Linked Copy
レンダー設定等はコピーされます。オブジェクト等はすべて追加前のシーンとリンクされます。次項で例を挙げます。Full Copy
レンダー設定等はコピーされます。オブジェクト等はすべて複製され、別名になります。
いずれもScene.001というシーンができ、シーンを切り替えられるようになります。
複数シーン間の関連性について
Linked Copyでシーンを増やしたとき、片方のシーンでオブジェクトの位置を変えると、もう片方のシーンでも反映されます。マテリアルの追加や編集も同様に、両方のシーンへ影響します。
一方で、レンダー設定は別々のものなので、片方のレンダラーを変えても、もう片方への影響はありません。
アウトライナで表示形式を「Blender File」へ変更することで、複数シーン間でのリンクの状態をもう少し詳しく確認できます。
各オブジェクトはリンクされているため、片方のシーン内で削除(デリート)してしまうと、両方のシーンから失われます。
もし片方のシーンのみリンクを無効化したいものがある場合、コレクションを使用して対象を管理する必要があります。
コレクション
コレクションは、オブジェクト等をまとめて管理するための機能を持ちます。
フォルダに似ていますが、中に入っているのは実際のオブジェクトではなく、オブジェクトへのリンク情報に似たものだと考えてください。
先程の図のリンク関係は次のようになります。
★「Scene Collection」は同じ名前で同じアイコンですが、これだけは別々のものです。
(Blenderの内部では別のもとして扱われています)
それ以外のコレクションは、これまでの説明と同じく、名前が同じであれば同一のものです。
リンクを無効化する
リンクを無効化するには、コレクションの横にあるチェックをオフにします。
ただし予め、無効化したいものだけを含むコレクションを用意しておく必要があります。
チェック内容はシーンをまたいで管理は共有さないという特徴があります。 無効化されたものは、そのシーンのレンダリングに反映されません。
なお厳密には、シーンがそれぞれ別のビューレイヤーというものを持ち、そのビューレイヤーがコレクションの無効化を管理しています。
リンクを捨てる
★リンクを捨てる手法は危険を伴うため、上記の無効化を推奨します
もし無効化ではなく、特定のシーンからリンクを捨てたい場合には、そのシーンのアウトライナ上で右クリックをして「Unlink」を選択します。
するとCollectionへの、Scene.001からのリンクが無くなります。 このとき、アンリンクする対象次第では、どのシーンからも失われてしまうので注意してください。
たとえばCubeをアンリンクしてしまうと、どのシーンからも見えなくなります。
リンクの追加
シーンを増やした後で追加した物が他のシーンへ反映されないことがあります。その際はリンクの図を思い浮かべてください。
下図は、Scene.001でスザンヌを追加した結果です。
この状態からスザンヌをSceneへリンクさせる方法は2つあります。
階層で解決
Collectionは両シーンへのリンクを持つため、Scene.001内でCollectionの階層にスザンヌを入れます。
Make Linksの実行
Scene.001のビューポートでctrl + LからSceneへのリンクを明示します。
この場合、アウトライナとリンクは次のようになります。まとめ
- 複数のシーンはそれぞれ、シーン設定を個別に持つことができます
- オブジェクトとコレクションは、リンクの関係にあります
- コレクションのうち一番上のScene Collectionだけは、同じ名前でも別々のものです
- リンクを無効化するには、コレクション横のチェックをオフにします。
- リンクを捨てるには、アウトライナで右クリック→Unlinkを実行します(要注意)
- コレクションとの階層変化、またはビューポートでのctrl + Lでリンクを新たに作成できます
シーンを管理できればBlenderの機能をさらに有効に使えます。次回は冒頭で紹介した、EeveeとCyclesを併用してのレンダリングを、複数のシーンを用いて行ってみます。
(最初に紹介した動画の手法のほうがEeveeで完結する上、影の調整も容易なぶんスマートだと思います。)
カレントでない時間でのアトリビュート値を取得する (Maya)
本日受講したセミナー
Maya のしくみ ~ 基礎から学ぶ DG とパラレル評価、そしてキャッシュプレイバック ~ | ボーンデジタル
にて佐々木さんが最後に「公式の標準機能が、MDGContextを用いて違う時間帯の評価を行うようになったということは・・・」とおっしゃっていました。(ニュアンスは多少違うかもしれませんが)
機能としての提供はあったが、基本機能への投入実績ができた以上、Mayaにとっての標準が新たに定義されたということです。
大変になるなぁと思うと同時に、今後はそのあたりの内部機能が安定していくだろうと思う次第でした。
ともあれ、カレントフレーム以外でのアトリビュート値を取得する方法を紹介いたします。
次のような条件では特に役立ちます。
- タイムスライダを動かせば値は判る
- でもシーンの更新に時間がかかる
- 知りたいのはチャンネルボックスの値だけ
想定
pCube1のtranslateXについて、frame11での値を知りたい
準備
キューブを作成してtranslateXのアニメーションをつけます。
指定フレームでの値の取得
以下のスクリプトを実行することで、11frameでの値を取得できます。
import maya.api.OpenMaya as om2 # pCube1という名前から、apiで扱うためのリストを生成 sel = om2.MGlobal.getSelectionListByName('pCube1') # リストから0番目(pCube1)の物体を、Dagとして取り扱える状態で取得 dag = sel.getDagPath(0) # Dagをtransformノードとして扱えるようにする trn = om2.MFnTransform(dag) # transformノードのtranslateXを管理する物(プラグ)を取得 plug = trn.findPlug('tx', 0) # 現在のfps設定を、時間を管理するクラス(MTime)から取得 fps = om2.MTime().unit # "現在のfpsでの、11frame" に該当するタイムオブジェクトを生成 frame_11 = om2.MTime(11.0, fps) # "現在のfpsでの、11frame" を表す、時間に関する文脈を生成 context = om2.MDGContext(frame_11) # translateXを管理するプラグへ "現在のfpsでの、11frame" という文脈を伝え、それに応じた値をfloatで受け取る value = plug.asFloat(context) print(value)
この図では、コマンド実行後にタイムスライダで11frame目を表示させていますが、カレントフレームがどこであっても同じ結果が得られます。
カレントフレームでの値を取得
カレントフレームでの値を取得したい場合には、以下のように後半を差し替えます
import maya.api.OpenMaya as om2 # pCube1という名前から、apiで扱うためのリストを生成 sel = om2.MGlobal.getSelectionListByName('pCube1') # リストから0番目(pCube1)の物体を、Dagとして取り扱える状態で取得 dag = sel.getDagPath(0) # Dagをtransformノードとして扱えるようにする trn = om2.MFnTransform(dag) # transformノードのtranslateXを管理する物(プラグ)を取得 plug = trn.findPlug('tx', 0) # ここまでは同じ # "現在のfps、現在のフレーム" を表す、時間に関する文脈を生成 context = om2.MDGContext(om2.MDGContext.kNormal) # translateXを管理するプラグへ "現在のfpsでの、11frame" という文脈を伝え、それに応じた値をfloatで受け取る value = plug.asFloat(context) print(value)
MDGContext.kNormal が「Normal=通常=今」を示すので、現在のfps, 現在のフレームを扱えます。
頂点のワールド座標を取得
メッシュシェイプに関しては、outMeshアトリビュートへのアクセスによってカレントフレーム以外の状態を取得できます。
ただし、outMeshアトリビュートを通して取得した形状はローカル座標しか扱えません。そのため、メッシュの親ノードのワールドマトリクスを取得し、それを使用して頂点座標値の変換を行う必要があります。
import maya.api.OpenMaya as om2 # pCubeShape1という名前から、apiで扱うためのリストを生成 sel = om2.MGlobal.getSelectionListByName('pCubeShape1') # リストから0番目(pCubeShape1)の物体を、Dagとして取り扱える状態で取得 odag = sel.getDagPath(0) # DagをMeshとして扱えるようにする mmesh = om2.MFnMesh(odag) # メッシュの最終的な形(outMeshアトリビュート)を管理するプラグを取得 plug_outmesh = mmesh.findPlug('outMesh', 0) # "現在のfpsでの、11frame" を表す、時間に関する文脈を生成 context = om2.MDGContext(om2.MTime(11, om2.MTime().unit)) # Meshの "現在のfpsでの11frame" にあたる形を取得 omeshdata = plug_outmesh.asMObject(context) # "現在のfpsでの11frame" にあたる形をMeshとして扱えるようにする # (カレントタイムのMeshとは別物) time_shifted_mmesh = om2.MFnMesh(omeshdata) # 各頂点のローカル座標を取得(outMesh経由で取得したものはワールド座標がない) # 最後をgetPoints(om2.MSpace.kWorld)にしてもローカル座標になる points = time_shifted_mmesh.getPoints() # 親階層(pCube1)のworldMatrixを管理するプラグを取得 plugs_mtrx = mmesh.findPlug('parentMatrix', 0) # parentMatrixはマルチアトリビュートなので、parentMatrix[0]のみを考慮 plug_mtrx = plugs_mtrx.elementByLogicalIndex(0) # parentMatrix[0]のプラグから "現在のfpsでの11frame" のworldMatrixを取得 omtrxdata = plug_mtrx.asMObject(context) # matrixとして扱えるようにする mmtrxdata = om2.MFnMatrixData(omtrxdata) # matrixを取得(親階層pCube1のworldMatrix) mmtrx = mmtrxdata.matrix() # 各頂点のローカル座標へ親階層のworldMatrixを適用 for pt in points: pt *= mmtrx # 各頂点位置がワールド座標になっていることを確認 import maya.cmds as cmds group = cmds.group(em=1, w=1) for pt in points: locator = cmds.spaceLocator(p=(pt.x, pt.y, pt.z)) cmds.parent(locator, group)
あとがき
多少無理してでも参加した甲斐のある、とても濃密なセミナーでした。
そして、Mayaは「とことん突き詰める人」向きなのだと改めて思ったり。
そうでなければ?Evaluation ToolkitではなくFix Toolkitが無難なラインで提供されていたと思います。
久しぶりにプロ向けのセミナーに参加した事もあり、失礼もあったかと思いますが、引き続きセミナーにアンテナを張っていきたい、と思った次第です。
Blender, Maya両対応のハードウェアレンダリングツールを公開いたしました。
複数のシーンへハードウェアレンダリングを行うツールを公開いたしました。
http://maya2blender.xsrv.jp/tools/hardware_render_animations/v010/document/index.html
Maya2019 (sp0)とBlenderは2.79で動作確認しております。(Blender2.80は未対応)
なお、Blender側は別途PySide2の環境が必要となります。
詳しくは上記リンクをご覧ください。
Blenderに関して2.80への対応を行うほか、PySide2に頼らないバージョンを作成する予定で、Mayaに関してはバックグラウンド実行を実装したいところです。
本ツールは、『同じパッケージでMayaへもBlenderへも対応する』をコンセプトに作成しました。コードをご覧いただければ、MayaでのコマンドとBlenderでのコマンドを見比べられて面白いかもしれません。
ツール毎に方言がある所だけをそれぞれの言葉で話させ、それ以外は共通の処理を行う作りになっています。
PySide2はHoudini, MotionBuilderにも備わっているので、同じアプローチでそれらのツールへも拡張できそうです。
そのための練習として作成しましたが、実際に拡張する予定はありません。
スクリプトの技量はまだまだですが、コツコツやっていこうと思います。
Blenderでワールドマトリクスからオブジェクトの軸方向を取得
2.79.6での検証です。2.8はまだ・・・
また、ボーンの扱いは特殊らしい?ので、未検証です。
取得対象
移動と回転をさせたキューブについて、各軸の方向を取得する方法の紹介です。
ワールドマトリクスとは
オブジェクトに親子関係がある場合、各オブジェクトのlocation, rotation, scaleへは、親との相対的な値が入っています。それに対し、親子関係を無視した(親子関係を解消した場合に相当する)状態を表すものをワールドマトリクスといいます。これを利用して、オブジェクトのXYZ軸がどの方向を向いているのかが取得できます。
ワールドマトリクスを取得するには以下のコードを実行します("Cube"の例)。
import bpy # Cubeのオブジェクトを取得 obj = bpy.data.objects["Cube"] # Cubeのワールドマトリクスを取得 matrix_world = obj.matrix_world # 取得したマトリクスを表示 print(matrix_world)
表示結果はシステムコンソールに表示されるので、確認するにはToggle System Console(システムコンソール切替)を押しておく必要があります。
マトリクスの読み方
Blenderのマトリクスは、左端の縦(一列目)がX軸の方向を表します。同様に二列目がY軸、三列目がZ軸の方向を表します。
それぞれ上から順に、原点から見た各軸のx, y, zの座標を示します。
例えばこのキューブのX軸の向きは、原点から見て
x=0.7272, y=-0.6838, z=-0.0602
の方向を向いたベクトルと並行だということになります。
なお、四列目は位置のワールド座標を表しており、
x=1.5527, y=-0.5653, z=3.1395
の場所にこのキューブがあることを示しています。
マトリクスの一番下の行は、とりあえず無視してください。
プログラム上で取得しやすくする
マトリクスは横の一行ごとに抽出することができ、 以下のコードを実行すると各列を抜き取れます。
import bpy obj = bpy.data.objects['Cube'] matrix_world = obj.matrix_world print(matrix_world) print(matrix_world[0]) # 最初の行を取得して表示 print(matrix_world[1]) # 2行目を表示 print(matrix_world[2]) # 3行目を表示 print(matrix_world[3]) # 最後の行を表示
ところが、Blenderでは各軸の情報が縦に並んでいるため、この方法では軸の情報が取得できません。
例えば二行目は、各軸の向きと位置のうち、y座標を並べたものが表示されてしまいます。
そこで以下のコードで、マトリクスの縦と横を入れ替えます
import bpy obj = bpy.data.objects['Cube'] matrix_world = obj.matrix_world print('---matrix_world---') print(matrix_world) # 縦と横を入れ替える(transpose) transposed_matrix = matrix_world.transposed() print('---transposed---') print(transposed_matrix) # 縦と横を入れ替えたマトリクスを表示
今回は軸の方向を調べるのが目的なので、四列目と四行目は不要になります。次のコードは縦横を入れ替えたマトリクスを三行三列だけにして、各行を表示させています。
import bpy obj = bpy.data.objects['Cube'] matrix_world = obj.matrix_world # 縦と横を入れ替える(transpose) transposed_matrix = matrix_world.transposed() print('---transposed---') print(transposed_matrix) # 縦と横を入れ替えたマトリクスを表示 # 3行3列に絞る(4行目、4列目を捨てる) transposed_3x3 = transposed_matrix.to_3x3() print('---transposed 3x3---') print(transposed_3x3) # 3行3列にしたマトリクスを表示 # 各行を表示 print(transposed_3x3[0]) # X軸の方向を表示 print(transposed_3x3[1]) # Y軸の方向を表示 print(transposed_3x3[2]) # Z軸の方向を表示
これで各軸の方向を取得することができました。
確認する
各軸の向きが取得できたので、その向きがあっているかを確認してみます。
下の図は、3つのEmptyを作成して名前を変え、取得した座標を各locationへ入力、それを原点のEmptyの子供にしたものです。
原点の親EmptyをCubeと同じ場所にすることで、Cubeの座標表示と各ロケータの位置が揃うことが確認できます。
あとがき
カメラの向いている方向を取得する必要があり、調べた副産物を記事にしてみました。
カメラの視線方向は取得したZ軸の方向に対して、各xyz座標値に-1を掛けた向き(=Z軸のマイナスの向き)になります。
今回の例では、"Cube"の代わりに"Camera"だったとして、取得したZ軸の向きが
x=-0.2276, y=-0.3230, z=0.9186
でしたので、カメラの視線方向は
x=0.2276, y=0.3230, z=-0.9186
となります。
Mayaでは「カメラの視線方向を取得」というそのものな命令があるのですが、Blenderでは見当たらなかったので入り用でしたらご参照ください。(ひょっとしたらあるのかも・・・)
あと、Mayaでワールドマトリクスを取得した場合、今回の「縦と横を入れ替えたマトリクス」と同等のものが取得されます。Blenderのように各軸の向きを縦並びにするケースをcolumn-major order、Mayaのように横並びにするものをrow-major orderと呼ぶそうで、アプリケーションによって違いがあるようです。
カメラの視線方向はBlenderもMayaも「マイナスZ方向」なのは変わりません。なぜマイナスなのだろう・・・