HoloLens の Spatial Understanding について調べてみた
参考URL
Spatial Understanding
Spatial mapping のメッシュ上にオブジェクトを置くとき、床・天井・壁の認識が必要となる。加えて、ホログラフィックオブジェクトの最も望ましい物理的な位置を決定するために、配置制約をもとに最適化することも必要となる。
- これち:この配置制約というのはおそらくSpatialMappingで取得したメッシュ内で、という制約
この課題を解決するため room solver を開発した。これらのコア技術は HoloToolkit.SpatialUnderstanding ライブラリ内に存在しこれを使うことで以下のことが可能となる。
1. 壁の空いたスペースを探す
2. 天井にオブジェクトを配置
3. キャラクターが座ることのできる場所の特定
4. その他の空間クエリ
これらのコードはソースコード内にあるためカスタマイズが可能である。また、C++コードは UWP の dll や Unity 内のプレハブにラップされている。
Ray Casting
部屋のスキャンが進むと床・天井・壁のラベルが生成される。PlayspaceRaycast関数はRayを飛ばし何かの表面にあたったら、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 };https://camo.githubusercontent.com/badbca8d962fe99d35fc861aade93fbb8dcc9072/68747470733a2f2f617a3833353932372e766f2e6d7365636e642e6e65742f73697465732f6d697865642d7265616c6974792f5265736f75726365732f696d616765732f726179636173742d726573756c742e6a7067 SurfaceTypes SurfaceType; float SurfaceArea; // Zero if unknown // (i.e. if not part of the topology analysis) DirectX::XMFLOAT3 IntersectPoint; DirectX::XMFLOAT3 IntersectNormal; };
Unityサンプルでは、カーソルがRayを飛ばし、Unityコライダーに衝突後、understandingモジュールを介し、UIエレメントに表示される。
SpaceVisualizer.csに使っているクエリなどが書かれているため、参考にするとよい。
Shape Queries
dll内のShapeAnalyzer_W
にてユーザが定義したカスタムshapeとのマッチが行われる。Unityサンプルではサンプルのshapeを定義し、結果をアプリ内のクエリメニューで表示している。このカスタムshapeは水平面でしか機能しないことに注意すべき。
例えばソファーは、平らな座面と平らな頂部によって決定される。以下はUnityで使われるクエリ例である。
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);
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), };
SpatialUnderstandingDll.cs
を読めばより詳しいことがわかる。
Object Placement Solver
object placement solver はオブジェクトルールと制約をもとに、オブジェクトを配置する理想的な位置を探すことができる。オブジェクトのクエリはSolver_RemoveObject
で削除されるまで持続する。
オブジェクト配置クエリは3つのパートからなる。
1. placement type
2. ルールのリスト
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)
placement typeは以下のenumで定義される。
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)
上画像のように床の上にオブジェクトを置きたい場合のサンプルコードは以下のように書くことができる。
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
構造体に、置く場所・次元・角度が返却される。
LevelSolver.cs
ファイルを読めば様々なクエリを確認することができる。