新・VRoidをBlender経由でUE4に持っていく自己流メモ

UE4
更新履歴
2019年末のUE4アドベントカレンダーにて、BlenderからUE4向けにfbxをエクスポートするプラグインが公開されましたので反映しました!

概要

Vroid Studioにて作成したキャラクターをUE4に持っていく方法の自分用メモです。

以前の記事ではVRoidStudio->Blender->Maya->UE4という流れで持っていきましたが、
今回はVRoidStudio->Blender->UE4という手順で進めます。

最近はVRM用アドオンも充実してきて、かなり簡単にUE4まで持っていくことができます!

なお、VRM形式の3DモデルをそのままUE4に持っていくのであれば、はるべえさん(@ruyo_h)が公開されているVRM4Uを使用するのが確実です。

この記事では、Blender上でモデルデータの改造や軽量化をしてからUE4に持っていくことを想定して、一度Blenderを経由する手順を紹介します。

環境

  • VRoid Studio v0.10.4
  • Unreal Engine 4.25.2
  • Blender 2.83.0

下準備

Blenderのアドオンをインポート

BlenderでVRMファイルを読み込んで編集するために、以下のアドオンをインポートしてください。

  • VRM IMPORTER
  • Cats Blender Plugin
  • X4UE: Exporter for Unreal Engine

VRM IMPORTER

「VRM IMPORTER」はblender上でVRMファイルを読み込むことを可能するアドオンです。

インポート方法はこちらをご確認ください。

Cats Blender Plugin

「Cats Blender Plugin」はVRMモデルを最適化することができるアドオンです。

インポート方法はこちらをご確認ください。

X4UE: Exporter for Unreal Engine

「X4UE: Exporter for Unreal Engine」はBlenderからFBXファイルにエクスポートする際に、自動でUE4向けの設定を行ってくれるアドオンです。

詳細な手順は墨崎達哉さん(@T_Sumisaki)の記事をご確認ください。

Blender本体、アドオン、VRM形式のそれぞれのバージョンは今後もアップデートされますので、組み合わせによってはうまく動かない可能性があることにご注意ください。

手順

1. VRoidStudioからVRMファイルをエクスポートする

今回もVRoidStudio公式テンプレートの「千駄ヶ谷 渋」ちゃんを使って説明していきます。

  1. 上部右端の[撮影・エクスポート]を選択
  2. 画面左の[エクスポート]を選択
  3. 画面右の[エクスポート]を選択
  4. VRM設定はそのまま[OK]を選択

これでVRM形式で保存されます。

尚、最新版のVRoid Studioには、エクスポートする際の右メニューに、[ポリゴンの削減]や[マテリアルの削減]などの機能が用意されています。こちらは適宜使用してください。

 

2. BlenderでVRMファイルを調整する

2.1. VRMファイルを読み込む

Blenderを起動し、初期配置されているCamera、Cube、Lightを削除します。

サイドバーを開き、[CATS]>[Import Model]を選択してVRMファイルをインポートします。

ファイルの読み込みに成功するとモデルが表示されます。

2.2. CATSの機能を使って最適化する

[CATS]>[Fix Model]の横にあるレンチをクリックしてオプションを開きます。
[Keep Upper Chest]のチェックを入れて、OKを選択します。

[Fix Model]を選択すると、モデルが最適化されます。

この状態で、ポーズモードにしてボーンを動かしてみてください。

ボーンを動かした時にきちんとモデルがついてくれば完了です。

稀にバインドが外れてしまい、ボーンを動かしてもモデルが動かついてこない時があります。その場合は再度[Fix Model]を実行すると正しく反映されます。

2.3. ボーン名をリネームする

ボーン名をUE4の標準スケルトンの名称にリネームします。
この作業は必須ではありませんが、名称を揃えておくと管理が楽なのでここで変えてしまいます。

BlenderはPythonを使うことができますので、以下のスクリプトを使用すれば一発で変換することができます。

ボーン名をUE4標準スケルトンの名称にリネームするPythonスクリプト
import bpy
context = bpy.context
obj = context.object

