8.9 KiB
+++ date = "2024-10-02" lastmod = "2024-10-03" tags = ["vrm","ue"] title = "ue5.5と最新のvmc事情" +++
unreal engine 5.5.0 preview(ue5.5p)がインストールできるようになっています。今回は最新環境のvmc事情を解説します。
vmcとは
webカメラから表情や動きをキャラクターに反映させるためのものです。vmcはprotocolとclientがあります。大抵はprotocolを指します。webカメラからの読み取りをcaptureといいます。つまり、captureとprotocolとclientを組みわせて動作します。ueで使うには更にvmcを受信してキャラクターに反映させるpluginが必要です。わけがわからないと思いますが、そんな感じです。
- https://github.com/sh-akira/VirtualMotionCapture
- https://github.com/ruyo/VRM4U
- https://github.com/HAL9HARUKU/VMC4UE
- https://github.com/HAL9HARUKU/ueOSC
- https://github.com/HAL9HARUKU/VRMMapExporter
- https://github.com/vrm-c/UniVRM
vmc4ueをue5.5でbuildしてみよう
この手順は興味がある人以外は読み飛ばすことを推奨します。表情を動かしたい人はこの方法で問題を解決することができません。
vmc4ue
はue5.1までしかbuildされていません。そこでue5.5でbuildして使えるようにしてみます。
まずc++でprojectを作成し、patchをsrcに当てて$project/Plugins/
に入れます。projectをueで開くとbuildされます。正常に終了するとeditorが開きます。
c++で作成したprojectには$project.slnが作成されますので、それを開いてrebuildしてもいいです。
なお、patchはbuildが通るよう適当に作ったものです。vmcは動きますが、表情は動きませんでした。
--- ./VMC4UE/VMC4UE/Source/VMC4UE/Source/VMC4UEBlueprintFunctionLibrary.cpp
+++ ./VMC4UEBlueprintFunctionLibrary.cpp
@@ -119,27 +119,29 @@ UVMC4UEStreamingSkeletalMeshTransform* UVMC4UEBlueprin
{
return nullptr;
}
-
+
+ UVMC4UEStreamingSkeletalMeshTransform* StreamingSkeletalMeshTransform = nullptr;
+
+ // Try to get existing transform
{
- // Get
FRWScopeLock RWScopeLock(OSCManager->RWLock, FRWScopeLockType::SLT_ReadOnly);
- auto StreamingSkeletalMeshTransform = OSCManager->StreamingSkeletalMeshTransformMap.Find(Port);
- if (StreamingSkeletalMeshTransform != nullptr)
+ auto FoundTransform = OSCManager->StreamingSkeletalMeshTransformMap.Find(Port);
+ if (FoundTransform != nullptr)
{
- return *StreamingSkeletalMeshTransform;
+ return *FoundTransform;
}
}
+
+ // Create new transform if not found
{
- // Create
FRWScopeLock RWScopeLock(OSCManager->RWLock, FRWScopeLockType::SLT_Write);
- auto StreamingSkeletalMeshTransform = OSCManager->StreamingSkeletalMeshTransformMap.Find(Port);
- if (StreamingSkeletalMeshTransform != nullptr)
+ auto FoundTransform = OSCManager->StreamingSkeletalMeshTransformMap.Find(Port);
+ if (FoundTransform != nullptr)
{
- return *StreamingSkeletalMeshTransform;
+ return *FoundTransform;
}
- UVMC4UEStreamingSkeletalMeshTransform* NewStreamingSkeletalMeshTransform = NewObject<UVMC4UEStreamingSkeletalMeshTransform>();
- //FRWScopeLock RWScopeLock2(NewStreamingSkeletalMeshTransform->RWLock, FRWScopeLockType::SLT_Write);
+ UVMC4UEStreamingSkeletalMeshTransform* NewStreamingSkeletalMeshTransform = NewObject<UVMC4UEStreamingSkeletalMeshTransform>();
OSCManager->StreamingSkeletalMeshTransformMap.Emplace(Port, NewStreamingSkeletalMeshTransform);
// Bind Port
@@ -149,9 +151,10 @@ UVMC4UEStreamingSkeletalMeshTransform* UVMC4UEBlueprin
OSCManager->OscReceivers.Emplace(OscReceiver);
- return NewStreamingSkeletalMeshTransform;
+ StreamingSkeletalMeshTransform = NewStreamingSkeletalMeshTransform;
}
- return nullptr;
+
+ return StreamingSkeletalMeshTransform;
}
void UVMC4UEBlueprintFunctionLibrary::RefreshConnection(float Seconds)
--- ./VMC4UE/Source/VMC4UEEd/Source/VMC4UEBoneMappingAssetFactory.cpp
+++ ./VMC4UEBoneMappingAssetFactory.cpp
@@ -5,6 +5,8 @@
#include "../../VMC4UE/Include/VMC4UEStreamingData.h"
#include "Dom/JsonObject.h"
#include "JsonObjectConverter.h"
+#include "UObject/ConstructorHelpers.h"
+#include "UObject/UObjectGlobals.h"
UVMC4UEBoneMappingAssetFactory::UVMC4UEBoneMappingAssetFactory(const FObjectInitializer &ObjectInitializer)
: Super(ObjectInitializer)
@@ -26,11 +28,12 @@
return UVMC4UEVRMMapping::StaticClass();
}
+
UObject *UVMC4UEBoneMappingAssetFactory::FactoryCreateText(UClass *InClass, UObject *InParent, FName InName, EObjectFlags Flags, UObject *Context, const TCHAR *Type, const TCHAR *&Buffer, const TCHAR *BuferEnd, FFeedbackContext *Warn)
{
FString TextData = FString(Buffer);
- UVMC4UEVRMMapping *NewAsset = CastChecked<UVMC4UEVRMMapping>(StaticConstructObject_Internal(InClass, InParent, InName, Flags));
+ UVMC4UEVRMMapping* NewAsset = NewObject<UVMC4UEVRMMapping>(InParent, InClass, InName, Flags);
if (!IsValid(NewAsset))
{
return nullptr;
$ git clone https://github.com/HAL9HARUKU/VMC4UE
$ patch -u ./VMC4UE/VMC4UE/Source/VMC4UE/Source/VMC4UEBlueprintFunctionLibrary.cpp < VMC4UEBlueprintFunctionLibrary.cpp.patch
$ patch -u ./VMC4UE/VMC4UE/Source/VMC4UEEd/Source/VMC4UEBoneMappingAssetFactory.cpp < VMC4UEBoneMappingAssetFactory.cpp.patch
vrm4uを5.5向けにbuildしてみる
この手順は興味がある人以外は読み飛ばすことを推奨します。表情を動かしたい人はこの方法で問題を解決することができません。
vrm4uは既に5.5のbuildをreleasesしています。
ただいくつかの処理が5,4,0向けのようです。それを書き直してbuildしてみます。
$ git clone https://github.com/ruyo/VRM4U
$ cd VRM4U
$ git reset --hard a261860872936c8654e1705a91cff6f8224dbee5
$ grep -R 5,4,0 ./Source/*|cut -d : -f 1|xargs sed -i '' 's/5,4,0/5,5,0/g'
$ grep -R 5,5,0 .
これを先程と同じ手順でue5.5で開いてbuildします。
vrm4u(vmc)はbuild後にも表情を動かせるのだろうか
わかりません。私の環境下では動きませんでした。他の人も動かない可能性がありますが、issueを読む限り動くようにも思えます。
この問題は
vrm4u 20241007
で修正されました
issue : ai/ue#9
それではどうするのか。livelink(face)を使います。ここからは少しめんどくさいことになりますが、かなり多くのアプリが必要です。
- 動き : webcam motion capture(vmc送信) + vseeface(vmc受信/送信) + vrm4u(vmc受信)
- 表情 : iphone + livelink face
まず、vrm4u
はwebcam motion capture
のvmcを直接受信できません。なので、一旦、vseefaceなどのclientで受信する必要があります。それをvrm4uで受信するportに送信します。なお、vmc4ue
では直接受信できて動きます。また、xr-animator, vseeface, vmc(client)も受信できて動いています。不思議な現象です。
vrm4u(vmc)はbuild後は表情が動かないので、表情はlivelinkを使用します。これはiphoneにlivelink face
というappがあります。ueでいくつかのpluginを有効にします。
- live link
- apple arkit
- apple arkit face support
/VRM4U/Util/Actor/latest/BP_LiveLinkFace
をmapにおいて、live link subject -> iphone
, target actor sk -> SK_$name
を設定します。
これでbuildすると表情を動かすことができます。
character(player)にlivelinkを当てるには
character(player)にlivelinkを当てるにはblueprintを編集する必要があります。characterのdirにでもBP_LiveLinkFace
をcopyしてBP_Player(CBP_Character)
に追加します。
ここでBP_LiveLinkFace -> livelink subject -> iphone
をセットしておきます。
次にBP_LiveLinkFace
を以下のような形でsk_$name
に置き換えます。場所はコメントを参考にしてください。私の場合は見た目をカスタマイズしているので少し複雑です。
https://blueprintue.com/blueprint/pu_xl52s/
vmcで行くべきか
基本的には依存関係が少なく使うアプリが少ないほうがいいですね。vmcの更新頻度やclient, vrm1のsupport状況を見るとueはlivelink路線のほうがいいかもしれません。
仮にwebcam motion capture -> vrm4u
で表情も体も動かせるならvmcで問題ないですが、表情にlivelinkを使うなら全部統一するのがいいですね。