Případová studie – Rozšíření možností prostorového mapování holoLensu

Při vytváření našich prvních aplikací pro Microsoft HoloLens jsme netrpělivě viděli, jak daleko můžeme na zařízení posouvat hranice prostorového mapování. Jeff Evertt, softwarový inženýr ve společnosti Microsoft Studios, vysvětluje, jak byla vyvinuta nová technologie kvůli potřebě větší kontroly nad tím, jak se hologramy umísťují do reálného prostředí uživatele.

Poznámka

HoloLens 2 implementuje nové prostředí Scene Understanding Runtime, které vývojářům Mixed Reality poskytuje strukturovanou reprezentaci prostředí na vysoké úrovni, která je navržená tak, aby byl vývoj pro aplikace s ohledem na životní prostředí intuitivní.

Přehrát video

Nad rámec prostorového mapování

Když jsme pracovali na Fragments a Young Conker, dvou z prvních her pro HoloLens, zjistili jsme, že když jsme prováděli procedurální umístění hologramů ve fyzickém světě, potřebovali jsme vyšší úroveň porozumění prostředí uživatele. Každá hra měla své vlastní specifické potřeby umístění: Například ve fragmentech jsme chtěli být schopni rozlišovat mezi různými povrchy – jako je podlaha nebo stůl – a umístit vodítka na relevantní místa. Také jsme chtěli být schopni identifikovat povrchy, na kterých by holografické znaky v životní velikosti mohly sedět, jako je gauč nebo židle. V Young Conker jsme chtěli, aby Conker a jeho soupeři mohli používat vyvýšené plochy v hráčské místnosti jako platformy.

Asobo Studios, náš vývojový partner pro tyto hry, se s tímto problémem potýkala a vytvořila technologii, která rozšiřuje možnosti prostorového mapování HoloLensu. Pomocí toho bychom mohli analyzovat místnost hráče a identifikovat povrchy, jako jsou stěny, stoly, židle a podlahy. To nám také umožnilo optimalizovat proti sadě omezení, abychom určili nejlepší umístění pro holografické objekty.

Kód prostorového porozumění

Vzali jsme původní kód asoba a vytvořili knihovnu, která tuto technologii zapouzdřuje. Microsoft a Asobo nyní tento kód zpřístupnily na platformě MixedRealityToolkit , abyste ho mohli použít ve svých vlastních projektech. Součástí je veškerý zdrojový kód, který vám umožní přizpůsobit ho vašim potřebám a sdílet svá vylepšení s komunitou. Kód pro řešitel jazyka C++ byl zabalen do knihovny DLL UPW a zpřístupněn unity s překryvným prefabem obsaženým v MixedRealityToolkit.

V ukázce Unity je mnoho užitečných dotazů, které vám umožní najít prázdné prostory na stěnách, umístit objekty na strop nebo na velké prostory na podlaze, identifikovat místa, kde mají znaky sedět, a nesčetné množství dalších dotazů na prostorové porozumění.

Zatímco řešení prostorového mapování, které poskytuje HoloLens, je navržené tak, aby bylo dostatečně obecné, aby vyhovovalo potřebám celé škály problémových prostorů, modul prostorového porozumění byl vytvořen tak, aby podporoval potřeby dvou specifických her. Jeho řešení je tedy strukturováno podle konkrétního procesu a sady předpokladů:

  • Pevná velikost playspace: Uživatel určuje maximální velikost playspace ve volání init.
  • Proces jednorázové kontroly: Proces vyžaduje samostatnou fázi skenování, ve které uživatel prochází a definuje herní prostor. Funkce dotazu nebudou fungovat, dokud nebude kontrola dokončena.
  • Uživatelem řízené "malování": Během fáze skenování se uživatel pohybuje a rozhlížel se po herním prostoru a efektivně maloval oblasti, které by měly být zahrnuty. Vygenerovaná síť je důležitá pro poskytování zpětné vazby uživatelů během této fáze.
  • Vnitřní nastavení domova nebo kanceláře: Funkce dotazů jsou navrženy kolem plochých povrchů a zdí v pravém úhlu. Jedná se o měkké omezení. Během fáze skenování se však provádí analýza primární osy, která optimalizuje teselaci sítě podél hlavní a vedlejší osy.

