HoloLens (1e generatie) Spatial 230: Ruimtelijke toewijzing

Belangrijk

De Mixed Reality Academy-zelfstudies zijn ontworpen met HoloLens (1e generatie), Unity 2017 en Mixed Reality Immersive Headsets in het achterhoofd. Daarom vinden we het belangrijk om deze zelfstudies te laten staan voor ontwikkelaars die nog steeds op zoek zijn naar hulp bij het ontwikkelen van deze apparaten. Deze zelfstudies worden niet bijgewerkt met de nieuwste toolsets of interacties die worden gebruikt voor HoloLens 2 en zijn mogelijk niet compatibel met nieuwere versies van Unity. Ze worden onderhouden om te blijven werken op de ondersteunde apparaten. Er is een nieuwe reeks zelfstudies geplaatst voor HoloLens 2.

Ruimtelijke toewijzing combineert de echte wereld en de virtuele wereld door hologrammen over de omgeving te leren. In MR Spatial 230 (Project Planet) leren we het volgende:

  • Scan de omgeving en draag gegevens over van de HoloLens naar uw ontwikkelcomputer.
  • Verken shaders en leer hoe u deze kunt gebruiken om uw ruimte te visualiseren.
  • Breek het ruimtegaas op in eenvoudige vlakken met behulp van meshverwerking.
  • Ga verder dan de plaatsingstechnieken die we hebben geleerd in MR Basics 101 en geef feedback over waar een hologram in de omgeving kan worden geplaatst.
  • Verken occlusie-effecten, dus wanneer uw hologram zich achter een echt object bevindt, kunt u het nog steeds zien met x-ray vision!

Ondersteuning voor apparaten

Cursus HoloLens Immersive headsets
MR Spatial 230: Ruimtelijke toewijzing ✔️

Voordat u begint

Vereisten

Projectbestanden

  • Download de bestanden die vereist zijn voor het project. Vereist Unity 2017.2 of hoger.
    • Als u nog steeds ondersteuning voor Unity 5.6 nodig hebt, gebruikt u deze release.
    • Als u nog steeds ondersteuning voor Unity 5.5 nodig hebt, gebruikt u deze release.
    • Als u nog steeds ondersteuning voor Unity 5.4 nodig hebt, gebruikt u deze release.
  • Maak het archiveren van de bestanden op uw bureaublad of een andere gemakkelijk te bereiken locatie ongedaan.

Notitie

Als u de broncode wilt bekijken voordat u deze downloadt, is deze beschikbaar op GitHub.

Notities

  • 'Alleen mijn code inschakelen' in Visual Studio moet worden uitgeschakeld (uitgeschakeld) onder Extra > opties > Foutopsporing om onderbrekingspunten in uw code te bereiken.

Unity instellen

  • Start Unity.
  • Selecteer Nieuw om een nieuw project te maken.
  • Noem het project Planet.
  • Controleer of de 3D-instelling is geselecteerd.
  • Klik op Project maken.
  • Zodra Unity wordt gestart, gaat u naar Projectinstellingen > bewerken > Player.
  • Zoek en selecteer in het deelvenster Inspector het groene Windows Store-pictogram .
  • Vouw Overige instellingen uit.
  • Schakel in de sectie Rendering de optie Virtual Reality Supported in .
  • Controleer of Windows Holographic wordt weergegeven in de lijst met Virtual Reality SDK's. Zo niet, selecteert u de + knop onder aan de lijst en kiest u Windows Holographic.
  • Vouw Publicatie-instellingen uit.
  • Controleer in de sectie Mogelijkheden de volgende instellingen:
    • InternetClientServer
    • PrivateNetworkClientServer
    • Microfoon
    • SpatialPerception
  • Ga naar Kwaliteit van projectinstellingen > bewerken >
  • Selecteer in het deelvenster Inspector onder het Windows Store-pictogram de zwarte vervolgkeuzepijl onder de rij Standaard en wijzig de standaardinstelling in Zeer laag.
  • Ga naar Assets > Import Package > Custom Package.
  • Navigeer naar de map ...\HolographicAcademy-Holograms-230-SpatialMapping\Starting .
  • Klik op Planet.unitypackage.
  • Klik op Openen.
  • Het venster Import Unity Package wordt weergegeven en klik op de knop Importeren .
  • Wacht totdat Unity alle assets heeft geïmporteerd die we nodig hebben om dit project te voltooien.
  • Verwijder in het deelvenster Hiërarchie de hoofdcamera.
  • Zoek in het deelvenster Project , de map HoloToolkit-SpatialMapping-230\Utilities\Prefabs , het object Hoofdcamera .
  • Sleep de prefab van de hoofdcamera naar het deelvenster Hiërarchie .
  • Verwijder in het deelvenster Hiërarchie het object Directional Light .
  • Zoek in het deelvenster Project , de map Holograms , het cursorobject .
  • Sleep & zet de prefab cursor neer in de hiërarchie.
  • Selecteer in het deelvenster Hiërarchie het object Cursor .
  • Klik in het deelvenster Inspector op de vervolgkeuzelijst Laag en selecteer Lagen bewerken....
  • Geef User Layer 31 de naam SpatialMapping.
  • De nieuwe scène opslaan: Bestand > Scène opslaan als...
  • Klik op Nieuwe map en geef de map de naam Scènes.
  • Geef het bestand de naam "Planet" en sla het op in de map Scenes .

Hoofdstuk 1 - Scannen

Doelen

  • Meer informatie over de SurfaceObserver en hoe de instellingen van invloed zijn op de ervaring en prestaties.
  • Maak een ruimtescan om de meshes van uw ruimte te verzamelen.

Instructies

  • Ga in het deelvenster Project naar de map HoloToolkit-SpatialMapping-230\SpatialMapping\Prefabs en zoek de prefab SpatialMapping .
  • Sleep & zet de prefab SpatialMapping neer in het deelvenster Hierarchy .

