Unityでの空間マッピング
空間マッピング を使用すると、HoloLens デバイスの周りの世界のサーフェスを表す三角形メッシュを取得できます。 配置、オクルージョン、部屋の分析にサーフェス データを使用して、Unity プロジェクトに追加の浸漬量を与えることができます。
Unityには、次の方法で開発者に公開される空間マッピングの完全なサポートが含まれています。
- MixedRealityToolkit で使用できる空間マッピング コンポーネント。これは、空間マッピングを開始するための便利で迅速なパスを提供します
- より低レベルの空間マッピング API。完全な制御を提供し、より高度なアプリケーション固有のカスタマイズを可能にする
アプリで空間マッピングを使用するには、AppxManifest で SpatialPerception 機能を設定する必要があります。
デバイスのサポート
機能 | HoloLens (第 1 世代) | HoloLens 2 | イマーシブ ヘッドセット |
---|---|---|---|
空間マッピング | ✔️ | ✔️ | ❌ |
SpatialPerception 機能の設定
アプリで空間マッピング データを使用するには、SpatialPerception 機能を有効にする必要があります。
SpatialPerception 機能を有効にする方法:
- Unity エディターで、[プレイヤーの設定] ウィンドウを開きます ([プロジェクト設定の編集] > [プレーヤー] >)
- [Windows ストア] タブを選択します
- [発行設定] を展開し、[機能] リストの [SpatialPerception] 機能をチェックします
注:
Unity プロジェクトを Visual Studio ソリューションに既にエクスポートしている場合は、新しいフォルダーにエクスポートするか、Visual Studio の AppxManifest でこの機能を手動で設定する必要があります。
空間マッピングには、少なくとも 10.0.10586.0 の MaxVersionTested も必要です。
- Visual Studio で、ソリューション エクスプローラーで Package.appxmanifest を右クリックし、[コードの表示] を選択します
- TargetDeviceFamily を指定する行を検索し、MaxVersionTested="10.0.10240.0" を MaxVersionTested="10.0.10586.0" に変更します。
- Package.appxmanifest を保存します。
Unityでマッピングを追加する方法
空間認識システム
MRTK で、さまざまな空間メッシュ オブザーバーの設定に関する情報については、「空間 認識 入門ガイド」を参照してください。
デバイス上のオブザーバーの詳細については、デバイス ガイドの メッシュ オブザーバーの構成に関するページを 参照してください。
シーン理解オブザーバーの詳細については、 シーン理解オブザーバー ガイドを参照してください。
上位レベルのメッシュ分析: 空間理解
注意
空間理解は 、シーン理解を優先して非推奨になりました。
MixedRealityToolkit は、Unityのホログラフィック API 上に構築されたホログラフィック開発用のユーティリティ コードのコレクションです。
空間理解
物理的な世界にホログラムを配置する場合、多くの場合、空間マッピングのメッシュとサーフェス平面を超えることが望ましいです。 配置を手続き的に行う場合は、より高いレベルの環境理解が望ましいです。 これは通常、床、天井、壁について決定する必要があります。 また、一連の配置制約に対して最適化して、ホログラフィック オブジェクトに最適な物理的な場所を決定することもできます。
ヤングコンカーとフラグメントの開発中、Asobo Studiosは部屋ソルバーを開発することでこの問題に正面から直面しました。 これらの各ゲームにはゲーム固有のニーズがありましたが、コア空間理解テクノロジを共有していました。 HoloToolkit.SpatialUnderstanding ライブラリは、このテクノロジをカプセル化しているため、壁に空のスペースをすばやく見つけ、天井にオブジェクトを配置し、座る文字の配置を識別し、その他の無数の空間理解クエリを使用できます。
すべてのソース コードが含まれており、ニーズに合わせてカスタマイズし、改善点をコミュニティと共有できます。 C++ ソルバーのコードは UWP dll にラップされ、MixedRealityToolkit に含まれるプレハブがドロップされた状態でUnityに公開されています。
モジュールについて
モジュールによって公開される主なインターフェイスは、単純なサーフェスクエリと空間クエリのトポロジ、オブジェクト検出用の形状、およびオブジェクト セットの制約ベースの配置のためのオブジェクト配置ソルバーの 3 つです。 これらのそれぞれについて以下に説明します。 3 つのプライマリ モジュール インターフェイスに加えて、レイ キャスト インターフェイスを使用して、タグ付けされたサーフェス タイプを取得し、カスタムの水密プレイスペース メッシュをコピーアウトできます。
レイ キャスト
ルーム スキャンが完了すると、床、天井、壁などのサーフェスに対してラベルが内部的に生成されます。
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 cm の立方体ボクセル表現に対して計算されます。 各ボクセルには、処理されたトポロジ データ (サーフェル) を含む一連のサーフェス要素が含まれています。 交差したボクセル セル内に含まれるサーベルが比較され、トポロジ情報を検索するために使用される最適な一致が比較されます。 このトポロジ データには、"SurfaceTypes" 列挙型の形式で返されるラベル付けと、交差するサーフェスのサーフェス領域が含まれます。
Unityサンプルでは、カーソルはフレームごとにレイをキャストします。 まず、Unityのコライダーに対して。 第二に、理解モジュールの世界表現に対する。 最後に、もう一度 UI 要素。 このアプリケーションでは、UI は優先順位を取得し、次に理解の結果を取得し、最後にUnityのコライダーを取得します。 SurfaceType は、カーソルの横にテキストとして報告されます。
カーソルの横に Surface の種類のラベルが付けられます
トポロジ クエリ
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サンプルでは、図形のセットを定義し、結果をアプリ内のクエリ メニューの [図形] タブで公開します。目的は、ユーザーが独自のオブジェクトシェイプクエリを定義し、アプリケーションで必要に応じてそれらを利用できるようにすることです。
形状解析は、水平方向のサーフェスでのみ機能します。 たとえば、ソファは、フラットシートサーフェスとソファバックのフラットトップによって定義されます。 図形クエリでは、特定のサイズ、高さ、およびアスペクト範囲の 2 つのサーフェスが検索され、2 つのサーフェスが配置され、接続されています。 API の用語を使用すると、ソファシートとバックトップは形状コンポーネントであり、アライメント要件は形状コンポーネントの制約です。
"sittable" オブジェクトに対して、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);
各図形クエリは、一連の図形コンポーネントによって定義され、それぞれに一連のコンポーネント制約と、コンポーネント間の依存関係を一覧表示する一連の図形制約が含まれます。 この例では、1 つのコンポーネント定義に 3 つの制約が含まれており、コンポーネント間に図形の制約はありません (コンポーネントは 1 つだけです)。
対照的に、ソファの形状には、2 つの図形コンポーネントと 4 つの図形の制約があります。 コンポーネントは、ユーザーのコンポーネント リスト (この例では 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" 呼び出しで削除されるまで保持され、制約付きマルチオブジェクト配置が可能になります。 オブジェクト配置クエリは、パラメーターを含む配置の種類、規則の一覧、制約の一覧の 3 つの部分で構成されます。 クエリを実行するには、次の 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" 構造体には、これらの定義を作成するための一連の静的ヘルパー関数が含まれています。 たとえば、オブジェクトをフロアに配置する場所を見つけるには、次の関数を使用できます。 public static 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)
次のオブジェクト配置クエリでは、サーフェスの端に 5 分の 1 のキューブを配置し、以前に配置した他のオブジェクトから離れて、部屋の中央付近に配置する場所を探しています。
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: カメラの位置ルールから離れたフロア クエリの 3 か所の結果を青色のボックスに表示する
レベルまたはアプリケーションのシナリオに必要な複数のオブジェクトの配置場所を解決する場合は、スペースが見つかる確率を最大化するために、まず不可欠で大きなオブジェクトを解決します。 配置順序は重要です。 オブジェクトの配置が見つからない場合は、制約の少ない構成を試してください。 一連のフォールバック構成を使用することは、多くのルーム構成で機能をサポートするうえで重要です。
会議室のスキャン プロセス
HoloLens によって提供される空間マッピング ソリューションは、問題のあるスペースの領域全体のニーズを満たすのに十分な汎用的なものとして設計されていますが、空間理解モジュールは、2 つの特定のゲームのニーズをサポートするために構築されました。 そのソリューションは、特定のプロセスと仮定のセットに基づいて構成されています。以下にまとめます。
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 を呼び出すために airtap を許可されます。 UpdateScan は、戻り値が dll の処理を完了したことを示すまで引き続き呼び出されます。
メッシュについて
理解 dll は、プレイスペースを 8 cm サイズのボクセル キューブのグリッドとして内部的に格納します。 スキャンの最初の部分では、主要なコンポーネント分析が完了し、部屋の軸が決定されます。 内部的には、これらの軸に配置されたボクセル空間が格納されます。 ボクセル ボリュームから等値面を抽出することで、約 1 秒ごとにメッシュが生成されます。
ボクセル ボリュームから生成されたメッシュ
トラブルシューティング
- SpatialPerception 機能が設定されていることを確認します
- 追跡が失われると、次の OnSurfaceChanged イベントによってすべてのメッシュが削除されます。
Mixed Reality Toolkit での空間マッピング
Mixed Reality Toolkit での空間マッピングの使用の詳細については、MRTK ドキュメントの「空間認識」セクションを参照してください。
次の開発チェックポイント
私たちがレイアウトしたUnity開発の過程に従っている場合は、MRTK コア構成要素を探索している最中です。 ここから、次の構成要素に進むことができます。
または、Mixed Realityプラットフォームの機能と API に移動します。
いつでもUnity開発チェックポイントに戻ることができます。