共用方式為


Unity 中的空間對應

空間對應 可讓您擷取三角形網格,以代表 HoloLens 裝置周圍世界中的表面。 您可以使用表面資料進行放置、遮擋和房間分析,為您的 Unity 專案提供額外的沉浸感。

Unity 包含對空間對應的完整支援,透過下列方式向開發人員公開:

  1. MixedRealityToolkit 中提供的空間對應元件,提供方便且快速的路徑來開始使用空間對應
  2. 較低層級的空間對應 API,可提供完整控制,並啟用更複雜的應用程式特定自訂

若要在應用程式中使用空間對應,必須在 AppxManifest 中設定 SpatialPerception 功能。

設備支持

功能 HoloLens (第一代) HoloLens 2 沉浸式耳機
空間映射 ✔️ ✔️

設定 SpatialPerception 功能

若要讓應用程式取用空間對應資料,必須啟用 SpatialPerception 功能。

如何啟用 SpatialPerception 功能:

  1. 在 Unity 編輯器中,開啟「播放器設定」窗格 (編輯>專案設定>播放器)
  2. 選擇 “Windows 應用商店” 選項卡
  3. 展開「發布設定」,勾選「功能」清單中的「空間感知」能力

注意事項

如果您已將 Unity 專案匯出至 Visual Studio 解決方案,則必須匯出至新的資料夾,或在 Visual Studio 的 AppxManifest 中手動設定此功能

空間對應也需要至少 10.0.10586.0 的 MaxVersionTested:

  1. 在 Visual Studio 中,以滑鼠右鍵按一下方案總管中的 Package.appxmanifest,然後選取 [檢視程式碼]
  2. 尋找指定 TargetDeviceFamily 的行,並將 MaxVersionTested=“10.0.10240.0” 變更為 MaxVersionTested=“10.0.10586.0”
  3. 儲存 Package.appxmanifest。

如何在 Unity 中新增對應

空間感知系統

在 MRTK 中,請參閱 空間感知快速入門 指南,以取得設定各種空間網格觀察者的相關資訊。

如需裝置上觀察者的相關資訊,請參閱 設定裝置的網狀觀察者 指南。

如需場景理解觀察者的相關資訊,請參閱 場景理解觀察者 指南。

更高層級的網格分析:空間理解

注意

「空間理解」已被取代,取而代之的是 「場景理解」。

MixedRealityToolkit 是以 Unity 全像攝影 API 為基礎建置的全像攝影開發公用程式程式碼集合。

空間理解

將全像投影放在實體世界中時,通常需要超越空間對應的網格和表面平面。 當按程序進行放置時,需要更高水準的環境理解。 這通常需要決定什麼是地板、天花板和牆壁。 您也可以針對一組放置條件約束進行最佳化,以判斷全像攝影物件的最佳實體位置。

在開發《Young Conker》和《Fragments》的過程中,Asobo Studios 透過開發房間求解器來直面這個問題。 這些遊戲中的每一個都有特定於遊戲的需求,但它們共享核心空間理解技術。 HoloToolkit.SpatialUnderstanding 程式庫封裝了這項技術,可讓您快速尋找牆壁上的空白空間、將物件放在天花板上、識別角色所在的位置,以及無數其他空間理解查詢。

包含所有源代碼,允許您根據需要對其進行自定義並與社區分享您的改進。 C++ 求解器的程式碼已包裝成 UWP dll,並公開給 Unity,並捨棄 MixedRealityToolkit 中包含的預製件。

了解模組

該模組公開了三個主要介面:用於簡單表面和空間查詢的拓撲、用於物件偵測的形狀,以及用於基於約束的物件集放置的物件放置求解器。 下面將介紹其中的每一個。 除了三個主要模組介面之外,光線投射介面可用於檢索標記的表面類型,並且可以複製自訂水密遊戲空間網格。

射線投射

房間掃描完成後,會在內部為地板、天花板和牆壁等表面產生標籤。 該 PlayspaceRaycast 函數接受光線,如果光線與已知表面碰撞,則返回,如果是,則返回有關該表面 RaycastResult的資訊。

