Räumliche Abfragen

Räumliche Abfragen sind Vorgänge, in denen Sie den Remote Rendering-Dienst fragen, welche Objekte sich in einem Bereich befinden. Häufig werden räumliche Abfragen verwendet, um Interaktionen zu implementieren, z. B. um herauszufinden, auf welches Objekt ein Benutzer verweist.

Alle räumlichen Abfragen werden auf dem Server ausgewertet. Dementsprechend handelt es sich bei den Abfragen um asynchrone Vorgänge und Ergebnisse mit einer Verzögerung, die von Der Netzwerklatenz abhängt.

Raycasts

Ein Ray cast ist eine räumliche Abfrage, bei der die Laufzeit überprüft, welche Objekte einen Strahl schneiden, beginnend an einer bestimmten Position und in eine bestimmte Richtung zeigen. Als Optimierung wird auch eine maximale Strahlendistanz angegeben, damit nicht nach Objekten gesucht wird, die zu weit entfernt sind. Obwohl hunderte von Ray casts jeden Frame auf der Serverseite rechenfähig sind, generiert jede Abfrage auch Netzwerkdatenverkehr, sodass die Anzahl der Abfragen pro Frame so niedrig wie möglich gehalten werden sollte.

async void CastRay(RenderingSession session)
{
    // trace a line from the origin into the +z direction, over 10 units of distance.
    RayCast rayCast = new RayCast(new Double3(0, 0, 0), new Double3(0, 0, 1), 10);

    // only return the closest hit
    rayCast.HitCollection = HitCollectionPolicy.ClosestHit;

    RayCastQueryResult result = await session.Connection.RayCastQueryAsync(rayCast);
    RayCastHit[] hits = result.Hits;
    if (hits.Length > 0)
    {
        var hitObject = hits[0].HitObject;
        var hitPosition = hits[0].HitPosition;
        var hitNormal = hits[0].HitNormal;
        var hitType = hits[0].HitType;
        // do something with the hit information
    }
}
void CastRay(ApiHandle<RenderingSession> session)
{
    // trace a line from the origin into the +z direction, over 10 units of distance.
    RayCast rayCast;
    rayCast.StartPos = {0, 0, 0};
    rayCast.EndPos = {0, 0, 10};

    // only return the closest hit
    rayCast.HitCollection = HitCollectionPolicy::ClosestHit;

    session->Connection()->RayCastQueryAsync(rayCast, [](Status status, ApiHandle<RayCastQueryResult> result)
    {
        if (status == Status::OK)
        {
            std::vector<RayCastHit> hits;
            result->GetHits(hits);

            if (hits.size() > 0)
            {
                auto hitObject = hits[0].HitObject;
                auto hitPosition = hits[0].HitPosition;
                auto hitNormal = hits[0].HitNormal;
                auto hitType = hits[0].HitType;

                // do something with the hit information
            }
        }
    });
}

Es gibt drei Treffererfassungsmodi:

  • Closest: In diesem Modus wird nur der nächstgelegene Treffer gemeldet.
  • Any: Bevorzugen Sie diesen Modus, wenn Alles, was Sie wissen möchten, ist , ob ein Ray etwas treffen würde, aber achten Sie nicht darauf, was genau getroffen wurde. Diese Abfrage kann erheblich einfacher ausgewertet werden, hat aber auch nur wenige Anwendungsfälle.
  • All: In diesem Modus werden alle Treffer entlang des Ray gemeldet, sortiert nach Entfernung. Verwenden Sie diesen Modus nur, wenn Sie wirklich mehr als den ersten Treffer benötigen. Begrenzen Sie die Anzahl der gemeldeten Treffer mit der Option MaxHits.

Mit der Komponente HierarchicalStateOverrideComponent können Sie Objekte selektiv aus der Berücksichtigung in Raycasts ausschließen.

Trefferergebnis

Das Ergebnis einer Raycastabfrage ist ein Array von Treffern. Das Array ist leer, wenn kein Objekt gefunden wurde.

Ein Treffer weist folgende Eigenschaften auf:

  • HitEntity: Welche Entität getroffen wurde.
  • SubPartId: Welcher Untermesh in einem MeshComponent getroffen wurde. Kann für eine Indizierung in MeshComponent.UsedMaterials und zum Suchen des Materials an diesem Punkt verwendet werden.
  • HitPosition: Die Weltraumposition, an der der Strahl das Objekt überschneiden.
  • HitNormal: Die Weltraumoberfläche normal des Gitters an der Position der Schnittmenge.
  • DistanceToHit: Der Abstand von der Strahlenstartposition zum Treffer.
  • HitType: Was vom Strahl getroffen wird: TriangleFrontFace, TriangleBackFace oder Point. Standardmäßig rendert ARR doppelseitig, sodass die dreieckigen Dreiecke, die der Benutzer sieht, nicht unbedingt nach vorne gerichtet sind. Wenn Sie in Ihrem Code zwischen TriangleFrontFace und TriangleBackFace unterscheiden möchten, stellen Sie zuerst sicher, dass Ihre Modelle mit einer korrekten Oberflächenausrichtung erstellt werden.

Räumliche Abfragen

Mit einer räumlichen Abfrage kann die Laufzeit überprüfen, welche MeshComponents mit einem benutzerdefinierten Volume überschneiden. Diese Prüfung wird ausgeführt, da die einzelne Prüfung basierend auf den Grenzen der einzelnen Gitterteils in der Szene und nicht auf einer einzelnen Dreiecksbasis durchgeführt wird. Als Optimierung kann eine maximale Anzahl getroffener Gittermodellkomponenten angegeben werden.
Während eine solche Abfrage manuell auf der Clientseite ausgeführt werden kann, kann es für große Szenen schneller sein, damit der Server dies berechnen kann.

Im folgenden Beispielcode wird gezeigt, wie Abfragen für ein achsenbündiges Begrenzungsfeld (AABB) ausgeführt werden. Varianten der Abfrage ermöglichen auch orientierte Begrenzungsfeldvolumen (SpatialQueryObbAsync) und Kugelvolumen (SpatialQuerySphereAsync).

async void QueryAABB(RenderingSession session)
{
    // Query all mesh components in a 2x2x2m cube.
    SpatialQueryAabb query = new SpatialQueryAabb();
    query.Bounds = new Microsoft.Azure.RemoteRendering.Bounds(new Double3(-1, -1, -1), new Double3(1, 1, 1));
    query.MaxResults = 100;

    SpatialQueryResult result = await session.Connection.SpatialQueryAabbAsync(query);
    foreach (MeshComponent meshComponent in result.Overlaps)
    {
        Entity owner = meshComponent.Owner;
        // do something with the hit MeshComponent / Entity
    }
}
void QueryAABB(ApiHandle<RenderingSession> session)
{
    // Query all mesh components in a 2x2x2m cube.
    SpatialQueryAabb query;
    query.Bounds.Min = {-1, -1, -1};
    query.Bounds.Max = {1, 1, 1};
    query.MaxResults = 100;

    session->Connection()->SpatialQueryAabbAsync(query, [](Status status, ApiHandle<SpatialQueryResult> result)
        {
            if (status == Status::OK)
            {
                std::vector<ApiHandle<MeshComponent>> overlaps;
                result->GetOverlaps(overlaps);

                for (ApiHandle<MeshComponent> meshComponent : overlaps)
                {
                    ApiHandle<Entity> owner = meshComponent->GetOwner();
                    // do something with the hit MeshComponent / Entity
                }
            }
        });
}

API-Dokumentation

Nächste Schritte