namelist = [
    ('Hips', 'Pelvis'),
    ('Spine', 'Spine_01'),
    ('Chest', 'Spine_02'),
    ('Upper Chest', 'Spine_03'),
    ('Left shoulder', 'Clavicle_L'),
    ('Left arm', 'UpperArm_L'),
    ('Left elbow', 'LowerArm_L'),
    ('Left wrist', 'Hand_L'),
    ('IndexFinger1_L', 'Index_01_L'),
    ('IndexFinger2_L', 'Index_02_L'),
    ('IndexFinger3_L', 'Index_03_L'),
    ('MiddleFinger1_L', 'Middle_01_L'),
    ('MiddleFinger2_L', 'Middle_02_L'),
    ('MiddleFinger3_L', 'Middle_03_L'),
    ('LittleFinger1_L', 'Pinky_01_L'),
    ('LittleFinger2_L', 'Pinky_02_L'),
    ('LittleFinger3_L', 'Pinky_03_L'),
    ('RingFinger1_L', 'Ring_01_L'),
    ('RingFinger2_L', 'Ring_02_L'),
    ('RingFinger3_L', 'Ring_03_L'),
    ('Thumb0_L', 'Thumb_01_L'),
    ('Thumb1_L', 'Thumb_02_L'),
    ('Thumb2_L', 'Thumb_03_L'),
    ('Right shoulder', 'Clavicle_R'),
    ('Right arm', 'UpperArm_R'),
    ('Right elbow', 'LowerArm_R'),
    ('Right wrist', 'Hand_R'),
    ('IndexFinger1_R', 'Index_01_R'),
    ('IndexFinger2_R', 'Index_02_R'),
    ('IndexFinger3_R', 'Index_03_R'),
    ('MiddleFinger1_R', 'Middle_01_R'),
    ('MiddleFinger2_R', 'Middle_02_R'),
    ('MiddleFinger3_R', 'Middle_03_R'),
    ('LittleFinger1_R', 'Pinky_01_R'),
    ('LittleFinger2_R', 'Pinky_02_R'),
    ('LittleFinger3_R', 'Pinky_03_R'),
    ('RingFinger1_R', 'Ring_01_R'),
    ('RingFinger2_R', 'Ring_02_R'),
    ('RingFinger3_R', 'Ring_03_R'),
    ('Thumb0_R', 'Thumb_01_R'),
    ('Thumb1_R', 'Thumb_02_R'),
    ('Thumb2_R', 'Thumb_03_R'),
    ('Neck', 'Neck_01'),
    ('Head', 'Head'),
    ('Left leg', 'Thigh_L'),
    ('Left knee', 'Calf_L'),
    ('Left ankle', 'Foot_L'),
    ('Left toe', 'Ball_L'),
    ('Right leg', 'Thigh_R'),
    ('Right knee', 'Calf_R'),
    ('Right ankle', 'Foot_R'),
    ('Right toe', 'Ball_R')
]

for name, newname in namelist:
    print(name)
    print(newname)
    # get the pose bone with name
    pb = obj.pose.bones.get(name)
    # continue if no bone of that name
    try:
        # rename
        pb.name = newname
    except:
        continue
自分用メモ:シェイプ名をリネームする
import bpy
context = bpy.context
obj = context.object

namelist = [
    ('MTH Up', 'Face_MTH_Up'),
    ('MTH Down', 'Face_MTH_Down'),
    ('MTH Angry', 'Face_MTH_Angry'),
    ('MTH Neutral', 'Face_MTH_Neutral'),
    ('MTH Fun', 'Face_MTH_Fun'),
    ('MTH Joy', 'Face_MTH_Joy'),
    ('MTH Sorrow', 'Face_MTH_Sorrow'),
    ('MTH Surprised', 'Face_MTH_Surprised'),
    ('MTH A', 'Face_MTH_A'),
    ('MTH I', 'Face_MTH_I'),
    ('MTH U', 'Face_MTH_U'),
    ('MTH E', 'Face_MTH_E'),
    ('MTH O', 'Face_MTH_O'),
    ('EYE Angry', 'Face_EYE_Angry'),
    ('EYE Close', 'Face_EYE_Close'),
    ('EYE Close R', 'Face_EYE_Close_R'),
    ('EYE Close L', 'Face_EYE_Close_L'),
    ('EYE Joy', 'Face_EYE_Joy'),
    ('EYE Joy R', 'Face_EYE_Joy_R'),
    ('EYE Joy L', 'Face_EYE_Joy_L'),
    ('EYE Sorrow', 'Face_EYE_Sorrow'),
    ('EYE Surprised', 'Face_EYE_Surprised'),
    ('EYE Extra', 'Face_EYE_Extra'),
    ('BRW Angry', 'Face_BRW_Angry'),
    ('BRW Fun', 'Face_BRW_Fun'),
    ('BRW Joy', 'Face_BRW_Joy'),
    ('BRW Sorrow', 'Face_BRW_Sorrow'),
    ('BRW Surprised', 'Face_BRW_Surprised'),
    ('ALL Angry', 'Face_ALL_Angry'),
    ('ALL Fun', 'Face_ALL_Fun'),
    ('ALL Joy', 'Face_ALL_Joy'),
    ('ALL Sorrow', 'Face_ALL_Sorrow'),
    ('ALL Surprised', 'Face_ALL_Surprised'),
    ('HA Fung1', 'Face_HA_Fung1'),
    ('HA Fung1 Low', 'Face_HA_Fung1_Low'),
    ('HA Fung1 Up', 'Face_HA_Fung1_Up'),
    ('HA Fung2', 'Face_HA_Fung2'),
    ('HA Fung2 Low', 'Face_HA_Fung2_Low'),
    ('HA Fung2 Up', 'Face_HA_Fung2_Up')
]


