コンテンツへスキップ

QualiArtsengineer blog

Mayaの布シミュレーションをVATで出力してUnityで再生する

Mayaの布シミュレーションをVATで出力してUnityで再生する

7 min read

サイバーエージェントゲーム・エンターテイメント事業部(SGE)に所属する子会社QualiArtsで、テクニカルアーティスト室に所属しているテクニカルアーティストの塩塚です。今回はMayaとUnityを用いて頂点アニメーションテクスチャ(VertexAnimationTexture)を実現する方法を紹介します。

また、本記事はQualiArtsの定期ブログ「QualiArts Tech Note」第6弾の記事となります。QualiArtsでは会社で使われている様々な技術の知見をブログとして配信しています。興味のある方は他の記事もチェックしてみてください。

テクニカルアーティストとは

テクニカルアーティスト通称TAとは、アーティストとエンジニアの橋渡しを行う職種です。QualiArtsには各プロジェクトに横断的に関わるテクニカルアーティスト室があり、私は2020年に新卒で入社しこちらに配属されました。TAと言っても役割は会社によってマチマチかと思いますが、私はエンジニア出身でパイプライン系のコードを書くことが多いです。具体的にはMayaのプラグインや、Unityのエディタ拡張の開発を行い、クリエイターの作業効率化などに携わっています。

頂点アニメーションテクスチャとは

VertexAnimationTexture略してVATとも呼ばれ、各フレームの頂点情報をテクスチャにベイクする技術です。クロスや波など、ボーンでは表現の難しい動きを記録/再生することに向いています。

一般的にクロスや流体のシミュレーションは処理が重いため、事前計算を行う場合があります。このとき計算した結果を保持しておき、ランタイムではその再生のみを行うことになります。VATではGPUでの再生処理が可能であるため、ネックになりやすいCPU負荷を抑えることができます。

今回は頂点アニメーションをテクスチャにベイクするMayaプラグインの作成を行い、ベイクしたテクスチャを使用した頂点アニメーションシェーダーの実装をUnityで行います。

実装したツールとシェーダー

今回実装したツールとシェーダーのソースはGitHubで公開しています。
MITライセンスとしますので、よろしければ試してみてください。

環境

環境は以下のとおりです。
周辺のバージョンであれば問題なく動作するかと思います。

  • Windows 10もしくはmacOS Catalina
  • Maya 2019
  • Unity 2019.4.5f1_Built-in Render Pipeline

Maya

リポジトリ:https://github.com/salt-k2t/VertexAnimationTextureMaya

こちらにツールとサンプルシーンを同梱しています。
MayaのクロスシミュレーションであるnClothを設定し、落とすだけのサンプルシーンです。
この布の動きをテクスチャにベイクします。
ツールの導入、使い方はリポジトリのREADMEを参照ください。

Maya実装詳細

今回は頂点位置のみをテクスチャにベイクすることにします。
MayaのプラグインはMel,Python,C++のいずれかで実装可能ですが今回はPythonを用い、UIや画像処理部分はQtを用います。

プラグインの雛形としては以下のドキュメントを参照ください。少し古いページですが”Hello World!”を出力するサンプルが確認できます。

https://help.autodesk.com/view/MAYAUL/2017/JPN/?guid=__files_Example_Plugins_htm

https://github.com/sonictk/Maya-devkit/blob/master/osx/devkit/plug-ins/scripted/helloWorldCmd.py

この雛形をベースにVATを実装していきます。
ここでは一部の処理をピックアップして紹介します。

Meshの各頂点の座標を取得

シーン上のオブジェクトを指定し、Maya APIを用いて情報を取得します。
ここでは”pSphere1”の頂点数、各頂点の座標を取得しています。

mesh = "pSphere1"
vertex_size = cmds.polyEvaluate(mesh, v=True)
for v in range(vertex_size):
    pos = cmds.pointPosition(mesh + ".vtx[" + str(v) + "]", l=True)
    print(pos)

# result
[0.14877812564373016, -0.9876883625984192, -0.048340942710638046]
[0.12655822932720184, -0.9876883625984192, -0.0919499322772026]
[0.0919499322772026, -0.9876883625984192, -0.12655822932720184]

各フレームで任意の処理を実行

アニメーションの再生開始時間と終了時間を取得して、その間で処理を行なっています。ここでは現在の時間を1ずつ動かし、各フレームにてprint処理を行っています。

start_time = cmds.playbackOptions(q=True, min=True)
end_time = cmds.playbackOptions(q=True, max=True)
current_time = start_time
while (current_time <= end_time): # Including the last time
    cmds.currentTime(current_time)
    print("current_time:" + str(current_time))
    current_time += 1

# result
current_time:1.0
current_time:2.0
current_time:3.0
...

画像を扱う

画像の作成、ピクセルカラーの設定、保存を行います。
Maya 2019はQt 5.6.1を使用することができますが、このバージョンでは32bitテクスチャまでしか作成することができません。
画像は32bit、ピクセルカラーは64bitになってしまい少し違和感がありますが、Maya 2019では仕方ありません。
Maya 2020ではQt 5.12.5を使用することができるため、64bitであるFormat_RGBA64を使用することができます。

