手部追蹤 — MRTK2

手部追蹤設定檔

手部追蹤設定檔位於輸入系統設定檔下。 它包含自訂手部表現的設定。

手部追蹤檔案

關節預製件

接頭預製件是用簡單的預製件來視覺化的。 掌狀接頭和食指接頭特別重要,擁有各自的預製件,而其他接頭則共用相同的預製件。

預設情況下,手動接頭預製件是簡單的幾何基元。 這些都可以如願更換。 若完全未指定預製件,則會建立空的 GameObject 。

警告

避免在 joint 預製件中使用複雜腳本或昂貴的渲染,因為 joint 物件會在每一幀都被轉換,且可能帶來顯著的效能負擔!

預設手部關節表示 聯合唱片公司
關節式手關節 輸入手關節

手工網狀預製件

若手部追蹤裝置提供完整定義的網格資料,則使用手部網格。 預製件中可渲染的網格會被裝置資料取代,因此像立方體這樣的虛擬網格就足夠了。 預製件的材料用於手工網格。

輸入手部網格

手部網格顯示可能會明顯影響效能,因此只要取消勾選 啟用手部網格視覺化 選項,就可以完全關閉。

手部視覺化設定

手部網格與手部關節視覺化可分別透過 手部網格視覺化模式 與 手部關節視覺化模式 來關閉或開啟。 這些設定是針對應用程式模式的,意即在編輯器 (中開啟部分功能,透過編輯器內模擬看到關節,例如) ,但在玩家建構) 部署到裝置 (時則關閉相同功能。

請注意,通常建議在編輯器 (開啟手部關節視覺化功能,這樣在編輯器內模擬時會顯示手部關節的) 位置;同時在播放器 (中關閉手部關節視覺化和手部網格視覺化,因為這會造成效能) 影響。

腳本編寫

每個手關節 MixedRealityPose的輸入系統可請求位置與旋轉。

或者系統允許存取跟隨關節的 遊戲物件 。 如果其他 GameObject 需要持續追蹤一個接點,這會很有用。

可用接頭列於列舉中 TrackedHandJoint 。

注意事項

當手部追蹤中斷時,關節物件會被摧毀! 確保使用聯合物件的腳本能優雅地處理此 null 情況,以避免錯誤!

存取特定的手型控制器

通常會使用特定的手型控制器,例如在處理輸入事件時。 此時可直接透過介面向裝置 IMixedRealityHand 請求聯合資料。

控制器的輪詢聯合姿勢

如果請求的接點因某些原因無法使用,函式會 TryGetJoint 回傳 false 。 此時所得姿態為 MixedRealityPose.ZeroIdentity。

public void OnSourceDetected(SourceStateEventData eventData)
{
  var hand = eventData.Controller as IMixedRealityHand;
  if (hand != null)
  {
    if (hand.TryGetJoint(TrackedHandJoint.IndexTip, out MixedRealityPose jointPose)
    {
      // ...
    }
  }
}

手部視覺化器的關節轉換

可向 控制器視覺化器請求聯合物件。

public void OnSourceDetected(SourceStateEventData eventData)
{
  var handVisualizer = eventData.Controller.Visualizer as IMixedRealityHandVisualizer;
  if (handVisualizer != null)
  {
    if (handVisualizer.TryGetJointTransform(TrackedHandJoint.IndexTip, out Transform jointTransform)
    {
      // ...
    }
  }
}

簡化的聯合資料存取

若未提供特定控制器,則提供工具類別以便方便存取手關節資料。 這些功能會要求目前追蹤的第一個可用手持裝置進行聯合資料。

來自 HandJointUtils 的輪詢聯合姿勢

HandJointUtils 是一個靜態類別,查詢第一個主動的手持裝置。

if (HandJointUtils.TryGetJointPose(TrackedHandJoint.IndexTip, Handedness.Right, out MixedRealityPose pose))
{
    // ...
}

從手部聯合服務的關節轉換

IMixedRealityHandJointService 保留一組持續存在的 GameObjects 來追蹤關節。

var handJointService = CoreServices.GetInputSystemDataProvider<IMixedRealityHandJointService>();
if (handJointService != null)
{
    Transform jointTransform = handJointService.RequestJointTransform(TrackedHandJoint.IndexTip, Handedness.Right);
    // ...
}

手部追蹤事件

如果不希望直接從控制器查詢資料,輸入系統也會提供事件。

聯合活動

IMixedRealityHandJointHandler 負責關節位置的更新。

public class MyHandJointEventHandler : IMixedRealityHandJointHandler
{
    public Handedness myHandedness;

    void IMixedRealityHandJointHandler.OnHandJointsUpdated(InputEventData<IDictionary<TrackedHandJoint, MixedRealityPose>> eventData)
    {
        if (eventData.Handedness == myHandedness)
        {
            if (eventData.InputData.TryGetValue(TrackedHandJoint.IndexTip, out MixedRealityPose pose))
            {
                // ...
            }
        }
    }
}

網狀事件

IMixedRealityHandMeshHandler 處理關節式手網的變更。

請注意,手部網格預設並非啟用。

public class MyHandMeshEventHandler : IMixedRealityHandMeshHandler
{
    public Handedness myHandedness;
    public Mesh myMesh;

    public void OnHandMeshUpdated(InputEventData<HandMeshInfo> eventData)
    {
        if (eventData.Handedness == myHandedness)
        {
            myMesh.vertices = eventData.InputData.vertices;
            myMesh.normals = eventData.InputData.normals;
            myMesh.triangles = eventData.InputData.triangles;

            if (eventData.InputData.uvs != null && eventData.InputData.uvs.Length > 0)
            {
                myMesh.uv = eventData.InputData.uvs;
            }

            // ...
        }
    }
}

已知問題

.NET Native

目前已知使用 .NET 後端的 Master 建置有問題。 在 .NET Native 中,IInspectable指標無法使用 Marshal.GetObjectForIUnknown. MRTK 利用此技術取得 SpatialCoordinateSystem 以接收平台的手部與眼睛資料。

我們已在原生的 Mixed Reality Toolkit 倉庫中提供了 DLL 原始碼作為解決方法。 請依照 README 中的指示,將產生的二進位檔複製到 Unity 資產中的 Plugins 資料夾。 之後,MRTK 提供的 WindowsMixedRealityUtilities 腳本會幫你解決這個變通方法。

如果你想建立自己的 DLL 或將此變通方法納入現有 DLL,該變通核心如下:

extern "C" __declspec(dllexport) void __stdcall MarshalIInspectable(IUnknown* nativePtr, IUnknown** inspectable)
{
    *inspectable = nativePtr;
}

以及它在你的 C# Unity 程式碼中的使用:

[DllImport("DotNetNativeWorkaround.dll", EntryPoint = "MarshalIInspectable")]
private static extern void GetSpatialCoordinateSystem(IntPtr nativePtr, out SpatialCoordinateSystem coordinateSystem);

private static SpatialCoordinateSystem GetSpatialCoordinateSystem(IntPtr nativePtr)
{
    try
    {
        GetSpatialCoordinateSystem(nativePtr, out SpatialCoordinateSystem coordinateSystem);
        return coordinateSystem;
    }
    catch
    {
        UnityEngine.Debug.LogError("Call to the DotNetNativeWorkaround plug-in failed. The plug-in is required for correct behavior when using .NET Native compilation");
        return Marshal.GetObjectForIUnknown(nativePtr) as SpatialCoordinateSystem;
    }
}