Bouwen en implementeren (deel 1)

  • Selecteer in Unity Instellingen voor bestandsbuilding>.
  • Klik op Geopende scènes toevoegen om de scene Van Het Planet toe te voegen aan de build.
  • Selecteer Universeel Windows-platform in de lijst Platform en klik op Platform wisselen.
  • Stel SDK in op Universal 10 en UWP Build Type op D3D.
  • Controleer Unity C#-projecten.
  • Klik op Build.
  • Maak een nieuwe map met de naam 'App'.
  • Klik één klik op de map App .
  • Druk op de knop Map selecteren .
  • Wanneer Unity klaar is met het bouwen, wordt er een Bestandenverkenner venster weergegeven.
  • Dubbelklik op de map App om deze te openen.
  • Dubbelklik op Planet.sln om het project in Visual Studio te laden.
  • Gebruik in Visual Studio de bovenste werkbalk om configuratie te wijzigen in Release.
  • Wijzig Platform in x86.
  • Klik op de vervolgkeuzepijl rechts van Lokale computer en selecteer Externe machine.
  • Voer het IP-adres van uw apparaat in het veld Adres in en wijzig De verificatiemodus in Universeel (Niet-versleuteld protocol).
  • Klik op Fouten opsporen -> Starten zonder foutopsporing of druk op Ctrl+F5.
  • Bekijk het deelvenster Uitvoer in Visual Studio voor de status van bouwen en implementeren.
  • Zodra uw app is geïmplementeerd, loopt u door de ruimte. U ziet de omringende oppervlakken bedekt met zwart-witte draadframe-meshes.
  • Scan je omgeving. Zorg ervoor dat u naar muren, plafonds en vloeren kijkt.

Bouwen en implementeren (deel 2)

Laten we nu eens kijken hoe ruimtelijke toewijzing de prestaties kan beïnvloeden.

  • Selecteer in Unity Window > Profiler.
  • Klik op Profiler > GPU toevoegen.
  • Klik op Active Profiler ><Voer IP in>.
  • Voer het IP-adres van uw HoloLens in.
  • Klik op Verbinden.
  • Bekijk het aantal milliseconden dat nodig is voordat de GPU een frame weergeeft.
  • Stop de uitvoering van de toepassing op het apparaat.
  • Ga terug naar Visual Studio en open SpatialMappingObserver.cs. U vindt deze in de map HoloToolkit\SpatialMapping van het project Assembly-CSharp (Universal Windows).
  • Zoek de functie Awake() en voeg de volgende regel code toe: TrianglesPerCubicMeter = 1200;
  • Implementeer het project opnieuw op uw apparaat en maak vervolgens opnieuw verbinding met de profiler. Bekijk de wijziging in het aantal milliseconden om een frame weer te geven.
  • Stop de uitvoering van de toepassing op het apparaat.

Opslaan en laden in Unity

Ten slotte gaan we onze ruimte-mesh opslaan en in Unity laden.

  • Ga terug naar Visual Studio en verwijder de lijn TrianglesPerCubicMeter die u in de vorige sectie hebt toegevoegd in de functie Awake( ).
  • Implementeer het project opnieuw op uw apparaat. We moeten nu lopen met 500 driehoeken per kubieke meter.
  • Open een browser en voer het IP-adres van uw HoloLens in om naar de Windows-apparaatportal te navigeren.
  • Selecteer de optie 3D-weergave in het linkerdeelvenster.
  • Selecteer onder Surface-reconstructie de knop Bijwerken .
  • Kijk hoe de gebieden die u op uw HoloLens hebt gescand, worden weergegeven in het weergavevenster.
  • Als u uw ruimtescan wilt opslaan, drukt u op de knop Opslaan .
  • Open de map Downloads om het opgeslagen kamermodel SRMesh.obj te vinden.
  • Kopieer SRMesh.obj naar de map Assets van uw Unity-project.
  • Selecteer in Unity het object SpatialMapping in het deelvenster Hierarchy .
  • Zoek het onderdeel Object Surface Observer (Script).
  • Klik op de cirkel rechts van de eigenschap Ruimtemodel .
  • Zoek en selecteer het SRMesh-object en sluit het venster.
  • Controleer of de eigenschap Room Model in het deelvenster Inspector nu is ingesteld op SRMesh.
  • Druk op de knop Afspelen om naar de preview-modus van Unity te gaan.
  • Met het onderdeel SpatialMapping worden de meshes uit het opgeslagen ruimtemodel geladen, zodat u ze in Unity kunt gebruiken.
  • Schakel over naar de scèneweergave om al uw kamermodellen weer te geven met de arcering van het draadframe.
  • Druk nogmaals op de knop Afspelen om de voorbeeldmodus af te sluiten.

OPMERKING: De volgende keer dat u de preview-modus in Unity opent, wordt de opgeslagen ruimte mesh standaard geladen.

Hoofdstuk 2 - Visualisatie

Doelen

  • Leer de basisbeginselen van shaders.
  • Visualiseer uw omgeving.

Instructies

  • Selecteer in het deelvenster Hierarchy van Unity het object SpatialMapping .
  • Zoek in het deelvenster Inspector het onderdeel Spatial Mapping Manager (Script).
  • Klik op de cirkel rechts van de eigenschap Surface Material .
  • Zoek en selecteer het BlueLinesOnWalls-materiaal en sluit het venster.
  • Dubbelklik in de map Shaders van het deelvenster Project op BlueLinesOnWalls om de shader in Visual Studio te openen.
  • Dit is een eenvoudige pixel (hoekpunt naar fragment) shader, waarmee de volgende taken worden uitgevoerd:
    1. Converteert de locatie van een hoekpunt naar de wereldruimte.
    2. Hiermee wordt het normale hoekpunt gecontroleerd om te bepalen of een pixel verticaal is.
    3. Hiermee stelt u de kleur van de pixel in voor weergave.

Bouwen en implementeren

  • Ga terug naar Unity en druk op Afspelen om de preview-modus te activeren.
  • Blauwe lijnen worden weergegeven op alle verticale oppervlakken van de ruimtegaas (die automatisch worden geladen uit onze opgeslagen scangegevens).
  • Ga naar het tabblad Scène om de weergave van de ruimte aan te passen en te zien hoe het hele mesh van de ruimte wordt weergegeven in Unity.
  • Zoek in het deelvenster Project de map Materialen en selecteer het materiaal BlueLinesOnWalls .
  • Wijzig enkele eigenschappen en bekijk hoe de wijzigingen worden weergegeven in de Unity-editor.
    • Pas in het deelvenster Inspector de waarde LineScale aan om de lijnen dikker of dunner te maken.
    • Pas in het deelvenster Inspector de waarde LinesPerMeter aan om te wijzigen hoeveel lijnen op elke wand worden weergegeven.
  • Klik nogmaals op Afspelen om de preview-modus af te sluiten.
  • Bouw en implementeer op de HoloLens en bekijk hoe de shader-rendering wordt weergegeven op echte oppervlakken.

Unity levert een uitstekend voorbeeld van materiaal, maar het is altijd een goed idee om rendering in het apparaat te bekijken.

Hoofdstuk 3 - Verwerking

