Zapytania przestrzenne

Zapytania przestrzenne to operacje, za pomocą których można zapytać usługę zdalnego renderowania, które obiekty znajdują się w danym obszarze. Zapytania przestrzenne są często używane do implementowania interakcji, takich jak ustalenie, który obiekt wskazuje użytkownik.

Wszystkie zapytania przestrzenne są oceniane na serwerze. W związku z tym zapytania są operacjami asynchronicznymi i wyniki docierają z opóźnieniem, które zależy od opóźnienia sieci.

Rzuty promieniowe

Rzutowanie promieni jest zapytaniem przestrzennym, w którym środowisko uruchomieniowe sprawdza, które obiekty przecinają promienie, zaczynając od danej pozycji i wskazując na określony kierunek. W ramach optymalizacji podana jest również maksymalna odległość promienia, aby nie wyszukiwać obiektów, które są zbyt daleko. Mimo że wykonywanie setek rzutów promieni jest wykonalne obliczenia po stronie serwera, każde zapytanie generuje również ruch sieciowy, więc liczba zapytań na ramkę powinna być tak niska, jak to możliwe.

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

Istnieją trzy tryby kolekcji trafień:

  • Closest: W tym trybie zgłaszany jest tylko najbliższy trafienie.
  • Any: Preferuj ten tryb, gdy wszystko, co chcesz wiedzieć, czy ray uderzy wszystko, ale nie obchodzi, co zostało trafione dokładnie. To zapytanie może być znacznie tańsze do oceny, ale ma również tylko kilka aplikacji.
  • All: W tym trybie są zgłaszane wszystkie trafienia wzdłuż promienia posortowane według odległości. Nie używaj tego trybu, chyba że naprawdę potrzebujesz więcej niż pierwsze trafienie. Ogranicz liczbę zgłoszonych trafień za pomocą MaxHits opcji .

Aby wykluczyć obiekty selektywnie z rozważanych rzutów promieni, można użyć składnika HierarchicalStateOverrideComponent .

Wynik trafienia

Wynikiem zapytania rzutowania promienia jest tablica trafień. Tablica jest pusta, jeśli żaden obiekt nie został trafiony.

Trafienie ma następujące właściwości:

  • HitEntity: która jednostka została osiągnięta.
  • SubPartId: Który submesh został trafiony w MeshComponent. Może służyć do indeksowania i MeshComponent.UsedMaterials wyszukiwania materiału w tym momencie.
  • HitPosition: położenie przestrzeni świata, w której promienie przecinały obiekt.
  • HitNormal: Powierzchnia przestrzeń kosmiczna na świecie normalna dla siatki na pozycji skrzyżowania.
  • DistanceToHit: Odległość od pozycji początkowej promienia do trafienia.
  • HitType: Co jest uderzone przez ray: TriangleFrontFace, lub PointTriangleBackFace . Domyślnie funkcja ARR renderuje się dwukrotnie, więc trójkąty widoczne przez użytkownika nie muszą być skierowane do przodu. Jeśli chcesz odróżnić modele TriangleFrontFace i TriangleBackFace w kodzie, najpierw upewnij się, że modele są tworzone z poprawnymi kierunkami twarzy.

Zapytania przestrzenne

Zapytanie przestrzenne umożliwia środowisku uruchomieniowemu sprawdzenie, które komponenty mesh przecinają się z woluminem zdefiniowanym przez użytkownika. Ta kontrola jest wykonywana w miarę wykonywania indywidualnego sprawdzania na podstawie granic każdej części siatki w scenie, a nie na poszczególnych trójkątach. W ramach optymalizacji można zapewnić maksymalną liczbę składników siatki trafień.
Chociaż takie zapytanie można uruchamiać ręcznie po stronie klienta, w przypadku dużych scen może to być o wiele większe szybciej, aby serwer mógł go obliczyć.

Poniższy przykładowy kod pokazuje, jak wykonywać zapytania względem wyrównanego pola ograniczenia osi (AABB). Warianty zapytania umożliwiają również zorientowane woluminy pola ograniczenia (SpatialQueryObbAsync) i woluminy sfery (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
                }
            }
        });
}

Dokumentacja interfejsu API

Następne kroki