Houdini VATを使用した群衆
HoudiniのVertex Animation Texture(以下VAT)のSoftを使用してUE4で群衆を作成します。従来のシンプルなワークフローを初めに取り扱い、さらに以下の2つの拡張を行います。拡張はどちらもUE4のマテリアルエディターでの作業になります。この記事の一番最後にマテリアルエディターのコピペ用のテキストファイルも用意しました。
- オブジェクトを回転したときの正しいライティング方向への対応(インスタンス系メッシュも対応)
- ピクセルノーマルマップ対応(VATのノーマルマップと区別するためにピクセルと付けています)
Houdiniは素人なので、より良い方法をご存知でしたらお知らせいただけると嬉しいです。
環境
- Windows10
- UE4.21.1
- Houdini Indie 17.0.416
HoudiniのGame Development Toolsetを導入しておいてください。
アニメーションデータ
この記事ではmixamoの適当なアニメーションをFBX for Unity形式でダウンロードしたものを使用しました。キャラクターメッシュとテクスチャ、アニメーションが全て入っています。記事で使用したキャラクターはLola B Styperekです。
従来のワークフロー
初めにVATのSoftワークフローを素直に実行してみます。
Houdini - FBXインポート
まずはHoudiniでの作業です。
ダウンロードした.fbxのアニメーションファイルを、適当に設定を変更してNew Sceneとしてインポートします。インポート後にシーンは適当な名前で保存してください。シーンが保存されていないとVATでデータを出力した際にWindowsのユーザーのexportフォルダに出力されてしまっていました。
キャラクターのスケール調整
このままVATで.fbxを出力するとUE4でメッシュのサイズが大きくなりすぎてしまうので、nullノードをスケルトンのルートに追加して、適当なスケールに調整します。
(オプション)キャラクターのハードエッジ対応
キャラクターがハードエッジを持っている場合はVertex SplitでPointを分割する条件としてN(法線)を指定すると、法線が異なる場合はPointを分割してくれるので、正しい結果になります。
VATの作成と設定と出力
VATを作成します。今回の群衆ではMethodにSoftを使用します。Export Nodeはインポートしたジオメトリを選択してください。今回、出力するテクスチャの設定はNormalize Data to 0-1 spaceをオン、Pack Normal into Alphaはオフにしてみました。設定が終わったらRenderボタンを押してデータを出力してください。
UE4 - アセットデータのインポート
ここからはUE4を使用します。
Houdiniのシーンファイルを保存したフォルダにexportフォルダが作成され、その中にVAT用のアセットデータが出力されています。それらをUE4にインポートしてください。Static Meshのインポート設定はGenerate Lightmap UVsをオフにしてください。Lightmap UVを生成してしまうと、VAT用に出力されたUV Channel 1が上書きされてしまいます。
HoudiniのVAT設定からSoft Vertex Animation UE4 Codeの項目を開いて、テキストを全てコピーしてください。
UE4のマテリアルエディターを開きペーストします。今回はカラーのアニメーションは行わないので画像の赤く囲んだ箇所は削除してください。
ノードのコメントに設定方法や接続箇所などの説明が書いてあるので指示通りにします。ただし、カラーのアニメーションは行わないのでNum Customized UVsは3にします。後ほどのための見た目の確認のためにBaseColorに1、Specularに0を入力しておきます。
一番左あたりのHoudiniのFPS値を変更してください。今回は30です。
VATで使用するべきSampler TypeとマテリアルエディターでのSampler Typeが異なっているのが気になるので直接テクスチャを指定して、一致させておきます。これによりVATのノーマルマップの方は法線のデコードを手動で行う必要があります。
一旦マテリアルを保存します。マテリアルの名前は適当にM_VatCrowdとしました。マテリアルからマテリアルインスタンスを作成してください。マテリアルインスタンスのパラメーターはVATの設定のものを入力し、テクスチャーも指定します。メッシュにマテリアルインスタンスを忘れずにアサインしてください。
確認するとアニメーションがガビガビしています。
これはStatic Mesh EditorのLOD 0 > Build Settings > Use Full Precision UVsをオンにして、Apply Changesボタンを押すことでUVの精度が上がり、解決します。通常UVのFloat精度は16bitですが、Full Precisionで32bitになります。16bit Floatの仮数部は11bitなので頂点数が2048より大きいと確実に精度が足りなくなりそうです。頂点数が2048より小さくても精度が足りない場合もあるので、頂点位置がガビガビしていたら、見た目の判断でFull Precisionにすると良いと思います。
これで従来のワークフローは完了です。
オブジェクトを回転したときのライティング対応
オブジェクトをマップに配置して、回転するとライティング結果も一緒に張り付いたように回転してしまいます。これは群衆として使用する場合には問題なので対応します。
そもそもどうしてこういう現象が起きてしまうのでしょうか。調べてみたところ、マテリアルの法線の座標空間の指定でTangent Space Normalがオフ、つまりWorld Space Normalとなっているときに、VertexNormalWSノードを使用していないとこうなるようです。VecterNormalWSノードにはオブジェクトの回転が伝わっていると推測できます。
このことからVATでの実装では、オブジェクトの回転をVATのノーマルマップに伝えると良さそうです。今回は群衆ということで、Z軸回転に対応すれば十分なので、その実装を行います。VATのノーマルマップをデコードした後の部分に下図のようにノードを追加してください。
RotateAboutAxisノードでベクトルの回転を行います。回転軸となるNormalizedRotationAxisにはZ軸の(0, 0, 1)を指定。ベクトルの回転なのでPivotPointには原点の(0, 0, 0)を指定。Positionには回転させたいベクトル、つまりVATのノーマルマップを入力します。RotationAngleにはZ軸の回転量を[0, 1]の範囲で入力する必要がありますが、FoliageZRotationノードがそのまま使用できます。最後にRotateAboutAxisは回転の差分のベクトルを出力するノードなので、元の回転したいVATのノーマルマップにAddすることで回転した結果の頂点法線を得ます。
FoliageZRotationノードの簡単な説明をします。オブジェクトのローカルスペースでの(1, 0, 0)ベクトルがワールドスペースへの変換、この場合はオブジェクトの回転を行うことにより、どの方向へのベクトルに変換されたのかが分かります。そのベクトルのxとy成分をアークタンジェント関数のタンジェント値として扱うことで、オブジェクトがxy平面でどれだけ回転したのかが分かります。
ちなみに、マテリアルエディタのCustom UVでの計算はバーテックスシェーダーで行われている、というとても大事な仕様があります。なので普段使っている際もバーテックスシェーダの処理で済むものは積極的にCustom UVを利用すると最適化できます。ただあまりやりすぎると運用が大変そうなので、ベースマテリアルを設計する時などは判断が少し難しいです。オブジェクトの回転はバーテックスシェーダで処理できるのでVATのノーマルマップ直後に実装しました。
これでオブジェクトのZ回転に対応できました。
ピクセルノーマルマップ対応
従来のVATのワークフローだと本来キャラクター用に作成されていたノーマルマップ(以下ピクセルノーマルマップ)が適応できません。群衆がカメラ近くで映る場合はピクセルノーマルマップも必要だと思うので対応します。
今回の記事で扱っているキャラクターのカラーマップとピクセルノーマルマップを取得したい場合は、一旦Houdiniへインポートしていた.fbxと同じデータをUE4へSkeletal Meshとしてインポートすることによってテクスチャを取得してください。
普段はピクセルノーマルマップをタンジェントスペースとしてマテリアルエディターのインプットに渡していて、BasePassでGBufferを作成する際にそれがワールドノーマルへ変換されています。今回はその計算をマテリアルエディターの中で計算してしまおうというコンセプトです。
まずはピクセルノーマルをワールドノーマルへ変更する部分をマテリアルファンクションとして実装しますが、エンジンコンテンツのDeriveTangentBasisノードが流用できそうなのでマテリアルファンクションをコピペした後、下図の用に変更してください。新しいマテリアルファンクションの名前はMF_TransformVatPixelNormalとしました。
ここでは何をしているのかというと、VAT用に用意されたノーマルマップとポジションテクスチャを元に指定されたアニメーションフレームの正規直交基底を作成し、タンジェントスペースのピクセルノーマルマップを座標変換することでワールドノーマルを作成しています。Absolute World PositionをExcluding...からIncluding...に変更することにより、VATのポジションテクスチャを使用したWorld Position Offsetを考慮しているところがミソです。
このマテリアルファンクションを使って実装します。ピクセルシェーダーで処理しなければいけないのでCustomUVからVATの頂点法線を受け取った後の部分に実装します。
これでピクセルノーマルマップに対応できました。
群衆
マテリアルのカラーマップなどの他の要素も自由に作成して完成です。
マテリアルコード
不具合があればお知らせください。