Doelen

  • Leer technieken voor het verwerken van ruimtelijke toewijzingsgegevens voor gebruik in uw toepassing.
  • Ruimtelijke kaartgegevens analyseren om vlakken te vinden en driehoeken te verwijderen.
  • Vlakken gebruiken voor het plaatsen van hologrammen.

Instructies

  • Zoek in het deelvenster Project van Unity, de map Hologrammen , het object SpatialProcessing .
  • Sleep & zet het spatialprocessing-object neer in het deelvenster Hierarchy .

De prefab SpatialProcessing bevat onderdelen voor het verwerken van de ruimtelijke kaartgegevens. SurfaceMeshesToPlanes.cs zoekt en genereert vlakken op basis van de ruimtelijke toewijzingsgegevens. We gebruiken vliegtuigen in onze toepassing om wanden, vloeren en plafonds te vertegenwoordigen. Deze prefab bevat ook RemoveSurfaceVertices.cs waarmee hoekpunten uit het ruimtelijke toewijzingsnet kunnen worden verwijderd. Dit kan worden gebruikt om gaten in het gaas te maken of om overtollige driehoeken te verwijderen die niet meer nodig zijn (omdat in plaats daarvan vliegtuigen kunnen worden gebruikt).

  • Zoek in het deelvenster Project van Unity, de map Hologrammen , het object SpaceCollection .
  • Sleep het spacecollection-object naar het deelvenster Hierarchy .
  • Selecteer in het deelvenster Hierarchy het object SpatialProcessing .
  • Zoek in het deelvenster Inspector het onderdeel Play Space Manager (Script).
  • Dubbelklik op PlaySpaceManager.cs om het te openen in Visual Studio.

PlaySpaceManager.cs bevat toepassingsspecifieke code. We voegen functionaliteit toe aan dit script om het volgende gedrag in te schakelen:

  1. Stop met het verzamelen van ruimtelijke toewijzingsgegevens nadat de scantijdlimiet (10 seconden) is overschreden.
  2. De ruimtelijke toewijzingsgegevens verwerken:
    1. Gebruik SurfaceMeshesToPlanes om een eenvoudigere weergave van de wereld te maken als vlakken (muren, vloeren, plafonds, enzovoort).
    2. Gebruik RemoveSurfaceVertices om oppervlaktedriehoeken te verwijderen die binnen de grenzen van het vlak vallen.
  3. Genereer een verzameling hologrammen in de wereld en plaats deze op wand- en vloervlakken in de buurt van de gebruiker.

Voltooi de codeeroefeningen die zijn gemarkeerd in PlaySpaceManager.cs of vervang het script door de voltooide oplossing van hieronder:

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Windows.Speech;
using Academy.HoloToolkit.Unity;

/// <summary>
/// The SurfaceManager class allows applications to scan the environment for a specified amount of time 
/// and then process the Spatial Mapping Mesh (find planes, remove vertices) after that time has expired.
/// </summary>
public class PlaySpaceManager : Singleton<PlaySpaceManager>
{
    [Tooltip("When checked, the SurfaceObserver will stop running after a specified amount of time.")]
    public bool limitScanningByTime = true;

    [Tooltip("How much time (in seconds) that the SurfaceObserver will run after being started; used when 'Limit Scanning By Time' is checked.")]
    public float scanTime = 30.0f;

    [Tooltip("Material to use when rendering Spatial Mapping meshes while the observer is running.")]
    public Material defaultMaterial;

    [Tooltip("Optional Material to use when rendering Spatial Mapping meshes after the observer has been stopped.")]
    public Material secondaryMaterial;

    [Tooltip("Minimum number of floor planes required in order to exit scanning/processing mode.")]
    public uint minimumFloors = 1;

    [Tooltip("Minimum number of wall planes required in order to exit scanning/processing mode.")]
    public uint minimumWalls = 1;

    /// <summary>
    /// Indicates if processing of the surface meshes is complete.
    /// </summary>
    private bool meshesProcessed = false;

    /// <summary>
    /// GameObject initialization.
    /// </summary>
    private void Start()
    {
        // Update surfaceObserver and storedMeshes to use the same material during scanning.
        SpatialMappingManager.Instance.SetSurfaceMaterial(defaultMaterial);

        // Register for the MakePlanesComplete event.
        SurfaceMeshesToPlanes.Instance.MakePlanesComplete += SurfaceMeshesToPlanes_MakePlanesComplete;
    }

    /// <summary>
    /// Called once per frame.
    /// </summary>
    private void Update()
    {
        // Check to see if the spatial mapping data has been processed
        // and if we are limiting how much time the user can spend scanning.
        if (!meshesProcessed && limitScanningByTime)
        {
            // If we have not processed the spatial mapping data
            // and scanning time is limited...

            // Check to see if enough scanning time has passed
            // since starting the observer.
            if (limitScanningByTime && ((Time.time - SpatialMappingManager.Instance.StartTime) < scanTime))
            {
                // If we have a limited scanning time, then we should wait until
                // enough time has passed before processing the mesh.
            }
            else
            {
                // The user should be done scanning their environment,
                // so start processing the spatial mapping data...

                /* TODO: 3.a DEVELOPER CODING EXERCISE 3.a */

                // 3.a: Check if IsObserverRunning() is true on the
                // SpatialMappingManager.Instance.
                if(SpatialMappingManager.Instance.IsObserverRunning())
                {
                    // 3.a: If running, Stop the observer by calling
                    // StopObserver() on the SpatialMappingManager.Instance.
                    SpatialMappingManager.Instance.StopObserver();
                }

                // 3.a: Call CreatePlanes() to generate planes.
                CreatePlanes();

                // 3.a: Set meshesProcessed to true.
                meshesProcessed = true;
            }
        }
    }