Proces skenování místností

Když načtete modul prostorového porozumění, první věc, kterou uděláte, je naskenovat prostor, takže všechny použitelné povrchy – jako je podlaha, strop a stěny – jsou identifikovány a označeny. Během procesu skenování se rozhlédnete po místnosti a "vymalujete" oblasti, které by měly být součástí skenování.

Síť, kterou uvidíte během této fáze, je důležitou vizuální zpětnou vazbou, která uživatelům umožňuje zjistit, které části místnosti se kontrolují. Knihovna DLL pro modul prostorového porozumění interně ukládá playspace jako mřížku voxelových krychlí o velikosti 8 cm. Během počáteční části skenování se dokončí analýza primárních komponent, která určí osy místnosti. Interně ukládá svůj prostor voxelu zarovnaný k těmto osám. Síť se generuje přibližně každou sekundu extrahováním izosurface z objemu voxelu.

Prostorová mapovací síť v bílé barvě a porozumění síti herních prostorů v zelené barvě

Prostorová mapovací síť v bílé barvě a porozumění síti herních prostorů v zelené barvě

Zahrnutý soubor SpatialUnderstanding.cs spravuje proces fáze skenování. Volá následující funkce:

  • SpatialUnderstanding_Init: Volá se jednou na začátku.
  • GeneratePlayspace_InitScan: Označuje, že by měla začít fáze kontroly.
  • GeneratePlayspace_UpdateScan_DynamicScan: Každý rámec volal, aby se aktualizoval proces kontroly. Umístění a orientace kamery se předává a používá se pro proces malování herního prostoru, jak je popsáno výše.
  • GeneratePlayspace_RequestFinish: Volá se k dokončení playspace. To použije oblasti "malované" během fáze skenování k definování a uzamčení playspace. Aplikace se může dotazovat na statistiky během fáze kontroly a také dotazovat vlastní síť za účelem poskytování zpětné vazby uživatelů.
  • Import_UnderstandingMesh: Během skenování se chování SpatialUnderstandingCustomMesh poskytované modulem a umístěné na prefabu pro pochopení bude pravidelně dotazovat vlastní síť vygenerovanou procesem. Kromě toho se to provádí ještě jednou po dokončení kontroly.

Tok skenování řízený chováním SpatialUnderstanding volá InitScan a pak UpdateScan každý snímek. Když dotaz na statistiku hlásí přiměřené pokrytí, může uživatel airtap volat RequestFinish označující konec fáze kontroly. UpdateScan se bude dál volat, dokud návratová hodnota nenaznačí, že knihovna DLL dokončila zpracování.

Dotazy

Po dokončení kontroly budete mít v rozhraní přístup ke třem různým typům dotazů:

  • Dotazy na topologii: Jedná se o rychlé dotazy založené na topologii skenované místnosti.
  • Dotazy obrazců: Tyto dotazy využívají výsledky dotazů topologie k vyhledání vodorovných povrchů, které jsou vhodné pro vlastní obrazce, které definujete.
  • Dotazy na umístění objektů: Jedná se o složitější dotazy, které najdou nejvhodnější umístění na základě sady pravidel a omezení objektu.

Kromě tří primárních dotazů je k dispozici rozhraní raycasting, které lze použít k načtení označených typů povrchů a vlastní vodotěsné sítě místnosti lze zkopírovat.

Dotazy na topologii

V rámci knihovny DLL zpracovává popisky prostředí správce topologie. Jak je uvedeno výše, většina dat je uložena v surfely, které jsou obsaženy v svazku voxelu. Kromě toho se struktura PlaySpaceInfos používá k ukládání informací o herním prostoru, včetně zarovnání světa (další podrobnosti níže), podlahy a výšky stropu.

Heuristika se používá k určení podlahy, stropu a zdí. Například největší a nejnižší vodorovná plocha s plochou větší než 1 m2 je považována za podlahu. Všimněte si, že v tomto procesu se používá také cesta ke kameře během procesu skenování.

Podmnožina dotazů vystavených správcem topologie je vystavena prostřednictvím knihovny DLL. Vystavené dotazy topologie jsou následující:

  • QueryTopology_FindPositionsOnWalls
  • QueryTopology_FindLargePositionsOnWalls
  • QueryTopology_FindLargestWall
  • QueryTopology_FindPositionsOnFloor
  • QueryTopology_FindLargestPositionsOnFloor
  • QueryTopology_FindPositionsSittable