struct RaycastResult
{
    enum SurfaceTypes
    {
        Invalid,    // No intersection
        Other,
        Floor,
        FloorLike,  // Not part of the floor topology,
                    //  but close to the floor and looks like the floor
        Platform,   // Horizontal platform between the ground and
                    //  the ceiling
        Ceiling,
        WallExternal,
        WallLike,   // Not part of the external wall surface,
                    //  but vertical surface that looks like a
                    //  wall structure
    };
    SurfaceTypes SurfaceType;
    float SurfaceArea;  // Zero if unknown
                        //  (i.e. if not part of the topology analysis)
    DirectX::XMFLOAT3 IntersectPoint;
    DirectX::XMFLOAT3 IntersectNormal;
};

在內部,光線投射是根據計算的遊戲空間的 8 厘米立方體體素表示法計算的。 每個體素都包含一組表面元素,其中包含經過處理的拓撲資料 (又稱表面) 。 比較相交體素單元中包含的表面,並使用最佳匹配來查找拓撲信息。 此拓撲資料包含以 “SurfaceTypes” 列舉形式傳回的標籤,以及相交曲面的表面積。

在 Unity 範例中,游標會在每個畫面中投射光線。 首先,對抗 Unity 的碰撞器。 其次,反對理解模塊的世界表示。 最後,再次是 UI 元素。 在此應用程式中,UI 獲得優先權,其次是理解結果,最後是 Unity 的碰撞器。 SurfaceType 會報告為游標旁邊的文字。

曲面類型標示在游標旁邊
曲面類型標示在游標旁邊

拓撲查詢

在 DLL 內,拓撲管理員會處理環境的標籤。 如上所述,大部分數據存儲在體素體積中。 此外,「PlaySpaceInfos」結構用於儲存有關遊戲空間的信息,包括世界對齊 (有關此的更多詳細資訊,包括以下) 、地板和天花板高度。 啟發式用於確定地板、天花板和牆壁。 例如,表面積大於 1-m2 的最大和最低水平面被視為樓板。

注意事項

掃描過程中的相機路徑也用於此過程。

拓撲管理員所公開的查詢子集會透過 dll 公開。 公開的拓撲查詢如下。

QueryTopology_FindPositionsOnWalls
QueryTopology_FindLargePositionsOnWalls
QueryTopology_FindLargestWall
QueryTopology_FindPositionsOnFloor
QueryTopology_FindLargestPositionsOnFloor
QueryTopology_FindPositionsSittable

每個查詢都有一組參數,特定於查詢類型。 在下列範例中,使用者指定所需體積的最小高度 & 寬度、地板上方的最小放置高度,以及體積前面的最小間隙量。 所有測量值均以米為單位。

EXTERN_C __declspec(dllexport) int QueryTopology_FindPositionsOnWalls(
    _In_ float minHeightOfWallSpace,
    _In_ float minWidthOfWallSpace,
    _In_ float minHeightAboveFloor,
    _In_ float minFacingClearance,
    _In_ int locationCount,
    _Inout_ Dll_Interface::TopologyResult* locationData)

每個查詢都會採用預先配置的「TopologyResult」結構陣列。 「locationCount」參數會指定傳入陣列的長度。 傳回值會報告傳回位置的數目。 此數字永遠不會大於傳入的 “locationCount” 參數。

「TopologyResult」包含傳回體積的中心位置、朝向 (方向(即法線) )以及找到的空間的尺寸。

struct TopologyResult
{
    DirectX::XMFLOAT3 position;
    DirectX::XMFLOAT3 normal;
    float width;
    float length;
};

注意事項

在 Unity 範例中,每個查詢都會連結至虛擬 UI 面板中的按鈕。 範例會將每個查詢的參數硬式編碼為合理的值。 如需更多範例,請參閱範例程式碼中的SpaceVisualizer.cs。

圖形查詢

在 dll 中,圖形分析器 (“ShapeAnalyzer_W”) 會使用拓撲分析器來比對使用者定義的自定義圖形。 Unity 範例會定義一組圖形,並透過應用程式內查詢功能表在圖形索引標籤內公開結果。目的是使用者可以定義自己的物件形狀查詢,並視應用程式需要使用這些查詢。

造型分析僅適用於水平曲面。 例如,沙發由平坦的座面和沙發靠背的平坦頂部定義。 形狀查詢會尋找特定大小、高度和縱橫向範圍的兩個表面,其中兩個表面已對齊並連接。 使用 API 術語,沙發座椅和靠背是造型元件,坐標系需求是造型元件約束。

Unity 範例 (ShapeDefinition.cs) 中針對「可坐」物件定義的範例查詢如下。