    /// <summary>
    /// Handler for the SurfaceMeshesToPlanes MakePlanesComplete event.
    /// </summary>
    /// <param name="source">Source of the event.</param>
    /// <param name="args">Args for the event.</param>
    private void SurfaceMeshesToPlanes_MakePlanesComplete(object source, System.EventArgs args)
    {
        /* TODO: 3.a DEVELOPER CODING EXERCISE 3.a */

        // Collection of floor and table planes that we can use to set horizontal items on.
        List<GameObject> horizontal = new List<GameObject>();

        // Collection of wall planes that we can use to set vertical items on.
        List<GameObject> vertical = new List<GameObject>();

        // 3.a: Get all floor and table planes by calling
        // SurfaceMeshesToPlanes.Instance.GetActivePlanes().
        // Assign the result to the 'horizontal' list.
        horizontal = SurfaceMeshesToPlanes.Instance.GetActivePlanes(PlaneTypes.Table | PlaneTypes.Floor);

        // 3.a: Get all wall planes by calling
        // SurfaceMeshesToPlanes.Instance.GetActivePlanes().
        // Assign the result to the 'vertical' list.
        vertical = SurfaceMeshesToPlanes.Instance.GetActivePlanes(PlaneTypes.Wall);

        // Check to see if we have enough horizontal planes (minimumFloors)
        // and vertical planes (minimumWalls), to set holograms on in the world.
        if (horizontal.Count >= minimumFloors && vertical.Count >= minimumWalls)
        {
            // We have enough floors and walls to place our holograms on...

            // 3.a: Let's reduce our triangle count by removing triangles
            // from SpatialMapping meshes that intersect with our active planes.
            // Call RemoveVertices().
            // Pass in all activePlanes found by SurfaceMeshesToPlanes.Instance.
            RemoveVertices(SurfaceMeshesToPlanes.Instance.ActivePlanes);

            // 3.a: We can indicate to the user that scanning is over by
            // changing the material applied to the Spatial Mapping meshes.
            // Call SpatialMappingManager.Instance.SetSurfaceMaterial().
            // Pass in the secondaryMaterial.
            SpatialMappingManager.Instance.SetSurfaceMaterial(secondaryMaterial);

            // 3.a: We are all done processing the mesh, so we can now
            // initialize a collection of Placeable holograms in the world
            // and use horizontal/vertical planes to set their starting positions.
            // Call SpaceCollectionManager.Instance.GenerateItemsInWorld().
            // Pass in the lists of horizontal and vertical planes that we found earlier.
            SpaceCollectionManager.Instance.GenerateItemsInWorld(horizontal, vertical);
        }
        else
        {
            // We do not have enough floors/walls to place our holograms on...

            // 3.a: Re-enter scanning mode so the user can find more surfaces by
            // calling StartObserver() on the SpatialMappingManager.Instance.
            SpatialMappingManager.Instance.StartObserver();

            // 3.a: Re-process spatial data after scanning completes by
            // re-setting meshesProcessed to false.
            meshesProcessed = false;
        }
    }

    /// <summary>
    /// Creates planes from the spatial mapping surfaces.
    /// </summary>
    private void CreatePlanes()
    {
        // Generate planes based on the spatial map.
        SurfaceMeshesToPlanes surfaceToPlanes = SurfaceMeshesToPlanes.Instance;
        if (surfaceToPlanes != null && surfaceToPlanes.enabled)
        {
            surfaceToPlanes.MakePlanes();
        }
    }

    /// <summary>
    /// Removes triangles from the spatial mapping surfaces.
    /// </summary>
    /// <param name="boundingObjects"></param>
    private void RemoveVertices(IEnumerable<GameObject> boundingObjects)
    {
        RemoveSurfaceVertices removeVerts = RemoveSurfaceVertices.Instance;
        if (removeVerts != null && removeVerts.enabled)
        {
            removeVerts.RemoveSurfaceVerticesWithinBounds(boundingObjects);
        }
    }

    /// <summary>
    /// Called when the GameObject is unloaded.
    /// </summary>
    private void OnDestroy()
    {
        if (SurfaceMeshesToPlanes.Instance != null)
        {
            SurfaceMeshesToPlanes.Instance.MakePlanesComplete -= SurfaceMeshesToPlanes_MakePlanesComplete;
        }
    }
}

Bouwen en implementeren

  • Voordat u naar de HoloLens implementeert, drukt u op de knop Afspelen in Unity om de afspeelmodus te activeren.
  • Nadat het ruimtemesh uit het bestand is geladen, wacht u 10 seconden voordat de verwerking op het mesh voor ruimtelijke toewijzing begint.
  • Wanneer de verwerking is voltooid, lijken vliegtuigen de vloer, muren, het plafond, enzovoort te vertegenwoordigen.
  • Nadat alle vliegtuigen zijn gevonden, zou je een zonnestelsel moeten zien verschijnen op een tafel of vloer in de buurt van de camera.
  • Er moeten ook twee posters op de wanden bij de camera verschijnen. Schakel over naar het tabblad Scène als u deze niet kunt zien in de gamemodus .
  • Druk nogmaals op de knop Afspelen om de afspeelmodus af te sluiten.
  • Bouw en implementeer op de HoloLens, zoals gebruikelijk.
  • Wacht totdat het scannen en verwerken van de ruimtelijke toewijzingsgegevens is voltooid.
  • Zodra je vliegtuigen ziet, probeer dan het zonnestelsel en posters in je wereld te vinden.

Hoofdstuk 4 - Plaatsing

Doelen

  • Bepaal of een hologram op een oppervlak past.
  • Geef feedback aan de gebruiker wanneer een hologram wel of niet op een oppervlak past.

Instructies

  • Selecteer in het deelvenster Hierarchy van Unity het object SpatialProcessing .
  • Zoek in het deelvenster Inspector het onderdeel Surface Meshes To Planes (Script).
  • Wijzig de eigenschap Tekenvlakken in Niets om de selectie te wissen.
  • Wijzig de eigenschap Tekenvlakken in Wand, zodat alleen wandvlakken worden weergegeven.
  • Dubbelklik in het deelvenster Project , de map Scripts , op Placeable.cs om het te openen in Visual Studio.

Het plaatsbare script is al gekoppeld aan de posters en het projectievak die zijn gemaakt nadat het zoeken naar het vliegtuig is voltooid. Het enige wat we hoeven te doen, is het commentaar op bepaalde code ongedaan maken. Met dit script wordt het volgende bereikt:

  1. Bepaal of een hologram op een oppervlak past door te raycasten vanuit het midden en de vier hoeken van de begrenzingskubus.
  2. Controleer het oppervlak normaal om te bepalen of het oppervlak glad genoeg is om het hologram leeg te laten zitten.
  3. Een begrenzingskubus rond het hologram weergeven om de werkelijke grootte weer te geven tijdens het plaatsen.
  4. Werp een schaduw onder/achter het hologram om te laten zien waar het op de vloer/wand wordt geplaatst.
  5. Geef de schaduw weer als rood, als het hologram niet op het oppervlak kan worden geplaatst, of groen als dat mogelijk is.
  6. Oriënteer het hologram opnieuw zodat het wordt uitgelijnd met het oppervlaktype (verticaal of horizontaal) waarvoor het affiniteit heeft.
  7. Plaats het hologram soepel op het geselecteerde oppervlak om te voorkomen dat u springt of vastklikt.

Verwijder alle opmerkingen in de onderstaande codeoefening of gebruik deze voltooide oplossing in Placeable.cs:

using System.Collections.Generic;
using UnityEngine;
using Academy.HoloToolkit.Unity;

/// <summary>
/// Enumeration containing the surfaces on which a GameObject
/// can be placed.  For simplicity of this sample, only one
/// surface type is allowed to be selected.
/// </summary>
public enum PlacementSurfaces
{
    // Horizontal surface with an upward pointing normal.
    Horizontal = 1,

    // Vertical surface with a normal facing the user.
    Vertical = 2,
}

/// <summary>
/// The Placeable class implements the logic used to determine if a GameObject
/// can be placed on a target surface. Constraints for placement include:
/// * No part of the GameObject's box collider impacts with another object in the scene
/// * The object lays flat (within specified tolerances) against the surface
/// * The object would not fall off of the surface if gravity were enabled.
/// This class also provides the following visualizations.
/// * A transparent cube representing the object's box collider.
/// * Shadow on the target surface indicating whether or not placement is valid.
/// </summary>
public class Placeable : MonoBehaviour
{
    [Tooltip("The base material used to render the bounds asset when placement is allowed.")]
    public Material PlaceableBoundsMaterial = null;

    [Tooltip("The base material used to render the bounds asset when placement is not allowed.")]
    public Material NotPlaceableBoundsMaterial = null;

    [Tooltip("The material used to render the placement shadow when placement it allowed.")]
    public Material PlaceableShadowMaterial = null;

    [Tooltip("The material used to render the placement shadow when placement it not allowed.")]
    public Material NotPlaceableShadowMaterial = null;

    [Tooltip("The type of surface on which the object can be placed.")]
    public PlacementSurfaces PlacementSurface = PlacementSurfaces.Horizontal;

    [Tooltip("The child object(s) to hide during placement.")]
    public List<GameObject> ChildrenToHide = new List<GameObject>();

    /// <summary>
    /// Indicates if the object is in the process of being placed.
    /// </summary>
    public bool IsPlacing { get; private set; }

    // The most recent distance to the surface.  This is used to 
    // locate the object when the user's gaze does not intersect
    // with the Spatial Mapping mesh.
    private float lastDistance = 2.0f;

    // The distance away from the target surface that the object should hover prior while being placed.
    private float hoverDistance = 0.15f;

    // Threshold (the closer to 0, the stricter the standard) used to determine if a surface is flat.
    private float distanceThreshold = 0.02f;

    // Threshold (the closer to 1, the stricter the standard) used to determine if a surface is vertical.
    private float upNormalThreshold = 0.9f;

    // Maximum distance, from the object, that placement is allowed.
    // This is used when raycasting to see if the object is near a placeable surface.
    private float maximumPlacementDistance = 5.0f;

    // Speed (1.0 being fastest) at which the object settles to the surface upon placement.
    private float placementVelocity = 0.06f;

    // Indicates whether or not this script manages the object's box collider.
    private bool managingBoxCollider = false;

    // The box collider used to determine of the object will fit in the desired location.
    // It is also used to size the bounding cube.
    private BoxCollider boxCollider = null;

    // Visible asset used to show the dimensions of the object. This asset is sized
    // using the box collider's bounds.
    private GameObject boundsAsset = null;

    // Visible asset used to show the where the object is attempting to be placed.
    // This asset is sized using the box collider's bounds.
    private GameObject shadowAsset = null;

    // The location at which the object will be placed.
    private Vector3 targetPosition;

    /// <summary>
    /// Called when the GameObject is created.
    /// </summary>
    private void Awake()
    {
        targetPosition = gameObject.transform.position;

        // Get the object's collider.
        boxCollider = gameObject.GetComponent<BoxCollider>();
        if (boxCollider == null)
        {
            // The object does not have a collider, create one and remember that
            // we are managing it.
            managingBoxCollider = true;
            boxCollider = gameObject.AddComponent<BoxCollider>();
            boxCollider.enabled = false;
        }

        // Create the object that will be used to indicate the bounds of the GameObject.
        boundsAsset = GameObject.CreatePrimitive(PrimitiveType.Cube);
        boundsAsset.transform.parent = gameObject.transform;
        boundsAsset.SetActive(false);

        // Create a object that will be used as a shadow.
        shadowAsset = GameObject.CreatePrimitive(PrimitiveType.Quad);
        shadowAsset.transform.parent = gameObject.transform;
        shadowAsset.SetActive(false);
    }

    /// <summary>
    /// Called when our object is selected.  Generally called by
    /// a gesture management component.
    /// </summary>
    public void OnSelect()
    {
        /* TODO: 4.a CODE ALONG 4.a */

        if (!IsPlacing)
        {
            OnPlacementStart();
        }
        else
        {
            OnPlacementStop();
        }
    }

    /// <summary>
    /// Called once per frame.
    /// </summary>
    private void Update()
    {
        /* TODO: 4.a CODE ALONG 4.a */

        if (IsPlacing)
        {
            // Move the object.
            Move();

            // Set the visual elements.
            Vector3 targetPosition;
            Vector3 surfaceNormal;
            bool canBePlaced = ValidatePlacement(out targetPosition, out surfaceNormal);
            DisplayBounds(canBePlaced);
            DisplayShadow(targetPosition, surfaceNormal, canBePlaced);
        }
        else
        {
            // Disable the visual elements.
            boundsAsset.SetActive(false);
            shadowAsset.SetActive(false);

            // Gracefully place the object on the target surface.
            float dist = (gameObject.transform.position - targetPosition).magnitude;
            if (dist > 0)
            {
                gameObject.transform.position = Vector3.Lerp(gameObject.transform.position, targetPosition, placementVelocity / dist);
            }
            else
            {
                // Unhide the child object(s) to make placement easier.
                for (int i = 0; i < ChildrenToHide.Count; i++)
                {
                    ChildrenToHide[i].SetActive(true);
                }
            }
        }
    }