Každý z dotazů má sadu parametrů specifických pro daný typ dotazu. V následujícím příkladu uživatel určuje minimální výšku & šířku požadovaného objemu, minimální výšku umístění nad podlahou a minimální velikost vůle před svazkem. Všechna měření jsou v metrech.

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)

Každý z těchto dotazů přebírá předem přidělené pole topologieResult struktur. Parametr locationCount určuje délku předávaného pole. Vrácená hodnota hlásí počet vrácených umístění. Toto číslo není nikdy větší než předaný parametr locationCount .

TopologyResult obsahuje středovou pozici vráceného svazku, směr směru (tj. normálního) a rozměry nalezeného prostoru.

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

Všimněte si, že v ukázce Unity je každý z těchto dotazů propojený s tlačítkem na virtuálním panelu uživatelského rozhraní. Vzorový pevný kóduje parametry pro každý z těchto dotazů na přiměřené hodnoty. Další příklady najdete v části SpaceVisualizer.cs v ukázkovém kódu.

Dotazy obrazců

Uvnitř knihovny DLL analyzátor obrazců (ShapeAnalyzer_W) používá analyzátor topologie k porovnání s vlastními obrazci definovanými uživatelem. Ukázka Unity má předdefinované sady obrazců, které jsou zobrazeny v nabídce dotazu na kartě obrazce.

Všimněte si, že analýza obrazců funguje pouze na vodorovných površích. Gauč je například definován plochou plochou sedadla a plochou horní částí opěradla gauče. Dotaz obrazce hledá dva povrchy s konkrétní velikostí, výškou a rozsahem stran, přičemž tyto dva povrchy jsou zarovnané a propojené. Pomocí terminologie rozhraní API jsou gaučové sedadlo a horní část zadní části gauče součástí obrazce a požadavky na zarovnání jsou omezení součástí obrazce.

Příklad dotazu definovaného v ukázce Unity (ShapeDefinition.cs) pro "sittable" objekty je následující:

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);

Každý dotaz obrazce je definován sadou součástí obrazce, z nichž každá obsahuje sadu omezení komponent a sadu omezení obrazců, která obsahuje seznam závislostí mezi komponentami. Tento příklad zahrnuje tři omezení v jedné definici komponenty a žádná omezení obrazce mezi komponentami (protože existuje pouze jedna komponenta).

Naproti tomu obrazec gauče má dvě součásti obrazce a čtyři omezení obrazce. Všimněte si, že komponenty jsou identifikované indexem v seznamu komponent uživatele (v tomto příkladu 0 a 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),
        };

Funkce obálky jsou k dispozici v modulu Unity pro snadné vytváření vlastních definic obrazců. Úplný seznam omezení komponent a obrazců najdete v části SpatialUnderstandingDll.cs v rámci struktur ShapeComponentConstraint a ShapeConstraint .

Modrý obdélník zvýrazní výsledky dotazu na tvar židle.

Modrý obdélník zvýrazní výsledky dotazu na tvar židle.

Řešitel umístění objektů

Dotazy na umístění objektů lze použít k identifikaci ideálních umístění ve fyzické místnosti pro umístění objektů. Řešitel najde nejvhodnější umístění s ohledem na pravidla a omezení objektu. Dotazy na objekty navíc přetrvávají, dokud se objekt neodebere pomocí volání Solver_RemoveObject nebo Solver_RemoveAllObjects , což umožňuje omezené umístění více objektů.

Dotazy na umístění objektů se skládají ze tří částí: typu umístění s parametry, seznamu pravidel a seznamu omezení. Pokud chcete spustit dotaz, použijte následující rozhraní 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)

Tato funkce přebírá název objektu, definici umístění a seznam pravidel a omezení. Obálky jazyka C# poskytují pomocné funkce pro výstavbu, které usnadňují vytváření pravidel a omezení. Definice umístění obsahuje typ dotazu, tedy jeden z následujících:

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

Každý typ umístění má sadu parametrů, které jsou pro tento typ jedinečné. ObjectPlacementDefinition Struktura obsahuje sadu statických pomocných funkcí pro vytváření těchto definic. Pokud například chcete najít místo pro umístění objektu na podlahu, můžete použít následující funkci:

public static ObjectPlacementDefinition Create_OnFloor(Vector3 halfDims)

Kromě typu umístění můžete zadat sadu pravidel a omezení. Pravidla nelze porušit. Možná umístění, která vyhovují typu a pravidlům, se pak optimalizují s ohledem na sadu omezení pro výběr optimálního umístění umístění. Všechna pravidla a omezení lze vytvořit pomocí zadaných funkcí statického vytváření. Příklad konstrukční funkce pravidla a omezení je uveden níže.

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

Níže uvedený dotaz na umístění objektu hledá místo pro umístění půlmetrové krychle na okraj povrchu, od ostatních dříve umístitných objektů a blízko středu místnosti.

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());

V případě úspěchu se vrátí struktura ObjectPlacementResult obsahující umístění umístění, rozměry a orientaci. Kromě toho je umístění přidáno do interního seznamu umístěných objektů knihovny DLL. Následné dotazy na umístění budou tento objekt brát v úvahu. Soubor LevelSolver.cs v ukázce Unity obsahuje další ukázkové dotazy.

Modré rámečky zobrazují výsledek ze tří dotazů Umístit na podlahu s pravidly

Modré rámečky zobrazují výsledek ze tří dotazů Umístit na podlahu s pravidly "mimo umístění kamery".

Tipy:

  • Při řešení umístění více objektů požadovaných pro scénář úrovně nebo aplikace nejprve vyřešte nepostradatelné a velké objekty, abyste maximalizovali pravděpodobnost, že se prostor najde.
  • Pořadí umístění je důležité. Pokud umístění objektů nelze najít, zkuste konfigurace s menším omezením. Mít sadu záložních konfigurací je zásadní pro podporu funkcí v mnoha konfiguracích místností.

Odlévání paprsků

Kromě tří primárních dotazů lze k načtení označených typů povrchů použít rozhraní odlévání paprsků a vlastní vodotěsnou síť playspace je možné zkopírovat. Po naskenování a finalizaci místnosti se vnitřně vygenerují popisky pro povrchy, jako je podlaha, strop a stěny. Funkce PlayspaceRaycast vezme paprsek a vrátí, pokud paprsek koliduje se známým povrchem, a pokud ano, informace o daném povrchu ve formě 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;
     };

Interně se raycast počítá proti vypočítané 8cm voxelové reprezentaci herního prostoru s krychlí. Každý voxel obsahuje sadu povrchových prvků se zpracovanými topologickými daty (označovanými také jako surfely). Povrchy obsažené v protínané buňce voxelu jsou porovnány a nejlepší shoda se používá k vyhledání informací o topologii. Tato data topologie obsahují popisky vrácené ve formě výčtu SurfaceTypes a také plochu protínaného povrchu.

V ukázce Unity kurzor přetypuje paprsek na každý snímek. Nejprve proti kolidérům Unity; za druhé, proti světové reprezentaci modulu porozumění; a nakonec proti prvkům uživatelského rozhraní. V této aplikaci získá uživatelské rozhraní prioritu, pak výsledek porozumění a nakonec kolidátory Unity. SurfaceType se hlásí jako text vedle kurzoru.

Raycast result reporting průsečíku s podlahou.

Raycast result reporting průsečíku s podlahou.

Získání kódu

Opensourcový kód je k dispozici v MixedRealityToolkit. Pokud používáte kód v projektu, dejte nám vědět na fórech pro vývojáře HoloLensu . Nemůžeme se dočkat, až uvidíme, co s tím uděláte!

O autorovi

Jeff Evertt, vedoucí softwarového inženýrství ve společnosti Microsoft Jeff Evertt je vedoucí softwarového inženýrství, který na HoloLensu pracoval od počátku, od inkubace až po vývoj zkušeností. Před HoloLensem pracoval na Xbox Kinectu a v herním průmyslu na nejrůznějších platformách a hrách. Jeff je nadšený do robotiky, grafiky a věcí s blikajícími světly, které jdou pípnutí. Rád se učí nové věci a pracuje na softwaru, hardwaru a zejména v prostoru, kde se tyto dvě věci protínají.

Viz také