for name, newname in namelist:
    print(name)
    print(newname)
    # get the pose bone with name
    pb = obj.data.shape_keys.key_blocks.get(name)
    # continue if no bone of that name
    try:
        # rename
        pb.name = newname
    except:
        continue

2.4. Rootボーンを追加する

VRMモデルはルートがHips(UE4ではPelvis)になっており、原点位置にルートボーンがありません。

ルートボーンはPelvis(旧:Hips)になっている

UE4のスケルトンに構造を近づけるために手動でRootボーンを追加します。

  1. 編集モードでアーマチュアを選択する。
  2. [追加]>[単一ボーン]を選択してボーンを原点位置に追加する。
  3. 追加したボーンの名前を「Root」に変更する。
  4. Rootボーンの長さを0.1にして小さくする。

    RootボーンをPelvisに繋がるように大きくする必要はありません。
  5. PelvisをRootの子にするためにペアレント化をする。この時、[オフセットを保持]を選択すること。

    Rootボーンが追加された

これで最低限のモデルの修正は完了ですが、他にもBlenderで加工したい場合はここで行います。

みっちーさん(@nyu___nSfms)の記事でいろいろな最適化例が紹介されています。

3. BlenderからFBXファイル形式でエクスポートする

BlenderからスケルタルメッシュをUE4に持っていく際の注意点は2つあります。

  1. ルートボーンにスケール値100が残ってしまう。
  2. ルートボーンに回転値が残ってしまう。

この問題の対策は、なんさん(@tarava777)の記事の手順で解決することができます。

これまではこの対応を手作業でしていましたが、2019年末のUE4アドベントカレンダーにてIndie-us Gamesの墨崎達哉さん(@T_Sumisaki)が上記の問題を解決するFBXエクスポーターを公開してくださいました!神!

おかげで手順はとても簡単になりました。以下に記載します。

  1. アウトライナのArmatureを右クリック>[階層を選択]をクリックする。
  2. [ファイル]>[エクスポート]>[FBX for UE4 (.fbx)]を選択する。
  3. オプションメニューの[Bones Axes]を確認する。
    1. [プライマリ]>[Z軸]になっていることを確認する。
    2. [セカンダリ]>[X軸]になっていることを確認する。
  4. [UE4 FBX Export]をクリックする。

4. UE4にインポートする

4.1. FBXをインポートする

UE4側でFBXをインポートします。

インポートオプションの[Import Morph Targets]のチェックはデフォルトでOFFになっているので忘れずチェックするようにしてください。表情のブレンドシェイプをインポートすることができます。

無事にインポートされました!

4.2. マテリアルをいい感じにする

インポートオプションでマテリアルを生成するようにした場合、自動的にマテリアルが作成されて割り当てられますが、お世辞にも綺麗とは言えません。

現状一番綺麗にVRoidを表示できるのは、はるべえさん(@ruyo_h)のVRM4Uかと思います。

詳細な手順は本記事では触れませんが、VRM4Uプラグインを使って一度VRMファイルをインポートし、生成されたマテリアルを使わせてもらうと綺麗に表示されます。

4.3. リターゲットの設定をする

UE4ではスケルトンのリターゲット設定をすることで、別のスケルトンのモーションをコピーして流用することができるようになります。特に人型のキャラクターを使用する際は恩恵が大きいので、きちんとリターゲットの設定をしておくことをオススメします。

  1. スケルトンアセットを開く。
  2. リターゲットオプションを表示する。
  3. [Rootボーンを右クリック]>[移動リターゲティングスケルトンを再起的に設定]を選択する。
  4. Rootボーンを個別で[Animation]に設定し、Pelvisボーンを個別で[Animation Scaled]に設定する。

    このような設定になればOK

  5. 画面右の[プレビューシーン設定]の[Mesh]>[Preview Mesh]の[Apply To Asset]をクリックする。

これでリターゲットが利用可能になりました!

まとめ

Blenderのアドオンが充実しているのでVRMをUE4に持っていくのがグッと楽になりました!

自分用のメモのつもりで書きましたが、みなさんのお役に立てれば幸いです!