    /// <summary>
    /// Verify whether or not the object can be placed.
    /// </summary>
    /// <param name="position">
    /// The target position on the surface.
    /// </param>
    /// <param name="surfaceNormal">
    /// The normal of the surface on which the object is to be placed.
    /// </param>
    /// <returns>
    /// True if the target position is valid for placing the object, otherwise false.
    /// </returns>
    private bool ValidatePlacement(out Vector3 position, out Vector3 surfaceNormal)
    {
        Vector3 raycastDirection = gameObject.transform.forward;

        if (PlacementSurface == PlacementSurfaces.Horizontal)
        {
            // Placing on horizontal surfaces.
            // Raycast from the bottom face of the box collider.
            raycastDirection = -(Vector3.up);
        }

        // Initialize out parameters.
        position = Vector3.zero;
        surfaceNormal = Vector3.zero;

        Vector3[] facePoints = GetColliderFacePoints();

        // The origin points we receive are in local space and we 
        // need to raycast in world space.
        for (int i = 0; i < facePoints.Length; i++)
        {
            facePoints[i] = gameObject.transform.TransformVector(facePoints[i]) + gameObject.transform.position;
        }

        // Cast a ray from the center of the box collider face to the surface.
        RaycastHit centerHit;
        if (!Physics.Raycast(facePoints[0],
                        raycastDirection,
                        out centerHit,
                        maximumPlacementDistance,
                        SpatialMappingManager.Instance.LayerMask))
        {
            // If the ray failed to hit the surface, we are done.
            return false;
        }

        // We have found a surface.  Set position and surfaceNormal.
        position = centerHit.point;
        surfaceNormal = centerHit.normal;

        // Cast a ray from the corners of the box collider face to the surface.
        for (int i = 1; i < facePoints.Length; i++)
        {
            RaycastHit hitInfo;
            if (Physics.Raycast(facePoints[i],
                                raycastDirection,
                                out hitInfo,
                                maximumPlacementDistance,
                                SpatialMappingManager.Instance.LayerMask))
            {
                // To be a valid placement location, each of the corners must have a similar
                // enough distance to the surface as the center point
                if (!IsEquivalentDistance(centerHit.distance, hitInfo.distance))
                {
                    return false;
                }
            }
            else
            {
                // The raycast failed to intersect with the target layer.
                return false;
            }
        }

        return true;
    }

    /// <summary>
    /// Determine the coordinates, in local space, of the box collider face that 
    /// will be placed against the target surface.
    /// </summary>
    /// <returns>
    /// Vector3 array with the center point of the face at index 0.
    /// </returns>
    private Vector3[] GetColliderFacePoints()
    {
        // Get the collider extents.  
        // The size values are twice the extents.
        Vector3 extents = boxCollider.size / 2;

        // Calculate the min and max values for each coordinate.
        float minX = boxCollider.center.x - extents.x;
        float maxX = boxCollider.center.x + extents.x;
        float minY = boxCollider.center.y - extents.y;
        float maxY = boxCollider.center.y + extents.y;
        float minZ = boxCollider.center.z - extents.z;
        float maxZ = boxCollider.center.z + extents.z;

        Vector3 center;
        Vector3 corner0;
        Vector3 corner1;
        Vector3 corner2;
        Vector3 corner3;

        if (PlacementSurface == PlacementSurfaces.Horizontal)
        {
            // Placing on horizontal surfaces.
            center = new Vector3(boxCollider.center.x, minY, boxCollider.center.z);
            corner0 = new Vector3(minX, minY, minZ);
            corner1 = new Vector3(minX, minY, maxZ);
            corner2 = new Vector3(maxX, minY, minZ);
            corner3 = new Vector3(maxX, minY, maxZ);
        }
        else
        {
            // Placing on vertical surfaces.
            center = new Vector3(boxCollider.center.x, boxCollider.center.y, maxZ);
            corner0 = new Vector3(minX, minY, maxZ);
            corner1 = new Vector3(minX, maxY, maxZ);
            corner2 = new Vector3(maxX, minY, maxZ);
            corner3 = new Vector3(maxX, maxY, maxZ);
        }

        return new Vector3[] { center, corner0, corner1, corner2, corner3 };
    }

    /// <summary>
    /// Put the object into placement mode.
    /// </summary>
    public void OnPlacementStart()
    {
        // If we are managing the collider, enable it. 
        if (managingBoxCollider)
        {
            boxCollider.enabled = true;
        }

        // Hide the child object(s) to make placement easier.
        for (int i = 0; i < ChildrenToHide.Count; i++)
        {
            ChildrenToHide[i].SetActive(false);
        }

        // Tell the gesture manager that it is to assume
        // all input is to be given to this object.
        GestureManager.Instance.OverrideFocusedObject = gameObject;

        // Enter placement mode.
        IsPlacing = true;
    }

    /// <summary>
    /// Take the object out of placement mode.
    /// </summary>
    /// <remarks>
    /// This method will leave the object in placement mode if called while
    /// the object is in an invalid location.  To determine whether or not
    /// the object has been placed, check the value of the IsPlacing property.
    /// </remarks>
    public void OnPlacementStop()
    {
        // ValidatePlacement requires a normal as an out parameter.
        Vector3 position;
        Vector3 surfaceNormal;

        // Check to see if we can exit placement mode.
        if (!ValidatePlacement(out position, out surfaceNormal))
        {
            return;
        }

        // The object is allowed to be placed.
        // We are placing at a small buffer away from the surface.
        targetPosition = position + (0.01f * surfaceNormal);

        OrientObject(true, surfaceNormal);

        // If we are managing the collider, disable it. 
        if (managingBoxCollider)
        {
            boxCollider.enabled = false;
        }

        // Tell the gesture manager that it is to resume
        // its normal behavior.
        GestureManager.Instance.OverrideFocusedObject = null;

        // Exit placement mode.
        IsPlacing = false;
    }

    /// <summary>
    /// Positions the object along the surface toward which the user is gazing.
    /// </summary>
    /// <remarks>
    /// If the user's gaze does not intersect with a surface, the object
    /// will remain at the most recently calculated distance.
    /// </remarks>
    private void Move()
    {
        Vector3 moveTo = gameObject.transform.position;
        Vector3 surfaceNormal = Vector3.zero;
        RaycastHit hitInfo;

        bool hit = Physics.Raycast(Camera.main.transform.position,
                                Camera.main.transform.forward,
                                out hitInfo,
                                20f,
                                SpatialMappingManager.Instance.LayerMask);

        if (hit)
        {
            float offsetDistance = hoverDistance;

            // Place the object a small distance away from the surface while keeping 
            // the object from going behind the user.
            if (hitInfo.distance <= hoverDistance)
            {
                offsetDistance = 0f;
            }

            moveTo = hitInfo.point + (offsetDistance * hitInfo.normal);

            lastDistance = hitInfo.distance;
            surfaceNormal = hitInfo.normal;
        }
        else
        {
            // The raycast failed to hit a surface.  In this case, keep the object at the distance of the last
            // intersected surface.
            moveTo = Camera.main.transform.position + (Camera.main.transform.forward * lastDistance);
        }

        // Follow the user's gaze.
        float dist = Mathf.Abs((gameObject.transform.position - moveTo).magnitude);
        gameObject.transform.position = Vector3.Lerp(gameObject.transform.position, moveTo, placementVelocity / dist);

        // Orient the object.
        // We are using the return value from Physics.Raycast to instruct
        // the OrientObject function to align to the vertical surface if appropriate.
        OrientObject(hit, surfaceNormal);
    }