shapeComponents = new List<ShapeComponent>()
{
    new ShapeComponent(
        new List<ShapeComponentConstraint>()
        {
            ShapeComponentConstraint.Create_SurfaceHeight_Between(0.2f, 0.6f),
            ShapeComponentConstraint.Create_SurfaceCount_Min(1),
            ShapeComponentConstraint.Create_SurfaceArea_Min(0.035f),
        }
    ),
};
AddShape("Sittable", shapeComponents);

每個圖形查詢都由一組圖形元件定義,每個元件都有一組元件條件約束和一組列出元件之間相依性的形狀條件約束。 此範例在單一元件定義中包含三個條件約束,且元件之間沒有形狀條件約束 (因為只有一個元件) 。

相反,沙發形狀有兩個形狀組件和四個形狀約束。 元件是由使用者元件清單中的索引識別,在此範例) 中 (0 和 1。

shapeConstraints = new List<ShapeConstraint>()
{
    ShapeConstraint.Create_RectanglesSameLength(0, 1, 0.6f),
    ShapeConstraint.Create_RectanglesParallel(0, 1),
    ShapeConstraint.Create_RectanglesAligned(0, 1, 0.3f),
    ShapeConstraint.Create_AtBackOf(1, 0),
};

Unity 模組中提供了包裝函式,以便輕鬆建立自訂形狀定義。 元件和形狀約束的完整清單可以在 “ShapeComponentConstraint” 和 “ShapeConstraint” 結構中的 “SpatialUnderstandingDll.cs” 中找到。

在此表面上發現矩形
在此表面上發現矩形

物件放置求解器

物件放置求解器可用來識別實體房間中放置物件的理想位置。 求解器將找到給定物件規則和約束的最佳擬合位置。 此外,物件查詢會持續存在,直到使用「Solver_RemoveObject」或「Solver_RemoveAllObjects」呼叫移除物件為止,從而允許限制多物件放置。 物件放置查詢由三個部分組成:具有參數的放置類型、規則清單和條件約束清單。 若要執行查詢,請使用下列 API。

public static int Solver_PlaceObject(
            [In] string objectName,
            [In] IntPtr placementDefinition,        // ObjectPlacementDefinition
            [In] int placementRuleCount,
            [In] IntPtr placementRules,             // ObjectPlacementRule
            [In] int constraintCount,
            [In] IntPtr placementConstraints,       // ObjectPlacementConstraint
            [Out] IntPtr placementResult)

此函數採用物件名稱、放置定義以及規則和約束清單。 C# 包裝函式提供建構協助程式函式,讓規則和條件約束建構變得容易。 放置定義包含查詢類型,也就是下列其中一項。

public enum PlacementType
{
    Place_OnFloor,
    Place_OnWall,
    Place_OnCeiling,
    Place_OnShape,
    Place_OnEdge,
    Place_OnFloorAndCeiling,
    Place_RandomInAir,
    Place_InMidAir,
    Place_UnderFurnitureEdge,
};

每個放置型別都有一組類型特有的引數。 「ObjectPlacementDefinition」 結構包含一組靜態協助程式函式,用於建立這些定義。 例如,要找到一個將物體放在地板上的地方,您可以使用以下函數。 公共靜態 ObjectPlacementDefinition Create_OnFloor (Vector3 halfDims) 除了放置類型之外,您還可以提供一組規則和約束。 不能違反規則。 然後,會針對一組條件約束最佳化滿足類型與規則的可能放置位置,以選取最佳放置位置。 每個規則和限制都可以由提供的靜態建立函數建立。 下面提供了一個示例規則和約束構建函數。

public static ObjectPlacementRule Create_AwayFromPosition(
    Vector3 position, float minDistance)
public static ObjectPlacementConstraint Create_NearPoint(
    Vector3 position, float minDistance = 0.0f, float maxDistance = 0.0f)

下列物件放置查詢會尋找一個位置,以將半公尺立方體放在表面邊緣,遠離其他先前放置的物件,並靠近房間的中心。

List<ObjectPlacementRule> rules =
    new List<ObjectPlacementRule>() {
        ObjectPlacementRule.Create_AwayFromOtherObjects(1.0f),
    };

List<ObjectPlacementConstraint> constraints =
    new List<ObjectPlacementConstraint> {
        ObjectPlacementConstraint.Create_NearCenter(),
    };

Solver_PlaceObject(
    “MyCustomObject”,
    new ObjectPlacementDefinition.Create_OnEdge(
        new Vector3(0.25f, 0.25f, 0.25f),
        new Vector3(0.25f, 0.25f, 0.25f)),
    rules.Count,
    UnderstandingDLL.PinObject(rules.ToArray()),
    constraints.Count,
    UnderstandingDLL.PinObject(constraints.ToArray()),
    UnderstandingDLL.GetStaticObjectPlacementResultPtr());

如果成功,則會傳回包含放置位置、維度和方向的 “ObjectPlacementResult” 結構。 此外,放置會新增至 dll 的內部放置物件清單。 後續的放置查詢將考慮此物件。 Unity 範例中的 “LevelSolver.cs” 檔案包含更多範例查詢。

物件放置的結果
圖 3:藍色方塊顯示三個查詢的結果如何放置在遠離相機位置規則的地板上

在求解一個關卡或應用場景所需的多個物體的放置位置時,首先求解不可或缺的大型物體,以最大限度地提高找到空間的機率。 放置順序很重要。 如果找不到物件放置,請嘗試較少限制的組態。 擁有一組後援組態對於支援許多會議室組態的功能至關重要。

房間掃描流程

雖然 HoloLens 提供的空間對應解決方案設計得足夠通用,可以滿足整個問題空間領域的需求,但空間理解模組的建置是為了支援兩個特定遊戲的需求。 其解決方案是圍繞特定流程和一組假設構建的,總結如下。

Fixed size playspace – The user specifies the maximum playspace size in the init call.

One-time scan process –
    The process requires a discrete scanning phase where the user walks around,
    defining the playspace.
    Query functions will not function until after the scan has been finalized.

使用者驅動的遊戲空間「繪畫」 – 在掃描階段,使用者會移動並環顧遊戲節奏,有效地繪製應包含的區域。 產生的網格對於在此階段提供使用者回饋非常重要。 室內家庭或辦公室設定 – 查詢功能是圍繞平面和牆壁以直角設計。 這是一個軟限制。 然而,在掃描階段,會完成主軸分析,以最佳化沿長軸和短軸的網格鑲嵌。 隨附的SpatialUnderstanding.cs檔案會管理掃描階段程序。 它會呼叫下列函式。

SpatialUnderstanding_Init – Called once at the start.

GeneratePlayspace_InitScan – Indicates that the scan phase should begin.

GeneratePlayspace_UpdateScan_DynamicScan –
    Called each frame to update the scanning process. The camera position and
    orientation is passed in and is used for the playspace painting process,
    described above.

GeneratePlayspace_RequestFinish –
    Called to finalize the playspace. This will use the areas “painted” during
    the scan phase to define and lock the playspace. The application can query
    statistics during the scanning phase as well as query the custom mesh for
    providing user feedback.

Import_UnderstandingMesh –
    During scanning, the “SpatialUnderstandingCustomMesh” behavior provided by
    the module and placed on the understanding prefab will periodically query the
    custom mesh generated by the process. In addition, this is done once more
    after scanning has been finalized.

由「SpatialUnderstanding」行為驅動的掃描流程會呼叫 InitScan,然後呼叫每個影格的 UpdateScan。 當統計資料查詢報告合理的涵蓋範圍時,允許使用者隔空點選以呼叫 RequestFinish 以指出掃描階段的結束。 UpdateScan 會繼續呼叫,直到其傳回值指出 dll 已完成處理為止。

了解網格

理解 dll 在內部將遊戲空間存儲為 8 厘米大小的體素立方體的網格。 在掃描的初始部分,完成主要組件分析以確定房間的軸線。 在內部,它儲存與這些軸對齊的體素空間。 透過從體素體積中提取等值面,大約每秒產生一個網格。

從體素體積產生的產生網格
從體素體積產生的產生網格

疑難排解

  • 確定您已設定 SpatialPerception 功能
  • 當追蹤遺失時,下一個 OnSurfaceChanged 事件會移除所有網面。

Mixed Reality 工具組中的空間對應

如需搭配 Mixed Reality 工具組使用空間對應的詳細資訊,請參閱 MRTK 檔的空間感知一節

下一個開發檢查點

如果您遵循我們所建置的 Unity 開發旅程,您正在探索 MRTK 核心建置組塊。 從這裡,您可以繼續下一個建置區塊:

或跳至 Mixed Reality 平臺功能和 API:

您隨時可以返回 Unity 開發檢查點

另請參閱