from PySide2 import QtGui
_MAX_SIZE_64 = 65535
_PATH_TO_IMAGE ="/Users/.../sample.png"

img = QtGui.QImage(256, 256, QtGui.QImage.Format_ARGB32) # Maya2019 can't use Format_RGBA64
img.fill(0)
img.setPixelColor(0, 0, QtGui.QColor.fromRgba64(_MAX_SIZE_64, 0, 0, _MAX_SIZE_64))
img.save(_PATH_TO_IMAGE, quality=100)

計算精度について

最低限の実装はここまでですが、使用ケースによっては32bitでは精度が心配な場合もあるかと思います。
そこでサンプル実装では頂点情報にバイアス係数をかけ、擬似的に計算精度に上げられるようにしました。Unityにて逆数のスケールをかけて、スケールを戻す処理を行っていることに注意してください。

生成されるテクスチャの例を上げます。
左の画像のようにグレーに近い画像の場合は、精度が低く情報が潰れてしまっている可能性があります。頂点情報がベイクされたものなので人間が読める形ではないのですが、適切にベイクされていれば右の画像のようになんらかの模様が見て取れるかと思います。使用ケースによって適切な計算精度を設定できるように調整してみてください。

また今回はaチャンネルは使用していませんが、Height情報のベイクに用いるなどさらなる拡張が可能です。

Unity

リポジトリ:https://github.com/salt-k2t/VertexAnimationTextureUnity

こちらにサンプルプロジェクトを用意しています。
Mayaツールでベイクしたテクスチャを元に布の動きを再生します。

Unity実装詳細

BuiltInレンダリングパイプラインのunlitシェーダーの頂点シェーダー部分を拡張して実装しました。

今回は紹介しませんが、シェーダーグラフでも同様の手法で再現が可能です。

頂点番号から位置情報(テクスチャx方向)を取得する

ピクセルの中心からサンプリングするため、頂点番号 + 0.5を座標としていることに注意してください。

#define ts _PosTex_TexelSize
float x = float(vid + 0.5f) * ts.x; // ts.x = 1.0/width

時間からフレーム情報(テクスチャy方向)を取得する

今回はマテリアルのプロパティでループの有無を設定できるようにしています。

#if ANIM_LOOP
t = fmod(t, 1.0f);
#else
t = saturate(t);
#endif
float y = 1.0f - t;

サンプリング

頂点番号と現在フレームからサンプリング座標を決定することができましたので、VATテクスチャからRGB画素をサンプリングします。

RGBの値を頂点のXYZ座標に設定します。

今回は精度を得るために擬似的にかけたスケールを戻し、中心化を行っています。

float4 pos = tex2Dlod(_PosTex, float4(x, y, 0.0f, 0.0f));

v2f o;
o.vertex = UnityObjectToClipPos(CorrectionValue
* float4(- (pos.x - 0.5f), pos.y - 0.5f, pos.z - 0.5f, 0.0f));
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;

テクスチャインポータの設定

ベイク後のテクスチャはその各画素に情報を持っているため、圧縮などを行うことができません。

そこでインポート設定を以下に変更します。

  • sRGB (Color Texture) : Off
  • Non-Power of 2 : None
  • Generate Mip Maps : Off
  • FilterNode : Point (no filter)
  • Format : RGBA 32 bit

各項目の内容に関してはUnity公式ドキュメントを参照ください。
(https://docs.unity3d.com/ja/2019.4/Manual/class-TextureImporter.html)

FBXインポータの設定

MayaとUnityで頂点番号が一致している必要があります。
そこでインポータ設定を以下に変更します。

Optimize Mesh:Nothing

詳細はUnity公式ドキュメントを参照ください。
https://docs.unity3d.com/ja/2018.4/Manual/FBXImporter-Model.html

最後に

今回はMayaとUnityを用いて頂点アニメーションテクスチャの実装を行いました。簡素なものとしましたので、各プロジェクト用に最適化する余地があるかと思います。今回紹介したスクリプトはMaya/UnityともにGitHubにて公開していますので、皆様の参考になれば幸いです。

VATは3D技術として珍しいものではなく、負荷対策として活用しているプロジェクトも多いかと思います。またゲームエンジンとDCCツールを分離して作り込むことができる利点もあり、エンジンとツールの両方から最適化することが可能です。

UnityエンジニアがMayaのプラグイン開発を行うのは抵抗があるかもしれませんが、チームにDCCツールがわかるエンジニア、引いてはTAが1人いるだけでクオリティやアセット制作効率が変わってくるかと思います。私はまだまだ駆け出しですが、モバイルゲーム開発においてもTAが増えていけばいいなと思っています。

2020年にサイバーエージェントにUnityクライアントエンジニアとして新卒入社。QualiArtsテクニカルアーティスト室に所属し、テクニカルアーティストとしてクリエイターの業務効率化に携わる。