    /// <summary>
    /// Orients the object so that it faces the user.
    /// </summary>
    /// <param name="alignToVerticalSurface">
    /// If true and the object is to be placed on a vertical surface, 
    /// orient parallel to the target surface.  If false, orient the object 
    /// to face the user.
    /// </param>
    /// <param name="surfaceNormal">
    /// The target surface's normal vector.
    /// </param>
    /// <remarks>
    /// The alignToVerticalSurface parameter is ignored if the object
    /// is to be placed on a horizontalSurface
    /// </remarks>
    private void OrientObject(bool alignToVerticalSurface, Vector3 surfaceNormal)
    {
        Quaternion rotation = Camera.main.transform.localRotation;

        // If the user's gaze does not intersect with the Spatial Mapping mesh,
        // orient the object towards the user.
        if (alignToVerticalSurface && (PlacementSurface == PlacementSurfaces.Vertical))
        {
            // We are placing on a vertical surface.
            // If the normal of the Spatial Mapping mesh indicates that the
            // surface is vertical, orient parallel to the surface.
            if (Mathf.Abs(surfaceNormal.y) <= (1 - upNormalThreshold))
            {
                rotation = Quaternion.LookRotation(-surfaceNormal, Vector3.up);
            }
        }
        else
        {
            rotation.x = 0f;
            rotation.z = 0f;
        }

        gameObject.transform.rotation = rotation;
    }

    /// <summary>
    /// Displays the bounds asset.
    /// </summary>
    /// <param name="canBePlaced">
    /// Specifies if the object is in a valid placement location.
    /// </param>
    private void DisplayBounds(bool canBePlaced)
    {
        // Ensure the bounds asset is sized and positioned correctly.
        boundsAsset.transform.localPosition = boxCollider.center;
        boundsAsset.transform.localScale = boxCollider.size;
        boundsAsset.transform.rotation = gameObject.transform.rotation;

        // Apply the appropriate material.
        if (canBePlaced)
        {
            boundsAsset.GetComponent<Renderer>().sharedMaterial = PlaceableBoundsMaterial;
        }
        else
        {
            boundsAsset.GetComponent<Renderer>().sharedMaterial = NotPlaceableBoundsMaterial;
        }

        // Show the bounds asset.
        boundsAsset.SetActive(true);
    }

    /// <summary>
    /// Displays the placement shadow asset.
    /// </summary>
    /// <param name="position">
    /// The position at which to place the shadow asset.
    /// </param>
    /// <param name="surfaceNormal">
    /// The normal of the surface on which the asset will be placed
    /// </param>
    /// <param name="canBePlaced">
    /// Specifies if the object is in a valid placement location.
    /// </param>
    private void DisplayShadow(Vector3 position,
                            Vector3 surfaceNormal,
                            bool canBePlaced)
    {
        // Rotate and scale the shadow so that it is displayed on the correct surface and matches the object.
        float rotationX = 0.0f;

        if (PlacementSurface == PlacementSurfaces.Horizontal)
        {
            rotationX = 90.0f;
            shadowAsset.transform.localScale = new Vector3(boxCollider.size.x, boxCollider.size.z, 1);
        }
        else
        {
            shadowAsset.transform.localScale = boxCollider.size;
        }

        Quaternion rotation = Quaternion.Euler(rotationX, gameObject.transform.rotation.eulerAngles.y, 0);
        shadowAsset.transform.rotation = rotation;

        // Apply the appropriate material.
        if (canBePlaced)
        {
            shadowAsset.GetComponent<Renderer>().sharedMaterial = PlaceableShadowMaterial;
        }
        else
        {
            shadowAsset.GetComponent<Renderer>().sharedMaterial = NotPlaceableShadowMaterial;
        }

        // Show the shadow asset as appropriate.
        if (position != Vector3.zero)
        {
            // Position the shadow a small distance from the target surface, along the normal.
            shadowAsset.transform.position = position + (0.01f * surfaceNormal);
            shadowAsset.SetActive(true);
        }
        else
        {
            shadowAsset.SetActive(false);
        }
    }

    /// <summary>
    /// Determines if two distance values should be considered equivalent. 
    /// </summary>
    /// <param name="d1">
    /// Distance to compare.
    /// </param>
    /// <param name="d2">
    /// Distance to compare.
    /// </param>
    /// <returns>
    /// True if the distances are within the desired tolerance, otherwise false.
    /// </returns>
    private bool IsEquivalentDistance(float d1, float d2)
    {
        float dist = Mathf.Abs(d1 - d2);
        return (dist <= distanceThreshold);
    }

    /// <summary>
    /// Called when the GameObject is unloaded.
    /// </summary>
    private void OnDestroy()
    {
        // Unload objects we have created.
        Destroy(boundsAsset);
        boundsAsset = null;
        Destroy(shadowAsset);
        shadowAsset = null;
    }
}

Bouwen en implementeren

  • Bouw net als voorheen het project en implementeer het op de HoloLens.
  • Wacht totdat het scannen en verwerken van de ruimtelijke toewijzingsgegevens is voltooid.
  • Wanneer u het zonnestelsel ziet, staart u naar het projectievak hieronder en voert u een selectiegebaar uit om het te verplaatsen. Terwijl het projectievak is geselecteerd, wordt er een begrenzingskubus rond het projectievak weergegeven.
  • Verplaats je hoofd om naar een andere locatie in de kamer te staren. Het projectievak moet uw blik volgen. Wanneer de schaduw onder het projectievak rood wordt, kunt u het hologram niet op dat oppervlak plaatsen. Wanneer de schaduw onder het projectievak groen wordt, kunt u het hologram plaatsen door nog een selectiebeweging uit te voeren.
  • Zoek en selecteer een van de holografische posters op een muur om deze naar een nieuwe locatie te verplaatsen. U ziet dat u de poster niet op de vloer of het plafond kunt plaatsen en dat deze correct op elke muur blijft staan terwijl u zich verplaatst.

Hoofdstuk 5 - Occlusie

Doelen

  • Bepaal of een hologram is overgedekt door de mesh van de ruimtelijke toewijzing.
  • Pas verschillende occlusietechnieken toe om een leuk effect te bereiken.

Instructies

Eerst laten we het ruimtelijke toewijzingsnetwerk andere hologrammen occluderen zonder de echte wereld op te nemen:

  • Selecteer in het deelvenster Hierarchy het object SpatialProcessing .
  • Zoek in het deelvenster Inspector het onderdeel Play Space Manager (Script).
  • Klik op de cirkel rechts van de eigenschap Secundair materiaal .
  • Zoek en selecteer het occlusiemateriaal en sluit het venster.

Vervolgens gaan we een speciaal gedrag aan de aarde toevoegen, zodat deze een blauwe markering heeft wanneer deze wordt overschreven door een ander hologram (zoals de zon) of door het ruimtelijke kaartgaas:

  • Vouw in het deelvenster Project in de map Hologrammen het object SolarSystem uit.
  • Klik op Aarde.
  • Zoek in het deelvenster Inspector het materiaal van de aarde (ondercomponent).
  • Wijzig in de vervolgkeuzelijst Shader de arcering in Aangepaste > occlusionRim. Dit geeft een blauwe markering rond de aarde weer wanneer deze wordt overgelicht door een ander object.

Ten slotte gaan we een x-ray vision-effect mogelijk maken voor planeten in ons zonnestelsel. We moeten PlanetOcclusion.cs bewerken (te vinden in de map Scripts\SolarSystem) om het volgende te bereiken:

  1. Bepaal of een planeet is overgedekt door de SpatialMapping-laag (ruimtenetten en vlakken).
  2. De draadmodelweergave van een planeet weergeven wanneer deze wordt afgesloten door de spatialmapping-laag.
  3. Verberg de draadmodelweergave van een planeet wanneer deze niet wordt geblokkeerd door de SpatialMapping-laag.

Volg de codeeroefening in PlanetOcclusion.cs of gebruik de volgende oplossing:

using UnityEngine;
using Academy.HoloToolkit.Unity;

/// <summary>
/// Determines when the occluded version of the planet should be visible.
/// This script allows us to do selective occlusion, so the occlusionObject
/// will only be rendered when a Spatial Mapping surface is occluding the planet,
/// not when another hologram is responsible for the occlusion.
/// </summary>
public class PlanetOcclusion : MonoBehaviour
{
    [Tooltip("Object to display when the planet is occluded.")]
    public GameObject occlusionObject;

    /// <summary>
    /// Points to raycast to when checking for occlusion.
    /// </summary>
    private Vector3[] checkPoints;

    // Use this for initialization
    void Start()
    {
        occlusionObject.SetActive(false);

        // Set the check points to use when testing for occlusion.
        MeshFilter filter = gameObject.GetComponent<MeshFilter>();
        Vector3 extents = filter.mesh.bounds.extents;
        Vector3 center = filter.mesh.bounds.center;
        Vector3 top = new Vector3(center.x, center.y + extents.y, center.z);
        Vector3 left = new Vector3(center.x - extents.x, center.y, center.z);
        Vector3 right = new Vector3(center.x + extents.x, center.y, center.z);
        Vector3 bottom = new Vector3(center.x, center.y - extents.y, center.z);

        checkPoints = new Vector3[] { center, top, left, right, bottom };
    }

    // Update is called once per frame
    void Update()
    {
        /* TODO: 5.a DEVELOPER CODING EXERCISE 5.a */

        // Check to see if any of the planet's boundary points are occluded.
        for (int i = 0; i < checkPoints.Length; i++)
        {
            // 5.a: Convert the current checkPoint to world coordinates.
            // Call gameObject.transform.TransformPoint(checkPoints[i]).
            // Assign the result to a new Vector3 variable called 'checkPt'.
            Vector3 checkPt = gameObject.transform.TransformPoint(checkPoints[i]);

            // 5.a: Call Vector3.Distance() to calculate the distance
            // between the Main Camera's position and 'checkPt'.
            // Assign the result to a new float variable called 'distance'.
            float distance = Vector3.Distance(Camera.main.transform.position, checkPt);

            // 5.a: Take 'checkPt' and subtract the Main Camera's position from it.
            // Assign the result to a new Vector3 variable called 'direction'.
            Vector3 direction = checkPt - Camera.main.transform.position;

            // Used to indicate if the call to Physics.Raycast() was successful.
            bool raycastHit = false;

            // 5.a: Check if the planet is occluded by a spatial mapping surface.
            // Call Physics.Raycast() with the following arguments:
            // - Pass in the Main Camera's position as the origin.
            // - Pass in 'direction' for the direction.
            // - Pass in 'distance' for the maxDistance.
            // - Pass in SpatialMappingManager.Instance.LayerMask as layerMask.
            // Assign the result to 'raycastHit'.
            raycastHit = Physics.Raycast(Camera.main.transform.position, direction, distance, SpatialMappingManager.Instance.LayerMask);

            if (raycastHit)
            {
                // 5.a: Our raycast hit a surface, so the planet is occluded.
                // Set the occlusionObject to active.
                occlusionObject.SetActive(true);

                // At least one point is occluded, so break from the loop.
                break;
            }
            else
            {
                // 5.a: The Raycast did not hit, so the planet is not occluded.
                // Deactivate the occlusionObject.
                occlusionObject.SetActive(false);
            }
        }
    }
}

Bouwen en implementeren

  • Bouw en implementeer de toepassing op HoloLens, zoals gebruikelijk.
  • Wacht totdat het scannen en verwerken van de ruimtelijke toewijzingsgegevens is voltooid (u ziet blauwe lijnen op de wanden).
  • Zoek en selecteer het projectievak van het zonnestelsel en zet het vak vervolgens naast een muur of achter een toonbank.
  • U kunt eenvoudige occlusie bekijken door zich te verbergen achter oppervlakken om te kijken naar de poster of het projectievak.
  • Zoek naar de aarde, er moet een blauw markeringseffect zijn wanneer deze zich achter een ander hologram of oppervlak bevindt.
  • Kijk hoe de planeten achter de muur of andere oppervlakken in de kamer bewegen. U hebt nu X-ray vision en kunt hun draadframeskeletten zien!

Het einde

Gefeliciteerd U hebt nu MR Spatial 230: Ruimtelijke toewijzing voltooid.

  • U weet hoe u uw omgeving scant en ruimtelijke toewijzingsgegevens naar Unity laadt.
  • U begrijpt de basisbeginselen van shaders en hoe materialen kunnen worden gebruikt om de wereld opnieuw te visualiseren.
  • U hebt kennisgemaakt met nieuwe verwerkingstechnieken voor het vinden van vlakken en het verwijderen van driehoeken uit een net.
  • U kon hologrammen verplaatsen en plaatsen op oppervlakken die logisch waren.
  • Je hebt verschillende occlusietechnieken ervaren en de kracht van x-ray vision benut!